2009-06-17 14 views
9

के लिए सबसे अधिक कुशल यूनिकोड हैश फ़ंक्शन मुझे डेल्फी 200 में सबसे तेज़ हैश फ़ंक्शन की आवश्यकता है जो यूनिकोड स्ट्रिंग से हैश वैल्यू बनाएगा जो बाल्टी में काफी यादृच्छिक रूप से वितरित करेगा।डेल्फी 200

मैं मूल रूप से GpStringHash से Gabr के HashOf समारोह के साथ शुरू किया:

function HashOf(const key: string): cardinal; 
asm 
    xor edx,edx  { result := 0 } 
    and eax,eax  { test if 0 } 
    jz @End   { skip if nil } 
    mov ecx,[eax-4] { ecx := string length } 
    jecxz @End  { skip if length = 0 } 
@loop:   { repeat } 
    rol edx,2  { edx := (edx shl 2) or (edx shr 30)... } 
    xor dl,[eax] { ... xor Ord(key[eax]) } 
    inc eax   { inc(eax) } 
    loop @loop  { until ecx = 0 } 
@End: 
    mov eax,edx  { result := eax } 
end; { HashOf } 

लेकिन मैंने पाया कि यह यूनिकोड तार से अच्छे नंबर नहीं मिला। मैं ने कहा कि Gabr की दिनचर्या डेल्फी 2009

तब के लिए अद्यतन नहीं किया गया है मैं डेल्फी 2009 के SysUtils में HashNameMBCS की खोज की और इस सरल कार्य करने के लिए यह अनुवाद (जहां "स्ट्रिंग" एक डेल्फी 2009 यूनिकोड स्ट्रिंग है):

function HashOf(const key: string): cardinal; 
var 
    I: integer; 
begin 
    Result := 0; 
    for I := 1 to length(key) do 
    begin 
    Result := (Result shl 5) or (Result shr 27); 
    Result := Result xor Cardinal(key[I]); 
    end; 
end; { HashOf } 

मैंने सोचा था कि जब तक मैं सीपीयू खिड़की को देखा और कोडांतरक कोड यह उत्पन्न देखा यह बहुत अच्छा था:

Process.pas.1649: Result := 0; 
0048DEA8 33DB    xor ebx,ebx 
Process.pas.1650: for I := 1 to length(key) do begin 
0048DEAA 8BC6    mov eax,esi 
0048DEAC E89734F7FF  call $00401348 
0048DEB1 85C0    test eax,eax 
0048DEB3 7E1C    jle $0048ded1 
0048DEB5 BA01000000  mov edx,$00000001 
Process.pas.1651: Result := (Result shl 5) or (Result shr 27); 
0048DEBA 8BCB    mov ecx,ebx 
0048DEBC C1E105   shl ecx,$05 
0048DEBF C1EB1B   shr ebx,$1b 
0048DEC2 0BCB    or ecx,ebx 
0048DEC4 8BD9    mov ebx,ecx 
Process.pas.1652: Result := Result xor Cardinal(key[I]); 
0048DEC6 0FB74C56FE  movzx ecx,[esi+edx*2-$02] 
0048DECB 33D9    xor ebx,ecx 
Process.pas.1653: end; 
0048DECD 42    inc edx 
Process.pas.1650: for I := 1 to length(key) do begin 
0048DECE 48    dec eax 
0048DECF 75E9    jnz $0048deba 
Process.pas.1654: end; { HashOf } 
0048DED1 8BC3    mov eax,ebx 

यह Gabr के कोड से काफ़ी अधिक कोडांतरक कोड होता जा रहा है।

गति सार का है। क्या मैं कुछ भी लिख सकता हूं जो मैंने लिखा था पास्कल कोड या असेंबलर जो मेरा कोड उत्पन्न हुआ था?


अनुवर्ती।

अंततः मैं SysUtils.HashNameMBCS पर आधारित हैशऑफ फ़ंक्शन के साथ गया। ऐसा लगता है कि यूनिकोड तारों के लिए एक अच्छा हैश वितरण प्रदान करता है, और यह काफी तेज़ प्रतीत होता है।

हां, बहुत सारे असेंबलर कोड उत्पन्न हुए हैं, लेकिन डेल्फी कोड जो इसे उत्पन्न करता है, इतना आसान है और केवल बिट-शिफ्ट ऑपरेशंस का उपयोग करता है, इसलिए यह विश्वास करना मुश्किल है कि यह तेज़ नहीं होगा।

+0

आपके अंतिम हैशऑफ में मुझे 1 से लंबाई (कुंजी) जाना चाहिए। – gabr

+0

@gabr: धन्यवाद। अब मैं देखता हूं कि मैंने "फॉलोअप" लिखा है, यह भी महसूस नहीं किया कि मैं अपने प्रश्न के बारे में एक ही काम का उपयोग कर समाप्त हुआ, सिवाय इसके कि मैंने अपने अनुवर्ती में गलती की है। मैं इसे फिर से लिखूंगा। – lkessler

उत्तर

9

एएसएम आउटपुट एल्गोरिदम गति का एक अच्छा संकेत नहीं है। इसके अलावा, मैं जो देख सकता हूं, कोड के दो टुकड़े लगभग समान काम कर रहे हैं। सबसे बड़ा अंतर स्मृति पहुंच रणनीति प्रतीत होता है और पहला निर्देशों के बराबर सेट के बजाय रोल-बाएं का उपयोग कर रहा है (shl | shr - सबसे उच्च स्तरीय प्रोग्रामिंग भाषाएं "रोल" ऑपरेटरों को छोड़ देती हैं)। उत्तरार्द्ध पूर्व की तुलना में पाइपलाइन बेहतर हो सकता है।

एएसएम अनुकूलन काला जादू है और कभी-कभी अधिक निर्देश कम से कम तेज़ी से निष्पादित होते हैं।

सुनिश्चित करने के लिए, दोनों बेंचमार्क करें और विजेता चुनें। यदि आपको दूसरे के आउटपुट पसंद हैं लेकिन पहला तेज है, तो दूसरे के मानों को पहले में प्लग करें।

rol edx,5 { edx := (edx shl 5) or (edx shr 27)... } 

ध्यान दें कि विभिन्न मशीनों इसलिए यदि गति तो हार्डवेयर है कि आप पर अंतिम एप्लिकेशन को चलाने की योजना पर यह बेंचमार्क सार वास्तव में है, अलग अलग तरीकों से कोड चलेंगे। मैं शर्त लगाने के लिए तैयार हूं कि डेटा के मेगाबाइट्स पर अंतर मिलीसेकंड का मामला होगा - जो ऑपरेटिंग सिस्टम से बहुत कम है, आपसे दूर है।


पीएस। मुझे विश्वास नहीं है कि यह एल्गोरिदम भी वितरण बनाता है, जिसे आपने स्पष्ट रूप से बुलाया है (क्या आपने हिस्टोग्राम चलाया है?)। आप डेल्फी में this hash function पोर्टिंग देख सकते हैं।यह उपरोक्त एल्गोरिदम जितना तेज़ नहीं हो सकता है लेकिन यह काफी तेज़ प्रतीत होता है और यह भी अच्छा वितरण देता है। फिर, हम शायद डेटा के मेगाबाइट्स पर अंतर के मिलीसेकंड के आदेश पर बात कर रहे हैं।

+1

मैं इस पर्याप्त से सहमत नहीं हो सकता। आधुनिक प्रोसेसर पर, असेंबलर को हाथ से अनुकूलित करने की कोशिश करना लगभग वास्तव में अतीत की बात नहीं है। – Lee

+0

मैं आपके विचारों की सराहना करता हूं। मैं असेंबलर कोड को अनुकूलित करने में पागल होने का प्रयास नहीं करना चाहता हूं। लेकिन मैं स्पष्ट उपरि को खत्म करना चाहता हूं। मेरे कार्यक्रम का एक रन हैश फ़ंक्शन को सैकड़ों लाखों बार कॉल कर सकता है क्योंकि इसका उपयोग लगभग हर चीज – lkessler

+2

@lkessler के लिए किया जाता है, यहां को खत्म करने के लिए बहुत अधिक ओवरहेड नहीं है। हैश फ़ंक्शन में निष्पादन के कुछ माइक्रोसेकंड से बाहर निकलने के बजाय आपको मूल्य को कैश करने के लिए स्थानों को ढूंढने के लिए अधिक अनुकूलन मिलेंगे। जब आप अपना आवेदन प्रोफाइल करते हैं और देखते हैं कि आपका अधिकांश समय हैश विधि में खर्च किया जा रहा है तो दो विकल्प हैं - हैश फ़ंक्शन को अनुकूलित करें (जाने के लिए बहुत कुछ नहीं) या इसे कम करने के तरीके को समझें। आपका सबसे अच्छा शर्त अभी बाद वाला है। – Talljoe

5

हम एक अच्छी छोटी प्रतियोगिता एक समय पहले आयोजित एक हैश "MurmurHash" कहा जाता है पर सुधार, का हवाला देते हुए विकिपीडिया:

यह असाधारण तेजी से, अक्सर दो से चार गुना तेजी से तुलनीय एल्गोरिदम जैसे FNV, जेनकींस 'lookup3 और ह्सिह के SuperFastHash से, उत्कृष्ट वितरण, हिमस्खलन व्यवहार और साथ किया जा रहा है के लिए विख्यात है समग्र टकराव प्रतिरोध।

आप उस प्रतियोगिता here के लिए सबमिशन डाउनलोड कर सकते हैं।

एक चीज जिसे हमने सीखा था, कभी-कभी अनुकूलन प्रत्येक सीपीयू पर परिणाम में सुधार नहीं करता है। मेरा योगदान एएमडी पर अच्छा प्रदर्शन करने के लिए tweaked था, लेकिन इंटेल पर इतना अच्छा प्रदर्शन नहीं किया। दूसरी तरफ भी हुआ (इंटेल ऑप्टिमाइज़ेशन एएमडी पर उप-इष्टतम चल रहा है)।

तो, जैसा कि तल्जोज ने कहा: अपने अनुकूलन को मापें, क्योंकि वे वास्तव में आपके प्रदर्शन के लिए हानिकारक हो सकते हैं!

साइड-नोट के रूप में: मैं ली से सहमत नहीं हूं; डेल्फी एक अच्छा संकलक और सब कुछ है, लेकिन कभी-कभी मैं इसे कोड उत्पन्न करता हूं जो कि इष्टतम नहीं है (यहां तक ​​कि सभी अनुकूलन के साथ संकलित होने पर भी)। उदाहरण के लिए, मैं नियमित रूप से इसे साफ़ करने वाले रजिस्टरों को देखता हूं जिन्हें पहले से ही केवल दो या तीन कथन साफ़ कर दिए गए थे। या ईएक्स को ईबीएक्स में रखा गया है, केवल इसे स्थानांतरित करने और ईएक्स में वापस रखने के लिए। इस तरह की चीज। मैं बस अनुमान लगा रहा हूं, लेकिन उस तरह के कोड को हाथ से अनुकूलित करने से निश्चित रूप से तंग धब्बे में मदद मिलेगी।

हालांकि सभी के ऊपर; सबसे पहले अपनी बाधा का विश्लेषण करें, फिर देखें कि बेहतर एल्गोरिदम या डेटास्ट्रक्चर का उपयोग किया जा सकता है, फिर पास्कल कोड को अनुकूलित करने का प्रयास करें (जैसे: मेमोरी-आवंटन को कम करें, संदर्भ गिनती से बचें, अंतिम रूप दें, कोशिश करें/आखिरकार, कोशिश करें/ब्लॉक को छोड़कर आदि) और फिर, केवल अंतिम उपाय के रूप में, असेंबली कोड अनुकूलित करें।

5

मैंने डेल्फी में दो असेंबली "अनुकूलित" फ़ंक्शंस लिखे हैं, या ठीक-ठीक पास्कल और बोर्लैंड असेंबलर दोनों में अधिक तेज़ हैश एल्गोरिदम ज्ञात हैं। पहला SuperFastHash का कार्यान्वयन था, और दूसरा मुर्मूरशैश 2 कार्यान्वयन था जो मेरे ब्लॉग पर टॉमी प्रामी के अनुरोध से ट्रिगर किया गया था ताकि मेरे सी # संस्करण को पास्कल कार्यान्वयन में अनुवाद किया जा सके। इसने discussion continued on the Embarcadero Discussion BASM Forums उत्पन्न किया, जिसके अंत में लगभग 20 कार्यान्वयन हुए (latest benchmark suite देखें) जो अंत में दिखाया गया कि इंटेल और एएमडी के बीच प्रति निर्देश चक्र के समय में बड़े अंतर के कारण सर्वोत्तम कार्यान्वयन का चयन करना मुश्किल होगा।

तो, उनमें से किसी एक को आज़माएं, लेकिन याद रखें, सबसे तेज़ होने का मतलब शायद हर समय एल्गोरिदम को एक सरल में बदलना होगा जो आपके वितरण को नुकसान पहुंचाएगा। कार्यान्वयन को ठीक करने में बहुत समय लगता है और आपके कार्यान्वयन की जांच करने के लिए एक अच्छा सत्यापन और बेंचमार्किंग सूट बेहतर बनाता है।

+0

डेवी: काम करने वाले व्यक्ति से सुनना अच्छा लगता है। मैंने लांगजो के जवाब पर अपनी टिप्पणी में आपके कार्यान्वयन पर ध्यान दिया, और चर्चा PhiS द्वारा इंगित की गई थी। ऐसा लगता है कि सुपरफास्टशैश में बहुत सी कोड है, खासकर जब आप इसे मेरे प्रश्न के हैशऑफ फ़ंक्शन में पास्कल की छः पंक्तियों से तुलना करते हैं। मैं सोच रहा हूं कि सुपरफैस्टहाश हैशऑफ की तुलना में तेज़ी से क्या करेगा, और यदि यह तेज़ है, तो कितना? – lkessler

+0

@lkessler: आपके प्रश्न सभी बिंदुओं में जो उल्लेख किया गया है, उसके बारे में बताते हैं, हैश फ़ंक्शन के अपने अपेक्षित उपयोग को अनुकरण करने के लिए एक बेंचमार्किंग प्रोग्राम बनाएं, दोनों गति और वितरण को मापें और आपको कारण मिल सकता है कि क्यों SuperFastHash/MurmurHash2 शायद धीमी है HashOf। छोटे तारों के लिए (10 वर्ण) मैं * उम्मीद करता हूं * हैशऑफ तेजी से होने के लिए, बड़े तारों के लिए अन्य कार्यों ने लाभ उठाने के लिए लूप को अनलॉक कर दिया है। –