Last active
March 17, 2021 07:19
-
-
Save daiplusplus/1a4fcd2e70374d3694c3a105061a6d1c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This file compares a variety of techniques to get a SUM of a list of integers. | |
// For https://stackoverflow.com/questions/66643270/why-does-list-sum-perform-poorly-as-compared-to-a-foreach#comment117808641_66643270 | |
// I get these results with this environment: | |
// CPU: i7-770HQ (Dell XPS laptop) | |
// OS: Windows 10 x64 | |
// .NET Core 5 | |
// Release build, AnyCPU | |
/* | |
Test_Sum_Delegate : 118ms | |
Test_MySum_DirectFunc_IEnum : 112ms | |
Test_MySum_IndirectFunc_IEnum: 114ms | |
Test_MySum_DirectCall_IEnum : 89ms | |
Test_MySum_DirectFunc_List : 58ms | |
Test_MySum_IndirectFunc_List : 58ms | |
Test_MySum_DirectCall_List : 37ms | |
Test_Sum_DelegateLambda : 109ms | |
Test_For_Inline : 4ms | |
Test_For_Delegate : 3ms | |
Test_ForUnrolled_Inline : 4ms | |
Test_ForUnrolled_Delegate : 4ms | |
Test_ForEach_Inline : 38ms | |
Test_ForEach_Delegate : 37ms | |
*/ | |
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Runtime.CompilerServices; | |
namespace ConsoleApp1 | |
{ | |
public class Program | |
{ | |
public static void Main() | |
{ | |
// const Int64 COUNT = 1000000000; | |
const Int64 COUNT = 10000000; | |
List<Int64> numbers = new List<Int64>(); | |
for( Int64 i = 0; i < COUNT; i++ ) | |
{ | |
numbers.Add( i ); | |
} | |
// | |
Run( numbers ); | |
Console.WriteLine( "---------------" ); | |
Run( numbers ); | |
} | |
private static void Run( List<Int64> numbers ) | |
{ | |
// Test_Sum( numbers ); | |
// Console.WriteLine(); | |
Test_Sum_Delegate( numbers ); | |
Console.WriteLine(); | |
Test_MySum_DirectFunc_IEnum( numbers ); | |
Console.WriteLine(); | |
Test_MySum_IndirectFunc_IEnum( numbers ); | |
Console.WriteLine(); | |
Test_MySum_DirectCall_IEnum( numbers ); | |
Console.WriteLine(); | |
Test_MySum_DirectFunc_List( numbers ); | |
Console.WriteLine(); | |
Test_MySum_IndirectFunc_List( numbers ); | |
Console.WriteLine(); | |
Test_MySum_DirectCall_List( numbers ); | |
Console.WriteLine(); | |
Test_Sum_DelegateLambda( numbers ); | |
Console.WriteLine(); | |
Test_For_Inline( numbers ); | |
Console.WriteLine(); | |
Test_For_Delegate( numbers ); | |
Console.WriteLine(); | |
Test_ForUnrolled_Inline( numbers ); | |
Console.WriteLine(); | |
Test_ForUnrolled_Delegate( numbers ); | |
Console.WriteLine(); | |
Test_ForEach_Inline( numbers ); | |
Console.WriteLine(); | |
Test_ForEach_Delegate( numbers ); | |
Console.WriteLine(); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static Int64 GetValue(Int64 v) | |
{ | |
return v + 1; | |
} | |
public static void Test_Sum( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = list.Sum(); | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_Sum) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_MySum_DirectFunc_IEnum( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = list.MySum_DirectFunc_IEnum(); | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_MySum_DirectFunc_IEnum) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_MySum_IndirectFunc_IEnum( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = list.MySum_IndirectFunc_IEnum(GetValue); | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_MySum_IndirectFunc_IEnum) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_MySum_DirectCall_IEnum( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = list.MySum_DirectCall_IEnum(); | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_MySum_DirectCall_IEnum) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_MySum_DirectFunc_List( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = list.MySum_DirectFunc_List(); | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_MySum_DirectFunc_List) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_MySum_IndirectFunc_List( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = list.MySum_IndirectFunc_List(GetValue); | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_MySum_IndirectFunc_List) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_MySum_DirectCall_List( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = list.MySum_DirectCall_List(); | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_MySum_DirectCall_List) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_Sum_Delegate( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = list.Sum(GetValue); | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_Sum_Delegate) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_Sum_DelegateLambda( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = list.Sum( n => (Int64)n + 1 ); | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_Sum_DelegateLambda) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
#region For | |
public static void Test_For_Inline( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = 0; | |
for( Int64 i = 0; i < list.Count; i++ ) | |
{ | |
totalCount = totalCount + 1; | |
} | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_For_Inline) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_For_Delegate( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = 0; | |
for( Int64 i = 0; i < list.Count; i++ ) | |
{ | |
totalCount = GetValue( totalCount ); | |
} | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_For_Delegate) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
#endregion | |
#region For Unrolled | |
public static void Test_ForUnrolled_Inline( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = 0; | |
Int64 i = 0; | |
Int64 max = list.Count - ( list.Count % 8 ); | |
for( ; i < max; i += 8 ) | |
{ | |
totalCount = totalCount + 1; | |
totalCount = totalCount + 1; | |
totalCount = totalCount + 1; | |
totalCount = totalCount + 1; | |
totalCount = totalCount + 1; | |
totalCount = totalCount + 1; | |
totalCount = totalCount + 1; | |
totalCount = totalCount + 1; | |
totalCount = totalCount + 1; | |
} | |
for( ; i < list.Count; i++ ) | |
{ | |
totalCount = checked(totalCount + 1); | |
} | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_ForUnrolled_Inline) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_ForUnrolled_Delegate( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = 0; | |
Int64 i = 0; | |
Int64 max = list.Count - ( list.Count % 8 ); | |
for( ; i < max; i += 8 ) | |
{ | |
totalCount = GetValue( totalCount ); | |
totalCount = GetValue( totalCount ); | |
totalCount = GetValue( totalCount ); | |
totalCount = GetValue( totalCount ); | |
totalCount = GetValue( totalCount ); | |
totalCount = GetValue( totalCount ); | |
totalCount = GetValue( totalCount ); | |
totalCount = GetValue( totalCount ); | |
totalCount = GetValue( totalCount ); | |
} | |
for( ; i < list.Count; i++ ) | |
{ | |
totalCount = GetValue( totalCount ); | |
} | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_ForUnrolled_Delegate) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
#endregion | |
#region ForEach | |
public static void Test_ForEach_Inline( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = 0; | |
foreach( var num in list ) | |
{ | |
totalCount = totalCount + 1; | |
} | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_ForEach_Inline) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
public static void Test_ForEach_Delegate( List<Int64> list ) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
long totalCount = 0; | |
foreach( var num in list ) | |
{ | |
totalCount = GetValue( totalCount ); | |
} | |
sw.Stop(); | |
Console.WriteLine( nameof(Test_ForEach_Delegate) + ": {0:N0}ms", sw.ElapsedMilliseconds ); | |
} | |
#endregion | |
} | |
public static class MyExtensions | |
{ | |
#region IEnumerable | |
public static long MySum_IndirectFunc_IEnum<TSource>(this IEnumerable<TSource> source, Func<TSource, Int64> selector) | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
if (selector == null) throw new ArgumentNullException(nameof(selector)); | |
long num = 0; | |
foreach (TSource item in source) | |
{ | |
num = checked(num + selector(item)); | |
} | |
return num; | |
} | |
public static long MySum_DirectFunc_IEnum(this IEnumerable<Int64> source) | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
Func<Int64,Int64> func = Program.GetValue; | |
long num = 0; | |
foreach (Int64 item in source) | |
{ | |
num = num + func(item); | |
} | |
return num; | |
} | |
public static long MySum_DirectCall_IEnum(this IEnumerable<Int64> source) | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
long num = 0; | |
foreach (Int64 item in source) | |
{ | |
num = num + Program.GetValue(item); | |
} | |
return num; | |
} | |
#endregion | |
#region List | |
public static long MySum_IndirectFunc_List<TSource>(this List<TSource> source, Func<TSource, Int64> selector) | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
if (selector == null) throw new ArgumentNullException(nameof(selector)); | |
long num = 0; | |
foreach (TSource item in source) | |
{ | |
num = checked(num + selector(item)); | |
} | |
return num; | |
} | |
public static long MySum_DirectFunc_List(this List<Int64> source) | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
Func<Int64,Int64> func = Program.GetValue; | |
long num = 0; | |
foreach (Int64 item in source) | |
{ | |
num = num + func(item); | |
} | |
return num; | |
} | |
public static long MySum_DirectCall_List(this List<Int64> source) | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
long num = 0; | |
foreach (Int64 item in source) | |
{ | |
num = num + Program.GetValue(item); | |
} | |
return num; | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment