为什么程序中的变量使用60%的执行时间?

时间:2011-12-15 18:33:00

标签: c++ variables global-variables

我正在用gprof分析我的代码,我得到以下输出:

   %   cumulative   self              self     total           
  time  seconds   seconds    calls  ms/call  ms/call  name    
 59.22      0.58     0.58       48    12.09    18.81  _GLOBAL__sub_I_inputHeight

inputHeight是一个全局变量。我不知道 GLOBAL _sub_I_inputHeight意味着什么,但它被调用了48次,并且占用了执行时间的60%(0,6)。

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

让我们看一下行为相似的一些最小例子:

  1 #include <iostream>
  2
  3 int global_int = 42;
  4
  5 int main(int argc, const char *argv[])
  6 {
  7     std::cout << "global_int = " << global_int << "\n";
  8 }



(gdb) bt
#0  global constructors keyed to global_int() () at global.cxx:8
#1  0x0804875d in __do_global_ctors_aux ()
#2  0x080484ec in _init ()
#3  0x080486f9 in __libc_csu_init (argc=1, argv=0xbffff0c4, envp=0xbffff0cc) at elf-init.c:79
#4  0xb7d2069c in __libc_start_main () from /lib/libc.so.6
#5  0x08048591 in _start () at ../sysdeps/i386/elf/start.S:119

这种回溯应该给我们至少一些谷歌。这些是全球价值观的构建者。

191 080486b1 <_GLOBAL__I_global_int>:
192  80486b1:       55                      push   %ebp
193  80486b2:       89 e5                   mov    %esp,%ebp
194  80486b4:       83 ec 18                sub    $0x18,%esp
195  80486b7:       c7 44 24 04 ff ff 00    movl   $0xffff,0x4(%esp)
196  80486be:       00
197  80486bf:       c7 04 24 01 00 00 00    movl   $0x1,(%esp)
198  80486c6:       e8 a6 ff ff ff          call   8048671 <_Z41__static_initialization_and_destruction_0ii>
199  80486cb:       c9                      leave
200  80486cc:       c3                      ret

反汇编并没有让我们在这里变得更聪明,它调用了函数__static_initialization_and_destruction_0(int,int),这更令人感兴趣:

171 08048671 <_Z41__static_initialization_and_destruction_0ii>:
172  8048671:       55                      push   %ebp
173  8048672:       89 e5                   mov    %esp,%ebp
174  8048674:       83 ec 18                sub    $0x18,%esp
175  8048677:       83 7d 08 01             cmpl   $0x1,0x8(%ebp)
176  804867b:       75 32                   jne    80486af <_Z41__static_initialization_and_destruction_0ii+0x3e>
177  804867d:       81 7d 0c ff ff 00 00    cmpl   $0xffff,0xc(%ebp)
178  8048684:       75 29                   jne    80486af <_Z41__static_initialization_and_destruction_0ii+0x3e>
179  8048686:       c7 04 24 d4 a0 04 08    movl   $0x804a0d4,(%esp)
180  804868d:       e8 9e fe ff ff          call   8048530 <_ZNSt8ios_base4InitC1Ev@plt>
181  8048692:       b8 50 85 04 08          mov    $0x8048550,%eax
182  8048697:       c7 44 24 08 20 a0 04    movl   $0x804a020,0x8(%esp)
183  804869e:       08
184  804869f:       c7 44 24 04 d4 a0 04    movl   $0x804a0d4,0x4(%esp)
185  80486a6:       08
186  80486a7:       89 04 24                mov    %eax,(%esp)
187  80486aa:       e8 61 fe ff ff          call   8048510 <__cxa_atexit@plt>
188  80486af:       c9                      leave
189  80486b0:       c3                      ret

这里的重要部分是在atexit()上注册了一个函数,该函数位于0x8048550并且名为std::ios_base::Init::~Init(),但仅当此函数的参数为​​0x1且{ {1}}。然后这个函数也调用ctor 0xffff,它正在初始化使用std :: cout所需的全局内容。

现在我们知道它在做什么,但不知道为什么。这里有一个初始gcc知识的人会知道更多,但我的猜测是,这是gcc确保每当调用一些代码来初始化一些全局/静态变量(在main之前运行)时的方式,也就是i /的ctor o子系统被调用,因此您可以随时使用std::ios_base::Init::Init()

至于为什么在代码中经常调用它,以及为什么需要这么长时间,我不能说太多,也许你可以在调试器中找到更多安装断点的方法。也许由于优化,这也是对该功能的一些错误的会计。

答案 1 :(得分:1)

当你运行一个gprofiled程序时,它会将数据写入一个文件,通常是gmon.out。该文件包含函数的地址,而不是名称。使用gprof实用程序读取数据时,它会使用程序中的调试信息解析名称。

这意味着如果你运行程序,重新编译它并执行gprof而不重新运行程序,输出就毫无意义。