从 System.Numerics.Vector<T> 块中的 nativeptr<T> 读取
Reading from nativeptr<T> in System.Numerics.Vector<T> chunks
在 F# 中,我们可以取消引用指向 'a
类型值的指针,就像这样
open FSharp.NativeInterop
let x = NativePtr.read p
其中 p
是 nativeptr<'a>
。
现在假设这个指针指向一个 'a
值的数组,并且我们想通过 System.Numerics.Vector<_>
使用 SIMD 处理这个数组。为此,我们必须将 n 个连续的 'a
值加载到 Vector<'a>
结构中。对于 .NET/managed 数组,这可以通过使用适当的 Vector<_>
构造函数来实现。但不幸的是,我们只有一个 pointer(在这种特殊情况下,它实际上指向非托管堆),所以我们不能使用现有的构造函数重载之一。
那么,简单地将 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<_>
的唯一选择是使用支持从指针加载的构造函数进行扩展?
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
在 F# 中,我们可以取消引用指向 'a
类型值的指针,就像这样
open FSharp.NativeInterop
let x = NativePtr.read p
其中 p
是 nativeptr<'a>
。
现在假设这个指针指向一个 'a
值的数组,并且我们想通过 System.Numerics.Vector<_>
使用 SIMD 处理这个数组。为此,我们必须将 n 个连续的 'a
值加载到 Vector<'a>
结构中。对于 .NET/managed 数组,这可以通过使用适当的 Vector<_>
构造函数来实现。但不幸的是,我们只有一个 pointer(在这种特殊情况下,它实际上指向非托管堆),所以我们不能使用现有的构造函数重载之一。
那么,简单地将 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<_>
的唯一选择是使用支持从指针加载的构造函数进行扩展?
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