I'm rewriting some of my extension methods using the new Span<T>
type and I'm having trouble finding a way to properly pin a generic instance to be able to use parallel code to work on it.
As an example, consider this extension method:
public static unsafe void Fill<T>(this Span<T> span, [NotNull] Func<T> provider) where T : struct
{
int
cores = Environment.ProcessorCount,
batch = span.Length / cores,
mod = span.Length % cores,
sizeT = Unsafe.SizeOf<T>();
//fixed (void* p0 = &span.DangerousGetPinnableReference()) // This doesn't work, can't pin a T object
void* p0 = Unsafe.AsPointer(ref span.DangerousGetPinnableReference());
{
byte* p = (byte*)p0; // Local copy for the closure
Parallel.For(0, cores, i =>
{
byte* start = p + i * batch * sizeT;
for (int j = 0; j < batch; j++)
Unsafe.Write(start + sizeT * j, provider());
});
// Remaining values
if (mod == 0) return;
for (int i = span.Length - mod; i < span.Length; i++)
span[i] = provider();
}
}
Here I just want to fill an input Span<T>
using some values provider, and since these vectors could be quite large I'd like to populate them in parallel.
This is just an example, so even if using parallel code here isn't 100% necessary, the question still stands, as I'd need to use parallel code again sooner or later anyways.
Now, this code does work, but since I'm never actually pinning the input span and given the fact that it could very well be pointing to some managed T[]
vector, which can be moved around all the time by the GC, I think I might have just been lucky to see it working fine in my tests.
So, my question is:
Is there any way to pin a generic
Span<T>
instance and get a simplevoid*
pointer to it, so that I can pass it around in closures to work on theSpan<T>
instance in parallel code?
Thanks!