実際にx86-64のVGAテキストモードビデオメモリでSIMDを使用していますかモード?これは面白いですが、実生活では実際にはそうであり、SIMDデータ操作のユースケースとして機能します。
しかし、ビデオメモリから実際に読み込んでいる場合は、キャッシュされていない読み込みが行われている可能性があります。これは悪いことであり、システムを再設計して行う必要はありません。 (提案のRossの答えを参照してください)
USWCビデオメモリでは、MOVNTDQAから大きなスピードアップを得ることができます。 Intel's articleを参照してください。NTについての私の答えは、here、特にthis oneを参照してください。ここでは、x86 ISAのマニュアルでNTのロードに関するセマンティクスをオーバーライドしないと説明しているので、弱い順序で使用しない限り、順序付けられたメモリ領域。
あなたが疑わしかったように、SIMD命令セットにはコピー命令がありません。あなたはロードとストアの間で自分でデータ処理をレジスタに登録しなければなりません。あなたのためにこれを行う単一のSSE/AVX命令さえありません。 (しかし、ARM NEONのunzip instructionは問題全体を解決します)。
あなたは(署名)の二つのベクトルをパックするSSE2 PACKUSWBを、使用すべきではuint8_tの一つのベクトルにダウンint16_t。各単語要素の上位バイトをゼロにした後、0..255に飽和してもデータはまったく変更されません。
ソースポインタを調整してキャッシュライン境界を越えるペナルティを最小限に抑え、いくつかのアドレッシングモードのトリックを使用してループ内に命令を保存する、実際の(テストされていない)ループ。
アライメントされていないロードでは、Nehalem以降ではキャッシュラインの境界を横切るときに余分な遅延が発生します。これは、ビデオメモリからのNTロードを使用する場合に最も便利です。または、大量のコピーの最後にsrcの終わりを超えて読み込む場合には、これはおそらく便利です。
ストアの2倍の負荷がかかるため、ロード/ストアのスループットが問題となる場合は、整列したストアの代わりに最適化されたロードが問題になります。しかし、キャッシュのロード/ストアのスループットを飽和させるにはALUの作業が多すぎるため、のアラインされていないロード(ポールRのループのような)を単純にしておくと、ほとんどのCPUとユースケースでうまく動作するはずです。 AVXの非破壊第3オペランドの符号化に
mov edx, CMD_BUFFER ; or RIP-relative LEA, or hopefully this isn't even static in the first place and this instruction is something else
;; rdi = source ; yes this is "backwards", but if you already have the src pointer in rdi, don't waste instructions
;; rcx = count
;; rdx = dest
pcmpeqw xmm7, xmm7 ; all ones (0xFF repeating)
psrlw xmm7, 8 ; 0x00FF repeating: mask for zeroing the high bytes
;cmp ecx, 16
;jb fallback_loop ; just make CMD_BUFFER big enough that it's ok to copy 16 bytes when you only wanted 1. Assuming the src is also padded at the end so you can read without faulting.
;; First potentially-unaligned 32B of source data
;; After this, we only read 32B chunks of 32B-aligned source that contain at least one valid byte, and thus can't segfault at the end.
movdqu xmm0, [rdi] ; only diff from loop body: addressing mode and unaligned loads
movdqu xmm1, [rdi + 16]
pand xmm0, xmm7
pand xmm1, xmm7
packuswb xmm0, xmm1
movdqu [rdx], xmm0
;; advance pointers just to the next src alignment boundary. src may have different alignment than dst, so we can't just AND both of them
;; We can only use aligned loads for the src if it was at least word-aligned on entry, but that should be safe to assume.
;; There's probably a way to do this in fewer instructions.
mov eax, edi
add rdi, 32 ; advance 32B
and rdi, -32 ; and round back to an alignment boundary
sub eax, edi ; how far rdi actually advanced
shr eax, 1
add rdx, rax ; advance dst by half that.
;; if rdi was aligned on entry, the it advances by 32 and rdx advances by 16. If it's guaranteed to always be aligned by 32, then simplify the code by removing this peeled unaligned iteration!
;; if not, the first aligned loop iteration will overlap some of the unaligned loads/store, but that's fine.
;; TODO: fold the above calculations into this other loop setup
lea rax, [rdx + rdx]
sub rdi, rax ; source = [rdi + 2*rdx], so we can just increment our dst pointer.
lea rax, [rdx + rcx] ; rax = end pointer. Assumes ecx was already zero-extended to 64-bit
; jmp .loop_entry ; another way to check if we're already done
; Without it, we don't check for loop exit until we've already copied 64B of input to 32B of output.
; If small inputs are common, checking after the first unaligned vectors does make sense, unless leaving it out makes the branch more predictable. (All sizes up to 32B have identical branch-not-taken behaviour).
ALIGN 16
.pack_loop:
; Use SSE4.1 movntdqa if reading from video RAM or other UCSW memory region
movdqa xmm0, [rdi + 2*rdx] ; indexed addressing mode is ok: doesn't need to micro-fuse because loads are already a single uop
movdqa xmm1, [rdi + 2*rdx + 16] ; these could optionally be movntdqa loads, since we got any unaligned source data out of the way.
pand xmm0, xmm7
pand xmm1, xmm7
packuswb xmm0, xmm1
movdqa [rdx], xmm0 ; non-indexed addressing mode: can micro-fuse
add rdx, 16
.loop_entry:
cmp rdx, rax
jb .pack_loop ; exactly 8 uops: should run at 1 iteration per 2 clocks
;; copies up to 15 bytes beyond the requested amount, depending on source alignment.
ret
、負荷がPANDs(vpand xmm0, xmm7, [rdi + 2*rdx]
)に折り畳むことができます。しかしindexed addressing modes can't micro-fuse on at least some SnB-family CPUsなので、宛先を基準にして送信元をアドレッシングするのではなく、add rdi, 32
とadd rdx, 16
の両方を展開することをお勧めします。
AVXは、2xload +と/ pack/storeの4つの融合ドメインuopsとループオーバーヘッドにループ本体をダウンさせます。アンローリングを行うことで、インテルハスウェルの理論的最大スループットは2ロード+ 1ストア/クロック(これは維持できませんが、ストアアドレスuopはp7を使用する代わりにp23サイクルを盗むことになります)すべてのL1キャッシュのヒット、96Bのピークスループット未満であると仮定すると(32バイトのベクターを使用して)〜84Bロードされ、クロックごとに保存されたようなものの持続可能なスループット数を-world。)
あなたはまた、使用することができます低位64ビットにパックされたベクトルの偶数バイトを得るために、2バイトシャッフル(SSSE3 PSHUFB)を使用します。 (その後、128ビットのロードごとに1つの64ビットMOVQストアを実行するか、2つ下半分をPUNPCKLQDQと組み合わせる)。しかし、(ソースデータの128ビットベクトルごとに)2シャッフル+ 2ストア、または3シャッフル+ 1ストアであるため、これはうんざりです。異なるシャッフルマスクを使用して、より安価にマージすることができます。偶数バイトを1つのベクトルの下半分と別のベクトルの上半分にシャッフルします。 PSHUFBは空きバイトもゼロにすることができるので、POR(少し高価なPBLENDWまたはAVX2 VPBLENDDの代わりに)と組み合わせることができます。これは2シャッフル+ 1ブール+ 1ストアですが、シャッフルにボトルネックが残ります。
PACKUSWBの方法は2ブール演算+ 1シャッフル+ 1ストアです(PANDはシャットルの場合は1クロック/ 3など、より多くの実行ポートで実行できるため、ボトルネックはありません)。(Skylake-avx512 but not on KNLで利用可能)
AVX512BWは切り捨て代わりの飽和と
VPMOVWB ymm1/m256 {k1}{z}, zmm2
(__m256i _mm512_cvtepi16_epi8 (__m512i a)
)、パックを提供します。 SSEパック命令とは異なり、入力は1つしかなく、結果はより狭くなります(これはメモリの宛先となります)。 (vpmovswb
とvpmovuswb
は類似しており、符号付きまたは符号なしの彩度でパックされています。pmovzx
と同じサイズのコンボが使用可能です(例:vpmovqb xmm1/m64 {k1}{z}, zmm2
)。
メモリデスティネーション機能はC/C++の組み込み関数でも公開されているため、Cでマスクされたストアを便利にコーディングすることができます(これはpmovzx
where it's inconvenient to use intrinsics and get the compiler to emit a pmovzx
loadからの素晴らしい変更です)。 (インテルCannonlakeで予想)
AVX512VBMIつの入力ベクトルから偶数バイトを受け取り、単一の結果ベクトルを生成するシャッフルマスク所与一VPERMT2Bと、ワン512Bの出力に2つの入力を行うことができます。
VPERM2TBがVPMOVWBよりも遅い場合は、一度に1つのベクトルにVPMOVWBを使用するのが最適です。それらが同じスループット/レイテンシ/ウオップカウントを持っていても、ゲインは非常に小さいので、AVX512BWの代わりに別のバージョンと検出AVX512VBMIを作る価値はありません。 (CPUがAVX512BWを持たなくてもAVX512VBMIを持っている可能性は低いですが)。
うん、まさに私が考えていたもの。結果ベクトルごとにシャッフルが1つしかないため、PSHUFBの任意の組み合わせよりも優れています。シャッフルのビット数はブール値のビットごとの演算数よりも低くなります。 –
私はそれが梱包ステップを行うのに十分であるべきだと思います。 – fuz
これはまさに私が探していたものです。とても有難い。 – poby