返回首页


在论坛中,人们常说的64位版本的方案消耗的内存和堆栈用量较大。所以说,他们通常认为,数据的大小已成为两次大。但是,这种说法是毫无根据的,因为大多数类型在C / C + +语言(CHAR,短,整数,浮点)的大小保持在64位系统相同。当然,例如,指针的大小增加,但远不是所有在程序中的数据的指针组成。之所以方案消耗的内存量增加的原因较为复杂。我决定这个问题的详细调查。
在这篇文章中,我将谈论堆栈和未来,我打算讨论的内存分配和二进制代码的大小。我想也马上要注意的文章,涵盖的语言C / C和微软的Visual Studio开发环境。
直到最近,我相信一个64位的应用程序代码不能在32位代码比较消耗堆栈更快的两倍。依托这一假设,在我的文章,我建议增加程序堆栈中的情况下两次。但现在我必须探索一种不愉快的事情:堆栈的消费量可能增长远远超过两次。我感到很惊讶,因为我认为最坏的情况下两次堆栈增长。晚了一点,我毫无根据的希望之所以会成为明确。但现在让我们来看看参数是如何在64位程序通过调用函数时。
开发x86 - 64架构的调用约定时,他们决定把各种版本的函数调用结束。在Win32there广泛的调用约定:在Win64上STDCALL,CDECL,fastcall,thiscall等,只有一个"本地调用约定??。像__cdecl修饰符被编译器忽略。我想大家都同意这样的调用约定AX是高尚的。
x86 - 64平台上调用约定类似于现有的x86 fastcall公约。前四个整数参数(从左至右)在x64约定,通过在64位,为此专门寄存器选择:RCX的:第一整数参数RDX的:第二整数参数奥迪R8:第三整数参数R9:第四整数参数
其余的整数参数通过堆栈传递。指针??代码>??被认为是一个整数参数,所以它总是放置到RCX寄存器。如果传递浮点值,其中前4位是通过在寄存器XMM0 XMM3,而未来通过堆栈传递。
依托这方面的资料,我得出的结论是一个64位程序可以在许多情况下保存不像一个32位的堆栈内存。对于如果参数是通过寄存器的功能代码是短暂的,没有必要保存在内存(堆栈)的论点,然后被消耗栈内存的大小必须要小。但并非如此。
虽然可以在寄存器中传递参数,编译器将所有的堆栈空间,为他们减少RSP寄存器(堆栈指针)的值相同的储备。每个函数都必须保留在堆栈的至少32个字节(对应寄存器RCX,RDX,R8,R9的4个64位值)。这在栈空间允许轻松地传递到堆栈中的功能寄存器的内容保存。被调用的函数不需要输入参数通过寄存器保存到堆栈,但保留在栈空间允许这样做如果有必要的。如果超过4个整数参数传递,一些额外的空间,必须保留在堆栈中。
让我们考虑一个例子。有些功能两个整型参数传递到一个子功能。编译器的参数的地方??值到寄存器RCX和RDX,同时从RSP寄存器中减去32字节。被调用函数的地址通过寄存器RCX和RDX的参数。如果该函数的代码需要某种目的而这些寄存器,它可以复制到堆栈中预留空间的大小32个字节的内容。
描述的功能,导致一个堆栈的消耗速度显著增长。即使该函数没有参数,将32个字节"有点过??堆栈反正,他们不会被用来无论如何然后。我没有找到这样的浪费机制的原因。有一些关于统一和简化调试解释,但这种信息过于空泛。
注意另一件事。堆栈指针可吸入悬浮粒子,必须在16字节边界对齐之前,下一个函数调用。因此,在64位代码调用不带参数的函数时,正在使用的堆栈的总大小是:8(返回地址)8(对齐)32(参数预留空间)= 48个字节!
让我们来看看它可能会导致在实践中。这里,进一步,我会为我的实验中使用的Visual Studio 2010。让我们这样一个递归函数:

void StackUse(size_t *depth)

{

  volatile size_t *ptr = 0;



  if (depth != NULL)

    ptr = depth;



  cout << *ptr << endl;



  (*ptr)++;



  StackUse(depth);



  (*ptr)--;

}

功能是故意混淆,以防止它转变成"什么?优化一个位??这里最主要的是:函数具有指针类型的参数和局部变量,指针类型。让我们来看看功能在32位和64位版本,以及如何可以递归调用时堆栈的大小是1兆字节(默认大小)多次使用的堆栈是多少。
推出32位:最后显示的数字(堆栈深度) - 51331
调用此函数时,编译器使用20个字节。
推出64位:最后显示的数字 - 21288
调用此函数时,编译器使用48个字节。
因此,64位版本的StackUse功能是超过两次超过32位的贪婪。
请注意,数据对齐规则的变化,也可能会影响消耗堆栈的大小。假设该函数采用以下结构作为一个参数:{C}
大小从12字节到24字节时,在64位版本重新编译??代码> S??结构增加,由于对齐规则的改变和变化??代码> B? ??成员的大小。结构是值传递到函数。 ,相应,结构也将需要两倍多的堆栈内存。
这一切​​如此糟糕?不要忘记,64位编译器可以处理比32位的寄存器。让复杂的实验功能的代码:
void StackUse(size_t *depth, char a, int b)

{

  volatile size_t *ptr = 0;



  int c = 1;

  int d = -1;



  for (int i = 0; i < b; i++)

    for (char j = 0; j < a; j++)

      for (char k = 0; k < 5; k++)

        if (*depth > 10 && k > 2)

        {

          c += j * k - i;

          d -= (i - j) * c;

        }



  if (depth != NULL)

    ptr = depth;



  cout << c << " " << d << " " << *ptr << endl;

  (*ptr)++;



  StackUse(depth, a, b);



  (*ptr)--;

}

这里是它的执行结果:
推出32位:最后显示的号码 - 16060
编译器使用64个字节的这个时候,当调用这个函数。
推出64位:最后显示的数字 - 21310
编译器仍然使用48个字节时,调用这个函数。
64位编译器使用此范例的额外的寄存器,并建立一个更高效的代码,使我们能够减少被消耗的堆栈内存量!结论人们无法预见一个程序的64位版本将在比较消耗到32位的堆栈内存有多少。它可能会减少(不太可能)等等。对于64位程序,你应该保留堆栈数量增加2-3倍。 3次比较好 - 只是为了安心。要做到这一点,看到的参数堆栈保留大小(/堆栈:储备开关)在项目设置。默认情况下,栈的大小是1兆字节。你不应该担心,如果您的64位程序消耗更多的堆栈内存。有更多的物理内存在64位系统。与64位系统上的大小2兆字节,8 GB内存的堆栈需要1兆字节的栈与2 GB的32位系统的内存少于%。

回答

评论会员:ED韦尔奇 时间:2012/01/26
您的文章是非常好的,但是,您的评论:"你不应该担心,如果您的64位程序会消耗更多的栈内存"是不是真的正确。具有更小的内存requirments方案更容易融入缓存,因此会更快。主内存实际上是在比较快取记忆体的速度很慢
评论会员:布莱克米勒 时间:2012/01/26
]
他的其他页面也很有趣
评论会员:MosheDavid123 时间:2012/01/26
"有更多的物理内存在64位系统"
如果我有8 MB的RAM,在机器的物理内存的大小并没有改变,即使我,同一台机器上安装64位Windows。 64位Windows有更多的虚拟内存地址空间2 ^ 64,而不是仅仅为2 ^ 32。我觉得这是你的意思,所以堆栈使用3 MB虚拟地址空间实在是嚼的变化
评论会员:。叶夫根雷日科夫 时间:2012/01/26
我熟悉的W / RCX,RAX ...从R8,R9来自他们的通用寄存器的一部分吗?

好文章{S0}