因此,我正在测量一些延迟,以了解在我的机器上执行一条添加指令所需的时间,以获得 CPI 的估计值。我首先编写了一个实现串行添加的线性版本(交错以利用管道)。然后我采用相同的代码并将添加的内容包装在一个循环中并重新评估它。我了解循环级并行性的影响,但我不明白它如何比仍应实现 DLP 的串行版本更快。我想可能是因为循环展开版本通过寄存器重命名更多地利用了管道,所以有更高的 IPC,但我也尝试增加线性版本的交错,但它并没有真正提高性能。我认为分支预测错误会导致循环版本慢很多,但事实并非如此。有什么想法吗?
#include <time.h>
#include <stdio.h>
#define ONE asm volatile( "add $20, %eax; add $10, %ecx");
#define FIVE ONE ONE ONE ONE ONE
#define TWOFIVE FIVE FIVE FIVE FIVE FIVE
#define HUNDO TWOFIVE TWOFIVE TWOFIVE TWOFIVE
#define THOUSAND HUNDO HUNDO HUNDO HUNDO HUNDO HUNDO HUNDO HUNDO HUNDO HUNDO
#define TENTHOUSAND THOUSAND THOUSAND THOUSAND THOUSAND THOUSAND THOUSAND THOUSAND THOUSAND THOUSAND THOUSAND
#define HUNDREDK TENTHOUSAND TENTHOUSAND TENTHOUSAND TENTHOUSAND TENTHOUSAND TENTHOUSAND TENTHOUSAND TENTHOUSAND TENTHOUSAND TENTHOUSAND
#define MILLION HUNDREDK HUNDREDK HUNDREDK HUNDREDK HUNDREDK HUNDREDK HUNDREDK HUNDREDK HUNDREDK HUNDREDK
static __inline__ unsigned long long rdtsc(void){
unsigned end, start;
__asm__ __volatile__("rdtsc" : "=a"(start), "=d"(end));
return ((unsigned long long)start) | (((unsigned long long)end)<<32);
}
int main(){
double CPI = 0;
long long start, end;
long long clocks;
int i;
start=rdtsc();
for(i=0; i < 10000; i++){
HUNDREDK
}
end=rdtsc();
//calculate the time elapsed in ns per access
clocks = end-start;
CPI = clocks/(double)(200000*10000); //divide by Number of instructions * loop
printf("Cycles Per Instruction %lf, Clocks %Ld\n", CPI, clocks);
}
两者之间的区别非常显着。线性版本的 IPC 约为 .2,循环版本的 IPC 约为 4。是的,我记得在评估这两个版本时更改了我除以的指令数量 :)
也许对我这样做的方式有些困惑,因为文件大小不是问题。我只是删除循环。两者处理不同数量的指令,但我也更改了最后除以的值。具有相同编译大小的结尾。
更新: 感谢您的回复。有几个问题。第一个是我进行测量的方式,一个版本的 IF 时间在整个循环中摊销,而另一个则没有。我运行了更多的代码,并且循环级别并行性的指令交错在循环中比在串行版本中更大。串行版本仍然有一些未重命名并导致管道停顿的写后写依赖项。
最佳答案
我的猜测是,因为您展开了如此多的迭代,所以代码非常大。不断将新的命令页面加载到缓存中的开销比迭代变量的开销要高得多。
就分支预测错误而言,循环实际上应该很少。它将预测最常使用的分支,正确率为 9999/10000 次。分支预测其实很好。
关于c++ - 为什么代码以线性方式比以循环方式运行得慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22030417/