x86-16 मशीन कोड (BubbleSort int8_t), 20 19 बाइट्स
x86-64 / 32 मशीन कोड (जंपडाउनसॉर्ट) 21 19 बाइट्स
बदलाव का:
@ Ped7g के लिए धन्यवाद lodsb
/ cmp [si],al
विचार के लिए, और एक सूचक वृद्धि / रीसेट के साथ एक साथ रखा है कि मैं देख रहा था। आवश्यकता नहीं है al
/ ah
हमें बड़े पूर्णांक के लिए लगभग समान कोड का उपयोग करने देता है।
नया (लेकिन संबंधित) एल्गोरिथ्म, कई कार्यान्वयन परिवर्तन: बबली सेलेक्शनोर्ट बाइट्स या डिडर्स के लिए एक छोटे x86-64 कार्यान्वयन की अनुमति देता है; ब्रेक- यहां तक कि x86-16 पर (बाइट्स या शब्द)। इसके अलावा आकार पर बग से बचा जाता है = 1 जो मेरे बबलसॉर्ट के पास है। निचे देखो।
यह पता चला है कि हर बार जब आप एक नया मिनट पाते हैं तो स्वैप के साथ मेरा चुलबुली चयन सॉर्ट पहले से ही एक ज्ञात एल्गोरिथ्म, जंपडाउन सॉर्ट है। इसका उल्लेख बबल सॉर्ट में किया गया है : एक पुरातात्विक एल्गोरिथम विश्लेषण (यानी बबल सॉर्ट चूसने के बावजूद लोकप्रिय कैसे हुआ)।
8-बिट हस्ताक्षरित पूर्णांक को अंदर रखता है । (अहस्ताक्षरित एक ही कोड का आकार है, बस बदलने के jge
एक करने के लिए jae
)। डुप्लिकेट कोई समस्या नहीं है। हम 16 बिट का उपयोग करके स्वैप करते हैं 8 (एक मेमोरी गंतव्य के साथ)।
बबल सॉर्ट प्रदर्शन के लिए बेकार है , लेकिन मैंने पढ़ा है कि यह मशीन कोड में लागू करने के लिए सबसे छोटा है। यह विशेष रूप से सच लगता है जब आसन्न तत्वों को स्वैप करने के लिए विशेष चालें होती हैं। यह बहुत ही इसका एकमात्र लाभ है, लेकिन कभी-कभी (वास्तविक जीवन में एम्बेडेड सिस्टम) यह बहुत ही कम सूचियों के लिए उपयोग करने के लिए पर्याप्त लाभ है।
मैंने बिना किसी स्वैप के शुरुआती समाप्ति को छोड़ दिया । मैंने विकिपीडिया के "अनुकूलित" बबलसॉर्ट लूप का इस्तेमाल किया n − 1
, जो n
-तो समय के लिए चलने पर अंतिम वस्तुओं को देखने से बचता है , इसलिए बाहरी लूप काउंटर आंतरिक लूप के लिए ऊपरी सीमा है।
एनएएसएम लिस्टिंग ( nasm -l /dev/stdout
), या सादे स्रोत
2 address 16-bit bubblesort16_v2:
3 machine ;; inputs: pointer in ds:si, size in in cx
4 code ;; requires: DF=0 (cld)
5 bytes ;; clobbers: al, cx=0
6
7 00000000 49 dec cx ; cx = max valid index. (Inner loop stops 1 before cx, because it loads i and i+1).
8 .outer: ; do{
9 00000001 51 push cx ; cx = inner loop counter = i=max_unsorted_idx
10 .inner: ; do{
11 00000002 AC lodsb ; al = *p++
12 00000003 3804 cmp [si],al ; compare with *p (new one)
13 00000005 7D04 jge .noswap
14 00000007 C144FF08 rol word [si-1], 8 ; swap
15 .noswap:
16 0000000B E2F5 loop .inner ; } while(i < size);
17 0000000D 59 pop cx ; cx = outer loop counter
18 0000000E 29CE sub si,cx ; reset pointer to start of array
19 00000010 E2EF loop .outer ; } while(--size);
20 00000012 C3 ret
22 00000013 size = 0x13 = 19 bytes.
cx
आंतरिक लूप के चारों ओर पुश / पॉप का मतलब है कि यह cx
= बाहरी_cx से 0 के साथ चलता है ।
ध्यान दें कि rol r/m16, imm8
एक 8086 निर्देश नहीं है, इसे बाद में (186 या 286) जोड़ा गया था, लेकिन यह 8086 कोड होने की कोशिश नहीं कर रहा है, सिर्फ 16-बिट x86। अगर SSE4.1 phminposuw
मदद करेगा, तो मैं इसका इस्तेमाल करूंगा।
इसका 32-बिट संस्करण (अभी भी 8-बिट पूर्णांक पर लेकिन 32-बिट पॉइंटर्स / काउंटरों के साथ काम कर रहा है) 20 बाइट्स (ऑपरेंड-आकार उपसर्ग पर rol word [esi-1], 8
) है
बग: आकार = 1 को आकार = 65536 माना जाता है, क्योंकि कुछ भी हमें बाहरी कार्य में प्रवेश करने से रोकता है / जबकि cx = 0 के साथ। (आप सामान्य रूप से इसके लिए उपयोग jcxz
करेंगे।) लेकिन सौभाग्य से 19-बाइट जंपडाउन सॉर्ट 19 बाइट्स है और इसमें कोई समस्या नहीं है।
मूल x86-16 20 बाइट संस्करण (बिना पेड 7 जी के विचार)। अंतरिक्ष को बचाने के लिए, विवरण के साथ इसके लिए संपादित इतिहास देखें ।
प्रदर्शन
आंशिक रूप से ओवरलैपिंग स्टोर / रीलोड (मेमोरी-डेस्टिनेशन रोटेट में) आधुनिक x86 CPUs (इन-ऑर्डर एटम को छोड़कर) पर स्टोर-फ़ॉरवर्डिंग स्टाल का कारण बनता है। जब एक उच्च मूल्य ऊपर की ओर बुदबुदाती है, तो यह अतिरिक्त विलंबता लूप-चालित निर्भरता श्रृंखला का हिस्सा होता है। पहली जगह में स्टोर / रीलोड चूसता है (जैसे हसवेल पर 5 साइकिल स्टोर-फ़ॉरवर्डिंग लेटेंसी), लेकिन एक फ़ॉर्वर्डिंग स्टाल इसे 13 चक्रों की तरह लाता है। आउट-ऑफ-ऑर्डर निष्पादन को इसे छिपाने में परेशानी होगी।
इसे भी देखें: स्टैक ओवरफ्लो: एक समान कार्यान्वयन के साथ इसके एक संस्करण के लिए स्ट्रिंग को छांटने के लिए बबल सॉर्ट , लेकिन जब कोई स्वैप की आवश्यकता नहीं होती है तो जल्दी आउट के साथ। यह स्वैपिंग के लिए xchg al, ah
/ का उपयोग करता है mov [si], ax
, जो 1 बाइट लंबा है और कुछ सीपीयू पर आंशिक-रजिस्टर स्टाल का कारण बनता है। (लेकिन यह अभी भी मेमोरी-डीएसटी घुमाव से बेहतर हो सकता है, जिसे फिर से मूल्य लोड करने की आवश्यकता होती है)। मेरी टिप्पणी में कुछ सुझाव हैं ...
x86-64 / x86-32 जंपडाउन सॉर्ट करें, 19 बाइट्स (सॉर्ट करें इंट 32_टी)
C86 से x86-64 सिस्टम V कॉलिंग कन्वेंशन
int bubblyselectionsort_int32(int dummy, int *array, int dummy, unsigned long size);
(रिटर्न मान = अधिकतम (सरणी [])) का उपयोग करके कॉल करने योग्य ।
यह https://en.wikipedia.org/wiki/Selection_sort है , लेकिन मिनट तत्व की स्थिति को याद रखने के बजाय, वर्तमान उम्मीदवार को सरणी में स्वैप करें । एक बार जब आप मिन (अनसोर्स्ड_ग्रीन) पा लेते हैं, तो इसे सॉर्ट किए गए क्षेत्र के अंत में स्टोर करें, जैसे सामान्य चयन सॉर्ट। यह एक के आधार पर छांटे गए क्षेत्र को बढ़ता है। (कोड में, rsi
सॉर्ट किए गए क्षेत्र के अंत में एक को इंगित करता है; lodsd
इसे आगे बढ़ाता है और mov [rsi-4], eax
मिनट को वापस इसमें संग्रहीत करता है।)
नाम जंप डाउन सॉर्ट का उपयोग बबल सॉर्ट में किया जाता है : एक पुरातात्विक एल्गोरिथम विश्लेषण । मुझे लगता है कि मेरी तरह वास्तव में एक जम्प अप प्रकार है, क्योंकि उच्च तत्व ऊपर की ओर कूदते हैं, नीचे छंटनी छोड़ते हैं, अंत नहीं।
यह एक्सचेंज डिज़ाइन सरणी के अनसर्टेड हिस्से को ज्यादातर रिवर्स-सॉर्ट किए गए क्रम में ले जाता है, जिससे बाद में बहुत सारे स्वैप हो जाते हैं। (क्योंकि आप एक बड़े उम्मीदवार के साथ शुरू करते हैं, और निचले और निचले उम्मीदवारों को देखते रहते हैं, इसलिए आप स्वैप करते रहते हैं।) मैंने इसे "चुलबुली" कहा, भले ही यह तत्वों को दूसरी दिशा में ले जाए। जिस तरह से यह तत्वों को स्थानांतरित करता है वह भी पीछे की ओर सम्मिलन-प्रकार की तरह थोड़ा सा होता है। इसे क्रिया में देखने के लिए, GDB का उपयोग करें, display (int[12])buf
आंतरिक loop
निर्देश पर एक ब्रेकपॉइंट सेट करें, और उपयोग करें c
(जारी रखें)। दोहराने के लिए वापसी दबाएँ। ("डिस्प्ले" कमांड जीडीबी को हर बार जब हम ब्रेकपॉइंट मारते हैं, तो पूरे एरे स्टेट को प्रिंट करने के लिए मिलता है)।
xchg
मेम के साथ एक अंतर्निहित lock
उपसर्ग है जो इस अतिरिक्त को धीमा करता है। संभवतः एक कुशल लोड / स्टोर स्वैप की तुलना में परिमाण के क्रम के बारे में; xchg m,r
Skylake पर प्रति 23c थ्रूपुट में से एक है, लेकिन एक कुशल स्वैप (reg, मेम) के लिए tmp reg के साथ लोड / स्टोर / मूव प्रति घड़ी एक तत्व को स्थानांतरित कर सकता है। यह एक एएमडी सीपीयू पर एक बदतर अनुपात हो सकता है जहां loop
निर्देश तेज है और आंतरिक लूप को उतना अधिक नहीं टोंक सकता है, लेकिन शाखा में अभी भी एक बड़ी अड़चन होगी क्योंकि स्वैप सामान्य होते हैं (और अनियोजित क्षेत्र जितना छोटा होता है उतना छोटा हो जाता है) )।
2 Address ;; hybrib Bubble Selection sort
3 machine bubblyselectionsort_int32: ;; working, 19 bytes. Same size for int32 or int8
4 code ;; input: pointer in rsi, count in rcx
5 bytes ;; returns: eax = max
6
7 ;dec ecx ; we avoid this by doing edi=esi *before* lodsb, so we do redundant compares
8 ; This lets us (re)enter the inner loop even for 1 element remaining.
9 .outer:
10 ; rsi pointing at the element that will receive min([rsi]..[rsi+rcx])
11 00000000 56 push rsi
12 00000001 5F pop rdi
13 ;mov edi, esi ; rdi = min-search pointer
14 00000002 AD lodsd
16 00000003 51 push rcx ; rcx = inner counter
17 .inner: ; do {
18 ; rdi points at next element to check
19 ; eax = candidate min
20 00000004 AF scasd ; cmp eax, [rdi++]
21 00000005 7E03 jle .notmin
22 00000007 8747FC xchg [rdi-4], eax ; exchange with new min.
23 .notmin:
24 0000000A E2F8 loop .inner ; } while(--inner);
26 ; swap min-position with sorted position
27 ; eax = min. If it's not [rsi-4], then [rsi-4] was exchanged into the array somewhere
28 0000000C 8946FC mov [rsi-4], eax
29 0000000F 59 pop rcx ; rcx = outer loop counter = unsorted elements left
30 00000010 E2EE loop .outer ; } while(--unsorted);
32 00000012 C3 ret
34 00000013 13 .size: db $ - bubblyselectionsort_int32
0x13 = 19 bytes long
के लिए एक ही कोड आकार int8_t
उपयोग: lodsb
/ scasb
, AL
, और परिवर्तन [rsi/rdi-4]
करने के लिए -1
। समान मशीन-कोड 32-बिट मोड में 8/32-बिट तत्वों के लिए काम करता है। 8/16-बिट तत्वों के लिए 16-बिट मोड को फिर से निर्मित ऑफसेट (और 16-बिट एड्रेसिंग मोड एक अलग एन्कोडिंग का उपयोग करें) के साथ बनाने की आवश्यकता है। लेकिन अभी भी सभी के लिए 19 बाइट्स।
यह dec ecx
उस तत्व के साथ तुलना करके प्रारंभिक से बचा जाता है जिस पर जाने से पहले इसे लोड किया जाता है। बाहरी लूप के अंतिम पुनरावृत्ति पर, यह अंतिम तत्व को लोड करता है, जांचता है कि क्या यह स्वयं से कम है, तो किया जाता है। यह इसे आकार = 1 के साथ काम करने की अनुमति देता है, जहां मेरा बबलसॉर्ट विफल रहता है (इसे आकार = 65536 के रूप में मानता है)।
मैंने इस कॉलर का उपयोग करके इस संस्करण (GDB में) का परीक्षण किया: इसे ऑनलाइन आज़माएं! । आप इसे TIO पर चला सकते हैं, लेकिन निश्चित रूप से कोई डिबगर या प्रिंटिंग नहीं। फिर भी, _start
यह कॉल इसे बाहर निकलने की स्थिति के साथ बाहर निकलता है = सबसे बड़ा तत्व = 99, ताकि आप यह देख सकें कि यह काम करता है।
[7 2 4 1] -> [4 2 3 1]
। इसके अलावा, क्या CSV सूची कोष्ठक के अंदर हो सकती है? इसके अलावा, विशिष्ट इनपुट प्रारूप कुछ भाषाओं के लिए बहुत उपयुक्त है, और दूसरों के लिए खराब है। यह इनपुट को कुछ सबमिशन के लिए एक बड़ा हिस्सा बनाता है, और दूसरों के लिए अनावश्यक बनाता है।