Wednesday 12 September 2012

On Windows 8 things aren’t always what they seem

I’ve been busy the last couple of months getting started on Windows 8.

Yesterday, I was trying to get data into a WriteableBitmap, and was a bit confused by what you could actually do with an IBuffer.  The interface only contains two methods, neither of which hands off the contents of the buffer.

Charles Petzold has an article that describes his attempts to get access to the buffer, and shows that C++ code isn’t all that much faster than the C# method of writing to a stream.  After reading his blog post, I discovered that I should have added a using statement to the top of my code to get the extension methods.

using System.Runtime.InteropServices.WindowsRuntime;


Once that’s been done, you discover that there are actually quite a few extension methods that work on IBuffer, and a promising one was


public static void CopyTo(this byte[] source, int sourceIndex, 
IBuffer destination, uint destinationIndex, int count);



I’ve been there, done that and got the t-shirt.  Don’t use this function for random access into the IBuffer.  I wrote the following loop:


bmp = new WriteableBitmap(240,240);
int e = bmp.PixelHeight * bmp.PixelWidth * 4;
byte [] twopixels = new byte[8];

int startCount = System.Environment.TickCount;
buf = bmp.PixelBuffer;

for (int j=0;j<100;j++)
// now run the loop 100 times.
for (int k=0;k<e;k+=8)
{
twopixels.CopyTo(0, buf, k, 8);
}
Debug.WriteLine(string.Format("{0} ticks elapsed", System.Environment.TickCount - startCount));


To my surprise, that code was ten times slower than this code:


byte [] colors = new byte[8];
bmp = new WriteableBitmap(240,240);
int e = bmp.PixelHeight * bmp.PixelWidth * 4;
byte [] Pixels = new byte[bmp.PixelHeight*bmp.PixelWidth*4];

int startCount = System.Environment.TickCount;
buf = bmp.PixelBuffer;

for (int j=0;j<100;j++)
// now run the loop 100 times.
for (int k=0;k<e;k+=8)
{
System.Buffer.BlockCopy(colors, 0, Pixels, k, 8);
}

using (Stream pixelStream = buf.AsStream())
{
await pixelStream.WriteAsync(Pixels, 0, Pixels.Length);
}
Debug.WriteLine(string.Format("{0} ticks elapsed", System.Environment.TickCount - startCount));


So, for whatever reason, byte.CopyTo is extremely inefficient.  Looking at the code, it appears that there is a lot of checking happening to the parameters, and also a call to Marshal.Copy deep inside the checks.


ADDENDUM


Having posted this I decided to go back and modify the slow version.


For reference, to run the code above 100 times took 4633 ticks for the first method, and 312 ticks for the second.


So, to try one last time, I did this:


bmp = new WriteableBitmap(240,240);
int e = bmp.PixelHeight * bmp.PixelWidth * 4;
byte [] twopixels = new byte[8];
int startCount = System.Environment.TickCount;
buf = bmp.PixelBuffer;
byte[] Pixels = buf.ToArray();
for (int j=0;j<100;j++)
// now run the loop 100 times.
for (int k=0;k<e;k+=8)
{
System.Buffer.BLockCopy(twopixels, 0, Pixels, k, 8);
}
Pixels.CopyTo(0,buf, 0, Pixels.Length)
Debug.WriteLine(string.Format("{0} ticks elapsed", System.Environment.TickCount - startCount));


To my surprise, this was the fastest of all of them taking only 234 ticks, an improvement of 33%!