【发布时间】:2017-03-02 16:43:33
【问题描述】:
在 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<_> 使用支持从指针加载的构造函数进行扩展?
【问题讨论】:
-
您不能将原始内存“重新解释”为特定类型的对象,即使 C# 编译器允许。这不是 C 语言。 CLR 类型不仅仅是编译时构造,它们还具有运行时表示。
unmanaged约束是有原因的。 -
Of course I can(并且,正如我所描述的,C# 编译器确实“让我”,但不是 F# 编译器。)。
Vector<T>是 blittable(T始终是非托管的,因为仅支持原始类型)并且归结为内存中的 128 (SSE) 或 256 位 (AVX) SIMD 步长,这是在其上实现高效 SIMD 内在函数的唯一方法. -
我不认为你的程序做你认为它做的事情。
IntPtr.Read扩展方法并没有将内存“重新解释”为Vector<double>,它实际上将几个字节复制到一个新位置。 -
p.Read<Vector<double>>将 16 (SSE) 或 32 (AVX) 字节复制到堆栈上,然后将其视为 Vector。它做的正是它应该做的。 -
@Fyodor 顺便说一句。看来您错过了我的问题的重点。 不是关于将内存块“转换”为其他类型,例如Vector
的托管数组或类似的东西。一点也不。
标签: .net pointers f# unmanaged simd