繁体   English   中英

如何使用新运算符使单个 object 大于 2GB?

[英]How can I make single object larger than 2GB using new operator?

我正在尝试使用 new 运算符使单个 object 大于 2GB。 但是如果object的大小大于0x7fffffff,分配的memory的大小就变得奇怪了。 我认为这是由编译器完成的,因为汇编代码本身使用了奇怪的 memory 分配大小。

我正在使用 Visual Stuio 2015,配置是 Release,x64。

它是VS2015的错误吗? 否则,我想知道为什么存在限制。

示例代码如下,带有汇编代码。

struct chunk1MB
{
    char data[1024 * 1024];
};

class chunk1
{
    chunk1MB data1[1024];
    chunk1MB data2[1023];
    char data[1024 * 1024 - 1];
};

class chunk2
{
    chunk1MB data1[1024];
    chunk1MB data2[1024];
};

    auto* ptr1 = new chunk1;
00007FF668AF1044  mov         ecx,7FFFFFFFh  
00007FF668AF1049  call        operator new (07FF668AF13E4h)  

    auto* ptr2 = new chunk2;
00007FF668AF104E  mov         rcx,0FFFFFFFF80000000h  // must be 080000000h
00007FF668AF1055  mov         rsi,rax  
00007FF668AF1058  call        operator new (07FF668AF13E4h)  

我不确定如何完全解决您的问题,因为在我正确看到的任何地方都没有得到回答。
Memory 模型很棘手,直到 x64 2GB 几乎是极限。
据我所知,Windows 中的基本 memory model 不支持大量分配。
大页面支持 1GB 的 memory。


但是我想指出不同的方向:
我发现实现类似目标的 3 种方法:

  1. 显而易见的答案 - 将您的分配分成更小的块 - 它更有效 memory 。
  2. 使用不同类型的交换,您可以将 memory 写入文件。
  3. 使用virtual memory,不确定对你有没有帮助,使用windows api VirtualAlloc
    const static SIZE_T giga = 1024 * 1024 * 1024;
    const static SIZE_T size = 4 * giga;
    BYTE* ptr = static_cast<BYTE*>(VirtualAlloc(nullptr, (SIZE_T)size, MEM_COMMIT, PAGE_READWRITE));
    VirtualFree(ptr, 0, MEM_RELEASE);

祝你好运。

使用像 clang-cl 这样未损坏的编译器,或者对最大 object 大小没有有意的有符号 32 位实现限制的编译器,以 MSVC 为准。 (这会受到 largeaddressaware 选项的影响吗?)

当前的 MSVC( Godbolt 上的 19.33 )具有相同的错误,尽管它似乎确实可以处理 2GiB static 个对象。 但不是 3GiB static 个对象; 添加另一个 1GiB 成员会在从对象的开头访问超过 2GiB 的字节时导致错误代码( Godbolt -
mov BYTE PTR chunk2 static_chunk2-1073741825, 2 - 注意负偏移量。)

GCC 针对 Linux 为 3GiB object 的情况生成了正确的代码,使用mov r64, imm64将绝对地址获取到寄存器中,因为 RIP 相对寻址模式不可用。 (一般来说,当 some.data /.bss 地址链接在低 2GiB 之外和/或远离代码超过 2GiB 时,您需要gcc -mcmodel=medium才能正常工作。)


MSVC 似乎已在内部将大小截断为带符号的 32 位,然后进行符号扩展。 请注意它传递给new的 arg: mov rcx, 0FFFFFFFF80000000h而不是mov ecx, 80000000h (这将在写入 32 位寄存器时通过隐式零扩展设置 RCX = 0000000080000000h。)

在返回sizeof(chunk2); 作为size_t ,它工作正常,但有趣的是在源代码中将大小打印为负数。 这可能是无辜的,例如,在意识到该值适合 32 位零扩展值之后,MSVC 的 asm 打印代码可能总是将 32 位整数打印为有符号十进制,并在注释中使用无符号十六进制。

它与将 arg 传递给new的方式明显不同; 在那种情况下,它在机器代码中使用了 64 位操作数大小,因此相同的 32 位立即数被符号扩展为 64 位,接近 SIZE_MAX 的巨大值,这当然远远大于任何可能的最大值 object x86-64 的大小。 (48 位虚拟地址空间是 size_t 的 64 位值范围的 1/65536)。

unsigned __int64 sizeof_chunk2(void) PROC                   ; sizeof_chunk2, COMDAT
        mov     eax, -2147483648              ; 80000000H
        ret     0
unsigned __int64 sizeof_chunk2(void) ENDP                   ; sizeof_chunk2

这看起来像是一个编译器错误或有意的实施限制; 如果未知,请向 Microsoft 报告。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM