为指针添加 64 位偏移量

发布于 2024-08-30 15:26:03 字数 549 浏览 3 评论 0原文

在 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

实际上你不能,因为类型参数没有“非托管”约束,但至少你可以在 < em>非通用方式。

在F#中我们终于得到了非托管约束;但我该如何进行指针算术呢?

In F#, there's the NativePtr module, but it seems to only support 32 bit offsets for its’ add/get/set functions, just like System.IntPtr does.

Is there a way to add a 64 bit offset to a native pointer (nativeptr<'a>) in F#? Of course I could convert all addresses to 64 bit integers, do normal integer operations and then convert the result again to nativeptr<'a>, but this would cost additional add and imul instructions. I really want the AGUs to perform the address calculations.

For instance, using unsafe in C# you could do something like

void* ptr = Marshal.AllocHGlobal(...).ToPointer();
int64 offset = ...;
T* newAddr = (T*)ptr + offset; // T has to be an unmanaged type

Well actually you can't, because there is no "unmanaged" constraint for type parameters, but at least you can do general pointer arithmetic in a non-generic way.

In F# we finally got the unmanaged constraint; but how do I do the pointer arithmetic?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

著墨染雨君画夕 2024-09-06 15:26:03

我不是该领域的专家,但我查看了 NativePtr 模块的 F# 实现,我认为将 nativeptr<'a> 转换为 '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)数据集的 MSR 团队正是使用了这种方法 - 他们将数据分区为 2GB 块(存储在数组中) )。

I'm not an expert in this field, but I took a look at the F# implementation of the NativePtr module and I think that there is no performance overhead associated with converting nativeptr<'a> to nativeint and back.

The implementation uses inline IL and the inline IL code doesn't contain any code - it is there just to make the F# compiler think that the value on the stack has a different type:

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

In fact, the NativePtr.add method also uses these two methods - it converts the pointer to nativeint and then adds the 32bit integer (multiplied by the size of the 'a type).

So, the following function should be fine:

let inline addNativeInt (x:nativeptr<'a>) (n:nativeint) : nativeptr<'a> = 
   (NativePtr.toNativeInt x) + n |> NativePtr.ofNativeInt

All functions used in the code should be inlined, so you'll end up with just a single instruction for addition (though, I have not verified that). You don't even have to worry about using the function multiple times in your code (you can work with nativeptr<'a> all the time and use this function for addition).

However, partitioning data may also be an option - as far as I know, the MSR team that was using F# for processing some large (>2GB) data sets used exactly this approach - they partitioned the data into 2GB blocks (stored in arrays).

隐诗 2024-09-06 15:26:03

成像数据很容易超过 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);

Imaging data can easily exceed 4 GiB
;)

It can't, x64 code has a +/-2 GB offset restriction. One of the reasons you can't allocate arrays larger than 2GB in .NET. This restriction exists in unmanaged 64-bit C/C++ code as well. There are libraries that work around this restriction, like WIC, but addressing all of the bitmap bits directly doesn't make sense when you use them.

Nevertheless, it is possible to generate such an address by casting IntPtr to long in C#:

IntPtr addr = SomeCall();
long offset = blah;
addr = (IntPtr)((long)addr + offset);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文