如何分析对函数的每次调用?

Posted

技术标签:

【中文标题】如何分析对函数的每次调用?【英文标题】:How to profile each call to a function? 【发布时间】:2009-07-03 11:47:37 【问题描述】:

我想以非标准方式分析我的执行情况。对给定函数使用 gprofValgrindOprofile...,我只能得到其执行时间的平均值。我想要的是获得这个执行时间的标准偏差。

例子:

void a()
  sleep ( rand() % 10 + 10 )

void b()
  sleep ( rand() % 14 + 2 )

main
  for (1 .. 100)
    a()
    b()

使用标准工具,ab 函数将具有相似的行为。你知道有什么工具可以自动给我这个结果吗?

我已经用 TAU 进行了测试,但直到现在,它并不真正相关。我认为这种方式是有解决方案的,但是我对 TAU 没有足够的信心。如果有人是 Tau 专家,我会尽量保持所有函数的执行时间,并在最后进行数学计算。但我不知道如何在 Tau 中指定它。

我想分析 C/C++ 代码,但如果你在其他编程语言方面有任何领先,我愿意。

【问题讨论】:

必须是自动的吗?只需编写几个宏并在表格中写下结果,然后您就可以计算出您想要的结果。 我会更好。如果不是,我必须为每个函数管理一个表,管理动态分配(无法预测函数调用的数量)。而如果你想对一个包含数百个函数的程序进行剖析,手动添加宏似乎是不现实的。 嗯,我帮不了你,但至于动态分配:你不需要它,只要有一个呼叫次数计数器和一个环形缓冲区。当然,您不会从所有调用中获得结果,但这两个应该可以帮助您缩小花费最多时间的功能。哦,如果你能以某种方式捕获函数调用,那么你可以让它自动化。 【参考方案1】:

分析工具并不神奇,只需几行代码,您就可以将自己的工具用于任何目的。

可能是这样的:

// code profile.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

class cProfile

public:
    // construct profiler for a particular scope
    // call at begining of scope to be timed
    // pass unique name of scope
    cProfile( const char* name )
    
        myName = string( name );
        QueryPerformanceCounter( (LARGE_INTEGER *)&myTimeStart );
    
    // destructor - automatically called when scope ends
    ~cProfile();

    // constructor - produces report when called without parameters
    cProfile();


private:
    typedef accumulator_set<__int64, stats<tag::variance(lazy)> > acc_t;
    static map < string, acc_t > myMap;
    string myName;
    __int64 myTimeStart;
;
map < string, accumulator_set<__int64, stats<tag::variance(lazy)> > > cProfile::myMap;


cProfile::~cProfile()

    __int64 t=0;
    QueryPerformanceCounter( (LARGE_INTEGER *)&t );
    t -= myTimeStart;


    map < string, acc_t >::iterator p = myMap.find( myName );
    if( p == myMap.end() ) 
        // this is the first time this scope has run
        acc_t acc;
        pair<string,acc_t > pr(myName,acc);
        p = myMap.insert( pr ).first;
    
    // add the time of running to the accumulator for this scope
    (p->second)( t );


// Generate profile report
cProfile::cProfile()

    __int64 f;
    QueryPerformanceFrequency( (LARGE_INTEGER *)&f );

    printf("%20s Calls\tMean (secs)\tStdDev\n","Scope");
    for( map < string, accumulator_set<__int64, stats<tag::variance(lazy)> > >::iterator p = myMap.begin();
        p != myMap.end(); p++ )
    
        float av = mean(p->second) / f;
        float stdev = sqrt( ((double) variance(p->second))  ) / f;
        printf("%20s %d\t%f\t%f\n",p->first.c_str(),
            boost::accumulators::count(p->second), av, stdev);
    

void a()

    cProfile profile("a"); 

    Sleep ( rand() % 10 + 10 );

void b()

    cProfile profile("b");

    Sleep ( rand() % 20 + 5 );



int _tmain(int argc, _TCHAR* argv[])

    for (int k=1;k<=100;k++) 
        a();
        b();
    

    cProfile profile_report;

    return 0;

哪个产生

       Scope Calls      Mean (secs)     StdDev
           a 100        0.014928        0.002827
           b 100        0.015254        0.005671

【讨论】:

【参考方案2】:

也许不适用,因为它是 gcc 特定的,但我发现这为我节省了一些 至少次。如果您使用 "-finstrument-functions" 标志编译代码,则使用此标志编译的模块中的每个函数的入口和出口点都将被对仪器函数的调用存根。您所要做的就是拥有一个可以读取一些高精度计数器的内联(例如 x86 上的 rdtsc,尽管请参阅this discussion)和大量记录:[ func_addr, is_enter, timer_value ],您将不断向其中写入在仪器功能中。退出后,将此数组转储到文件并离线分析。

与您可能正在寻找的“自动化”方法相去甚远 - 但希望这是有用的。下面的示例显示了 gcc 使用 -finstrument-functions 编译时的行为。如果您不包含该标志,它将“正常”工作。

#include <stdio.h>
#include <stdlib.h>

void __cyg_profile_func_enter(void *fn, void *call)
    __attribute__ ((no_instrument_function));
void __cyg_profile_func_exit(void *fn, void *call)
    __attribute__ ((no_instrument_function));

void __cyg_profile_func_enter(void *fn, void *call) 
  printf("Enter %x,%x\n",fn,call);

void __cyg_profile_func_exit(void *fn, void *call) 
  printf("Exit %x,%x\n",fn,call);


int foo(int i) 
  printf("inside foo\n");


int main(int argc, char *argv[]) 
  printf("inside main 1\n");
  foo(123);
  printf("inside main 2\n");
  exit(0);

【讨论】:

【参考方案3】:

我认为 Apple 的 Shark 分析工具可以为每个函数生成平均值。当然,这只对 Mac 有帮助。

【讨论】:

【参考方案4】:

实际上 Oprofile 可以从调用图视图中分析函数,这意味着具有不同调用者的相同被调用例程将在不同的统计信息中进行分析。

为报告尝试 opreport 命令。

【讨论】:

以上是关于如何分析对函数的每次调用?的主要内容,如果未能解决你的问题,请参考以下文章

如何对整个 python 项目进行分析?

分析特定函数 C++

如何使用strace+pstack利器分析程序性能

每次打开组件时如何调用函数?

如何查找对类的所有对象的所有调用

如何在每次测试之前重置 Jest 模拟函数调用计数