您的位置:首页 > 其它

一则表驱动法的应用实例

2014-05-20 15:02 387 查看

1 需求场景

考虑如下需求场景:

终端按固定时间间隔(单位为分钟)生成诊断日志(格式为UserName-Status-yyyy-mm-dd-hh-mm.log),并上传至服务器。若终端与服务器的传输通道中断,则终端本地暂存最新的N个日志文件,即第(N+1)个周期生成的新日志将覆盖第1个周期的旧日志,以此类推。待传输恢复后,终端一次性上传该N个日志。

本文主要讨论该需求中“最新日志覆盖最旧日志”的功能点。为突出层次,文中“覆盖”用先删除旧日志后创建新日志实现。实际中先删后建和先建后删各有优点,前者适于内存受限(如仅允许存在N个固定大小的日志),后者更为安全(避免创建新日志失败但已删除旧日志)。

创建文件时调用现成库函数即可,而删除时需确定当前最旧的那个文件。下文将介绍几种删除判决的实现方式(时间复杂度依次降低)。

2 实现思路

2.1 比较文件的创建时间

Linux系统中文件没有创建时间的概念,只有访问时间(atime)、修改时间(mtime)和状态改动时间(ctime)。因该需求中日志创建后立即写入内容,其后内容和状态(权限与属性等)均不再改变,故可用mtime或ctime表征创建时间。

调用stat库函数即可获取日志的三种时间,比较各日志的mtime或ctime(整数)信息即可。

#include <sys/time.h>
#define TIME_ELAPSED(codeToTime) do{ \
struct timeval beginTime, endTime; \
gettimeofday(&beginTime, NULL); \
{codeToTime;} \
gettimeofday(&endTime, NULL); \
long secTime  = endTime.tv_sec - beginTime.tv_sec; \
long usecTime = endTime.tv_usec - beginTime.tv_usec; \
printf("[%s(%d)]Elapsed Time: SecTime = %lds, UsecTime = %ldus!\n", __FUNCTION__, __LINE__, secTime, usecTime); \
}while(0)

#include <unistd.h>
void CalcTime1(void){
unsigned short wFileIdx = 0;
for(; wFileIdx < 1; wFileIdx++){
//time_t精度为秒,批量测试文件创建时需作秒级延迟;
//但这样会影响计时统计,因为sleep会"淹没"CreateLogFile1(单次执行微妙级)
//sleep(1);
char szFileName[FILE_NAME_LEN] = {0};
sprintf(szFileName, "Log-Clover-%05d.log", wFileIdx);
CreateLogFile1(szFileName);
}
}
void CalcTime2(void){
unsigned short wFileIdx = 0;
for(; wFileIdx < 1000; wFileIdx++){
char szFileName[FILE_NAME_LEN] = {0};
sprintf(szFileName, "Log-Clover-%05d.log", wFileIdx);
CreateLogFile2(szFileName);
}
}
void CalcTime3(void){
unsigned short wFileIdx = 0;
for(; wFileIdx < 1000; wFileIdx++){
char szFileName[FILE_NAME_LEN] = {0};
sprintf(szFileName, "Log-Clover-%05d.log", wFileIdx);
CreateLogFile3(szFileName);
}
}

int main(void){
TIME_ELAPSED(CalcTime1());
TIME_ELAPSED(CalcTime2());
TIME_ELAPSED(CalcTime3());
DIR *pDir = NULL;
TIME_ELAPSED(pDir = opendir(gDirectoryName));
TIME_ELAPSED(readdir(pDir));
struct stat tFileStatus;          //文件状态信息
char szAbsFile[DIR_FILE_LEN] = {0};  //文件绝对路径
sprintf(szAbsFile, "%s/%s", gDirectoryName, "Log-Clover-00002.log");
TIME_ELAPSED(stat(szAbsFile, &tFileStatus));
TIME_ELAPSED(remove(szAbsFile));
FILE *pFile = NULL;
TIME_ELAPSED(pFile = fopen(szAbsFile, "a+"));
TIME_ELAPSED(fputs(szAbsFile, pFile));
TIME_ELAPSED(fclose(pFile));
TIME_ELAPSED(closedir(pDir));
TIME_ELAPSED(strcmp("Log-Clover-00000.log","Log-Clover-00001.log"));

TIME_ELAPSED({int i=0;for(;i<10000;i++){strcmp("Log-00000","Log-00001");}});
TIME_ELAPSED({int i=0;for(;i<10000;i++){int new=20;if(FILE_NUM!=new+1){int old=new+1;}}});
TIME_ELAPSED(int new=20;if(FILE_NUM!=new+1){int old=new+1;});

return 0;
}


View Code
统计结果如下所示:

对象

条件

耗时(us)

2.1: CalcTime1()

单次

792

2.2: CalcTime2()

单次

214

2.2: CalcTime2()

FILE_NUM=30,循环1000次

274357

2.4: CalcTime3()

FILE_NUM=30,循环1000次

77789

opendir()

单次

81

readdir()

单次

13

stat()

单次

9

remove()

单次

78

fopen()

单次

33

fputs()

单次

8

fclose()

单次

39

closedir()

单次

8

strcmp()

单次

2

strcmp()

循环10000次

27

Oldest = (Latest + 1) mod FILE_NUM

循环10000次

36

Oldest = (Latest + 1) mod FILE_NUM

单次

1

注意:计时结果仅作粗略参考,且每次统计时结果可能略有不同。

可见,文件操作函数(opendir、fopen等)的计时噪声会“淹没“比较代码,导致2.4节表驱动法相比其他方法效果不甚显著。同时考虑到表驱动法其实无需调用opendir/closedir函数,故执行效率可进一步提高。

5 总结

本文给出的表驱动法适用于如下场景:

在按照时空顺序依次创建的若干对象中,查找符合指定时空规则的某个对象(如第N个最老对象),而不关心对象内部信息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: