从System.Numerics.Vector <t>块中的nativeptr <t>读取

时间:2016-10-20 15:24:09

标签: .net pointers f# unmanaged simd

在F#中,我们可以取消引用指向类型为'a的值的指针,如此

open FSharp.NativeInterop
let x = NativePtr.read p

其中pnativeptr<'a>

现在假设此指针指向'a值的数组,并且我们希望通过System.Numerics.Vector<_>使用SIMD处理此数组。为此,我们必须将 n 连续的'a值加载到Vector<'a>结构中。对于.NET /托管数组,可以使用适当的Vector<_>构造函数来实现。但不幸的是,我们所拥有的只是一个指针(事实上,在这种特殊情况下指向非托管堆),因此我们不能使用现有的构造函数重载之一。

那么,只需将p重新解释为nativeptr<Vector<'a>>

即可
let inline cast<'T, 'U when 'U : unmanaged and 'T: unmanaged> (ptr: nativeptr<'T>) =
    ptr |> NativePtr.toNativeInt |> NativePtr.ofNativeInt<'U>

let v = p |> NativePtr.cast<'a, Vector<'a>> |> NativePtr.read

可悲的是,这不起作用,因为nativeptr的类型参数必须满足unmanaged约束 - Vector<_>as a generic data type不会。

现在,解决此问题的一种方法是将C#与NativeInterop结合使用,因为C#编译器不强制执行unmanaged约束。然而,我真的很想留在F#。

有没有希望这可以从F#中有效高效?或者是等待Vector<_>使用支持从指针加载的构造函数扩展的唯一选项?

1 个答案:

答案 0 :(得分:-1)

在F#本身似乎没有解决这个问题。因此,我在C#中实现了一个小包装器,它基本上只转发到NativeInteropEx.NativePtr,但剥离了C#编译器未知的unmanaged约束:

using NativeInteropEx;
using System.Numerics;
using System.Runtime.CompilerServices;

public struct VectorView<T> where T: struct
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector<T> Get(IntPtr p, int idx) {
        return p.Get<Vector<T>>(idx);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector<T> Get(IntPtr p, long idx) {
        return p.Get<Vector<T>>(idx);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Set(IntPtr p, int idx, Vector<T> value) {
        p.Set(idx, value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Set(IntPtr p, long idx, Vector<T> value) {
        p.Set(idx, value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector<T> Read(IntPtr p) {
        return p.Read<Vector<T>>();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void Write(IntPtr p, Vector<T> value) {
        p.Write(value);
    }
}

使用此中间层,我们现在可以在F#中将nativeptr<'T>取消引用为Vector<'T>

# baseAddress: nativeptr<'T> when 'T: unmanaged
# idx: int64
let v = VectorView<'T>.Get(baseAddress, idx)
# v: Vector<'T> of Vector<'T>.Count 'T values starting with baseAddress + sizeof<'T> * idx