20180913 update - New post with updated example; enjoy!
There has been a recent push for performance improvements in the dotnet framework and as part of that, and any performance improvement work, there has been analysis done around the memory allocations being used for certain operations. I'm probably nearer to novice than expert in this matter however the launch of Span
The main starting point for me was the January 2018 MSDN magazine article by Stephen Toub called C# - All About Span: Exploring a New .NET Mainstay which starts off with good introduction with examples however gets complicated quickly. It is worth a read!
The article got me thinking, and made my head hurt to be honest, but one paragraph which was relatively short and almost throw away made me want to investigate further.
But what is a Span
Let's start off with how the original post describes it:
is a new value type at the heart of .NET. It enables the representation of contiguous regions of arbitrary memory, regardless of whether that memory is associated with a managed object, is provided by native code via interop, or is on the stack. And it does so while still providing safe access with performance characteristics like that of arrays.
Sounds good doesn't it? Nice and powerful and all that good stuff, but will it impact me? How can I use it? Well the rest of the first half of the original post goes into more examples so I won't duplicate it here. The paragraph which I did think was worth investigating was:
Spans provide a multitude of benefits beyond those already mentioned. For example, spans support the notion of reinterpret casts, meaning you can cast a
Span<byte>to be a
Span<int>(where the 0th index into the
Span<int>maps to the first four bytes of the
Span<byte>). That way if you read a buffer of bytes, you can pass it off to methods that operate on grouped bytes as ints safely and efficiently.
How do you do this then? Well this is what I came up with.
// set an plain integer and convert it to an byte array int number = 42; byte numberBytes = BitConverter.GetBytes(number); // now through the implicit casting convert to a span<byte> Span<byte> asBytes = numberBytes; // now using the extension method convert Span<int> asInts = asBytes.NonPortableCast<byte,int>(); // check that it's all pointing to the same pointer references Assert.Equal(42, asInts); Assert.Equal(number, asInts); asInts = 123456; int converted = BitConverter.ToInt32(numberBytes, 0); Assert.Equal(123456, converted);
This is crazy!
NonPortableCast extension method was the hardest part to discover and use.
public static Span<TTo> NonPortableCast<TFrom, TTo>(this Span<TFrom> source) where TFrom : struct where TTo : struct;
The signature itself seems relatively straight forward but it took a little while to see what it was doing. The interesting item from the code above was it was all pointing to the same data and any updates it was reflected at all points. In the example it started off as 42 in the
numberBytes however once converted at the end of the snippet was now 123456 even though it it's variable had not been manipulated explicitly. Madness!
I will be interested to see how people start to use this feature in library code which isn't created by the Microsoft Team.
I'd be interested in hearing your thoughts. Please contact me on Twitter @WestDiscGolf