处理器基准测试实用程序
测量处理器性能
引言
本文讨论了一个处理器基准测试工具。结果令人惊讶。就像如今的万事万物都会打破我们的固有观念一样,处理器也不例外。
就在前几天,我收到了一封关于21世纪被打破的固有观念的电子邮件。内容大致是这样的:“……最高的篮球运动员是中国人……最受欢迎的说唱歌手是白人,而最好的高尔夫球员是……”。所以,看到一个打破固有观念的处理器基准测试,也就不足为奇了。
背景
我一直很好奇新一代处理器性能如何。它们有多快?不仅仅是一个代表处理器整体的数字,而是一个真实的值,代表在给定时间内处理器可以执行多少给定类型的操作。例如,每秒执行多少次整数加法。每秒执行多少次双精度除法?……

Using the Code
代码的开发是为了使用宏作为简化的手段。这样,宏参数就可以表示任何所需的基准测试操作。宏 `TIMEDOP`(定时操作)接受两个参数
- 要生成的函数名称
- 在定时循环中执行的代码
//
// The timing loop generation macros. You may roll your own by changing the second
// argument to the macro to a piece of code.
// If you use variables in the code snippet, make sure you declare them first.
//
// Here is an example of tinkering between add and increment:
//
// TIMEDOP(do_int, count2++ );
// TIMEDOP(do_int, count2 +=3 );
//
// Note the missing (or optional) semicolon (;) at the end of the snippets.
TIMEDOP(do_int, count2++);
TIMEDOP(do_mul, count *= 33); // I chose the constants randomly
TIMEDOP(do_div, count /= 13); // they have no influence on the timing
TIMEDOP(do_sub, count -= 10);
TIMEDOP(do_mod, count %= 13);
TIMEDOP(do_str, memcpy(str, str2, 1024));
TIMEDOP(do_str2, memcpy(str, str2, 1));
TIMEDOP(do_dbladd, dop2 += dop1; dop1+=3);
TIMEDOP(do_dbladd3, dop2 = dop1 + dop3; dop1+= 2);
TIMEDOP(do_dbldiv, dop2 /= 103);
TIMEDOP(do_dblsin, dop2 = sin(dop1); );
TIMEDOP(do_func, noop(count2));
TIMEDOP(do_cos, cos(dop1););
TIMEDOP(do_tan, tan(dop1););
TIMEDOP(do_sqrt, sqrt(dop1););
// After declaring, one may call the function as follows:
int count = do_int(); // count receives the count of instructions / 100
// To make sure the time elapsed is accurate, the performance counter is
// queried twice, as to measure how long it takes to query the performance counter.
QueryPerformanceCounter(&PerformanceCount);\
double dd = largeuint2double(PerformanceCount);\
QueryPerformanceCounter(&PerformanceCount3);\
double skew = largeuint2double(PerformanceCount3) - dd;\
// Then, in the main loop, every time the performance counter is
// queried, the code extends the expected time by the time it
// took the performance counter to respond
... in the while loop ...
timeforonesec += skew; /* Compensate for skew */
if(currentcount - startcount > timeforonesec)
break;
测量精度
自然,有很多进程在竞争处理器资源。因此,测得的基准结果会有很大的波动。为了弥补这一点,该工具会保持一个运行平均值。
为了测试代码的准确性,并证明补偿技巧是否按预期工作,我陷入了一个两难的境地。老的海森堡不确定性原理显现出来,我无法观察处理器速度,因为观察进程干扰了结果。换句话说,我尝试对处理器做的任何事情都会干扰测量。
最后,为了测试补偿代码,我在一个测试中执行了基准测试代码片段(来自一个宏)一百次,在另一个测试中执行了五十次。如果补偿代码工作正常,第二个测试的结果应该是第一个测试的两倍。它奏效了!太棒了!
我想到了测量处理器速度涉及到与量子力学相关的理论。太酷了。这难道不是编程的全部意义吗?
描述的基准测试项目
以下是基准测试屏幕上各项的简要说明
函数名 | 代码 | 描述 | 注释 | |
do_int, |
count2++ |
简单的整数加法和/或增量 | 增量和加法速度相同 | |
do_mul, |
count *= 33 |
简单的整数乘法 | 速度几乎与加法一样快 | |
do_div |
count /= 13 |
|
比双精度加法慢 | |
do_mod |
count %= 13 |
简单的整数取模 | 速度与除法相同 | |
do_str |
memcpy(str, str2, 1024) |
复制1k字符串 | 涉及内存。这是一个很好的测试,看看DDR 400升级是否有效。 | |
do_str2 |
memcpy(str, str2, 1) |
复制1字节字符串 | 库函数调用开销 | |
do_dbladd |
dop2 += dop1; dop1+=3 |
加两个双精度数 | 优化器知道第二个变量没有改变,所以我们对其进行了填充。 | |
do_dbladd3 |
dop2 = dop1 + dop3; dop1+= 2 |
加两个双精度数,并赋值 | 也进行了填充 | |
do_dbldiv |
dop2 /= 103 |
双精度除法 | 比整数运算快 | |
do_dblsin |
dop2 = sin(dop1) |
三角函数 | 比预期慢 | |
do_func |
noop(count2) |
调用一个空函数 | 调试版本慢很多 | |
do_cos |
cos(dop1) |
三角函数 | 所有三角函数执行速度相似 | |
do_tan |
tan(dop1) |
三角函数 | ||
do_sqrt |
sqrt(dop1) |
平方根 | 处理器在这里工作很努力 |
调试版本
调试版本与发布版本的性能相似。唯一显著的区别在于函数调用。这(可能)是由于调试器跟踪函数调用堆栈的开销造成的。
发布版本
我们不得不禁用优化,因为它(自然)会破坏代码。优化器知道我们在无缘无故地进行重复计算和重复调用,所以它会将其优化掉。
关注点
我用这个工具测试了几种处理器(Core2、Sempron、AMD_64)。令我惊讶的是,我发现过去学到的许多格言都被打破了。例如,在学习C或C++时,你会发现建议:“`variable++`”比“`variable += 1`”更快。根据基准测试,`var1 += var2`和`var1++;`的速度相同。
另一个常识是,处理双精度数比处理整数慢。根据基准测试结果并非如此。在我的Athlon 64上,`int`除法每秒45000次,而`double`除法每秒153000次。
整数乘法和加法一样快。这也是一个意想不到的结果。
如果所有这些听起来难以置信,那么请务必自己测试一下。尝试运行代码,如果代码有错误,我希望收到反馈。
反馈
请将您处理器的截图发送给我(peterglen@verizon.net)。
历史
- 2008年5月29日:初始版本