Skip to content

Instantly share code, notes, and snippets.

@daiplusplus
Last active March 17, 2021 07:19
Show Gist options
  • Save daiplusplus/1a4fcd2e70374d3694c3a105061a6d1c to your computer and use it in GitHub Desktop.
Save daiplusplus/1a4fcd2e70374d3694c3a105061a6d1c to your computer and use it in GitHub Desktop.
// 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