最近在帮同事处理一些比较复杂的逻辑过程,在此过程中,需要一个对函数调用时间成本进行判断的函数。
纵然,每次写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[1024] = {'\0'};
- //记录日志
- FILE* pFile = fopen("TimeCost.log", "a");
- if(pFile != NULL)
- {
- char szTimeNow[30] = {'\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[100];
- char m_szFileName[300];
- 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)
[2013-08-29 11:56:37]dbTimeInterval more than (100) < (500), d:\freeeyeswork\2013年\timecost\timecost\timecost.cpp:CTest::func:90行.
来看看Linux下的输出结果
[2013-08-29 13:49:13]dbTimeInterval more than (100) < (500), TimeCost.cpp:func:90行.
恩,达到目标需求,交付。同事很喜欢,一行代码达到目的。
此代码在windows和linux下测试通过。
|