如何固定通用Span< T>使用Parallel.For进行处理的实例? [英] How to pin a generic Span<T> instance to work on it with Parallel.For?

查看:82
本文介绍了如何固定通用Span< T>使用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 simple void* pointer to it, so that I can pass it around in closures to work on the Span<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&lt; T&gt;使用Parallel.For进行处理的实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆