I want to do operations on Span<T> in parallel but something like this is not legal:
void DoSomething(Span<int> buffer, int option1, int option2)
{
.......
}
void ParallelDoSomething(Span<int> buffer)
{
var size = buffer.Length;
Parallel.Invoke(() => DoSomething(buffer, 0, size / 2),
() => DoSomething(buffer, size/2, size)); //not legal
}
since compiler complains that: Cannot use ref, out, or in parameter 'buffer' inside an anonymous method, lambda expression, query expression, or local function
How can I execute in parallel methods which take Span<T> as arguments?
The problem here is that a
Span<T>cannot be allowed onto the heap - it is only valid on the stack - which means that it can't be either boxed, or used as a field on aclass(or astructexcept aref struct). This rules out both captured variables and most common forms ofstateparameters.If you have the luxury of changing the input to be a memory, you can capture the memory, and get the span inside the lambda body:
If you can't change the input, you can still do it... by cheating. You can pin the existing span, create a memory that covers that pinned data, and use that memory just like you would if it had been passed in as a memory. This isn't entirely trivial, since you need to write your own pointer-based memory manager implementation, but: it works. Here's an example from protobuf-net: https://github.com/protobuf-net/protobuf-net/blob/main/src/protobuf-net.Core/Meta/TypeModel.cs#L767-L789
Or perhaps more conveniently, pin the span and capture the pointer directly, noting that the compiler doesn't normally allow this (to prevent the pointer being used by a delegate later), but since we know the timing semantics, we can make it happy by duplicating the pointer:
or if we wanted to fix slice the span at the point of input: