【发布时间】:2014-03-28 15:12:15
【问题描述】:
因此,我正在测量一些延迟,即在我的机器上执行添加指令以获取 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 约为 0.2,循环版本的 IPC 约为 4。是的,我记得在评估两者时更改了除以的指令数量 :)
也许我这样做的方式有些混乱,因为文件大小不是问题。我只是删除循环。两者处理不同数量的指令,但我也更改了最后除以的值。以相同的编译大小结束。
更新: 感谢您的回复。有几个问题。第一个是我进行测量的方式,一个版本的 IF 时间在循环中被摊销,而另一个则没有。我运行了更多代码,并且循环级并行性的指令交错在循环中比在串行版本中更大。串行版本仍然有一些写入后写入依赖项,这些依赖项没有被重命名并导致管道停止。
【问题讨论】:
-
“有和没有循环”到底是什么意思?
-
当你在没有循环的情况下运行时,你记得要从 CPI 计算中删除
*10000,不是吗? =) -
如果我删除循环并线性运行代码,即手动展开它,那么它会慢得多。 @Arkku 是的,我做到了:)
-
在任何情况下,如果您将循环扩展为接近相同数量的
add操作,您的代码将变得如此之大,以至于通过缓存移动代码数据将占主导地位.我猜是代码大小与指令数量的问题。 -
@jeremycole,我在所有缓存和不同级别的指令/文件大小中测量了这一点,它仍然非常一致。
标签: c++ c performance architecture