【问题标题】:Profiling for runtime in C在 C 中为运行时进行分析
【发布时间】:2023-03-17 08:07:01
【问题描述】:

我正在尝试找到一种方法,当我的工具运行时,我可以写出选定数量的函数的运行时间。假设我的工具拥有的大量功能列表中,我需要写出单个调用 y()、z()、a1() ....所花费的时间。

X(){

 y();
 ..
 ..
 z();
 ..
 ..
 a1();
 ..
 ..
 b1();
 ..
 ..
}

我不想使用 gprof,因为它需要单独运行,而且客户无法在那里运行 gprof。在许多情况下,我无法访问产生长时间运行的测试。一种记录高级函数运行时间的方法将很有帮助。我不想在 time() 系统调用周围环绕每个函数调用,然后打印它。宏可以帮助吗?有什么想法吗?

【问题讨论】:

    标签: c profiling


    【解决方案1】:

    您说您不想包装每个调用,但让我向您展示我如何处理计时器调用,以便可以在编译时将其删除,也许以后可以将其包装在另一个宏中:

    #ifndef NOTIME
    #  include <ctime>
    #  define CLOCK_TICK(acc, ctr)  ctr = std::clock()
    #  define CLOCK_TOCK(acc, ctr)  acc += (std::clock() - ctr)
    #  define CLOCK_RESET(acc) acc = 0
    #  define CLOCK_REPORT(acc) 1000. * double(acc) / double(CLOCKS_PER_SEC)
    
    static clock_t t1a, t1c, t2a, t2c; // as many pairs as you need
    
    #else
    #  define CLOCK_TICK(acc, ctr)
    #  define CLOCK_TOCK(acc, ctr)
    #  define CLOCK_RESET(acc)
    #  define CLOCK_REPORT(acc) 0
    #endif
    

    现在你可以像这样使用它了:

    CLOCK_RESET(t1a);
    for (int i = 0; i != 250000; ++i)
    {
      CLOCK_TICK(t1a, t1c);
      some_expensive_function();
      CLOCK_TOCK(t1a, t1c);
    }
    std::cout << "Time spent in some_expensive_function(): " << CLOCK_REPORT(t1a) << "ms.\n";
    

    使用可变参数宏,您甚至可以结束函数调用:

    #define timed_call(ACC, CTR, func, ...) do { CLOCK_TICK(ACC, CTR); \
                                                 func(__VA_ARGS__);    \
                                                 CLOCK_TOCK(ACC,CTR);  \
                                               } while (false)
    

    【讨论】:

    • 所以现在对于每个函数调用我需要做两个调用一个正常的另一个,它被包装在 timed_call() 中?
    • 问题是在这种情况下,我需要更改一大堆函数调用并将其包装在 timed_call() 周围。
    • @David:是的,这种方法有很多限制……你可以将函数foo(x,y,z); 称为timed_call(t1a, t1c, foo, x, y, z);。我认为最重要的改进是允许返回值。我不确定你是否可以在不接触任何代码的情况下完全添加此功能...
    【解决方案2】:

    您的客户不需要运行 gprof。如果您使用 profiling 编译可执行文件,他只需要正常运行应用程序,然后收集 gmon.out 数据文件并将其交付给您。然后,您将运行 gprof 以生成分析数据。

    如果您想更好地控制代码的哪些部分被分析,那么您可以尝试开源Shiny 分析器。要使用它,您可以链接到一个小型 C++ 库,并在要分析的每个函数的开头插入一个宏。还有用于分析代码块的选项。分析器输出是通过应用程序的函数调用生成的,因此您可以决定如何以及何时生成输出。

    【讨论】:

    • 分析代码不是让程序变得更大更慢吗?
    • 是的,当然。我想这只会在性能问题得到解决之前使用。
    • @Miguel 感谢您提供指向 Shiny Profiler 的链接。向客户提供检测构建 (-pg) 是不可能的。检测构建运行非常缓慢。我没有找到关于 Shiny Profiler 的太多文档。这个用途广泛吗?
    【解决方案3】:

    为什么不用这样的宏来包装每个调用:

    #define WRAP(fcall) do {\
      .. before stuff .. \
      fcall; \
      .. after call .. \
      } while(0)
    

    然后代替每个

    foo(x, y, z);
    

    你写

    WRAP(foo(x, y, z));
    

    如果你想禁用它,只需注释掉之前/之后的东西。

    它可能不漂亮,但它有效。

    【讨论】:

    • 这是一个非常好的方法......唯一的问题是现在我需要环绕我的所有函数(这是围绕 WRAP() 调用的大数字)。有什么办法可以将所有函数包装在 WRAP() 周围的文件中?
    • @David:编辑器宏?这可能需要一整天,但一旦完成,它就完成了。
    • 我认为这不是只做一次的问题.. 更大的问题是它使代码看起来很难看,尤其是当它分散在许多函数中的代码周围时。
    • @David:丑陋在旁观者的眼中 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-25
    相关资源
    最近更新 更多