TheDeveloperBlog.com

Home | Contact Us

C-Sharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript | SQL | PHP | Angular | HTML

C# Tuple Versus KeyValuePair

This C# article compares the performance of Tuple and KeyValuePair.

Tuple vs. KeyValuePair.

Tuple and KeyValuePair have different performance. You wonder which is more efficient: the Tuple type with two items, or the KeyValuePair type with those same two items. We benchmark these two collections in a variety of program contexts.

KeyValuePair

Tuple/KeyValuePair performance results

	   Tuple<string, string>
	   KeyValuePair<string, string>

8.23 ns -- Allocate Tuple
0.32 ns -- Allocate KeyValuePair

1.93 ns -- Pass Tuple as argument
2.57 ns -- Pass KeyValuePair as argument

1.91 ns -- Return Tuple
6.09 ns -- Return KeyValuePair

2.79 ns -- Load Tuple from List
4.18 ns -- Load KeyValuePair from List

Benchmark. We see four separate tests. The tests use different numbers of iterations to test the types, but the average time in nanoseconds for each operation is computed. Let's look at each test with a short description.

Allocation: In this test, one instance of a Tuple and a KeyValuePair is allocated. The new-keyword is used.

Argument: Here, one instance is passed as an argument to another (not inlined) method.

Return: In this test, one instance is passed as an argument to a method and then returned.

Load: In this method, one instance is loaded from a reference stored in a List collection.

C# program that benchmarks Tuple/KeyValuePair

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

class Program
{
    static void Main()
    {
	Allocation();
	Argument();
	Return();
	Load();
	Console.Read();
    }

    static void Allocation()
    {
	const int max = 1000000;
	var a = new Tuple<string, string>("", "");
	var b = new KeyValuePair<string, string>("", "");

	var s1 = Stopwatch.StartNew();
	for (int i = 0; i < max; i++)
	{
	    var tuple = new Tuple<string, string>("cat", "dog");
	}
	s1.Stop();
	var s2 = Stopwatch.StartNew();
	for (int i = 0; i < max; i++)
	{
	    var pair = new KeyValuePair<string, string>("cat", "dog");
	}
	s2.Stop();
	Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) /
	    max).ToString("0.00 ns"));
	Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) /
	    max).ToString("0.00 ns"));
	Console.WriteLine();
    }

    static void Argument()
    {
	const int max = 10000000;
	var a = new Tuple<string, string>("", "");
	var b = new KeyValuePair<string, string>("", "");
	X(a);
	X(b);

	var s1 = Stopwatch.StartNew();
	for (int i = 0; i < max; i++)
	{
	    X(a);
	}
	s1.Stop();
	var s2 = Stopwatch.StartNew();
	for (int i = 0; i < max; i++)
	{
	    X(b);
	}
	s2.Stop();
	Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) /
	    max).ToString("0.00 ns"));
	Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) /
	    max).ToString("0.00 ns"));
	Console.WriteLine();
    }

    static void Return()
    {
	const int max = 10000000;
	var a = new Tuple<string, string>("", "");
	var b = new KeyValuePair<string, string>("", "");
	Y(a);
	Y(b);

	var s1 = Stopwatch.StartNew();
	for (int i = 0; i < max; i++)
	{
	    Y(a);
	}
	s1.Stop();
	var s2 = Stopwatch.StartNew();
	for (int i = 0; i < max; i++)
	{
	    Y(b);
	}
	s2.Stop();
	Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) /
	    max).ToString("0.00 ns"));
	Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) /
	    max).ToString("0.00 ns"));
	Console.WriteLine();
    }

    static void Load()
    {
	const int max = 10000000;
	var a = new Tuple<string, string>("cat", "dog");
	var b = new KeyValuePair<string, string>("cat", "dog");
	List<Tuple<string, string>> list1 = new List<Tuple<string, string>>();
	list1.Add(a);
	Z(list1);

	List<KeyValuePair<string, string>> list2 = new List<KeyValuePair<string, string>>();
	list2.Add(b);
	Z(list2);

	var s1 = Stopwatch.StartNew();
	for (int i = 0; i < max; i++)
	{
	    Z(list1);
	}
	s1.Stop();
	var s2 = Stopwatch.StartNew();
	for (int i = 0; i < max; i++)
	{
	    Z(list2);
	}
	s2.Stop();
	Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) /
	    max).ToString("0.00 ns"));
	Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) /
	    max).ToString("0.00 ns"));
	Console.WriteLine();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void X(Tuple<string, string> a)
    {
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void X(KeyValuePair<string, string> a)
    {
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static Tuple<string, string> Y(Tuple<string, string> a)
    {
	return a;
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static KeyValuePair<string, string> Y(KeyValuePair<string, string> a)
    {
	return a;
    }

    static char Z(List<Tuple<string, string>> list)
    {
	return list[0].Item1[0];
    }

    static char Z(List<KeyValuePair<string, string>> list)
    {
	return list[0].Key[0];
    }
}

Output

8.23 ns
0.32 ns

1.93 ns
2.57 ns

1.91 ns
6.09 ns

2.79 ns
4.18 ns

Discussion. Tuple was faster in every test than KeyValuePair except in allocation performance. Therefore, if your program does any work beyond allocating the collections, it is a better idea to use Tuple instead of KeyValuePair.

And: The nanoseconds lost from allocating a Tuple could be quickly made up after a few function calls with a Tuple argument.

Using structs (such as KeyValuePair) is usually a bad idea. While structs are faster to allocate, they slow down many other important parts of your program. With structs, simple method calls suddenly become many times slower.

And: This is because they are value types and must be copied on every invocation.

Struct Versus ClassValueType Examples: Int, DateTime

Summary. We compared the Tuple type to the KeyValuePair type in a collection that has two elements. The KeyValuePair was faster to allocate. But the Tuple was faster in every other test. This is an important performance result.

Therefore: I recommend avoiding KeyValuePair entirely and using Tuple or custom classes (which would be similar to Tuple in performance).


Related Links

Adjectives Ado Ai Android Angular Antonyms Apache Articles Asp Autocad Automata Aws Azure Basic Binary Bitcoin Blockchain C Cassandra Change Coa Computer Control Cpp Create Creating C-Sharp Cyber Daa Data Dbms Deletion Devops Difference Discrete Es6 Ethical Examples Features Firebase Flutter Fs Git Go Hbase History Hive Hiveql How Html Idioms Insertion Installing Ios Java Joomla Js Kafka Kali Laravel Logical Machine Matlab Matrix Mongodb Mysql One Opencv Oracle Ordering Os Pandas Php Pig Pl Postgresql Powershell Prepositions Program Python React Ruby Scala Selecting Selenium Sentence Seo Sharepoint Software Spellings Spotting Spring Sql Sqlite Sqoop Svn Swift Synonyms Talend Testng Types Uml Unity Vbnet Verbal Webdriver What Wpf