Fra*_*ank 5 .net f# interop pointers native
在F#中,有NativePtr模块,但它似乎只为其'add/get/set函数支持32位偏移,就像System.IntPtr一样.
有没有办法在F#中为本机指针(nativeptr <'a>)添加64位偏移量?当然,我可以将所有地址转换为64位整数,执行正常的整数运算,然后将结果再次转换为nativeptr <'a>,但这会花费额外的add和imul指令.我真的希望AGU执行地址计算.
例如,在C#中使用unsafe,你可以做类似的事情
void* ptr = Marshal.AllocHGlobal(...).ToPointer();
int64 offset = ...;
T* newAddr = (T*)ptr + offset; // T has to be an unmanaged type
Run Code Online (Sandbox Code Playgroud)
实际上你不能,因为类型参数没有"非托管"约束,但至少你可以用非泛型方式进行通用指针运算.
在F#中,我们终于得到了非托管约束; 但是如何进行指针运算呢?
我不是该领域的专家,但我查看了该模块的 F# 实现,我认为转换和返回NativePtr没有相关的性能开销。nativeptr<'a>nativeint
该实现使用内联 IL,并且内联 IL 代码不包含任何代码 - 它只是为了让 F# 编译器认为堆栈上的值具有不同的类型:
let inline ofNativeInt (x:nativeint) = (# "" x : nativeptr<_> #)
let inline toNativeInt (x:nativeptr<_>) = (# "" x : nativeint #)
Run Code Online (Sandbox Code Playgroud)
事实上,该NativePtr.add方法也使用了这两个方法——它将指针转换为nativeint32 位整数,然后相加(乘以类型的大小'a)。
所以,下面的函数应该没问题:
let inline addNativeInt (x:nativeptr<'a>) (n:nativeint) : nativeptr<'a> =
(NativePtr.toNativeInt x) + n |> NativePtr.ofNativeInt
Run Code Online (Sandbox Code Playgroud)
代码中使用的所有函数都应该内联,因此您最终只会得到一条添加指令(尽管我尚未验证这一点)。您甚至不必担心在代码中多次使用该函数(您可以nativeptr<'a>一直使用该函数并使用该函数进行添加)。
然而,对数据进行分区也可能是一种选择 - 据我所知,使用 F# 处理一些大型(>2GB)数据集的 MSR 团队正是使用了这种方法 - 他们将数据分区为 2GB 块(存储在数组中) 。