给指针添加64位偏移量

5
在F#中,有一个NativePtr模块,但它似乎只支持32位偏移量的add/get/set函数,就像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# 中,我们终于拥有了“未管理”的约束;但我该如何执行指针算术?


起初我想:“不能这样做真是糟糕。” 但重新思考后,只有在您拥有如此大的连续内存分配时,指针偏移才有意义。超过4GiB的连续分配数组的用例是什么? - sblom
@sblom:图像数据很容易超过4 GiB ;) 当然,您可以对数据进行分区等操作;但是,如果可以使用更大量的连续分配内存,我想知道如何做到。 - Frank
2个回答

3

我不是这个领域的专家,但我查看了NativePtr模块的F#实现,我认为将nativeptr<'a>转换为nativeint然后再转回去是没有性能开销的。

该实现使用内联IL,内联IL代码不包含任何代码 - 它只是让F#编译器认为栈上的值具有不同的类型:

let inline ofNativeInt (x:nativeint)    = (# "" x : nativeptr<_> #)
let inline toNativeInt (x:nativeptr<_>) = (# "" x : nativeint    #)

实际上,NativePtr.add方法也使用了这两种方法——它将指针转换为nativeint,然后添加32位整数(乘以a类型的大小)。
因此,下面的函数应该可以工作:
let inline addNativeInt (x:nativeptr<'a>) (n:nativeint) : nativeptr<'a> = 
   (NativePtr.toNativeInt x) + n |> NativePtr.ofNativeInt

在代码中使用的所有函数都应该是内联的,这样你最终只会得到一个单独的加法指令(虽然我没有验证过)。你甚至不必担心在代码中多次使用该函数(你可以一直使用nativeptr<'a>,并使用此函数进行加法操作)。

然而,分区数据也可能是一种选择——据我所知,使用 F# 处理某些大型(>2GB)数据集的 MSRTeam 正是采用了这种方法——他们将数据分成 2GB 的块(存储在数组中)。


+1 非常有趣,谢谢。我也尝试查看 NativePtr,但至少 Reflector 只显示“// 此项已被混淆,无法翻译”。 - Frank
2
@Novox:您可以下载适用于Visual Studio 2008的F#版本 - 它附带源代码(在“source”目录中)。这是非常有用的信息来源 :-)。有趣的是,反编译器无法处理该代码! - Tomas Petricek

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);

第一句:正如我在问题中所说,我不想强制转换为长整型,我想使用AGU进行指针算术运算。第二句:AllocHGlobal(Win32中的GlobalAlloc)可以在x64上愉快地分配> 2 GiB。那么这个“+/-2 GB偏移限制”是什么? - Frank
这不是分配限制,而是寻址限制。请继续推进此线程,准确说明为什么需要寻址超过2GB。AGU对我来说是另一个晦涩的三个字母的缩写。 - Hans Passant
1
AGU是CPU的地址生成单元。你有关于偏移限制的参考资料吗?为什么需要这个?如果你使用每个计算节点32 GiB RAM的HPC集群,你肯定想要充分利用它,对吧?对于复杂模型,FEM刚度矩阵可能会变得非常大,即使它们是稀疏的。对于数百万个有限体积单元的CFD模拟来说更是如此。或者想想像MRT图像的相干增强扩散图像滤波之类的应用。 - Frank
顺便问一下:当你遇到这个偏移限制时,具体会发生什么?我使用Tomas建议的技巧实现了一个极简的64位本地数组,目前看起来似乎可以工作。 - Frank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接