在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
实际上你不能,因为类型参数没有“非托管”约束,但至少你可以在非泛型中做一般的指针算术方式。
在F#中,我们终于得到了非托管约束;但是如何进行指针运算呢?
答案 0 :(得分:3)
我不是这个领域的专家,但是我看了NativePtr
模块的F#实现,我认为将nativeptr<'a>
转换为{{}没有任何性能开销。 1}}然后回来。
实现使用内联IL,并且内联IL代码不包含任何代码 - 只是为了让F#编译器认为堆栈上的值具有不同的类型:
nativeint
实际上,let inline ofNativeInt (x:nativeint) = (# "" x : nativeptr<_> #)
let inline toNativeInt (x:nativeptr<_>) = (# "" x : nativeint #)
方法也使用这两种方法 - 它将指针转换为NativePtr.add
,然后添加32位整数(乘以nativeint
类型的大小)。
所以,以下函数应该没问题:
'a
代码中使用的所有函数都应该内联,因此您最终只会添加一条指令(尽管如此,我还没有验证过)。您甚至不必担心在代码中多次使用该函数(您可以一直使用let inline addNativeInt (x:nativeptr<'a>) (n:nativeint) : nativeptr<'a> =
(NativePtr.toNativeInt x) + n |> NativePtr.ofNativeInt
并使用此函数进行添加)。
但是,分区数据也可能是一个选项 - 据我所知,使用F#处理一些大型(> 2GB)数据集的MSR团队正是使用这种方法 - 他们将数据分区为2GB块(存储在数组中。)
答案 1 :(得分:1)
成像数据很容易超过4 GiB ;)
不能,x64代码有+/- 2 GB的偏移限制。在.NET中无法分配大于2GB的数组的原因之一。此限制也存在于非托管64位C / C ++代码中。有些库可以解决这个限制,比如WIC,但在使用它们时直接解析位图位的所有是没有意义的。
然而,可以通过在C#中将IntPtr转换为long来生成这样的地址:
IntPtr addr = SomeCall();
long offset = blah;
addr = (IntPtr)((long)addr + offset);