public void DummyMethod(string data) { } string keyString = "234567"; DummyMethod(keyString.Substring(0, 2)); DummyMethod(keyString.Substring(2, 2)); DummyMethod(keyString.Substring(4, 2));
Remember string is immutable, and each time we are invoking the Substring method, we are allocating a new memory location is allocated with the substring. Now that isn’t a scenario you wouldn’t want to be if you are working on a memory intensive application and you want to parse a really long string. But what is we could parse the string right from the memory location allocated for keyString. That would be extremely efficient right ?
That’s where Span<T> comes in. It would allow us to point to a contiguous region of memory, and allow us to parse through it without needing a different memory allocation. Let’s rewrite the above code using Span<T>.
public void DummyMethod(ReadOnlySpan data) { } ReadOnlySpan keyString = "234567".AsReadOnlySpan(); DummyMethod(keyString.Slice(0, 2)); DummyMethod(keyString.Slice(2, 2)); DummyMethod(keyString.Slice(4, 2));
Notice that the string has a nice little extension method to create a ReadOnlySpan. We are also using the Slice method (instead of Substring) to access the specific part of memory location. We will do a bit of bench marking to understand the performance implication. Let’s create a BenchmarkDemo Class for our demonstrative purpose. Complete source code is available at my Github.
[Benchmark] public void UsingSubString() { string keyString = "long string"; for (int i = 0; i < IterationLimit; i++) DummyStringMethod(keyString.Substring(0, i)); } void DummyStringMethod(string _) { } [Benchmark] public void UsingSpan() { ReadOnlySpan keyString = "long string".AsReadOnlySpan(); for (int i = 0; i < IterationLimit; i++) DummySpanMethod(keyString.Slice(0, i)); } void DummySpanMethod(ReadOnlySpan _) { }
The above code is for demonstrative purpose, hence we are using a dummy string "long string". In the example in Github, you can find the same replaced with lorem ipsum.
Alright, now lets run the Benchmark code and analyse the memory allocation against iteration loops of 10,100, and 400. We are using BenchmarkDotNet for example.
As you can see the “UsingSubString” method uses a huge amount of memory as the calls to substring increases. On other hand, Span based method hardly uses any and doesn’t have any different as the number of calls increases.
Updated API Details : https://bytelanguage.net/2018/10/27/updated-span-api-nonportablecast-and-asreadonlyspan-to-memorymarshal-cast-and-asspan/
LikeLike