需要分析帮助

我有一个分析问题 - 想象我有下面的代码...

void main()
{
    well_written_function();
    badly_written_function();
}
void well_written_function()
{
    for (a small number)
    {
        highly_optimised_subroutine();
    }
}
void badly_written_function()
{
    for (a wastefully and unnecessarily large number)
    {
        highly_optimised_subroutine();
    }
}
void highly_optimised_subroutine()
{
    // lots of code
}

如果我在vtune(或其他分析器)下运行它,很难发现任何错误。 所有的热点将出现在已经优化过的标记为“//许多代码”的部分。 badly_written_function()不会以任何方式突出显示,即使它是造成所有麻烦的原因。

vtune有一些功能可以帮助我找到问题吗?

是否有某种模式可以让我找到badly_written_function() 及其所有子函数所花费的时间?


这通常被称为“callgraph profile”,我相当确信Visual Studio会这样做。


滚动你自己的非常简单的分析器并不难。 插入到main()中:

int main()
{
    profileCpuUsage(1);                 // start timer #1
    well_written_function();
    profileCpuUsage(2);                 // stop timer #1, and start timer #2
    badly_written_function();
    profileCpuUsage(-1);                // print stats for timers #1 and #2
    return 0;
}

哪里:

#define NUMBER(a) ((int)(sizeof(a) / sizeof(a)[0]))

void profileCpuUsage(int slice)
{
    static struct {
        int iterations;
        double elapsedTime;
    } slices[30];                             // 0 is a don't care slice

    if (slice < 0) {                          // -1 = print
        if (slices[0].iterations)
            for (slice = 1; slice < NUMBER(slices); slice++)
                printf("Slice %2d  Iterations %7d  Seconds %7.3fn", slice,
                    slices[slice].iterations, slices[slice].elapsedTime);
    }
    else {
        static int i;                         // = previous slice
        static double t;                      // = previous t1
        const double t1 = realElapsedTime();  // see below for definition
        assert (slice < NUMBER(slices));
        slices[i].iterations  += 1;
        slices[i].elapsedTime += t1 - t;      // i = 0 first time through
        i = slice;
        t = t1;
    }
}

现在,无可否认,在使用此profileCpuUsage()的简单示例中,不会带来太多好处。 它的缺点是要求您通过在适当的位置调用profileCpuUsage()手动测试代码。

但优点包括:

  • 你可以定时任何代码片段,而不仅仅是程序。
  • 您可以快速添加和删除,就像执行二分查找来查找和/或删除代码热点一样。
  • 它只关注你感兴趣的代码。
  • 便携!
  • 一个棘手的不可移植的事情是定义函数realElapsedTime(),以便它提供足够的粒度以获得有效时间。 这通常适用于我(使用CYGWIN下的Windows API):

    #include <windows.h>
    double realElapsedTime(void)   // <-- granularity about 50 microsec on test machines
    {
        static LARGE_INTEGER freq, start;
        LARGE_INTEGER count;
        if (!QueryPerformanceCounter(&count))
            assert(0 && "QueryPerformanceCounter");
        if (!freq.QuadPart) {      // one time initialization
            if (!QueryPerformanceFrequency(&freq))
                assert(0 && "QueryPerformanceFrequency");
            start = count;
        }
        return (double)(count.QuadPart - start.QuadPart) / freq.QuadPart;
    }
    

    对于直接的Unix有一个共同点:

    double realElapsedTime(void)                      // returns 0 first time called
    {
        static struct timeval t0;
        struct timeval tv;
        gettimeofday(&tv, 0);
        if (!t0.tv_sec)
            t0 = tv;
        return tv.tv_sec - t0.tv_sec + (tv.tv_usec - t0.tv_usec) / 1000000.;
    }
    

    realElapsedTime()给出了挂钟时间,而不是处理时间,这通常是我想要的。

    还有其他便于使用RDTSC实现更精细粒度的方法; 请参阅http://en.wikipedia.org/wiki/Time_Stamp_Counter及其链接,但我没有尝试过这些。

    编辑: ravenspoint的非常好的答案似乎与我的不太相似。 的回答很好的使用描述性字符串,而不仅仅是难看的数字,我经常与沮丧。 但是这可以通过仅十几行额外的行来解决(但是这几乎使行数加倍!)。

    请注意,我们希望避免使用malloc(),并且我甚至对strcmp()有点怀疑。 所以切片的数量永远不会增加。 散列冲突只是被标记为相当被解析:人类分析器可以通过手动从30增加切片的数量或者通过改变描述来解决这个问题。 未经测试

    static unsigned gethash(const char *str)    // "djb2", for example 
    {
        unsigned c, hash = 5381;
        while ((c = *str++))
            hash = ((hash << 5) + hash) + c;    // hash * 33 + c 
        return hash;
    }
    
    void profileCpuUsage(const char *description)
    {
        static struct {
            int iterations;
            double elapsedTime;
            char description[20];               // added!
        } slices[30];
    
        if (!description) {
            // print stats, but using description, mostly unchanged...
        }
        else {
            const int slice = gethash(description) % NUMBER(slices);
            if (!slices[slice].description[0]) { // if new slice
                assert(strlen(description) < sizeof slices[slice].description);
                strcpy(slices[slice].description, description);
            }
            else if (!!strcmp(slices[slice].description, description)) {
                strcpy(slices[slice].description, "!!hash conflict!!");
            }
            // remainder unchanged...
        }
    }
    

    另一点是,通常你会想要禁用发布版本的分析; 这也适用于ravenspoint的答案。 这可以通过使用邪恶宏定义它的技巧来完成:

    #define profileCpuUsage(foo)                // = nothing
    

    如果这样做,您当然需要在定义中添加圆括号以禁用禁用宏:

    void (profileCpuUsage)(const char *description)...
    

    我可以建议我自己的开源探测器raven :: set :: cRunWatch吗? 它专门针对这个问题而设计,并且在Windows上使用Visual Studio 2008标准版进行工作,所以您不需要为包含分析器的版本付费。

    我已经把你的代码重新安排了一下,这样它就可以在没有前向声明的情况下进行编译,并将必要的调用添加到cRunWatch

    // RunWatchDemo.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    
    void highly_optimised_subroutine()
    {
        raven::set::cRunWatch runwatch("highly_optimised_subroutine");
        Sleep( 2 );
    }
    
    
    void badly_written_function()
    {
        raven::set::cRunWatch runwatch("badly_written_function");
        for (int k = 1; k < 1000; k++ )
        {
            highly_optimised_subroutine();
        }
    }
    
    void well_written_function()
    {
        raven::set::cRunWatch runwatch("well_written_function");
       for (int k = 1; k < 10; k++ )
        {
            highly_optimised_subroutine();
        }
    }
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    raven::set::cRunWatch::Start();
    
        well_written_function();
        badly_written_function();
    
    raven::set::cRunWatch::Report();
    
        return 0;
    }   
    

    运行时会产生输出

    raven::set::cRunWatch code timing profile
                        Scope   Calls       Mean (secs)     Total
    highly_optimised_subroutine     1008    0.002921        2.944146
       badly_written_function        1      2.926662        2.926662
        well_written_function        1      0.026239        0.026239
    

    这表明badly_written_function是非常接近的第二次使用者,因此是罪魁祸首。

    您可以从这里获取cRunWatch您将认识到用户指南中的示例代码:-)

    链接地址: http://www.djcxy.com/p/1163.html

    上一篇: Profiling help required

    下一篇: Is it possible to modify the content of HttpRequest POST in an IIS HttpModule?