2012-11-01 50 views
3

陣列我能有這樣的聯盟與__m256和兩個__m128

union eight_floats_t 
    { 
    __m256 a; 
    __m128 b[2]; 
    }; 
    eight_floats_t eight_floats; 

工會不得不註冊一個256位的兩個128位部分的即時訪問?

編輯:我打算了解這種方法對性能的影響。

+2

你當然可以。但是如果編譯器不知道如何優化它,那麼你會付出性能損失。 – Mysticial

回答

10

你當然可以做到這一點。 C和C++語言允許你這樣做。它很可能會做你想做的事情。

但是,您使用AVX的事實意味着您關心性能。因此,瞭解這是SSE程序員陷入的最常見(性能)陷阱之一可能很有用。 (和許多沒有注意到)

問題1:

目前的編譯器實現,例如在使用存儲位置的結合。所以這是第一個問題,每次從不同的領域訪問聯合時,它都會將數據強制到內存中並將其讀回。這是一個減速。

這裏就是MSVC2010生成(與優化):

eight_floats a; 
a.a = vecA[0]; 

__m128 fvecA = a.b[0]; 
__m128 fvecB = a.b[1]; 
fvecA = _mm_add_ps(fvecA,fvecB); 

vmovaps YMMWORD PTR a$[rbp], ymm0 
movaps xmm1, XMMWORD PTR a$[rbp+16] 
addps xmm1, XMMWORD PTR a$[rbp] 
movaps XMMWORD PTR fvecA$[rbp], xmm1 
movss xmm1, DWORD PTR fvecA$[rbp] 

你可以看到,它被刷新到內存中。

問題2:

第二減速更糟。當你向內存寫入內容並立即以不同的字號訪問它時,您可能會觸發商店到加載的停頓。 (通常大於10個週期)

這是因爲當前處理器上的加載存儲隊列通常不用於處理這種(不尋常的)情況。所以他們通過簡單地將隊列衝入內存來處理它。


「正確」 的方式來訪問AVX數據類型的下限和上半部分是使用:

  • _mm256_extractf128_ps()
  • _mm256_insertf128_ps()
  • _mm256_castps256_ps128()

和家人。同樣也適用於其他數據類型。

也就是說,編譯器可能足夠聰明,可以識別你正在做什麼,並使用這些指令。 (至少MSVC2010沒有。)

+0

值得注意的是,這實際上不應該在當前μarches上存儲轉發停頓; 32B商店被破解爲兩個16B商店μops,每個商店都沒有危險地轉發到相應的加載操作。然而,這不應該讓你的將軍「不要這樣做」。 –

+0

很高興知道。我不知道英特爾也是如此。雖然我想在未來,32字節的商店可能會變成「本地」。 – Mysticial

+0

@Mystical:即使它們是原生的,我也希望轉發能夠繼續工作(在實際運行中,英特爾已經投入了大量的精力來完成轉發工作,而這些轉發工作並非病理性錯位 - 例如,最近的μarches轉發16B存儲到任何不通過8B邊界的較小負載,以及明顯的16B負載 - 順便說一下,這些都記錄在其優化手冊中)。 –

2

是的,你可以。你試過了嗎?

請注意,C標準規定,訪問不是最近寫入的工會成員的行爲是未指定的行爲 - 具體而言,如果您寫入一個成員然後再讀取另一個成員,另一個有未指定的值(C99§6.2.6.1/ 7)。然而,這是一個非常常見的習慣用法,並得到所有主要編譯器的支持。實際上,以任何順序閱讀和寫給工會的任何成員都是可以接受的做法(source)。

+0

你確定這是UB嗎?海灣合作委員會手冊實際上建議這種做法,以避免類型打字指針 – hirschhornsalz

+0

我試過了,但我想了解它的性能影響,正如Mysticial所假設的那樣。謝謝。 –

+0

@hirschhornsalz:我仔細一看,你說得對 - 這不是UB。 C99§6.2.6.1/ 7說:「當一個值存儲在union類型的對象的成員中時,對象表示的字節與該成員不對應,但與其他成員相對應的字節取未指定的值。」 –