2016-05-31 69 views
1

我正在編譯使用gcc -O2 -march = native標誌的相同基準。然而,有趣的是,當我看着objdump時,它實際上產生了一些指令,如vxorpd等,我認爲這應該只在啓用了ftree-vectorize時出現(並且O2不應該默認啓用此功能)?如果我添加了-m32標誌在32位指令中編譯時,這些打包的指令消失了。任何遇到類似情況的人都可以提供一些解釋?謝謝。奇怪的gcc6.1 -O2編譯行爲

+0

通過一個具體的例子,你的問題會更容易回答。 –

回答

3

XORPD是傳統的SSE2指令,它在兩個壓縮的雙精度浮點值上執行按位邏輯異或。

VXORPD是該指令的矢量版本。實質上,它是帶有VEX prefix的經典SSE2 XORPD指令。這就是操作碼中「V」前綴的意思。它與AVX(高級矢量擴展)一起引入,並且在任何支持AVX的架構上都受支持。 (實際上有兩個版本,適用於128位AVX寄存器的VEX.128編碼版本和適用於256位AVX2寄存器的VEX.256編碼版本。)

所有傳統SSE SSE2指令可以添加一個VEX前綴,給它們一個三操作數的形式,並允許它們與其他新的AVX指令進行更有效的交互和調度。它也避免了the high cost of transitions between VEX and non-VEX modes。否則,這些新的編碼保持相同的行爲。因此,編譯器通常會在目標體系結構支持它們時生成這些指令的VEX前綴版本。顯然,在你的情況下,march=native指定了一個至少支持AVX的體系結構。

在GCC和Clang上,即使優化關閉(-O0),實際上也會發出這些指令,所以啓用優化時您肯定會獲得這些指令。 -ftree-vectorize開關以及任何其他矢量化特定的優化開關都不需要開啓,因爲這實際上與矢量化代碼無關。更確切地說,代碼流沒有改變,只是指令的編碼。

你可以想到的最簡單的代碼,看到這一點:

double Foo() 
{ 
    return 0.0; 
} 
Foo(): 
     vxorpd xmm0, xmm0, xmm0 
     ret 

所以這解釋了爲什麼你看到VXORPD和它的朋友,當你編譯一個64位的建設與-march=native開關。

這留下了爲什麼你不要看到它時,你扔-m32開關(這意味着爲32位平臺生成代碼)。針對這些平臺時,SSE和AVX指令仍然可用,並且我相信它們將在某些情況下使用,但由於32位ABI的顯着差異,它們不能經常使用。具體而言,32位ABI要求在x87浮點堆棧上返回浮點值。由於這需要使用x87浮點指令,因此優化程序傾向於堅持使用這些指令,除非它嚴格地向量化一段代碼。這是將x87堆棧中的值重新混合到SIMD寄存器並再次返回的唯一時間。否則,這是一個很少或沒有實際好處的性能消耗。

你也可以在行動中看到這一點。看看輸出什麼樣的變化只是扔-m32開關:

Foo(): 
     fldz 
     ret 

FLDZ是在浮點堆棧的頂部,在那裏它已準備好要返回到裝載恆爲零的x87 FPU指令呼叫者,召集者。顯然,當你讓代碼更復雜時,你更可能改變優化器的啓發式並且說服它發出SIMD指令。如果您啓用基於矢量化的優化,則您更有可能仍然是。

+0

嗨科迪格雷,謝謝你的回覆。還有一個問題需要跟進。在使用fastmath進行編譯時,由於這些vex前綴說明,我看到了一個很大的加速。你知道這些vex-prefix指令如何比原來的x87指令表現得更好嗎?謝謝 – PST

+0

@PST好的,常規的SSE指令往往比x87指令更快。這有多個複雜的原因。其中最重要的一點是x87 FPU可以在基於堆棧的系統中運行,並具有所有伴隨的限制,而SSE實施使用寄存器。這意味着沒有時間浪費在堆棧上壓入/彈出值,或者在堆棧的不同位置交換值。 SSE比x87更快的另一個原因僅僅是它是一個更新的實現,並且已經相應地進行了優化。 –

+0

然後,我的回答已經解釋了爲什麼以VEX爲前綴的SSE指令比常規的SSE指令更快。因此,您將獲得兩項性能改進:首先從x87切換到SSE,然後從SSE切換到VEX編碼的SSE。英特爾工程師必須在過去15 - 20年內達到某種程度。 :-) –

1

只需添加到Cody Gray's very good answer,您可以通過輸出到彙編器並打開-fverbose-asm來檢查gcc的內部啓用選項。

例如:

gcc -O2 -fverbose-asm -S -o test.S test.c 

將列出test.S在選定的優化級別(這裏-O2)啓用所有的優化選項。

+0

另請參閱http://gcc.godbolt.org/,您可以在其中看到編譯器asm輸出並消除了噪聲。 –