找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 3923|回复: 0

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

[复制链接]
发表于 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实现一个一样的东东?
答案是有的。
  1. unsigned long GetSystemTickCount()
  2.         {
  3. #ifdef WIN32
  4.                 return GetTickCount();
  5. #else
  6.                 struct timespec ts;
  7.                 clock_gettime(CLOCK_MONOTONIC, &ts);
  8.                 return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
  9. #endif
  10.         }
复制代码
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花费的时间

恩,看到这里,我非常满意,这里的宏完全可以满足我的需求。
材料准备完毕,厨师开始做菜了,呵呵。
首先,实现一个类,这个类是负责计算时间成本,并自动根据需要写入文件。
  1. //用于计算时间消耗类
  2. class CTimeCost
  3. {
  4. public:
  5.         CTimeCost(int nMillionSecond, const char* pFunctionName, const char* pFileName, int nLine)
  6.         {
  7.                 m_nMillionSecond = nMillionSecond;
  8.                 sprintf(m_szFunctionName, "%s", pFunctionName);
  9.                 sprintf(m_szFileName, "%s", pFileName);
  10.                 m_nFileLine = nLine;
  11.                 TimeBegin();
  12.         };
  13.         ~CTimeCost()
  14.         {
  15.                 TimeEnd();
  16.         };
  17.         void TimeBegin()
  18.         {
  19.                 m_lBegin = GetSystemTickCount();
  20.         };
  21.         void TimeEnd()
  22.         {
  23.                 m_lEnd = GetSystemTickCount();
  24.                 long lTimeInterval = m_lEnd - m_lBegin;  //转换成毫秒
  25.                 if(lTimeInterval >= (long)m_nMillionSecond)
  26.                 {
  27.                         char szLog[1024] = {'\0'};
  28.                         //记录日志
  29.                         FILE* pFile = fopen("TimeCost.log", "a");
  30.                         if(pFile != NULL)
  31.                         {
  32.                                 char szTimeNow[30] = {'\0'};
  33.                                 time_t tNow = time(NULL);
  34.                                 struct tm* tmNow = localtime(&tNow);
  35.                                 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);
  36.                                 sprintf(szLog, "[%s]dbTimeInterval more than (%d) < (%d), %s:%s:%d行.\n", szTimeNow, m_nMillionSecond, lTimeInterval, m_szFileName, m_szFunctionName, m_nFileLine);
  37.                                 fwrite(szLog, strlen(szLog), sizeof(char), pFile);
  38.                         }
  39.                         fclose(pFile);
  40.                 }
  41.         };
  42. private:
  43.         unsigned long GetSystemTickCount()
  44.         {
  45. #ifdef WIN32
  46.                 return GetTickCount();
  47. #else
  48.                 struct timespec ts;
  49.                 clock_gettime(CLOCK_MONOTONIC, &ts);
  50.                 return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
  51. #endif
  52.         }
  53. private:
  54.         long         m_lBegin;
  55.         long         m_lEnd;
  56.         unsigned int m_nMillionSecond;
  57.         char         m_szFunctionName[100];
  58.         char         m_szFileName[300];
  59.         int          m_nFileLine;
  60. };
复制代码
这里的代码就不一行行解释了,C++有基础的人一看就懂。
然后,我来实现这个宏,
  1. #define __TIMECOST(cost) CTimeCost timecost(cost, __FUNCTION__, __FILE__, __LINE__);
复制代码
这里的cost,就是你设置的能容忍的超时时间,如果超过了这个时间,就会自动记录日志文件。
那么来看看,怎么用。
  1. class CTest
  2. {
  3. public:
  4.         CTest() {};
  5.         ~CTest() {};
  6.         void func()
  7.         {
  8.                 __TIMECOST(100);
  9. #if WIN32
  10.                 Sleep(500);
  11. #else
  12.                 usleep(500*1000);   //usleep单位是微秒
  13. #endif
  14.         }
  15. };
复制代码
好了,我们来详细测试一下。
  1. int main(int argc, char* argv[])
  2. {
  3.         CTest Test;
  4.         Test.func();
  5.         getchar();
  6.         return 0;
  7. }
复制代码
代码在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下测试通过。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?用户注册

×
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-4-30 03:25 , Processed in 0.013448 second(s), 8 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表