2016-03-16 203 views
-2

我對MOVSD彙編指令有些困惑。我寫了一些計算一些矩陣乘法的數字代碼,只是使用普通的C代碼而沒有SSE內在函數。我甚至不包括用於編譯的SSE2內在函數的頭文件。但是當我檢查彙編輸出時,我看到:SSE指令MOVSD(擴展:x86,x86-64上的浮點標量和向量操作)

1)使用128位向量寄存器XMM; 2)調用SSE2指令MOVSD。

據我所知,MOVSD實質上是在單精度浮點運算。它只使用XMM寄存器的低64位並設置高64位0.但我只是不明白兩件事:

1)我從來沒有給編譯器提供任何使用SSE2的提示。另外,我使用GCC而不是intel編譯器。據我所知,英特爾編譯器會自動尋找向量化的機會,但GCC不會。那麼GCC如何知道使用MOVSD?或者,這個x86指令在SSE指令集之前就已經存在很久了,而SSE2中的_mm_load_sd()內部函數僅僅是爲了向標量計算使用XMM寄存器提供向後兼容性?

2)爲什麼編譯器不使用其他浮點寄存器,80位浮點堆棧或64位浮點寄存器?爲什麼它必須使用XMM寄存器(通過設置高64位0,並且實質上浪費了該存儲)? XMM是否提供更快的訪問?


順便說一下,我有關於SSE2的另一個問題。我只是看不到_mm_store_sd()和_mm_storel_sd()之間的區別。兩者都將較低的64位值存儲到一個地址。有什麼不同?性能差異?對齊差異?

謝謝。


更新1:

OKAY,很明顯當我第一次問這個問題,我缺乏關於如何管理着CPU浮點運算的一些基本知識。所以專家們傾向於認爲我的問題是無意義的。由於我沒有包含最短的樣本C代碼,人們可能會認爲這個問題也很模糊。在這裏,我將提供a review作爲答案,希望對於任何不清楚現代CPU浮點運算的人都會有所幫助。

+5

在64位模式下,調用約定已經強制SSE寄存器用於浮點參數和返回值。由於SSE寄存器不是以堆棧的形式組織的,而且它們更多,因此編譯器更容易使用。有標量SSE指令。另外,有趣的是你擔心會浪費一半的空間 - 如果你完全不使用它們,*所有的*都會浪費;) – Jester

+1

沒有64位浮點指針寄存器。只有80位浮點堆棧和XMM寄存器。 MOVSD指令是標量指令,而不是矢量指令,因此它的使用並不意味着自動矢量化。 –

+1

那些不是浮點寄存器。 –

回答

3

浮點標量/矢量處理對現代CPU的審查

矢量處理的想法可以追溯到老的時候vector processors,但這些處理器已經被現代建築與緩存的系統所取代。所以我們專注於現代CPU,特別是x86x86-64。這些體系結構是main stream in high performance scientific computing

自i386以來,英特爾推出了浮點堆棧,其中浮點數最多可以保持80位寬。該堆棧通常被稱爲x87 or 387 floating point "registers",其中一組爲x87 FPU instructions。 x87堆棧不是真正的可直接尋址的寄存器,如通用寄存器,因爲它們在堆棧中。訪問註冊st(i)是通過偏移堆棧頂部寄存器%st(0)或者簡單地%st。藉助指令FXCH將當前棧頂%st和某個偏移寄存器%st(i)之間的內容交換,可以實現隨機訪問。但FXCH可能會施加一些性能損失,儘管最小化。x87堆棧默認情況下通過計算80位精度的中間結果來提供高精度計算,以最大限度地減少數值不穩定算法中的舍入誤差。但是,x87指令是完全標量的。

矢量化的第一個努力是MMX instruction set,它執行整數矢量操作。 MMX下的向量寄存器是64位寬寄存器MMX0,MMX1,...,MMX7。每個可以用來保存64位整數,或者以「打包」格式保存多個較小的整數。然後可以將一條指令一次應用於兩個32位整數,四個16位整數或八個8位整數。因此,現在有用於標量整數操作的傳統通用寄存器,以及用於不具有共享執行資源的整數向量操作的新MMX。但MMX以標量x87 FPU操作共享執行資源:每個MMX寄存器對應於x87寄存器的低64位,而x87寄存器的高16位未使用。這些MMX寄存器都可直接尋址。但是,混疊使得在同一個應用程序中使用浮點和整數向量操作很困難。爲了最大限度提高性能,程序員經常只在一種模式或另一種模式下使用處理器,並儘可能延遲它們之間的相對較慢的切換。

後來,SSE沿着x87堆棧的一側創建了一組單獨的128位寬寄存器XMM0-XMM7。關於單精度浮點運算(32位)的SSE指令專注於專用;整數向量操作仍使用MMX寄存器和MMX指令集執行。但是現在兩個操作可以同時進行,因爲它們不共享執行資源。 重要的是要知道SSE不僅執行浮點向量操作,還執行浮點標量操作。從本質上講,它提供了一個浮動操作發生的新地方,並且x87堆棧不再是執行浮動操作的先決選擇。使用XMM寄存器進行標量浮點運算比使用x87堆棧快,因爲所有XMM寄存器都更容易訪問,而x87堆棧不能在沒有FXCH的情況下隨機訪問。當我發佈我的問題時,我顯然不知道這個事實。我不清楚的另一個概念是通用寄存器是整數/地址寄存器。即使它們是x86-64上的64位,它們也無法保持64位浮點。主要原因是與通用寄存器相關的執行單元是ALU(算術邏輯單元&),它不適用於浮點計算。

SSE2是一個重大進展,因爲它擴展了矢量數據類型,所以SSE2指令(標量或向量)可以用於所有C標準數據類型。事實上這種擴展使MMX過時了。另外,x87堆疊不像以前那麼重要。由於有兩個可以進行浮點運算的可選位置,因此可以爲編譯器指定選項。例如,對於GCC,編譯時使用標記

-mfpmath=387 

將計劃傳統x87堆棧上的浮點操作請注意,這似乎是32位x86的默認設置,即使SSE已經可用。例如,我有一臺2007年製造的Intel Core2Duo筆記本電腦,它已經配備了SSE版本,最高版本爲SSE4,而GCC仍然會默認使用x87堆棧,這使得科學計算不必要地變慢。在這種情況下,我們需要使用標記編譯

-mfpmath=sse 

GCC將在XMM寄存器上安排浮點運算。 64位x86-64用戶不需要擔心這種配置,因爲這是x86-64上的默認配置。這種信號只會影響標量浮點運算。如果我們使用矢量指令和編譯器編寫代碼,則帶有標記的代碼可以是隻有XMM寄存器才能進行計算的唯一位置。換句話說,這個標誌打開-mfpmath = sse。欲瞭解更多信息,請參閱GCC's configuration of x86, x86-64。有關編寫SSE2 C代碼的示例,請參閱我的其他帖子How to ask GCC to completely unroll this loop (i.e., peel this loop)?

SSE指令集雖然非常有用,但不是最新的向量擴展。 AVX,advanced vector extensions通過提供3個操作數和4個操作數指令來增強SSE。如果您不清楚這意味着什麼,請參閱number of operands in instruction set。 3操作數指令優化了科學計算中常見的fused multiply-add (FMA)操作:1)使用1個較少的寄存器; 2)減少寄存器之間的顯式數據移動量; 3)本身加速FMA計算。例如使用AVX,請參閱@Nominal Animal's answer to my post

+0

針對32位x86的gcc默認爲可以在i686(PPro)之前的任何x86 CPU上運行的代碼。如果您希望它定位當前主機的指令集擴展,請使用'-march = native'。此外,32位ABI被定義爲返回x87堆棧上的FP參數。有關ABI文檔,請參閱[x86標記wiki信息頁面](http://stackoverflow.com/tags/x86/info)。 –

+1

大多數3操作數AVX指令只有一個不是其中一個源的單獨目標。 FMA是主要的例外:它仍然會銷燬其一個操作數,如SSE指令。 (英特爾在最後一刻從FMA4改爲FMA3,但沒有告訴AMD,因此AMD推土機支持4操作數FMA4,但不支持英特爾風格的FMA3)。此外,SSE有一些3操作數指令:'pblendvb'使用'xmm0'作爲隱含的第4操作數。因此,英特爾本來可以在沒有AVX的情況下實現FMA,而FMA是AVX如何減少對「mov」指令的需求的一個壞例子。 –

+0

除此之外,這個答案在我的快速瀏覽中看起來是正確的。 IDK對其他初學者會有多大用處,因爲它看起來有點漫長。 IIRC,x86 insn集擴展的很多歷史已經在幾個月前收集的[SSE標記wiki頁面](http://stackoverflow.com/tags/sse/info)中。 –