C++ 计算函数的执行时间

在对程序进行性能评估时, 需要计算某个函数的执行耗时。思路是利用c++的RAII,在函数执行结束时,自动计算耗时并打印。

class Timer
{
public:
    Timer(const char* funcName)
    {
        _funcname = funcName;
        _begin = clock();
    }

    ~Timer()
    {
        _end = clock();
        _elapsed = _end - _begin;

        printf("Function name: %s\nElapsed : %f\n", _funcname ,_elapsed);
    }  

private:
    double        _begin;
    double        _end;
    double        _elapsed;
    const char*   _funcname;
};

下面是这个类的用法。

void function()
{
    Timer timer(__FUNCTION__);

    // Do your calculation
}

如你所见, 只需要把这个对象放在函数的最开始就可以了。 当程序的执行流程离开这个函数时, timer对象会被析构, 析构函数会被调用, 这样一来, 写在析构函数里的计时代码也就会被执行了。

但是这种做法难免有点丑陋,不妨用宏处理一下。 对需要计算耗时的函数,只要在函数首行写一个 marker 就可以了。也就是说,我想达到这样的效果:

void function()
{
    PROFILE_THIS();

    // Do your calculation
}

同时,如果function函数中出现了其他叫timer的变量,就会发生冲突。 这需要对变量名进行混淆。我的处理是在timer后面加当前行号。 为了做这件事,我们需要在预处理阶段进行符号拼接。

#define PROFILE_THIS_IMPL(funcname)    Timer CONCAT(_timer, __LINE__)(funcname)
#define PROFILE_THIS() PROFILE_THIS_IMPL(__FUNCTION__)

CONCAT是用于符号拼接的宏函数,如果当前的行号是43,那么这个宏代换后的结果是Timer _timer43(__FUNCTION)CONCAT的实现,并没有那么简单,这里有一个坑要踩。我们先看它的实现。

#define CONCAT_IMPL(x, y)  x##y
#define CONCAT(x, y) CONCAT_IMPL(x, y)

这里竟然多套了一层。 如果直接写CONCAT(x, y) x##y会发生什么呢?请看下面的代码

#define CONCAT(x, y) x##y

CONCAT(a, __LINE__)  // 结果是a__LINE__

也就是说,__LINE__这个宏并不会被预处理器替换。所以这里多加了一层的目的就是要让其他宏符号被替换。

在最终的Release版本中, 我们不再需要计算函数耗时。我希望对代码进行编译控制,也就是说,在Release模式下, PROFILE_THIS();失效, 那么我们只需要在Release模式下,使这个宏为空就好了。

#ifdef PROFILE
#    define PROFILE_THIS() PROFILE_THIS_IMPL(__FUNCTION__)
#else
#    define PROFILE_THIS()
#endif
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容