11.14

2. 返转
返转wraparound)是指当时钟的时标计数器值到达最大值后,如果再增加就变为 0 
过程。12 小时制的模拟时钟在每天的正午和午夜各会进行一次返转。Windows 98 在连续
运行 49 天后会因 32 位毫秒时标计数器的返转而挂起(请参见 Q216641。当两位数的年
份返转时会发生 Y2K 问题。玛雅日历在 2012 年返转,因为玛雅人认为那就是世界末日。
UNIX 时间戳(自 UTC1970  1  1  00:00 起的带符号的 32 位秒数)会在 2038  1 
发生返转,这可能会称为某些“历史悠久”的嵌入式系统的“世界末日”。返转的问题出
在缺少额外的位去记录数据,导致下次时间增加后的数值比上次时间的数值小。会返转的
时钟仅适用于测量持续时间小于返转间隔的时间。
例如,在 Windows 上,GetTickCount() 函数会返回一个分辨率为 1 毫秒的 32 位无符号的
整数值作为时标计数值。那么,GetTickCount() 的返回值会每 49 天返转一次。也就是说,
GetTickCount() 适用于测量那些所需时间小于 49 天的操作。如果一个程序在某个操作开始
时和结束时分别调用了 GetTickCount(),两个返回值之间的差值就是两次调用之间经过的
毫秒数。例如:
DWORD start = GetTickCount();
DoBigTask();
DWORD end = GetTickCount();
cout << "Startup took " << end-start << " ms" << endl;
C++ 实现无符号算术的方式去确保了即使在发生返转时也可以得到正确的结果。
GetTickCount() 对于记住自程序启动后所经过的时间是比较低效的。许多“历史悠久”的
服务器可以持续运行数个月甚至数年。返转的问题在于,由于缺少位数去记录返转的次
数,end-start 的结果中可能体现不出发生了返转,或是体现出一个或者多个返转。
 Windows Vista 开始,微软加入了 GetTickCount64() 函数,它会返回一个 64 位无符号且
显示分辨率为 1 毫秒的时标计数值。GetTickCount64() 的结果只有在数百万年后才会发生
返转。这就意味着,几乎不会有人能够见证返转发生了。
3. 分辨率不是准确性
 Windows 上,GetTickCount() 会返回一个无符号的 32 位整数值。如果一个程序在某个
操作开始和结束时分别调用了 GetTickCount(),两个返回值之间的差值就是两次调用之间
经过的毫秒单位的执行时间。因此,GetTickCount() 的分辨率是 1 毫秒。
例如,下面这段代码通过在循环中反复调用 Foo(),在 Windows 上测量了名为 Foo() 的函
 数的相对性能。通过在代码块开始和结束时得到的时标计数值,我们可以计算出循环处理
所花费的时间:
DWORD start = GetTickCount();
for (unsigned i = 0; i < 1000; ++i) {
Foo();
}
DWORD end = GetTickCount();
cout << "1000 calls to Foo() took " << end-start << "ms" << endl;
如果 Foo() 中包含了大量的计算,那么这段代码的输出结果可能如下:
1000 calls to Foo() took 16ms
不幸的是,从微软网站中关于 GetTickCount() 的文档(https://msdn.microsoft.com/en-us/
library/windows/desktop/ms724408(v=vs.85).aspx)来看,调用 GetTickCount() 的准确性
可能是 10 毫秒或 15.67 毫秒。也就是说,如果连续调用两次 GetTickCount(),那么结果
之间的差值可能是 0 或者 1 毫秒,也可能是 1015  16 毫秒。因此,测量的基础精度
 15 毫秒,额外的分辨率毫无价值。之前代码块的输出结果可能会是 10ms20ms 或精
确的 16ms
GetTickCount() 特 别 让 人 沮 丧 的 一 点 是, 除 了 分 辨 率 是 1 毫 秒 外, 无 法 确 保 在 两 台
Windows 计算机中该函数是以某种方式或是相同方式实现的。
我在 Windows 上测试了许多计时函数,试图找出它们在某一台计算机(基于 i7 处理器的
Surface 3 平板电脑)的某个操作系统(Windows 8.1)上的可用分辨率。示例代码 3-1 中的
测试循环地调用了测量时间的函数,并检查这些连续的函数调用的返回值之间的差值。如
果时标的可用分辨率大于函数调用的延迟,那么这些连续的函数调用的返回值将要么相
同,要么它们之间的差值是若干个基础时标,单位是函数的分辨率。我计算了非零差值的
平均值,以排除操作系统偷用时间片段去执行其他任务的误差。
 
posted @ 2024-12-16 14:29  欧吼吼  阅读(2)  评论(0编辑  收藏  举报