在使用 MSVC 的行內匯編玩 FPU 時,我對釋放 FPU 暫存器以提高性能感到有點困惑......
例如:
#include <stdio.h>
double fpu_add(register double x, register double y) {
double res = 0.0;
__asm {
fld x
fld y
fadd
fstp res
}
return res;
}
int main(void) {
double x = fpu_add(5.0, 2.0);
(void) printf("x = %f\n", x);
return 0;
}
我什么時候需要ffree行內匯編中的 FPU 暫存器?
在那個例子中,如果我決定注冊ffree,性能會更好嗎?st(1)
也是fstp以下說明的簡寫嗎?
__asm {
fst res
ffree st(0)
}
注意:我知道 FPU 指令現在有點老了,但是將它們作為另一種選擇與 SSE 一起處理
uj5u.com熱心網友回復:
該ffree指令允許您在free不實際更改堆疊指標的情況下標記 x87 fo 堆疊的任何插槽。所以ffree st(0)不會彈出堆疊,只是將堆疊的頂部值標記為空閑/無效,因此任何嘗試訪問它的后續指令都會獲得浮點例外。
要實際彈出到堆疊,您需要兩者ffree st(0)和fincstp(以增加指標)。或者更好的是,fstp st(0)用一條便宜的指令來做這兩件事。或者fstp st(1)保留堆疊頂值并丟棄舊的st(1).
但是使用p其他指令的后綴版本通常會更好、更容易(而且更快)。在你的情況下,你可能想要
__asm {
fld x // push x on the stack
fld y // push y on the stack
faddp // pop a value and add it to the (now) tos
fstp res // pop and store tos
}
這最終會壓入和彈出兩個值,使 fp 堆疊保持與以前相同的狀態。將東西留在 fp 堆疊上可能會導致其他 fp 代碼出現問題,如果編譯器正在生成 x87 fp 代碼,那么應該避免。
或者更好的是,使用記憶體源fadd來保存指令,如果您正在優化 CPU 的速度并不慢。(檢查Agner Fog 的 microarch PDF 和 P5 Pentium 及更新版本的指令表:似乎很好,至少收支平衡,并且在更現代的 CPU 上節省了一個 uop,比如可以對記憶體源運算元進行微融合的 Core2。)
__asm {
fld x // push x on the stack
fadd y // ST0 = y
fstp res // pop and store tos
}
但是 MSVC 行內 asm 對于包裝單個指令(如fadd,強制輸入在記憶體中)本質上很慢,即使編譯器在 asm 陳述句之前將它們放在暫存器中也是如此。并強制將結果存盤在 asm 中,然后為return陳述句重新加載,除非您使用 hack,例如在沒有陳述句的情況下將值留在st(0)函式末尾并從函式末尾脫落。return(即使在行內時,MSVC 實際上也支持這一點,但 clang-cl / clang-fasm-blocks不支持。)
GNU C inline asm 可以包裝一條fadd帶有適當約束的指令,以在 x87 暫存器中請求輸入并告訴編譯器輸出在哪里(in st(0)),但是您仍然必須在 and 之間進行選擇,fadd而faddp不是讓編譯器根據是否選擇它有暫存器中的值或記憶體中的值。(https://stackoverflow.com/tags/inline-assembly/info)
編譯器并不可怕,它們會從純 C 源代碼中生成至少這么好的代碼。行內匯編通常對性能沒有用處,除非您正在撰寫一個為特定 CPU 仔細調整的整個回圈,或者編譯器在某些方面做得不好的情況。(查看編譯器優化的 asm 輸出,例如在https://godbolt.org/上)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/497582.html
