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

Why does a variable in my program use 60% of the execution time?

本文关键字:执行时间 变量 程序 为什么 我的      更新时间:2023-10-16

我正在使用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( 的执行时间。

有什么想法吗?

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

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

让我们看一些行为类似的最小示例:

  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() 但仅当该函数的参数0x10xffff时。然后这个函数也会调用 ctor std::ios_base::Init::Init()它初始化使用 std::cout 所需的全局内容。

现在我们知道它在做什么,但不知道为什么。在这里,具有初始 gcc 知识的人会知道更多,但我的猜测是,这就是 gcc 确保每当调用一些代码来初始化一些全局/静态变量(在 main 之前运行(时,也会调用 I/O 子系统的 ctor,以便您始终可以在那里使用 std::cout

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