現代CPU由於存在多級流水線,對於分支處理又存在分支預測功能,因此在實際cpu的運行過程中,beq指令的週期不是固定的。
另外,即使對於其他指令,比如lw指令,其指令週期也不是固定的。取決於該指令跟後面的指令有沒有依賴關係。 使用2週期的那些lw指令的bne指令都是需要用到前一條lw指令的結果,所以必須等前一條完成之後才能執行。比如 lw a0,1540(t1)與bne a0,x0,-1,bne指令必須等 a0 的 load 結果出來之後才能開始執行。lw 本身都是4週期的,如果前後沒有依賴關係就可以pipeline起來,看起來就是一個週期完成一樣。比如圖中前面那些1個週期的lw指令都是沒有依賴關係的。另外,lw 週期數跟具體實現有關係,最少2週期,如果存儲接口插入了等待週期會更長,具體到某個指令多少得分析微架構。所以說,並不能直接從代碼段知道一個函數的實際運行時間,需要實際運行看看。
執行一個小程序進行測試(測試cpu爲8級流水線),從log中獲知一共使用了31個不同的指令。指令和指令次數爲:
1, 跳轉和返回指令
jal指令:
即使沒有依賴關係,也是最少5週期:本測試一共有2983個jal指令,其指令週期和次數有:
可以看出,jalr指令的大部分的指令週期是5。
jalr指令:
jalr指令一共有2279個jalr指令,其指令週期和次數有:
可以看出,jalr指令的大部分的指令週期是5。
mret指令
mret指令,一共79個:
大部分爲9個週期。
2, add和addi指令
add指令,一共4129個,1個週期和4個週期大約各佔一半。
addi指令,一共22708個,大部分是1個週期。
僞代碼中的li指令,會轉換爲addi指令來執行。比如li a5,4指令,會轉換爲addr a5,x0,4。
3, load和store指令
lw指令
lw指令,一共24338個,1個週期和2個週期的大約各佔一半。
lbu指令
lbu指令,一共1572個:
可以看出,lbu指令的時間週期並沒有比lw指令少。
sw指令
sw指令,一共13636個,大部分需要1個時鐘週期。
sb和sh指令
sb和sh指令與sw指令類似,大部分需要1個時鐘週期。
4,分支預測指令
bne指令
bne指令,一共5389個:
66%是7個週期。
有1次15 cycle是因爲後面來了一箇中斷,需要跳轉到中斷服務函數。
有11次用了13個cycle, 主要就是分支開銷, 沒有指令預測, 結果遇到分支跳轉,開銷就大;一般跳轉後面的時間長, 很大可能都是分支跳轉的開銷。開銷最大的情況是跳轉後 又遇到 cache miss; 計算這個開銷是 從取指令到提交的流水線級數+cache miss的取指時間。
總結:所以我們在寫代碼的時候,最好把運行概率大的那個分支寫到if中。
bge,begu,blt指令
這三個指令大部分都是6個週期。可以看到,分支跳轉指令是比較耗時的。
5, 與狀態寄存器有關的指令
csrrs,csrrw,csrrwi,這些指令在實際測試中一般耗費4個時鐘週期。
6,nop指令
nop指令,一共1060個:
7,其他指令
大部分都是1個時鐘週期。