在我嘗試 64 位 ARM 架構的冒險中,我注意到一個特殊的速度差異,這取決于是否用于從子例程回傳br
或ret
是否用于從子例程回傳。
; Contrived for learning/experimenting purposes only, without any practical use
foo:
cmp w0, #0
b.eq .L0
sub w0, w0, #1
sub x30, x30, #4
ret
.L0:
ret ; Intentionally duplicated 'ret'
該子例程的目的是通過回傳到首先呼叫的指令(即緊接在指向的指令之前的指令)來使呼叫者foo
“重新進入”foo
w0
次數。有一些粗略的計時,有一些足夠高的值,平均需要大約 1362 毫秒。奇怪的是,更換第一與使它運行在快兩倍,只服用550平均毫秒左右。foo
foo
x30
w0
ret
br x30
如果將測驗簡化為僅重復呼叫帶有裸ret
/的子例程,則時間差異就會消失br x30
。是什么讓上述人為的子程式變慢ret
?
我在某種 ARMv8.2(Cortex-A76 Cortex-A55)處理器上對此進行了測驗。我不確定 big.LITTLE 會在多大程度上弄亂時間,但它們在多次運行中似乎非常一致。這絕不是真正的 [微] 基準測驗,而是“如果運行 N 次,這大約需要多長時間”的事情。
uj5u.com熱心網友回復:
大多數現代微體系結構都有一個特殊的呼叫/回傳預測器,它們在實際程式中往往相互匹配。(并且對于具有許多呼叫站點的函式來說,很難以任何其他方式預測回傳:它是一個間接分支。)
通過手動處理退貨地址,您會使這些退貨預測錯誤。所以每ret
一個都會導致分支預測錯誤,除了你沒有玩過的那個x30
。
但是,如果您使用的間接分支不是專門識別為ret
習語br x30
的分支,例如,CPU 將使用其標準的間接分支預測方法,當br
重復到達同一位置時,這種方法效果很好。
谷歌快速搜索從 ARM 中找到了一些關于 Cortex-R4 的關于 32 位模式(4 項回圈緩沖區)的微架構上的回傳預測器堆疊的資訊:https : //developer.arm.com/documentation/ddi0363/ e/預取單元/回傳堆疊
對于 x86,https://blog.stuffedcow.net/2018/04/ras-microbenchmarks/是一篇關于總體概念的好文章,以及關于各種 x86 微架構如何在面對事物時保持其預測準確性的一些細節就像錯誤推測執行必須回滾的call
orret
指令一樣。
(x86 有一個實際的ret
操作碼;ARM64 是一樣的:ret
操作碼就像br
,但暗示這是一個函式回傳。其他一些 RISC 像 RISC-V 沒有單獨的操作碼,只是假設分支-向鏈接暫存器注冊是一個回報。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/399950.html