簡體   English   中英

為什么代碼以線性方式運行比循環運行慢?

[英]Why does code run slower in linear fashion than in a loop?

因此,我正在測量一些等待時間,該等待時間是在計算機上執行添加指令以獲取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。是的,我記得在評估兩者時要更改除以的指令量:)

也許對我的操作方式有些困惑,因為文件大小不是問題。 我只是刪除循環。 兩者處理的指令數量不同,但最后我還要更改除以的值。 最終具有相同的編譯大小。

更新:感謝您的答復。 有幾個問題。 第一種是我進行測量的方式,一個版本的中頻時間在循環中攤銷,而另一個版本則沒有。 我運行了更多代碼,並且循環級別並行處理中的指令交織比串行版本中的更大。 串行版本仍具有一些“寫后寫”依賴關系,這些依賴關系未重命名並導致流水線停滯。

我的猜測是,由於展開了大量的迭代,因此代碼非常龐大。 不斷將命令的新頁面加載到緩存中的開銷比迭代變量要高得多。

就分支錯誤預測而言,循環實際上應該很少。 它將預測最常使用的分支,正確的次數為9999/10000次。 分支預測實際上非常好。

更有可能的原因是,在MILLION情況下達到了L3緩存,而在HUNDREDK情況下則從未離開L1 / L2緩存。

ONE的大小在6到8個字節之間( 來源 )-很抱歉,不准確; 對於組裝而言,效果不是很好,但是對於包絡計算而言已經足夠了。

考慮到這一點,並假設最好的情況是三個字節(兩個add總共六個字節):

  • HUNDREDK KB
  • MILLION MB

假設L1高速緩存為64 KB,L2高速緩存為256 KB( ),則MILLION代碼一直溢出到L3(在CPU內核之外),而HUNDREDK主要在L1和L2(在CPU內核中)預取)- 預取來源

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM