因此,我正在测量一些延迟,即在我的机器上执行添加指令需要多长时间来估算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时间在整个循环中摊销,而另一个版本没有。我运行了一些代码,循环级并行的指令交错在循环中比在串行版本中更大。串行版本仍然有一些写入后写入依赖关系,这些依赖关系没有被重命名并导致管道停滞。
答案 0 :(得分:4)
我的猜测是,因为你已经展开了如此大量的迭代,所以代码非常大。不断将新页面命令加载到缓存中的开销远高于迭代变量的开销。
就分支错误预测而言,循环实际上应该很少。它将预测最常用的分支,即9999/10000次。分支预测实际上非常好。
答案 1 :(得分:0)
更可能的原因是MILLION
案例的L3缓存与HUNDREDK
案例中的L1 / L2缓存没有关系。
ONE
的大小介于6到8个字节之间(source) - 抱歉不精确;对于装配来说并不是那么好,但它足以用于背包计算。
考虑到这一点,并假设三个字节的最佳情况(两个add
的总共六个字节):
HUNDREDK
~600 KB MILLION
~6 MB 假设L1缓存为64 KB,L2缓存为256 KB(source),MILLION
代码一直溢出到L3(CPU核心之外),而{{ 1}}主要在L1和L2中预取(在CPU核心中) - source for prefetching