ForNext, For, Delegate, Lamba Performance for x86 and x64
I have often changed fornext statements to lambda notation when prompted by code analysis tools. Every time that I did the change, I asked myself “I need to verify that this is the correct choice in terms of performance”. I assumed that it is because Microsoft had been trying to improve performance (which it did with XDocument being twice as fast as XmlDocument, which I documented in an earlier blog). I created a simple collection of different ways of walking an enumeration, with some very interesting results.
Compiled for x86 explicitly
Method | Best Time | x86 | DATA | |||
No Op | 1.95 | 0 | 0 | 0 | 0 | |
Ienumerate: foreach/var | 16.6 | 16.6 | 17.58 | 19.53 | 18.55 | 16.6 |
Ienumerate: foreach/typed | 16.6 | 17.6 | 18.55 | 18.55 | 16.6 | 16.6 |
Array: for i | 7.81 | 7.81 | 8.79 | 7.81 | 8.79 | 7.81 |
Array: foreach/var | 7.81 | 7.81 | 8.79 | 7.81 | 7.81 | 7.81 |
Array: Array.ForEach (delegate) | 6.84 | 7.81 | 7.81 | 6.84 | 8.79 | 7.81 |
Array: Array.ForEach (lamda) | 6.84 | 7.81 | 11.72 | 6.84 | 7.81 | 7.81 |
I then compiled for x64 explicitly and had a shock…
Method | Best Time | x64 | DATA | |||
No Op | 1.95 | 0 | 0 | 0 | 0 | |
Ienumerate: foreach/var | 19.53 | 45.9 | 22.46 | 19.53 | 20.51 | 20.51 |
Ienumerate: foreach/typed | 18.55 | 21.5 | 19.53 | 18.55 | 20.51 | 21.48 |
Array: for i | 10.74 | 12.7 | 10.74 | 12.7 | 12.7 | 13.67 |
Array: foreach/var | 9.77 | 11.7 | 9.77 | 10.74 | 11.72 | 14.64 |
Array: Array.ForEach (delegate) | 8.79 | 9.77 | 8.79 | 8.79 | 8.79 | 11.72 |
Array: Array.ForEach (lamda) | 7.81 | 9.77 | 7.81 | 8.79 | 9.77 | 9.77 |
x64 was not faster, but slower than x86. This is different then with C++ and suggests that the CLR 64 bit implementation has some significant short coming still.
So what is the bottom line:
- if practical use arrays and not iEnumerable (collections, lists etc)
- use Array.ForEach, lamda notation appears to match the best performance.
I confirmed my assumption that using Lambda notation was the right choice; but blew the assumption that x64 performed better than x86 out of the water. In short, compiling for x64 seems to be a political promise that is a political promise……
The code for folks that wish to replicate my experiment. I loaded a large Xml file to provide a big enumeration.
if (xmlFile.Exists) { XDocument doc = XDocument.Load(xmlFile.FullName); IEnumerable<XElement> enumerable = doc.Descendants(); XElement[] asList = enumerable.ToArray(); for (int repeat = 0; repeat < 5; repeat++) for (int pattern = 0; pattern < 7; pattern++) { int cnt = 0; DateTime start = DateTime.Now; switch (pattern) { case 1: foreach (var item in enumerable) { if (item.Name.LocalName.Length > 0) cnt++; } break; case 2: foreach (XElement item in enumerable) { if (item.Name.LocalName.Length > 0) cnt++; } break; case 3: for (int i = 0; i < asList.Length; i++) { if (asList[i].Name.LocalName.Length > 0) cnt++; } break; case 4: foreach (XElement item in asList) { if (item.Name.LocalName.Length > 0) cnt++; } break; case 5: Array.ForEach(asList, delegate(XElement item) { if (item.Name.LocalName.Length > 0) cnt++; }); break; case 6: Array.ForEach(asList, item => { if (item.Name.LocalName.Length > 0) cnt++; }); break; default: break; } DateTime end = DateTime.Now; TimeSpan elapse = end - start; Console.WriteLine(String.Format("{0} msec for {1} elements. Pattern:{2}", elapse.TotalMilliseconds, cnt, pattern)); } }
Comments
Post a Comment