如何固定通用Span< T>使用Parallel.For进行处理的实例? [英] How to pin a generic Span<T> instance to work on it with Parallel.For?
问题描述
我正在使用新的Span<T>
类型重写某些扩展方法,但我无法找到正确固定通用实例以使用并行代码进行处理的方法.
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();
}
}
在这里,我只想使用一些值提供程序来填充输入Span<T>
,并且由于这些向量可能很大,因此我想并行填充它们.
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.
这只是一个示例,因此即使在此处使用并行代码也不是 100%必要,问题仍然存在,因为我需要使用并行 还是迟早要再次编码.
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.
现在,该代码可以工作,但是由于我从未真正固定输入范围,并且考虑到它很可能指向某个托管的T[]
向量,因此可以GC一直都在移动它,我想我可能很幸运地看到它在我的测试中运行良好.
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.
所以,我的问题是:
有什么方法可以固定通用
Span<T>
实例并获得指向它的简单void*
指针,以便我可以在闭包中传递它,以并行代码在Span<T>
实例上工作?
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?
谢谢!
推荐答案
我想我可能已经找到了使用Unsafe
类中新方法之一的解决方法,我已经对其进行了测试,到目前为止,它似乎仍然有效.在这里:
I think I might have found a workaround using one of the new methods in the Unsafe
class, I've tested it and so far it seems to work. Here it is:
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,
size = Unsafe.SizeOf<T>();
ref T r0 = ref span.DangerousGetPinnableReference();
fixed (byte* p0 = &Unsafe.As<T, byte>(ref r0))
{
byte* p = p0;
Parallel.For(0, cores, i =>
{
byte* pi = p + i * batch * size;
for (int j = 0; j < batch; j++, pi += size)
Unsafe.Write(pi, provider());
}).AssertCompleted();
// Remaining values
if (mod < 1) return;
for (int i = span.Length - mod; i < span.Length; i++)
Unsafe.Write(p + i * size, provider());
}
}
基本上,由于我无法固定ref T
值,因此我尝试使用Unsafe.As<T, byte>(ref T value)
获取ref byte
变量并将其固定.由于它指向相同的地址,所以我认为(希望)将其固定就好了,它应该在IL中做同样的事情.
Basically, since I can't pin a ref T
value, I tried to get a ref byte
variable using Unsafe.As<T, byte>(ref T value)
and to pin that one instead. Since it points to the same address, I think (hope) it's pinned just fine, it should be doing the same thing in IL.
这篇关于如何固定通用Span< T>使用Parallel.For进行处理的实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!