注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

gmd20的个人空间

// 编程和生活

 
 
 

日志

 
 

格式化时间戳字符串时snprintf的性能问题  

2013-01-05 17:52:38|  分类: 程序设计 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一个典型的格式化时间代码如下

int main (int, char**)
{

LARGE_INTEGER freq, t0, t1;
QueryPerformanceFrequency(&freq);
size_t number = 10000000;

int total_counter = 0;

char cachedTimeString[128];
struct timeb timebuffer;
ftime(&timebuffer);
struct tm * now = localtime(&timebuffer.time);

QueryPerformanceCounter(&t0);
for (int i=0; i< number; i++) {

//int writtenLen = _snprintf(cachedTimeString,128,
// "%04d-%02d-%02d %02d:%02d:%02d.%03u",
//1900 + now->tm_year, now->tm_mon + 1,now->tm_mday,now->tm_hour,now->tm_min,
//now->tm_sec,timebuffer.millitm);

int writtenLen = strftime(cachedTimeString,128,"%Y-%m-%d %H:%M:%S.",now);
int a = timebuffer.millitm;
int b = a % 100;
cachedTimeString[writtenLen] = '0' + a/100;
cachedTimeString[writtenLen+1] = '0' + b/10;
cachedTimeString[writtenLen+2] = '0' + b % 10;
cachedTimeString[writtenLen+3] = '\0';

total_counter += writtenLen;
}
QueryPerformanceCounter(&t1);

std::cout << cachedTimeString << std::endl;
int time = (((t1.QuadPart-t0.QuadPart)*1000000)/freq.QuadPart);
std::cout << "执行 " << number <<" 次, 耗时 " << time << " 微秒" << std::endl;

std::cout << total_counter;
int a;
cin >> a;
return 0;
}




因为现在的log的时间戳还没有使用cache机制,每条语句都去获取系统时间然后格式化。使用性能测试工具测试发现,相对于ftime的性能开销,接下来的snprintf的开销更多,在某个模块里面,ftime只占 2%到 4%的cpu利用率,但snprntf却占到40%以上。看上去应该抓哟是消耗在那个整型数的输出上面了,使用snprintf的时候,指定了数字的最小长度,snprintf先要解析出这个最小长度了这个地方也是比较耗时的吧。改成用strftime之后,少了这个解析工作,应该已经预先知道时间的数字的长度了。测试的上面的代码,strftime的版本大概比snprintf的版本要快7倍左右。


这个时间格式化代码性能消耗还是很大的,所以最后还是想nginx那样使用cache机制,把这个时间字符串缓存起来,一般log也不需要很大的精度,也可以减少gettimeofday函数的调用。 用个1毫秒的定时器,定时更新时间,然后象nginx那样每次事件完成之后也更新一下时间,这样可以避免事件驱动程序时间超时事件得不到及时响应的问题,保证全局time 得到及时更新。不过如果多线程,这个时间字符串的cache的更新还是需要注意同步问题,是用多个cache,每次循环更新,减少更新的时候写和读的冲突,nginx好像就是这样的。


另外,即使每条log都要格式化时间字符串,也可以发现时间输出的前面部分到秒为止都是一样的,没必要每次都格式化,把每秒的时间做个cache,然后一秒内的时间都用这个cache的就可以恶劣,像下面这样。可以减少大量的格式化调用。 总的来说snprintf的用来输出整数的时候性能是比较差的,特别是你指定了最小输出长度的时候,如果可能的话,尽量用strcpy 等字符串操作取代它吧。



int UpdateTimeString(char * outputTimeString, int size)
{
int writtenLen = 0;
struct timeb timebuffer;

#ifdef WIN32
// compare to ftime, about 50% faster.
FILETIME systemTimeAsFileTime;
GetSystemTimeAsFileTime(&systemTimeAsFileTime);
ULARGE_INTEGER ltime;
ltime.LowPart = systemTimeAsFileTime.dwLowDateTime;
ltime.HighPart = systemTimeAsFileTime.dwHighDateTime;
//1970-01-01 00:00:00 +0000 (UTC) - January 1, 1601 UTC = 11644473600 seconde
//timebuffer.time = ltime.QuadPart/10000000 - 11644473600;
//timebuffer.millitm = (ltime.QuadPart / 10000 ) % 1000;

// 这个比上面那个减少一次除法操作? 从nginx代码里面学来的
// 下面这两个除法的性能消耗要比GetSystemTimeAsFileTime大的多
// 每个除法30% 左右,GetSystemTimeAsFileTime才 1.2%
ltime.QuadPart -= 116444736000000000;
timebuffer.time = ltime.QuadPart / 10000000;
timebuffer.millitm = (ltime.QuadPart % 10000000)/10000;
#else
    // linux 用gettimeofday 应该比 ftime要快吧? 
ftime(&timebuffer);
#endif
f

static time_t lastTime = 0;
static char lastTimeString[32] = "";

if (timebuffer.time == lastTime) {
strcpy(outputTimeString, lastTimeString);
//writtenLen += strlen(outputTimeString);
//2013-01-11 13:07:57.
writtenLen += 20;
} else {
lastTime = timebuffer.time;
struct tm * now = localtime(&timebuffer.time);
// strftime is about 7 times faster than snprintf
writtenLen += strftime(lastTimeString,32,"%Y-%m-%d %H:%M:%S.",now);
strcpy(outputTimeString, lastTimeString);
}

int a = timebuffer.millitm / 100;
int b = timebuffer.millitm % 100;
outputTimeString[writtenLen] = '0' + a;
outputTimeString[writtenLen+1] = '0' + b / 10;
outputTimeString[writtenLen+2] = '0' + b % 10;
outputTimeString[writtenLen+3] = '\0';
writtenLen +=3;

return writtenLen;
}
加了每秒的字符串cache之后,之前的snprintf占40%的被消除了,strcpy那里只占0.4%左右了,作用还是很明显的。主要的消耗是在ftime上面了。这个职能在前面说的使用cache的办法,才能避免这个调用了。


 
  评论这张
 
阅读(785)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017