C ++浮点常量是否总是存储在静态内存中?

时间:2015-11-19 17:56:16

标签: c++ floating-point constants

在他的书Optimizing Software in C++中,Agner Fog给出了以下例子:

  

字符串常量和浮点常量存储在static中   记忆。例如:

// Example 7.2
a = b * 3.5;
c = d + 3.5;
     

这里,常量3.5将存储在静态存储器中。大多数编译器   会认识到这两个常数是相同的,所以只有一个   不断需要存储。

所有浮点常量始终是否都存储在静态内存中?

为什么它们不能存储在堆栈上?在他给出的例子中说。

2 个答案:

答案 0 :(得分:1)

我基于你的伪代码创建了一个简单的工作示例:

int main()
{
  double b = 3.0;
  double d = 3.14;
  double a = b * 3.5;
  double c = d + 3.5;
  return 0;
}

然后编译它:

g++ -O0 -o const const.cc

然后在调试器中使用它:

gdb const
...
(gdb) disass main
Dump of assembler code for function main:
   0x00000000004004ed <+0>: push   %rbp
   0x00000000004004ee <+1>: mov    %rsp,%rbp
   0x00000000004004f1 <+4>: movabs $0x4008000000000000,%rax
   0x00000000004004fb <+14>:    mov    %rax,-0x20(%rbp)
   0x00000000004004ff <+18>:    movabs $0x40091eb851eb851f,%rax
   0x0000000000400509 <+28>:    mov    %rax,-0x18(%rbp)
   0x000000000040050d <+32>:    movsd  -0x20(%rbp),%xmm1
   0x0000000000400512 <+37>:    movsd  0xae(%rip),%xmm0        # 0x4005c8
   0x000000000040051a <+45>:    mulsd  %xmm1,%xmm0
   0x000000000040051e <+49>:    movsd  %xmm0,-0x10(%rbp)
   0x0000000000400523 <+54>:    movsd  -0x18(%rbp),%xmm1
   0x0000000000400528 <+59>:    movsd  0x98(%rip),%xmm0        # 0x4005c8
   0x0000000000400530 <+67>:    addsd  %xmm1,%xmm0
   0x0000000000400534 <+71>:    movsd  %xmm0,-0x8(%rbp)
   0x0000000000400539 <+76>:    mov    $0x0,%eax
   0x000000000040053e <+81>:    pop    %rbp
   0x000000000040053f <+82>:    retq   
End of assembler dump.
(gdb) b main
Breakpoint 1 at 0x4004f1
(gdb) r
Starting program: /home/sasha/stackoverflow/const 

Breakpoint 1, 0x00000000004004f1 in main ()
(gdb) p  *(double*)0x4005c8
$2 = 3.5

所以你可以从所有这些中看到,常量3.5存储在地址0x4005c8中,在main结束后只有136个字节。两次使用相同的地址来引用它,尽管它在反汇编中显示不同 - 第一次为0xae(%rip),第二次为movsd 0x98(%rip)。这是因为rip的值随着执行的进行而不断变化 - 它是指令指针。

objdump -t的帮助下,您可以看到上述地址属于.rodata部分。

请注意,3.0和3.14常量是在没有内存引用的情况下显式编码的,每个:

movabs $0x4008000000000000,%rax

movabs $0x40091eb851eb851f,%rax

显然,因为它们没有被重复gcc所以决定将它们存储在内存中是不值得的。

更新:正如JSF所指出的那样,不使用存储而非直接常量的决定确实取决于使用情况。我确认如果我将double d = 3.14更改为double d = b + 3.14,则3.14将成为内存引用。

我使用-O0来保持简单。通过这样一个简单示例中的优化,程序集被优化为可以获得相同结果的东西,但其方式与有用的东西相距甚远,无法说明这一点。

答案,至少在gcc的情况下,似乎确实是&#34;它取决于&#34;。但是,如果你很好奇,不仅仅是信任一本书或一篇博文,最好是做一个简单的例子并把它分开 - 你从马的口中直接得到它,你学到了很多东西更多。

答案 1 :(得分:1)

谢谢你的例子。你是正确的,当使用gcc编译64位模式且优化关闭(-O0)时,它会将常量存储在代码中。如果编译为32位模式,它会将常量存储在内存中。

如果启用优化(-O3),那么计算将被简化优化,因为它们未被使用,而main只返回0.

如果你创建一个执行某些浮点计算并返回float或double的函数,并在优化时编译,那么你会看到浮点常量存储在静态内存中。

我试过了:

float test(float x) {
    return x*0.9f + 3.1f;
}

g ++ -c -S -O3 test.cpp

cat test.s

得到了:

mulss   .LC0(%rip), %xmm0
addss   .LC1(%rip), %xmm0
ret

其中.LC0和.LC1是只读数据段中的常量。不幸的是,x86和x86-64指令集没有在浮点寄存器或向量寄存器中存储立即数的指令。它必须首先将常量存储在通用寄存器中,然后将其传输到xmm寄存器,这不是最佳的(除了数据高速缓存负载较重但代码高速缓存不存在的情况)。