x86 मशीन कोड (MMX / SSE1), 26 बाइट्स (4x int16_t)
x86 मशीन कोड (SSE4.1), 28 बाइट्स (4x int32_t या uint32_t)
x86 मशीन कोड (SSE2), 24 बाइट्स (4x फ्लोट 32) या 27B intvt को c32
(अंतिम संस्करण जो फ्लोट को int32 में परिवर्तित करता है, एक ही फ्लोट के लिए जाने वाले बड़े पूर्णांकों के लिए बिल्कुल सही नहीं है। फ्लोट इनपुट के साथ, राउंडिंग कॉलर की समस्या है और यह फ़ंक्शन सही ढंग से काम करता है अगर कोई NaN नहीं हैं, तो फ्लोट की पहचान करना = तुलना करना == अधिकतम करने के लिए। पूर्णांक संस्करण सभी इनपुट के लिए काम करते हैं, उन्हें हस्ताक्षरित 2 के पूरक के रूप में मानते हैं।)
ये सभी एक ही मशीन कोड के साथ 16/32/64-बिट मोड में काम करते हैं।
एक स्टैंग-आर्ग्स कन्वेंशन जिसे कॉल करना संभव है, दो बार आर्गन्स पर लूप करना संभव है (अधिकतम और फिर तुलना करना), संभवतः हमें एक छोटा कार्यान्वयन दे रहा है, लेकिन मैंने उस दृष्टिकोण की कोशिश नहीं की है।
x86 SIMD में एकल निर्देश ( pmovmskb
या movmskps
या पीडी) के रूप में वेक्टर-> पूर्णांक बिटमैप है , इसलिए MMX / SSE निर्देश कम से कम 3 बाइट्स लंबे होते हुए भी इसके लिए स्वाभाविक था। SSSE3 और बाद के निर्देश SSE2 से अधिक लंबे हैं, और MMX / SSE1 निर्देश सबसे कम हैं। pmax*
SSE1 (mmx regs के लिए) और SSE2 (xmm regs के लिए) केवल हस्ताक्षरित शब्द (16-बिट) और अहस्ताक्षरित बाइट के साथ (पैक-पूर्णांक लंबवत अधिकतम) के विभिन्न संस्करणों को पेश किया गया था।
( pshufw
और pmaxsw
एमएमएक्स रजिस्टर पर कैटमी पेंटियम III के साथ नए हैं, इसलिए वास्तव में उन्हें एसएसई 1 की आवश्यकता है, न कि केवल एमएमएक्स सीपीयू फीचर बिट।)
यह C3 से unsigned max4_mmx(__m64)
i386 सिस्टम V ABI के साथ कॉल करने योग्य है, जो एक __m64
arg को पास करता है 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 बाइट्स) को क्या बचाया होगा । हमें जरूरत नहीं है 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 तक पेश नहीं किया गया था, जबकि MMX / SSE2 के बाद से आकार और हस्ताक्षर के अन्य 3 संयोजन मौजूद थे।
या एक एक्सएमएम रजिस्टर (और pshufd
इसके बजाय pshufw
) में 32-बिट पूर्णांक के साथ , प्रत्येक निर्देश को movmskps
पैक / और के स्थान को छोड़कर एक और उपसर्ग बाइट की आवश्यकता होगी । लेकिन pmaxsd
/ pmaxud
एक अतिरिक्त अतिरिक्त बाइट की आवश्यकता ...
C86 सेunsigned max4_sse4(__m128i);
x86-64 सिस्टम V, या MSVC वेक्टरकॉल ( -Gv
) के साथ कॉल करने योग्य है , जो दोनों XMM में पास __m128i
/ __m128d
/ __m128
args के साथ शुरू होते हैं 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-बाइट से शुरुआत करें, 27-बाइट फंक्शन बनाते हुए, जो कि सभी पूर्णांकों के लिए काम करता है, जो IEEE बाइनरी 32 के रूप में प्रतिनिधित्व करते हैं।float
, और इनपुट के कई संयोजन जहां कुछ इनपुट मिलते हैं रूपांतरण के दौरान 2, 4, 8, या जो भी हो, के कई पर गोल किया गया। (तो यह SSE4.1 संस्करण की तुलना में 1 बाइट छोटा है, और केवल SSE2 के साथ किसी भी x86-64 पर काम करता है।)
यदि फ्लोट इनपुट में से कोई भी 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
/ पीडी दोनों बाहर हैं। यदि हम एक स्केलर अधिकतम तक सीमित कर रहे थे, तो हम उन का उपयोग कर सकते थे, लेकिन अगर हमारे पास पहले से ही सभी तत्वों में अधिकतम नहीं है, तो तुलना करने से पहले इसे प्रसारित करने के लिए एक और निर्देश का खर्च आता है।
संबंधित: SSE4.1 एक एक्सएमएम रजिस्टर में phminposuw
न्यूनतम की स्थिति और मूल्य पा सकते हैं uint16_t
। मुझे नहीं लगता कि इसे अधिकतम के लिए उपयोग करने के लिए 65535 से घटाना एक जीत है, लेकिन अधिकतम बाइट्स या हस्ताक्षरित पूर्णांक के लिए इसका उपयोग करने के बारे में एक एसओ उत्तर देखें ।