машинний код x86 (MMX / SSE1), 26 байт (4x int16_t)
машинний код x86 (SSE4.1), 28 байт (4х int32_t або uint32_t)
машинний код x86 (SSE2), 24 байти (4x float32) або 27B в cvt int32
(Остання версія, яка перетворює int32 у плаваючий, не є абсолютно точною для великих цілих чисел, які округляються до одного плаваючого елемента. З введенням поплавця округлення є проблемою виклику, і ця функція працює правильно, якщо немає NaN, ідентифікуючи поплавці, які порівнюють == до максимуму цілі версії працюють для всіх входів, трактуючи їх як підписані доповнення 2.)
Усі вони працюють в 16/32/64-бітному режимі з тим же машинним кодом.
Конвенція, що викликає стеки, дає змогу двічі перебирати аргументи (знаходячи макс, а потім порівнюючи), можливо, даючи нам меншу реалізацію, але я не пробував такого підходу.
x86 SIMD має векторні> цілісні растрові карти як єдину інструкцію ( pmovmskbабо movmskpspd), тому це було природно, навіть якщо інструкції MMX / SSE мають принаймні 3 байти. Інструкції SSSE3 та новіших версій довші, ніж SSE2, а інструкції MMX / SSE1 - найкоротші. Різні версії pmax*(пакували цілочисельні вертикальні максимуми) були введені в різний час, при цьому SSE1 (для regx mmx) і SSE2 (для xmm reg) мали лише підписане слово (16-бітне) та непідписаний байт.
( pshufwа pmaxswрегістри MMX є новими в Katmai Pentium III, тому вони дійсно потребують SSE1, а не лише біт функції процесора MMX.)
Це можна викликати з C, як unsigned max4_mmx(__m64)у системі i386 V ABI, яка передає __m64аргумент в mm0. (Не x86-64 System 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 байти). Нам це не потрібно, and eax, 0x0fоскільки pmovmskbв регістрі MMX вже нульові верхні байти. MMX-регістри шириною лише 8 байт, тому 8-бітний AL охоплює всі можливі ненульові біти.
Якби ми знали, що наші дані були негативними, ми моглиpacksswb mm1, mm0 б створити негативні підписані байти у верхніх 4-х байтах mm1, уникаючи необхідності andпісля pmovmskb. Таким чином 24 байти.
Пакет x86 з підписаною насиченістю розглядає вхід і вихід як підписані, тому він завжди зберігає біт знака. ( https://www.felixcloutier.com/x86/packsswb:packssdw ). Факт забави: x86-пакет з ненаписаною насиченістю все ще розглядає вхід як підписаний. Це може бути причиною, що PACKUSDWне було введено до SSE4.1, тоді як інші 3 комбінації розміру та підпису існували з MMX / SSE2.
Або з 32-бітовими цілими числами в регістрі XMM (і pshufdзамість них pshufw) для кожної інструкції знадобиться ще один байт префікса, за винятком movmskpsзаміни пакета / та. Але pmaxsd/ pmaxudпотрібен додатковий додатковий байт ...
викликати з C , якunsigned max4_sse4(__m128i); з x86-64 System V, або MSVC vectorcall ( -Gv), обидва з яких проходять __m128i/ __m128d/ __m128арг в XMM регуляров починаючи з 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Формат може являти собою широкий діапазон цілочисельних значень ...
Або якщо ви вважаєте, що це загинає правила занадто далеко, почніть з 3-байтового 0F 5B C0 cvtdq2ps xmm0, xmm0перетворення, зробивши 27-байтну функцію, яка працює для всіх цілих чисел, які точно представлені як IEEE binary32 float, і багатьох комбінацій входів, де деякі входи отримують округлене до кратного 2, 4, 8 або будь-якого іншого під час перетворення. (Отже, це на 1 байт менше, ніж версія SSE4.1, і працює на будь-якому x86-64 лише з SSE2.)
Якщо будь-який з поплавкових входів є NaN, зауважте, що він maxps a,bточно реалізує (a<b) ? a : b, зберігаючи елемент від 2-го операнда не упорядкованим . Таким чином, можливо, це може повернутися з ненульовою растровою картою, навіть якщо вхід містить деякий 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нами все ще найкраща ставка: shufps dst,src,imm8читається вхід для нижньої половини dst з dst . І нам потрібні неруйнівні копії та переміщення обох разів, тому 3-байт movhlpsі unpckhps/ pd обидва вийшли. Якби ми звужувались до скалярного максимуму, ми могли б використовувати їх, але це коштує ще одну інструкцію для трансляції перед порівнянням, якщо у нас уже немає макс у всіх елементах.
Пов'язано: SSE4.1 phminposuwможе знайти положення та значення мінімуму uint16_tв регістрі XMM. Я не думаю, що виграти від 65535 виграш, щоб використовувати його для max, але дивіться SO відповідь про його використання для max байтів або підписаних цілих чисел.