Usually I don't start using pointers for a few microseconds here and there.
But with this implementation I started getting results of factor 2.
using System; class BenchmarkPointers { public static void Main() { #if ALLOW_UNSAFE Console.WriteLine("This is safe code"); #else Console.WriteLine("This is unsafe code"); #endif int bs = 0x100000; byte[] buffer = new byte[bs]; Random rnd = new Random(DateTime.Now.GetHashCode()); long total = 0; long avg = 0; int max = 100; DateTime start = DateTime.Now; for(int i = 0; i <= max; i++) { start = DateTime.Now; RandomizeBuffer(ref buffer, bs, rnd); TimeSpan ts = DateTime.Now - start; total += (long)ts.TotalMilliseconds; Console.WriteLine("Pass {0} took {1} ms", i, ts.TotalMilliseconds); } avg = total / max; Console.WriteLine("Avarage time for one pass was: {0}ms\nTotal time over {1} passes was {2}ms", avg, max, total); } public static #if ALLOW_UNSAFE unsafe #endif void RandomizeBuffer(ref byte[] buffer, int bufferSize, Random rnd) { #if ALLOW_UNSAFE fixed(byte * pBuffer = buffer) #endif for(int i = 0; i < bufferSize; i += 4) { int k = rnd.Next(int.MinValue, int.MaxValue); #if ALLOW_UNSAFE // One of the rare moments when I like to use pointers (with type casting) *((int*)(pBuffer + i)) = k; #else byte[] bits = BitConverter.GetBytes(k); buffer[i ] = bits[0]; buffer[i + 1] = bits[1]; buffer[i + 2] = bits[2]; buffer[i + 3] = bits[3]; #endif } } }
Now essentially I need to put the 32bit integers I get from Random.Next into a byte array.
I started implementing the randomizer as
unsafe void RandomizeBuffer(byte **pBuffer, int bufferSize, Random rnd)
Thats why I got the size in the signature and thats also why I first used the 'pointery' approach. (Since then I removed one deref in the method body)
Then I remembered that C# has reference parameters so I refactored... but noticed my code took longer to execute.
Hence the little benchmark here.
I'm wondering whether there is a managed/safe way to break up lot's of ints into bytes.
Of course since results can vary from circumstance to circumstance here my latest results:
csc Benchmark.cs ... Avarage time for one pass was: 31ms Total time over 100 passes was 3197ms
and
csc -define:ALLOW_UNSAFE -unsafe benchmark.cs ... Avarage time for one pass was: 19ms Total time over 100 passes was 1931ms
Also I noticed with the unsafe execution there is quiet a fluctuation in execution times of the passes. i.E.:
Pass 85 took 15.6097 ms Pass 86 took 15.6251 ms Pass 87 took 31.2502 ms Pass 88 took 15.626 ms Pass 89 took 15.6246 ms
I can only imagine it has something to do with the CPU Pipeline. But I am no expert in CPU Architecture or Compiler Internals
Any thoughts on this are welcome!
(Yes also those that say: Don't waste your time with this kind of micro management)
Edit:
Using StopWatch and 1000 passes the results are similar, though the fluctuations have been eliminated.
safe - Avarage time for one pass was: 24ms Total time over 1000 passes was 24606ms
unsafe - Avarage time for one pass was: 16ms Total time over 1000 passes was 16965ms
DateTime.Now
to benchmark anything. As you're discovering here, it typically has a resolution of about ~15 ms, which isn't very useful for precise timing. That's why you're getting those fluctuations.\$\endgroup\$