编辑:我原来的例子有一个愚蠢的错误。在修好之后,我仍然会得到奇怪的结果。
在我天真的尝试以“蛮力”的方式衡量我的CPU速度时,我制作了以下程序:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#pragma comment(linker, "/entry:mainCRTStartup")
#pragma comment(linker, "/Subsystem:Console")
int mainCRTStartup()
{
char buf[20];
clock_t start, elapsed;
unsigned long count = 0;
start = clock();
__asm
{
mov EAX, 0;
_loop:
add EAX, 3; // accounts for itself and next 2 instructions
cmp EAX, 0xFFFFFFFF - 0x400;
jb _loop;
mov count, EAX;
}
elapsed = clock() - start;
_gcvt(count * (long long)CLOCKS_PER_SEC / (elapsed * 1000000000.0), 3, buf);
puts(buf);
}
将其分解为:
mainCRTStartup:
push ebp
mov ebp,esp
sub esp,28h
mov dword ptr [count],0
call dword ptr [_clock]
mov dword ptr [start],eax
mov eax,0
_loop:
add eax,03h
cmp eax,0FFFFFBFFh
jb _loop
mov dword ptr [count],eax
call dword ptr [_clock]
sub eax,dword ptr [start]
... // call _gcvt, _puts, etc.
mov esp,ebp
pop ebp
ret
请注意,循环是3条指令,因此eax
的最终值应该是指令总数。
为什么我在运行时会得到4.2?
答案 0 :(得分:11)
因为instruction-level parallelism和superscalar architecture允许在单个pipelined时钟周期内执行多条指令。
例如,在您的代码中,branch prediction 通过以下方式有效消除<{strong> cmp
指令,除了最后一次_loop
次迭代:
cmp
和jb
,jb
分支。当然,(2)在 last 迭代中抛出,导致管道被清除。额外的~20个周期(对于20级流水线)可忽略不计,因为您的循环大约为10 ^ 9条指令。
编译器不应该优化这个
处理器硬件始终在数据路径中寻找优化机会;编译器只是尝试组织指令来利用给定体系结构的模式。例如,硬件流水线操作可以在没有software pipelining的情况下增加IPC,特别是对于相对hazard的免费代码,例如您的示例。
答案 1 :(得分:9)
由于CPU速度不是以每秒字节数来衡量,而是以每秒的指令周期来衡量,特别是在x86上,因此某些指令需要超过1个周期。
请参阅this page了解指令时间。 (实际上,这只能达到486 - 仍在寻找现代处理器的良好参考。)
答案 2 :(得分:2)
指令执行的周期数与字节的大小没有直接关系。除了具有多个执行单元和推测执行的现代CPU功能之外,还不可能事先确定给定的代码块需要多长时间才能执行的准确性。
答案 3 :(得分:1)
您可以使用 rdtsc 指令测量循环,该指令计算CPU内部频率的周期。两个读数之间的差异是经过的循环次数。让你的代码执行1000个循环,乘以3(循环中的指令)并除以经过的循环。这将为您提供每个周期的说明。然后,您可以根据CPU自己的频率进行扩展。
请记住,由于您的代码太短,很可能会从缓存级别1(或预取器内部)执行,这使得它仅对该情况有效,而不是一般的CPU。流水线可能太短,无法做出有价值的东西。
至于教学时间this page似乎比AShelly建议的更新。它由Torbjörn Granlund的Swedish Royal Institute of Technology定期重新计算。