[Update 2014-09-13]
As the question on SO has evolved I have now included poth PInvoke and Memset methods into my tests. The most interesting observation is that the Memset method performs excellent on 64bit, but poorly on 32bit. If you are compiling for 32bit, go with unsafe or PIvoke, if you are running 64bit, Memset delegate is the way to go.
Iterations: 1000
Enumerable: 00:00:12.3817249
Parallel Enumerable: 00:00:17.6506353
For loop: 00:00:01.7095341
Parallel for loop: 00:00:06.9824434
Unsafe: 00:00:00.7028914
Here are the results running on Windows 8.1, 64bit i5 processor (Lenovo T420s) with .Net 4.5.1.
Array Length: 1048576
32bit execution - 5000 iterations
EnumerableFill: 00:00:50.1071043
ParallelEnumerableFill: 00:01:12.2980480
ForLoopFill: 00:00:05.3504656
ParallellForLoopFill: 00:00:45.5518340
UnsafeFill: 00:00:02.2804084
MemsetFill: 00:00:03.9383964
PInvokeFill: 00:00:02.4391258
32bit execution - 10000 iterations
UnsafeFill: 00:00:04.1653674
MemsetFill: 00:00:07.2561020
PInvokeFill: 00:00:04.2709875
64bit execution - 10000 iterations
UnsafeFill: 00:00:03.9618905
MemsetFill: 00:00:03.5594970
PInvokeFill: 00:00:03.8012791
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | using System; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; using System.Threading.Tasks; namespace FillArrayBenchmark { internal class Program { private static readonly Action<intptr byte = "" int = "" > MemsetDelegate; private static int _arrLength = 1048576; static Program() { var dynamicMethod = new DynamicMethod( "Memset" , MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, null , new [] { typeof (IntPtr), typeof ( byte ), typeof ( int )}, typeof (Util), true ); ILGenerator generator = dynamicMethod.GetILGenerator(); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldarg_1); generator.Emit(OpCodes.Ldarg_2); generator.Emit(OpCodes.Initblk); generator.Emit(OpCodes.Ret); MemsetDelegate = (Action<intptr byte = "" int = "" >) dynamicMethod.CreateDelegate( typeof (Action<intptr byte = "" int = "" >)); } private static void Main( string [] args) { EnumerableFill(12); ParallelEnumerableFill(12); ForLoopFill(12); ParallellForLoopFill(12); UnsafeFill(12); MemsetFill(12); PInvokeFill(12); int iteration = 10000; Stopwatch sw; byte b = 129; sw = Stopwatch.StartNew(); for ( int i = 0; i < iteration; i++) { EnumerableFill(b); } sw.Stop(); Console.WriteLine( "EnumerableFill: " + sw.Elapsed); sw = Stopwatch.StartNew(); for ( int i = 0; i < iteration; i++) { ParallelEnumerableFill(b); } sw.Stop(); Console.WriteLine( "ParallelEnumerableFill: " + sw.Elapsed); sw = Stopwatch.StartNew(); for ( int i = 0; i < iteration; i++) { ForLoopFill(b); } sw.Stop(); Console.WriteLine( "ForLoopFill: " + sw.Elapsed); sw = Stopwatch.StartNew(); for ( int i = 0; i < iteration; i++) { ParallellForLoopFill(b); } sw.Stop(); Console.WriteLine( "ParallellForLoopFill: " + sw.Elapsed); sw = Stopwatch.StartNew(); for ( int i = 0; i < iteration; i++) { UnsafeFill(b); } sw.Stop(); Console.WriteLine( "UnsafeFill: " + sw.Elapsed); sw = Stopwatch.StartNew(); for ( int i = 0; i < iteration; i++) { MemsetFill(b); } sw.Stop(); Console.WriteLine( "MemsetFill: " + sw.Elapsed); sw = Stopwatch.StartNew(); for ( int i = 0; i < iteration; i++) { PInvokeFill(b); } sw.Stop(); Console.WriteLine( "PInvokeFill: " + sw.Elapsed); } private static void EnumerableFill( byte value) { byte [] a = Enumerable.Repeat(value, _arrLength).ToArray(); } private static void ParallelEnumerableFill( byte value) { byte [] a = ParallelEnumerable.Repeat(value, _arrLength).ToArray(); } private static byte [] ForLoopFill( byte value) { var a = new byte [_arrLength]; for ( int i = 0; i < _arrLength; i++) { a[i] = value; } return a; } private static byte [] ParallellForLoopFill( byte value) { var a = new byte [_arrLength]; Parallel.For(0, _arrLength, i => { a[i] = value; }); return a; } private static unsafe byte [] UnsafeFill( byte value) { Int64 fillValue = BitConverter.ToInt64( new [] {value, value, value, value, value, value, value, value}, 0); var a = new byte [_arrLength]; Int64* src = &fillValue; fixed ( byte * ptr = &a[0]) { var dest = (Int64*) ptr; int length = _arrLength; while (length >= 8) { *dest = *src; dest++; length -= 8; } var bDest = ( byte *) dest; for ( byte i = 0; i < length; i++) { *bDest = value; bDest++; } } return a; } public static byte [] MemsetFill( byte value) { var a = new byte [_arrLength]; GCHandle gcHandle = GCHandle.Alloc(a, GCHandleType.Pinned); MemsetDelegate(gcHandle.AddrOfPinnedObject(), value, _arrLength); gcHandle.Free(); return a; } private static byte [] PInvokeFill( byte value) { var arr = new byte [_arrLength]; GCHandle gch = GCHandle.Alloc(arr, GCHandleType.Pinned); MemSet(gch.AddrOfPinnedObject(), value, _arrLength); gch.Free(); return arr; } [DllImport( "msvcrt.dll" , EntryPoint = "memset" , CallingConvention = CallingConvention.Cdecl, SetLastError = false )] public static extern IntPtr MemSet(IntPtr dest, int value, int count); } public static class Util { private static readonly Action<intptr byte = "" int = "" > MemsetDelegate; static Util() { var dynamicMethod = new DynamicMethod( "Memset" , MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, null , new [] { typeof (IntPtr), typeof ( byte ), typeof ( int )}, typeof (Util), true ); ILGenerator generator = dynamicMethod.GetILGenerator(); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldarg_1); generator.Emit(OpCodes.Ldarg_2); generator.Emit(OpCodes.Initblk); generator.Emit(OpCodes.Ret); MemsetDelegate = (Action<intptr byte = "" int = "" >) dynamicMethod.CreateDelegate( typeof (Action<intptr byte = "" int = "" >)); } public static void Memset( byte [] array, byte what, int length) { GCHandle gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned); MemsetDelegate(gcHandle.AddrOfPinnedObject(), what, length); gcHandle.Free(); } } } </intptr></intptr></intptr></intptr></intptr></intptr> |