[英]How do I efficiently reorder bytes of a __m256i vector (convert int32_t to uint8_t)?
[英]Converting to and from __m256i and std::vector<uint32_t>
我想轉換為__m256i
實例和std::vector<uint32_t>
實例(僅包含8個元素)。
到目前為止,我想出了這個:
using vu32 = std::vector<uint32_t>;
__m256i v2v(const vu32& in) {
assert(in.size() == 8);
return _mm256_loadu_si256(reinterpret_cast<const __m256i*>(in.data()));
}
vu32 v2v(__m256i in) {
vu32 out(8);
_mm256_storeu_si256(reinterpret_cast<__m256i*>(out.data()), in);
return out;
}
安全嗎?
有沒有更慣用的方法呢?
首先,SIMD向量和std::vector
基本上沒有任何關系。 我知道你已經知道這一點,但未來的讀者應該仔細考慮這是否真的是他們想要做的事情。
它是安全的; .data()
必須返回一個可以在任何有效索引處讀取或寫入的指針 。 考慮到真正的std::vector
庫的實現細節,它在實踐中肯定是安全的。 就紙上標准而言,我非常清楚。
從評論來看,似乎你擔心嚴格別名的UB。
通過may_alias
指針類型(包括char*
或__m256i*
)讀/寫其他對象就可以了。 memcpy(&a, &b, sizeof(a))
是修改的對象的表示的一個常見的例子a
經由char*
。 memcpy本身並沒有什么特別之處; 由於char*
別名特殊情況,這是明確定義的。
may_alias
是一個GNU C擴展,它允許您定義除char
之外的類型,這些類型允許以char*
的方式進行別名。 GNU C對__m128
/ __m256i
的定義是根據GNU C本機向量,如typedef long long __m256i __attribute((vector_size(32), may_alias));
其他C ++實現(如MSVC) __m256i
不同的方式定義__m256i
,但英特爾內在函數API保證在其他類型的別名矢量指針在char*
/ memcpy
任何情況下都是合法的。
另請參見硬件向量指針和相應類型之間的`reinterpret_cast`是否為未定義的行為?
另外: SSE:_mm_load / store與使用直接指針訪問之間的區別 - loadu
/ storeu
就像在解除引用之前轉換向量類型的aligned(1)
版本。 因此,關於指針和別名的所有這些推理都適用於將指針傳遞給_mm_storeu
,而不僅僅是直接解除引用。
成語; 當然,這看起來很像慣用的C ++。 我可能仍然使用帶有內在函數的C風格的強制轉換,因為reinterpret
是如此之久以至於整數向量的設計不佳的內在函數API需要它到處都是。 也許si256 load / loadu和store / storeu的模板化包裝函數是合適的,可以從任何指針類型轉換為__m256i*
或const __m256i*
。
我可能更喜歡的東西,通過__m256i
元素的構造out
,不過,從潛在歸零存儲器中,然后存儲載體停止愚蠢的編譯器。 但希望這不會發生。
實際上,在存儲向量之前,gcc和clang會將死存儲優化為零8個元素。 任何使用嘗試vector(begin, end)
迭代器的構造函數,而不是使事情變得更糟,與異常處理額外的代碼上的商店/重裝的頂部in
堆棧(約new
),然后將其存儲到新分配的內存。
看看有關Godbolt編譯器瀏覽器的一些嘗試,請注意它們保存/恢復r13
,其中@ Bee的版本沒有,以及在通過該函數的正常路徑之外生成的額外代碼。 這消失了-fno-exceptions
,但是它們與@ Bee的版本相同,而不是更好。 所以使用問題中的代碼; 它至少和我嘗試不同的任何嘗試一樣。
我可能也更喜歡做一些事情,以獲得分配有32字節對齊內存的新std::vector<uint32_t>
,如果可以的話,不改變模板類型。 我不確定這是否可行。
即使我們可以在實踐中使這個初始分配保持一致而不改變類型以使其成為將來使用的編譯時保證,這可能會有所幫助。 將未對齊處理留給HW的AVX代碼將受益於沒有緩存行拆分。
但我不認為這可能是沒有黑客std::vector
的自定義構造函數,使用對齊的new
進行初始分配,假設它與常規delete
兼容。
如果您可以在代碼中的任何位置使用std::vector<uint32_t, some_aligned_allocator>
,那么可能值得這樣做。 但是,如果必須將其傳遞給使用法vector<uint32_t>
代碼,則可能不值得。
您可能會欺騙您的編譯器,因為該類型與常規std::vector<uint32_t>
是二進制兼容的(但不是源兼容的),在對齊new / delete與plain new / delete兼容的系統上。 但我不建議這樣做。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.