如何实现实时统计速度?
其实这个说起来不难。但是实际操作确实会有些问题的,稍不注意,就得不到自己要的效果。
首先要明白时间统计的原理。在统计速度时,可不要使用系统时间来计算,因为那样也太不准确了。使用函数GetTickCount精度稍微高些,这个精度是ms级别,对于一般的场合都使用,对于严格的要求,还是不够,那就要使用多媒体计时器或者高精度计时器。这两个的资料,请查阅MSDN。
时间统计,其实就是在读取写入或者下载上传时,完成一批数据后,使用这批数据量除以使用的时间,得到的就是速度。这个道理大家都懂。但是实际操作确实有些问题的。下面来看看。
在循环中实时显示速度,也就是说,读取1GB数据,每次读取1MB,那么久是10GB除以1MB次,即1K(1000)次。在这1000次中,每操作一次数据,就会形成数据流,完成操作后,就花费了时间,这时使用数据量除以时间的得到的自然就是实时的速度。所以我们会在操作前获得一次时间,操作后获得时间,得到差值,差值就是使用的时间。速度就出来了。但是有时候发现,差值时间是0.这样程序崩溃,出现除0错误。你知道是为什么吗?
这个问题就是我们经常遇见的问题。刚开始做这个,以为是很简单。但是做着却发现很不如意,也不知道问题出在哪了。接下来,与你说说原因。
上面提到了精度的问题。GetTickCount的精度是毫秒级别。加入每次操作的数据量很小,在现代的CPU环境下,处理速度极快,毫秒级实在是太长了,所以就不精确了。数据量小,一毫秒都没用,数据就处理完了。开始和结束的毫秒值一样,这样时间差就为0了,这样就出现了除零错误。这种问题,刚接触这个方面的编程的往往就不知道,以为很简单,实则没那么简单。在这种情况下,要处理好精度问题,就要想办法了,要让时间形成差值不为零即可。当然不要人为的延时,那是错误的。
解决精度问题,可以从两个方面着手。
1. 第一个就是数据量,保证足够的数据量,就可以保证需要一定毫秒数的时间才能完成。比如说我把每次操作的数据量提到100MB,这样,自然就耗时比较长,加入总线速度为100M/S,这样一次操作就要耗时1S多(考虑还有其他统计操作耗时),这样就把精确度提到秒级级别,那么GetTickCount作为毫秒级的代表,完全是没有问题的。
2. 第二个就是拉长统计时间。使用取模操作,每个一定数量统计一次结果,这样时间的操作次数的累积,知道累积的数据量和累积的时间,自然也就是把精确度拉到毫秒级或者秒级了,这样也不存在精度问题。
不管你是用什么方法,都是围绕这两个方法来的。当然,也可以使用精确度更高的,比如纳秒级的。不过一般情况下用不着。
下面说明一下GetTickCount函数的使用。
原型声明:
DWORD GetTickCount(VOID);
直接返回一个DWORD值,这个值代表距离系统启动的时间值,每次调用就得到调用时相对于系统启动时间的值。要统计相隔时间,只要调用两次,相减就得到两次统计之间的差值。这个数学道理很简单的。这个函数存储的值得类型是DWORD类型,因此,持续统计到49.7天后又归零了。
使用此函数需要包含Winbase.h和Windows.h两个头文件。Windows系列平台都支持。
下面给出基本使用代码示例:
DWORD dwStart = GetTickCount();// - 这中间是你操作数据操作的地方
memset(缓冲地址,数值,字节大小);// - 填充数据
DWORD dwEnd = GetTickCount();
DWORD dwSpan = dwEnd - dwStart;// - 得到毫秒数
DWORD dwSpeed = 数据字节量*1000/1024/dwSpan;// - 得到KB/s
代码注意:1. 提高数据量:把填充的字节数提高到一定的数值,多尝试。
2. dwStart 和 dwEnd 相隔要足够长,保证dwSpan 不能为零,最好对dwSpan进行检测,确保不为零。
以下是MFC中使用的代码:
CFile file;
file.Open(_T("F:\\test.dat"),CFile::modeReadWrite | CFile::modeCreate);
BYTE * pBuf = new BYTE[1024*1024];
memset(pBuf,0x00,1024*1024);
DWORD dwBeginTime = GetTickCount();
for(int i=0;i<1024;i++)
{
file.Write(pBuf,1024*1024);
if (i%50==0)
{
DWORD dwEndTime;
DWORD dwSpanTime;
dwEndTime = GetTickCount();
dwSpanTime = dwEndTime - dwBeginTime;
CString str;
str.Format(_T("速度:%d M/S ,用时:%d S"),(i*1000/dwSpanTime),dwSpanTime/1000);
//::MessageBox(NULL,str,_T("speed"),MB_OK);
}
}
delete []pBuf;
以上代码就不仔细解释了。核心结构都是上文解释过的。如果时间隔得远就是那一段时间的平均速度,如果时间相隔短,则近似为瞬时速度。