freeeyes 发表于 2013-8-30 09:23:03

一个标准的时间超时成本宏

最近在帮同事处理一些比较复杂的逻辑过程,在此过程中,需要一个对函数调用时间成本进行判断的函数。
纵然,每次写time = Bgein(),time = end, end - beging 也行,但是看上去实在是太杂乱了,而且代码重复且无必要。
于是抽空,封装了一个时间成本的宏,利用类的构造和析构,达成目标。
实现的目标:
(1)代码要在windows和Linux都可以编译使用。
(2)代码计算要到毫秒级
(3)调用方式,在要监测的函数上只能有一行代码,力求最大的简洁。
(4)自动生成超时函数日志,记录发生时间,所在函数名,行数,文件名。
综合以上的需求,花了一上午写了出来。
代码不多,但是其中遇到的一些概念和问题。我留在这里,供大家做一个参考。
这个宏实现本身不复杂,对于时间的选用,这里正好做一个比较详细的测试。
首先,考虑到windows和Linux下通用,那么就要用time.h下的标准时间API,这样才能排除差异性。
首先考虑的最简单,就是time_t,通过asctime()一系列函数,实现时间的记录,但是问题是,这个在某些系统下只能到秒级。与需求(2)不符合。
于是考虑了time.h中的clock()一系列API,经测试,在windows下,Sleep(1000),可以获得正确的数值,但是在Linux,我用usleep(1000*1000),结果是0,很奇怪,于是查了一下man,发现clock是这么解释的,clock是记录进程占用CPU的运行时间,而在linux下,sleep()挂起是不计算为CPU时间的,所以永远是0,我查阅了windows的MSDN,发现这里的实现,和linux是有差异的,虽然这里也是进程时间,但是sleep()的挂起依旧计算在clock内,且获得的这个值明显是一个进程运行时间,而非当前时间。这个方法也只能PASS。
其实仔细想想,我只想知道一个相对时间差距,至于现在是何年月日,并无必要知道,问题的关键是,正确在不同平台下获得一样的结果,且在时间修改的前提下,我一样能获得时间相对的差值。
windows倒是有gettickcount(),那么,沿着这个思路,我有没有方法在Linux实现一个一样的东东?
答案是有的。 unsigned long GetSystemTickCount()
        {
#ifdef WIN32
                return GetTickCount();
#else
                struct timespec ts;

                clock_gettime(CLOCK_MONOTONIC, &ts);

                return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
#endif
        }clock_gettime是一个标准的time.h 提供的C++ API,号称能达到纳秒级别(1秒=1000毫秒 = 1000*1000微秒 = 1000*1000*1000纳秒)
但是从实际来看,windows和linux硬件上是有差异的,如果硬件不支持到纳秒级的终端(大部分硬件不支持),那么操作系统会random一个随机的三位数,填充微秒和纳秒,实际多数情况下,纳秒的获得是没有意义的。一般毫秒级别的就足够了,特殊需求另说。
我只要获得毫秒级别的就够了。
于是,看看clock_gettime支持哪些宏:
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户该成其他,则对应的时间相应改变CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间恩,看到这里,我非常满意,这里的宏完全可以满足我的需求。
材料准备完毕,厨师开始做菜了,呵呵。
首先,实现一个类,这个类是负责计算时间成本,并自动根据需要写入文件。//用于计算时间消耗类
class CTimeCost
{
public:
        CTimeCost(int nMillionSecond, const char* pFunctionName, const char* pFileName, int nLine)
        {
                m_nMillionSecond = nMillionSecond;
                sprintf(m_szFunctionName, "%s", pFunctionName);
                sprintf(m_szFileName, "%s", pFileName);
                m_nFileLine = nLine;
                TimeBegin();
        };

        ~CTimeCost()
        {
                TimeEnd();
        };

        void TimeBegin()
        {
                m_lBegin = GetSystemTickCount();
        };

        void TimeEnd()
        {
                m_lEnd = GetSystemTickCount();
                long lTimeInterval = m_lEnd - m_lBegin;//转换成毫秒
                if(lTimeInterval >= (long)m_nMillionSecond)
                {
                        char szLog = {'\0'};
                        //记录日志
                        FILE* pFile = fopen("TimeCost.log", "a");
                        if(pFile != NULL)
                        {
                                char szTimeNow = {'\0'};
                                time_t tNow = time(NULL);
                                struct tm* tmNow = localtime(&tNow);
                                sprintf(szTimeNow, "%04d-%02d-%02d %02d:%02d:%02d", tmNow->tm_year + 1900, tmNow->tm_mon + 1, tmNow->tm_mday, tmNow->tm_hour, tmNow->tm_min, tmNow->tm_sec);
                                sprintf(szLog, "[%s]dbTimeInterval more than (%d) < (%d), %s:%s:%d行.\n", szTimeNow, m_nMillionSecond, lTimeInterval, m_szFileName, m_szFunctionName, m_nFileLine);
                                fwrite(szLog, strlen(szLog), sizeof(char), pFile);
                        }
                        fclose(pFile);
                }
        };

private:
        unsigned long GetSystemTickCount()
        {
#ifdef WIN32
                return GetTickCount();
#else
                struct timespec ts;

                clock_gettime(CLOCK_MONOTONIC, &ts);

                return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
#endif
        }

private:
        long         m_lBegin;
        long         m_lEnd;
        unsigned int m_nMillionSecond;
        char         m_szFunctionName;
        char         m_szFileName;
        int          m_nFileLine;
};
这里的代码就不一行行解释了,C++有基础的人一看就懂。
然后,我来实现这个宏,#define __TIMECOST(cost) CTimeCost timecost(cost, __FUNCTION__, __FILE__, __LINE__);这里的cost,就是你设置的能容忍的超时时间,如果超过了这个时间,就会自动记录日志文件。
那么来看看,怎么用。class CTest
{
public:
        CTest() {};
        ~CTest() {};

        void func()
        {
                __TIMECOST(100);
#if WIN32
                Sleep(500);
#else
                usleep(500*1000);   //usleep单位是微秒
#endif
        }
};好了,我们来详细测试一下。int main(int argc, char* argv[])
{
        CTest Test;
        Test.func();
        getchar();
        return 0;
}代码在windows下和linux下,得到的结果一致。
输出日志结果为:(windows)
dbTimeInterval more than (100) < (500), d:\freeeyeswork\2013年\timecost\timecost\timecost.cpp:CTest::func:90行.
来看看Linux下的输出结果
dbTimeInterval more than (100) < (500), TimeCost.cpp:func:90行.

恩,达到目标需求,交付。同事很喜欢,一行代码达到目的。
此代码在windows和linux下测试通过。

页: [1]
查看完整版本: 一个标准的时间超时成本宏