我想比較一個ZMM向量并使用它的結果并執行vpandn.
在AVX2,我這樣做:
vpcmpgtb ymm0, ymm0, ymm1
vpandn ymm0, ymm0, ymm2
vpxor ymm0, ymm0, ymm3
但在 中AVX512BW,vpcmpgtb回傳結果為K。那么
我應該如何執行它的結果呢?vpandnvpxorAVX512BW
vpcmpgtb k0, zmm0, zmm1
vpandn ??
vpxor ??
uj5u.com熱心網友回復:
k暫存器有單獨的說明;它們的助記符都以 開頭,k因此很容易在指令表中找到,例如kandnq k0, k0, k1.
以及kunpck...(連接,而不是交錯),kadd/ kshift,kor/ kand/ knot/ kxor,甚至是kxnor(為收集/分散生成全一的便捷方式)。當然也kmov包括(包括到/從記憶體或 GP 整數),kortest以及ktest用于分支。
它們都以位元組/字/雙字/四字大小表示受影響的掩碼位數,結果為零擴展。(Xeon Phi 上沒有 AVX-512BW,只有位元組和字大小,因為 16 位覆寫了一個 ZMM,其元素與 dword 一樣小。但所有具有 AVX-512 的主流 CPU 都有 AVX-512BW,因此有 64 位掩碼暫存器。)
您有時可以將其折疊到另一個操作中,以避免需要單獨的指令來組合掩碼。要么反轉比較,以便您可以ktest直接使用分支,或者如果要屏蔽,請使用零屏蔽比較掩碼。(不支持將掩碼比較/測驗合并到第三個現有掩碼中。)
AVX-512 整數比較將謂詞作為立即數,而不是僅作為eqand存在gt,因此您可以反轉條件并使用and而不是需要andn。(在有符號和無符號vpcmpub中可用,也不同于任何以前的 x86 SIMD 擴展。因此,如果您以前添加128了翻轉高位pcmpgtb,您不再需要它,只需這樣做vpcmpub。)
vpcmpngtb k1, zmm3, zmm1 ; k0 can't be used for masking, only with k instructions
vpcmpeqb k2{k1}, zmm4, zmm1 ; This is zero-masking even without {z}, because merge masking isn't supported for this
相當于(性能除外):
vpcmpngtb k1, zmm3, zmm1
vpcmpeqb k2, zmm4, zmm1
kand k2, k2, k1
也相當于將kandn比較gt作為注釋(第一個)運算元,就像你的問題一樣。
k...掩碼指令通常只能在埠 0 上運行,性能不佳。 https://uops.info/。
屏蔽比較(或其他指令)必須等待屏蔽暫存器輸入準備好,然后才能開始處理其他運算元。您可能希望它支持掩碼的延遲轉發,因為僅在回寫時使用它們,但 IIRC 不支持。盡管如此,只有 1 條指令而不是 2 條指令仍然更好。除非它是高延遲并且掩碼操作是低延遲并且您受到延遲限制,否則讓兩條能夠并行執行的第一條指令并不是更好。但在使用 512 位暫存器時,執行單元的吞吐量通常是一個瓶頸。(因為埠 1 上的向量 ALU 已關閉。)
有些k指令在當前 CPU 上只有 1 個周期的延遲,而另一些則是 4 個周期的延遲。(如kshift和kunpck,和kadd。)
這些掩碼比較掩碼指令的內在函式_mm256_mask_cmp_ep[iu]_mask是,帶有一個__mmask8/16/32/64輸入運算元(以及兩個向量和一個立即謂詞)和一個掩碼回傳值。像 asm 一樣,盡管這是零掩蔽而不是合并掩蔽,但它們使用..._mask_...而不是。..._maskz_...
將蒙版應用于矢量
顯然,這個問題想將掩碼與另一個向量一起使用,而不僅僅是獲得掩碼之類的vpmovmskb東西。AVX-512在寫入向量目標時具有類似合并屏蔽zmm0{k1}和零屏蔽。zmm0{k1}{z}如果您了解 AVX2 asm 但還不了解 AVX-512 新內容的基礎知識,請參閱Kirill Yukhin 的幻燈片,介紹一系列 AVX-512 功能和它們的 asm 語法。
;; original code
vpaddb ymm0, ymm1, ymm2
vpcmpgtb ymm0, ymm0, ymm3 ; sum > y3 (signed compare)
vpandn ymm0, ymm0, ymm4 ; masked y4
vpxor ymm0, ymm1, ymm0 ; y0 = y1^y4 in bytes where compare was false
; y0 = y1 where it was true
在 AVX-512 CPU 上使用 256 位向量,可以vpternlogd用來替換最后 2 條指令(只要避免 ymm16..31,仍然使用 AVX2 compare-into-vector)。不幸的是,AVX-512 根本沒有比較向量,只有掩碼。如果您的程式沒有在 SIMD 回圈中花費大量時間,尤其是在 512 位向量的最大渦輪懲罰較高的 CPU 上, 256 位向量可能是一個不錯的選擇。(對于整數向量來說沒什么大不了的,除了乘法之外的 SIMD 整數是“輕量級”,而不是“重度”)
對于 512 位向量,我們必須使用掩碼。完全幼稚的插入方式是將掩碼擴展回帶有vpmovm2b zmm0, k1然后vpandnq/vpxorq不帶掩碼的向量。或者vpternlogd,在這種情況下,如果沒有屏蔽,仍然可以將總數減少到 4 條指令,結合 andn/xor。
零掩蔽vmovdqu8 zmm0{k1}{z}, zmm4是替換vpandn. 或者在異或之后進行混合,使用掩碼作為控制運算元。那仍然是 4 條指令,它們都需要一個執行單元。
如果可能的話,例如在 32 位元素1的不同問題中,合并屏蔽 XOR 會很好(在復制暫存器不變之后,如果不能破壞 zmm1 ,mov-elimination 可以作業2 )。
但是AVX-512 沒有 bitwise-booleans 的位元組掩碼;只有vpxordandvpxorq允許在 32 或 64 位元素中進行屏蔽。AVX-512BW 僅添加了位元組/字元素大小指令vmovdqu,以及即使沒有掩碼也關心邊界的指令,例如vpaddb和vpshufb。
對于指令級并行性,我們最好的選擇是與比較并行進行 XOR,然后在比較掩碼結果準備好后修復該結果。
vpaddb zmm0, zmm1, zmm2
vpcmpgtb k1, zmm0, zmm3 ; (sum > z3) signed compare, same as yours
vpxord zmm0, zmm1, zmm4
vmovdqu8 zmm0{k1}, zmm1 ; replace with z1 in bytes where (z1 z2 > z3)
; z0 = z1^z4 in bytes where compare was false
; z0 = z1 where it was true.
最后的指令同樣可以是vpblendmb zmm0{k1}, zmm0, zmm1(手動),它與合并屏蔽的不同之處vmovdqu8僅在于能夠將混合結果寫入第三個暫存器。
根據您要對該vpxord結果執行的操作,您可能能夠進一步優化周圍的代碼,也許可以使用vpternlogd更多的按位布林值。或者也許通過合并掩蔽或零掩蔽到其他東西。例如,也許復制zmm1并在其中進行合并蒙版vpaddb,而不是進行混合。
另一種更糟糕的方法,指令級并行度較低,是使用與 AVX2 代碼相同的順序(其中更多 ILP 方式需要vpblendvb更昂貴的 a。)
; Worse ILP version, direct port of your AVX2 logic
vpaddb zmm0, zmm1, zmm2
vpcmpngtb k1, zmm0, zmm3 ; !(sum > z3) signed compare
vmovdqu8 zmm0{k1}{z}, zmm4 ; zmm4 or 0, like your vpandn result
vpxord zmm0, zmm0, zmm1 ; z0 = z1^z4 in bytes where compare was true
; leaving z0=z1 bytes where the mask was zero (k1[i]==0)
; this is for the inverted compare, ngt = le condition
在這種情況下,每條指令都取決于前一條指令的結果,因此從k1準備就緒到最終準備就緒的總延遲是 3 個周期而不是 4 個。(假設 ZMM4 足夠早地準備好,zmm0早期版本可以vpxord與 并行運行。)vpcmpb
零掩碼(和合并掩碼)vmovdqu8在 Skylake-X 和 Alder Lake 上具有 3 個周期的延遲(https://uops.info/)。與 相同vpblendmb,但vmovdqu3264 具有 1 個周期的延遲。
vpxord即使有屏蔽也有 1 個周期的延遲,但有屏蔽vpaddb有 3 個周期的延遲,而沒有屏蔽則有 1 個。因此,似乎位元組屏蔽始終是 3-cycle latency,而 dword/qword 屏蔽保持與未屏蔽指令相同的延遲。不過吞吐量不受影響,所以只要你有足夠的指令級并行度,如果它不是一個長回圈攜帶的 dep 鏈,亂序 exec 就可以隱藏延遲。
腳注 1:更寬的元素允許屏蔽布林值
這是為了將來使用不同元素大小的讀者的利益。如果您不需要,您絕對不想將位元組元素擴大到 dword,這將使每個向量完成 1/4 的作業,只是為了通過 mov-elimination 節省 1 個后端 uop:
; 32-bit elements would allow masked xor
; but there is no vpxorb
vpaddd zmm0, zmm1, zmm2
vpcmpngtd k1, zmm0, zmm3 ; !(sum > z3) signed compare
;vpxord zmm1{k1}, zmm1, zmm4 ; if destroying ZMM1 is ok
vmovdqa64 zmm0, zmm1 ; if not, copy first
vpxord zmm0{k1}, zmm1, zmm4 ; z0 = z1^z4 in dwords where compare was true
; leaving z0=z1 dwords where the mask was zero (k1[i]==0)
腳注 2:
vmovdqu8 zmm0, zmm1不需要執行單元。但是vmovdqu8 zmm0{k1}{z}, zmm1,和其他 512 位微控制器一樣,它只能在當前英特爾 CPU 的埠 0 或 5 上運行,包括 Ice Lake 和 Alder Lake-P(在未禁用其 AVX-512 支持的系統上)。
Ice Lake僅針對 GP-integer 而不是 vector 打破了 mov-elimination,因此暫存器的精確副本仍然比進行任何掩碼或其他作業便宜。只有兩個 SIMD 執行埠使得后端成為比使用 256 位向量的代碼更常見的瓶頸,尤其是在 Ice Lake 以及后來在 Ice Lake 中使用 5-wide 前端,在 Alder Lake / Sapphire 中使用 6-wide急流。
不過,大多數代碼都有重要的加載/存盤和整數作業。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/481971.html
