簡體   English   中英

強制AVX內在函數使用SSE指令

[英]Forcing AVX intrinsics to use SSE instructions instead

不幸的是我有一個AMD打樁機cpu,它似乎與AVX指令有問題:

使用256位AVX寄存器進行的內存寫入非常慢。 測量的吞吐量比之前的型號(Bulldozer)慢5到6倍,比兩個128位寫入慢8到9倍。

根據我自己的經驗,我發現mm256內在函數比mm128要慢得多,而且我假設它是因為上述原因。

我真的想編寫最新的指令集AVX,同時仍然能夠以合理的速度在我的機器上測試構建。 有沒有辦法迫使mm256內在函數使用SSE指令? 我正在使用VS 2015。

如果沒有簡單的方法,那么艱難的方式呢。 用自定義標題替換<immintrin.h> ,該標題包含我自己的內在函數定義,可以編碼為使用SSE? 不確定這是多么合理,在我完成這項工作之前,如果可能的話,更喜歡更簡單的方法。

使用Agner Fog的Vector類庫並將其添加到Visual Studio中的命令行: -D__SSE4_2__ -D__XOP__

然后使用AVX大小的矢量,如Vec8f用於8個浮點數。 在沒有AVX啟用的情況下進行編譯時,它將使用文件vectorf256e.h ,它使用兩個SSE寄存器模擬AVX。 例如Vec8f繼承自Vec256fe ,其開頭如下:

class Vec256fe {
protected:
    __m128 y0;                         // low half
    __m128 y1;                         // high half

如果使用/arch:AVX -D__XOP__進行編譯,則VCL將使用文件vectorf256.h和一個AVX寄存器。 然后您的代碼適用於AVX和SSE,只需更改編譯器開關。

如果您不想使用XOP請不要使用-D__XOP__


正如Peter Cordes在他的回答中指出的那樣,如果你的目標只是為了避免256位加載/存儲,那么你可能仍然需要VEX編碼指令(雖然不清楚這會產生差異,除非在某些特殊情況下)。 你可以使用這樣的矢量類來做到這一點

Vec8f a;
Vec4f lo = a.get_low();  // a is a Vec8f type
Vec4f hi = a.get_high();
lo.store(&b[0]);         // b is a float array
hi.store(&b[4]);

然后使用/arch:AVX -D__XOP__編譯/arch:AVX -D__XOP__

另一種選擇是使用Vecnf然后執行的一個源文件

//foo.cpp
#include "vectorclass.h"
#if SIMDWIDTH == 4
typedef Vec4f Vecnf;
#else
typedef Vec8f Vecnf;
#endif  

並像這樣編譯

cl /O2 /DSIMDWIDTH=4                     foo.cpp /Fofoo_sse
cl /O2 /DSIMDWIDTH=4 /arch:AVX /D__XOP__ foo.cpp /Fofoo_avx128
cl /O2 /DSIMDWIDTH=8 /arch:AVX           foo.cpp /Fofoo_avx256

這將創建三個帶有一個源文件的可執行文件。 您可以使用/c編譯它們而不是鏈接它們,而是創建一個CPU調度程序。 我將XOP與avx128一起使用,因為除了AMD之外,我認為沒有充分的理由使用avx128。

您不想使用SSE指令。 你想要的是256b商店作為兩個獨立的128b商店,仍然使用VEX編碼的128b指令。 即128b AVX vmovups


gcc有-mavx256-split-unaligned-load...-store選項(例如-march=sandybridge一部分啟用,大概也適用於Bulldozer-family( -march=bdver2-march=bdver2機)。這不能解決但是,當編譯器知道內存對齊時會出現問題。


您可以使用宏來覆蓋正常的256b存儲內在函數

// maybe enable this for all BD family CPUs?

#if defined(__bdver2) | defined(PILEDRIVER) | defined(SPLIT_256b_STORES)
   #define _mm256_storeu_ps(addr, data) do{ \
      _mm_storeu_ps( ((float*)(addr)) + 0, _mm256_extractf128_ps((data),0)); \
      _mm_storeu_ps( ((float*)(addr)) + 4, _mm256_extractf128_ps((data),1)); \
   }while(0)
#endif

gcc為Piledriver( -march=bdver2 )定義__bdver2 (Bulldozer版本2)。

您可以對(對齊) _mm256_store_ps執行相同_mm256_store_ps ,或者只使用未對齊的內在函數。

編譯器將_mm256_extractf128(data,0)優化為簡單的_mm256_extractf128(data,0) 即它應該編譯為

vmovups       [rdi], xmm0         ; if data is in xmm0 and addr is in rdi
vextractf128  [rdi+16], xmm0, 1

然而, 對godbolt的測試顯示gcc和clang是啞的 ,然后提取到寄存器然后存儲。 ICC正確生成雙指令序列。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM