【问题标题】:Why is function call taking significantly longer than code executed according to gprof?为什么函数调用花费的时间比根据 gprof 执行的代码要长得多?
【发布时间】:2012-09-19 23:53:33
【问题描述】:

我正在学习如何使用 gprof 分析我的代码。对于我的一个应用程序,我有以下输出:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
 10.27      1.20     1.20                             Location::get_type() const (location.cpp:20 @ 40a4bd)

我看到了这个

  1.20      4.98     0.14 34662692     0.00     0.00  Location::get_type() const (location.cpp:19 @ 40a4ac)

这里是函数

char Location::get_type() const {
    return type;
}

我假设 gprof 的第一行是指函数需要执行的总时间,而第二行是指返回语句所需的时间。我有其他函数是返回ints 的同一类的吸气剂,但函数时间和返回语句时间之间的差异只有大约 0.1 秒,而与我发布的时间一样,时间差是 1.06 秒(其他 getter 的调用次数减少了大约 200 万次,这与调用总数相比是很小的)。与其中的一行代码相比,什么可以解释函数调用的更高时间?

值得一提的是,我使用 -g -pg 进行编译,因为我在逐行模式下使用 gprof。

编辑: 答案之一建议我查看程序集输出。看不懂,所以发在这里。我已经发布了两个函数调用的汇编代码。第一个是 get_floor(),它相对较快(~.10 秒)。第二个是get_type(),速度很慢。

_ZNK8Location9get_floorEv:
.LFB5:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movq    %rdi, -8(%rbp)
        movq    -8(%rbp), %rax
        movl    8(%rax), %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE5:
        .size   _ZNK8Location9get_floorEv, .-_ZNK8Location9get_floorEv
        .align 2
        .globl  _ZNK8Location8get_typeEv
        .type   _ZNK8Location8get_typeEv, @function
_ZNK8Location8get_typeEv:
.LFB6:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movq    %rdi, -8(%rbp)
        movq    -8(%rbp), %rax
        movzbl  12(%rax), %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc

【问题讨论】:

  • get_type 是虚函数吗?
  • 一种可能是粒度问题;分析机制运行 0.01 (1/100th) 秒,然后检查程序计数器的位置,并假定您在函数中。但是,如果函数很小,我相信你最终可能会得到错误的函数,因为配置文件将 16 或 32 字节的机器代码视为都在同一个函数中。
  • 你得到了很好的答案,但我很想知道是什么让你专注于gprof。有老师推荐吗?是否有博客、书籍或某些文档推荐过它? It has a number of issues.
  • @MikeDunlavey 这是我看到的第一个,并且在这里获得了很高的评价:stackoverflow.com/questions/375913/…。我没有意识到它有这么多问题,我会尝试另一个分析器并回到这个问题。

标签: c++ profiling profiler gprof


【解决方案1】:

第一个突然出现在我脑海中的是函数调用的成本不是零 - 每次调用它时都必须设置和拆除堆栈帧。

可能是您的差异的根源,您应该检查汇编程序输出(例如 gcc -S)以查看底层代码是什么。

【讨论】:

  • 感谢您的提示。我已经发布了程序集输出,因为我无法理解它。
【解决方案2】:

基于代码检测的分析器对于这种非常快速的功能几乎毫无用处,您可以从中获得的任何东西都只是随机数。

原因是现代 CPU 上的执行时间是一个非常复杂的函数,包含大量参数和检测(添加到函数中以更新统计信息的代码)会搞砸一切。对于像这样的短函数,单独的检测可能比被检测的代码本身要多得多。

为了分析快速功能,您应该使用像OProfile 这样的被动分析器,它只运行程序并以准定期的时间间隔检查它所在的位置。你没有得到精确的计数器,但是你可以从随机采样中得到的近似值更接近真实的东西,因为程序的行为并没有发生太大的改变。

查看程序在哪里浪费时间的另一种方法是进行实验。如果您怀疑代码的某个部分是瓶颈,请尝试执行 10 次(这通常不难)并检查程序的总执行时间更改了多少。如果减速不大,那么您知道即使能够完全删除该代码也不会为您节省太多。

【讨论】:

    【解决方案3】:

    如果您想测量 1 立方厘米盒子内的空气温度,并在其中放置一个 0.8 cm3 头的温度计,那么您不是在测量温度。空气,但温度计之一。

    您的函数是如此之小,以至于分析器实际上正在测量它自己的开销。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-17
      • 2021-02-14
      • 2012-03-25
      • 1970-01-01
      • 1970-01-01
      • 2021-04-14
      • 2014-08-08
      • 2017-06-03
      相关资源
      最近更新 更多