x86マシンコード(MMX / SSE1)、26バイト(4x int16_t)
x86マシンコード(SSE4.1)、28バイト(4x int32_tまたはuint32_t)
x86マシンコード(SSE2)、24バイト(4x float32)または27Bからctt int32
(int32をfloatに変換する最後のバージョンは、同じfloatに丸める大きな整数に対して完全に正確ではありません。float入力では、丸めは呼び出し元の問題であり、NaNがない場合、この関数は正しく機能し、比較するfloatを識別します==整数バージョンはすべての入力に対して機能し、符号付き2の補数として扱います。)
これらはすべて、同じマシンコードで16/32/64ビットモードで動作します。
stack-args呼び出し規約は、argsを2回ループすることを可能にし(maxを検索してから比較する)、おそらくより小さな実装になりますが、私はそのアプローチを試していません。
x86 SIMDは単一の命令(pmovmskb
or movmskps
またはpd)としてvector-> integerビットマップを持っているため、MMX / SSE命令が少なくとも3バイトの長さであっても、これは当然のことでした。SSSE3以降の命令はSSE2よりも長く、MMX / SSE1命令は最短です。pmax*
(packed-integer vertical max)の異なるバージョンが異なるタイミングで導入され、SSE1(mmx regの場合)とSSE2(xmm regの場合)には符号付きワード(16ビット)と符号なしバイトしかありませんでした。
(pshufw
およびpmaxsw
MMXレジスタはKatmai Pentium IIIで新しく追加されたため、実際にはMMX CPU機能ビットだけでなく、SSE1が必要です。)
これはunsigned max4_mmx(__m64)
、__m64
引数をに渡すi386 System V ABIと同様に、Cから呼び出すことができmm0
ます。(渡さないのx86-64システムV、__m64
でxmm0
!)
line code bytes
num addr
1 global max4_mmx
2 ;; Input 4x int16_t in mm0
3 ;; output: bitmap in EAX
4 ;; clobbers: mm1, mm2
5 max4_mmx:
6 00000000 0F70C8B1 pshufw mm1, mm0, 0b10110001 ; swap adjacent pairs
7 00000004 0FEEC8 pmaxsw mm1, mm0
8
9 00000007 0F70D14E pshufw mm2, mm1, 0b01001110 ; swap high/low halves
10 0000000B 0FEECA pmaxsw mm1, mm2
11
12 0000000E 0F75C8 pcmpeqw mm1, mm0 ; 0 / -1
13 00000011 0F63C9 packsswb mm1, mm1 ; squish word elements to bytes, preserving sign bit
14
15 00000014 0FD7C1 pmovmskb eax, mm1 ; extract the high bit of each byte
16 00000017 240F and al, 0x0F ; zero out the 2nd copy of the bitmap in the high nibble
17 00000019 C3 ret
size = 0x1A = 26 bytes
があった場合pmovmskw
、packsswb
とand
(3 + 2バイト)を節約したはずです。MMXレジスタではすでに上位バイトがゼロであるand eax, 0x0f
ためpmovmskb
、必要ありません。MMXレジスターは8バイト幅しかないので、8ビットALはすべての可能な非ゼロビットをカバーします。
入力が負でないことがわかっている場合packsswb mm1, mm0
、の上位4バイトに負でない符号付きバイトを生成し、after mm1
の必要性を回避できます。 したがって、24バイトです。and
pmovmskb
符号付き飽和を備えたx86パックは、入力と出力を符号付きとして処理するため、常に符号ビットを保持します。(https://www.felixcloutier.com/x86/packsswb:packssdw)。おもしろい事実:符号なし飽和のx86パックでも、入力は符号付きとして扱われます。これがPACKUSDW
SSE4.1まで導入されなかった理由かもしれませんが、MMX / SSE2以降、サイズと署名のその他の3つの組み合わせが存在していました。
または、XMMレジスタに(およびのpshufd
代わりにpshufw
)32ビット整数を使用するとmovmskps
、pack / and を置き換えることを除いて、すべての命令にプレフィックスバイトがさらに1つ必要になります。しかしpmaxsd
/ pmaxud
追加の追加バイトが必要です...
Cからコールunsigned max4_sse4(__m128i);
するx86-64システムV、またはMSVCのvectorcall(と-Gv
通過両方とも)、__m128i
/ __m128d
/ __m128
XMMのREGSで引数が始まりますxmm0
。
20 global max4_sse4
21 ;; Input 4x int32_t in xmm0
22 ;; output: bitmap in EAX
23 ;; clobbers: xmm1, xmm2
24 max4_sse4:
25 00000020 660F70C8B1 pshufd xmm1, xmm0, 0b10110001 ; swap adjacent pairs
26 00000025 660F383DC8 pmaxsd xmm1, xmm0
27
28 0000002A 660F70D14E pshufd xmm2, xmm1, 0b01001110 ; swap high/low halves
29 0000002F 660F383DCA pmaxsd xmm1, xmm2
30
31 00000034 660F76C8 pcmpeqd xmm1, xmm0 ; 0 / -1
32
33 00000038 0F50C1 movmskps eax, xmm1 ; extract the high bit of each dword
34 0000003B C3 ret
size = 0x3C - 0x20 = 28 bytes
または、入力をとして受け入れる場合float
、SSE1命令を使用できます。float
フォーマットは、整数値の広い範囲を表すことができ...
または、ルールを曲げすぎていると思われる場合は0F 5B C0 cvtdq2ps xmm0, xmm0
、変換する3バイトから始めて、IEEEバイナリ32として正確に表現できるすべての整数に対して機能する27バイトの関数を作成しfloat
、一部の入力が取得される入力の多くの組み合わせ変換中に2、4、8などの倍数に丸められます。(つまり、SSE4.1バージョンよりも1バイト小さく、SSE2のみを備えたすべてのx86-64で動作します。)
フロート入力のいずれかがNaNで、ノートの場合はmaxps a,b
正確に実装し(a<b) ? a : b
、順不同で第二オペランドからの要素を保ちます。そのため、入力にNaNが含まれている場合でも、それらがどこにあるかによって、これがゼロ以外のビットマップで返される可能性があります。
unsigned max4_sse2(__m128);
37 global max4_sse2
38 ;; Input 4x float32 in xmm0
39 ;; output: bitmap in EAX
40 ;; clobbers: xmm1, xmm2
41 max4_sse2:
42 ; cvtdq2ps xmm0, xmm0
43 00000040 660F70C8B1 pshufd xmm1, xmm0, 0b10110001 ; swap adjacent pairs
44 00000045 0F5FC8 maxps xmm1, xmm0
45
46 00000048 660F70D14E pshufd xmm2, xmm1, 0b01001110 ; swap high/low halves
47 0000004D 0F5FCA maxps xmm1, xmm2
48
49 00000050 0FC2C800 cmpeqps xmm1, xmm0 ; 0 / -1
50
51 00000054 0F50C1 movmskps eax, xmm1 ; extract the high bit of each dword
52 00000057 C3 ret
size = 0x58 - 0x40 = 24 bytes
のコピーアンドシャッフルpshufd
は、私たちの最善の策です:fromのshufps dst,src,imm8
下半分の入力を読み取ります。また、非破壊的なコピーとシャッフルの両方が必要なため、3バイトと/ pdは両方とも使用できません。スカラーの最大値に絞り込んでいる場合、それらを使用できますが、すべての要素に最大値がまだない場合、比較する前にブロードキャストするために別の指示が必要です。dst
dst
movhlps
unpckhps
関連:SSE4.1 phminposuw
はuint16_t
、XMMレジスターで最小値の位置と値を見つけることができます。65535から減算して最大値に使用するのは良いことではないと思いますが、最大バイト数または符号付き整数に使用することに関するSOの回答を参照してください。