在F#中,我们可以取消引用指向类型为'a
的值的指针,如此
open FSharp.NativeInterop
let x = NativePtr.read p
其中p
是nativeptr<'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<_>
使用支持从指针加载的构造函数扩展的唯一选项?
答案 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