मैंने हाल ही में अपनी कंपनी में विभिन्न डेटा संरचनाओं पर एक बेंचमार्क चलाया है, इसलिए मुझे लगता है कि मुझे एक शब्द छोड़ने की जरूरत है। किसी चीज़ को सही तरीके से बेंचमार्क करना बहुत जटिल है।
बेंचमार्किंग
वेब पर हम शायद ही कभी (अगर कभी) एक अच्छी तरह से इंजीनियर बेंचमार्क पाते हैं। आज तक मुझे केवल ऐसे बेंचमार्क मिले जो पत्रकार तरीके से किए गए (बहुत जल्दी और कालीन के नीचे दर्जनों वेरिएबल को व्यापक रूप से)।
1) आपको कैश वार्मिंग के बारे में विचार करना होगा
बेंचमार्क चलाने वाले अधिकांश लोग टाइमर की विसंगति से डरते हैं, इसलिए वे हजारों बार अपना सामान चलाते हैं और पूरे समय लेते हैं, वे बस हर ऑपरेशन के लिए एक ही हजार बार लेने के लिए सावधान हैं, और फिर उस तुलनीय पर विचार करें।
सच्चाई यह है कि वास्तविक दुनिया में, यह बहुत कम समझ में आता है, क्योंकि आपका कैश गर्म नहीं होगा, और आपके ऑपरेशन को केवल एक बार कॉल किया जाएगा। इसलिए आपको आरडीटीसीटी का उपयोग करके बेंचमार्क करने की आवश्यकता है, और समय सामान उन्हें केवल एक बार कॉल करना है। इंटेल एक कागज बना दिया है का वर्णन कैसे RDTSC उपयोग करने के लिए (कार्यक्रम यह स्थिर करने के लिए की शुरुआत में एक सीपीयूआईडी निर्देश का उपयोग कर पाइप लाइन फ्लश करने के लिए, और यह बुला कम से कम 3 बार)।
2) आरडीटीसीटी सटीकता माप
मैं यह करने की सलाह भी देता हूं:
u64 g_correctionFactor; // number of clocks to offset after each measurement to remove the overhead of the measurer itself.
u64 g_accuracy;
static u64 const errormeasure = ~((u64)0);
#ifdef _MSC_VER
#pragma intrinsic(__rdtsc)
inline u64 GetRDTSC()
{
int a[4];
__cpuid(a, 0x80000000); // flush OOO instruction pipeline
return __rdtsc();
}
inline void WarmupRDTSC()
{
int a[4];
__cpuid(a, 0x80000000); // warmup cpuid.
__cpuid(a, 0x80000000);
__cpuid(a, 0x80000000);
// measure the measurer overhead with the measurer (crazy he..)
u64 minDiff = LLONG_MAX;
u64 maxDiff = 0; // this is going to help calculate our PRECISION ERROR MARGIN
for (int i = 0; i < 80; ++i)
{
u64 tick1 = GetRDTSC();
u64 tick2 = GetRDTSC();
minDiff = std::min(minDiff, tick2 - tick1); // make many takes, take the smallest that ever come.
maxDiff = std::max(maxDiff, tick2 - tick1);
}
g_correctionFactor = minDiff;
printf("Correction factor %llu clocks\n", g_correctionFactor);
g_accuracy = maxDiff - minDiff;
printf("Measurement Accuracy (in clocks) : %llu\n", g_accuracy);
}
#endif
यह एक विसंगति मापक है, और यह समय-समय पर -10 ** 18 (64 बिट्स पहले नकारात्मक मूल्यों) से बचने के लिए, सभी मापा मूल्यों में से न्यूनतम लेगा।
आंतरिक और न इनलाइन विधानसभा के उपयोग पर ध्यान दें। पहली इनलाइन असेंबली आजकल कंपाइलरों द्वारा शायद ही कभी समर्थन की जाती है, लेकिन सभी के बहुत खराब होने पर, कंपाइलर इनलाइन असेंबली के चारों ओर एक फुल ऑर्डरिंग बैरियर बनाता है, क्योंकि यह अंदर के स्टैटिक का विश्लेषण नहीं कर सकता है, इसलिए यह वास्तविक दुनिया के सामान को बेंचमार्क करने के लिए एक समस्या है, खासकर जब सामान को कॉल करना एक बार। तो एक आंतरिक यहाँ अनुकूल है, क्योंकि यह संकलक फ्री-री-निर्देशों के आदेश को नहीं तोड़ता है।
3) पैरामीटर
अंतिम समस्या यह है कि लोग आमतौर पर परिदृश्य के बहुत कम बदलावों के लिए परीक्षण करते हैं। एक कंटेनर प्रदर्शन इससे प्रभावित होता है:
- संभाजक
- निहित प्रकार का आकार
- निहित प्रकार के कॉपी ऑपरेशन, असाइनमेंट ऑपरेशन, मूव ऑपरेशन, कंस्ट्रक्शन ऑपरेशन के कार्यान्वयन की लागत।
- कंटेनर में तत्वों की संख्या (समस्या का आकार)
- प्रकार में तुच्छ 3. संचालन हैं
- प्रकार POD है
बिंदु 1 महत्वपूर्ण है क्योंकि कंटेनर समय-समय पर आवंटित करते हैं, और यह बहुत मायने रखता है यदि वे सीआरटी "नया" या कुछ उपयोगकर्ता परिभाषित ऑपरेशन का उपयोग कर आवंटित करते हैं, जैसे कि पूल आवंटन या फ्रीलास्ट या अन्य ...
( pt 1 के बारे में रुचि रखने वाले लोगों के लिए, सिस्टम एलिमिनेटर प्रदर्शन प्रभाव के बारे में gamedev पर रहस्य सूत्र में शामिल हों )
प्वाइंट 2 इसलिए है क्योंकि कुछ कंटेनरों (ए) का कहना है कि चारों ओर सामान की प्रतिलिपि बनाने में समय खो जाएगा, और बड़े प्रकार का ओवरहेड जितना बड़ा होगा। समस्या यह है कि जब दूसरे कंटेनर बी से तुलना की जाती है, तो ए छोटे प्रकारों के लिए बी पर जीत सकता है, और बड़े प्रकारों के लिए खो सकता है।
अंक 3, बिंदु 2 के समान है, सिवाय इसके कि कुछ भार कारक द्वारा लागत को गुणा करता है।
बिंदु 4 कैश मुद्दों के साथ मिश्रित बड़े ओ का प्रश्न है। कुछ खराब-जटिलता वाले कंटेनर कम संख्या के प्रकारों (जैसे map
बनाम vector
, के लिए कम-जटिलता वाले कंटेनरों को बेहतर ढंग से पछाड़ सकते हैं , क्योंकि उनका कैश लोकलिटी अच्छा है, लेकिन map
मेमोरी को टुकड़े करता है)। और फिर कुछ क्रॉसिंग बिंदु पर, वे खो देंगे, क्योंकि निहित समग्र आकार मुख्य मेमोरी को "लीक" करना शुरू कर देता है और कैश मिस हो जाता है, इसके अलावा तथ्य यह है कि स्पर्शोन्मुख जटिलता महसूस की जा सकती है।
प्वाइंट 5 कंपाइलर्स के बारे में है कि वे सामान को खाली करने में सक्षम हैं जो संकलन समय पर खाली या तुच्छ हैं। यह बहुत कुछ संचालन को अनुकूलित कर सकता है, क्योंकि कंटेनरों को विस्थापित किया जाता है, इसलिए प्रत्येक प्रकार का अपना प्रदर्शन प्रोफ़ाइल होगा।
बिंदु 5 के समान बिंदु 6, PODs इस तथ्य से लाभ उठा सकते हैं कि कॉपी निर्माण सिर्फ एक यादगार है, और कुछ कंटेनरों में इन मामलों के लिए एक विशिष्ट कार्यान्वयन हो सकता है, टी के लक्षण के अनुसार एल्गोरिदम का चयन करने के लिए आंशिक टेम्पलेट विशेषज्ञता या SFINAE का उपयोग कर सकते हैं।
फ्लैट के नक्शे के बारे में
जाहिरा तौर पर फ्लैट का नक्शा लोकी एसोसावेक्टर की तरह एक सदिश वेक्टर आवरण है, लेकिन कुछ पूरक आधुनिकीकरण के साथ सी ++ 11 के साथ आ रहा है, एकल तत्वों के डालने और हटाने में तेजी लाने के लिए चाल शब्दार्थ का शोषण कर रहा है।
यह अभी भी एक ऑर्डर किया गया कंटेनर है। अधिकांश लोगों को आमतौर पर ऑर्डर देने वाले हिस्से की आवश्यकता नहीं होती है, इसलिए इसका अस्तित्व है unordered..
।
क्या आपने माना है कि शायद आपको इसकी आवश्यकता है flat_unorderedmap
? जो कुछ ऐसा होगा google::sparse_map
या ऐसा कुछ होगा - एक खुला पता हैश मैप।
ओपन एड्रेस हैश मैप्स की समस्या यह है कि उस समय rehash
उन्हें नए विस्तारित फ्लैट भूमि के चारों ओर सब कुछ कॉपी करना पड़ता है, जबकि एक मानक अनियंत्रित मानचित्र को सिर्फ हैश इंडेक्स को फिर से बनाना पड़ता है, जबकि आवंटित डेटा रहता है जहां यह है। बेशक नुकसान यह है कि स्मृति नरक की तरह खंडित है।
ओपन एड्रेस हैश मैप में रिहैस का मानदंड तब होता है जब क्षमता लोड फैक्टर द्वारा गुणा की गई बाल्टी वेक्टर के आकार से अधिक हो जाती है।
एक विशिष्ट भार कारक है 0.8
; इसलिए, आपको इस बारे में ध्यान रखने की आवश्यकता है, यदि आप इसे भरने से पहले अपने हैश मानचित्र को पूर्व-आकार दे सकते हैं, तो हमेशा पूर्व-आकार: intended_filling * (1/0.8) + epsilon
यह आपको गारंटी देगा कि भरने के दौरान कभी भी पूर्वाभास न करें और सब कुछ पुन: व्यवस्थित करें।
बंद पते के नक्शे ( std::unordered..
) का लाभ यह है कि आपको उन मापदंडों की परवाह नहीं करनी है।
लेकिन boost::flat_map
एक आदेश दिया वेक्टर है; इसलिए, इसमें हमेशा एक लॉग (N) स्पर्शोन्मुख जटिलता होती है, जो खुले पते हैश मैप (अमूर्त निरंतर समय) से कम अच्छी होती है। आपको उस पर भी विचार करना चाहिए।
बेंचमार्क परिणाम
यह एक परीक्षण है जिसमें विभिन्न मानचित्र ( int
कुंजी और __int64
/ somestruct
मान के रूप में) और std::vector
।
परीक्षण प्रकार की जानकारी:
typeid=__int64 . sizeof=8 . ispod=yes
typeid=struct MediumTypePod . sizeof=184 . ispod=yes
निवेशन
संपादित करें:
मेरे पिछले परिणामों में एक बग शामिल था: उन्होंने वास्तव में आदेशित सम्मिलन का परीक्षण किया था, जिसने फ्लैट मानचित्रों के लिए बहुत तेज़ व्यवहार प्रदर्शित किया था।
मैंने उन परिणामों को बाद में इस पृष्ठ पर छोड़ दिया क्योंकि वे दिलचस्प हैं।
यह सही परीक्षण है:
मैंने क्रियान्वयन की जाँच की है, यहाँ सपाट नक्शों में लागू किया गया कोई ऐसा काम नहीं है। प्रत्येक सम्मिलन मक्खी पर होता है, इसलिए यह मानदंड स्पर्शोन्मुख प्रवृत्ति प्रदर्शित करता है:
नक्शा: O (N * log (N))
हैशमैप: O (N)
वेक्टर और फ़्लैटमैप: O (N * N)
चेतावनी : के लिए इसके बाद 2 परीक्षण std::map
और दोनों flat_map
रों रहे हैं गाड़ी है और वास्तव में परीक्षण का आदेश दिया प्रविष्टि (बनाम अन्य कंटेनरों के लिए यादृच्छिक प्रविष्टि हाँ यह खेद भ्रामक है।):
हम देख सकते हैं कि आदेश दिया गया सम्मिलन, पीछे धकेलने का परिणाम है, और बहुत तेज़ है। हालांकि, मेरे बेंचमार्क के गैर-चार्टेड परिणामों से, मैं यह भी कह सकता हूं कि यह बैक-इंसर्शन के लिए पूर्ण अनुकूलता के पास नहीं है। 10k तत्वों पर, पूर्व-आरक्षित वेक्टर पर सही बैक-इंसर्शन इष्टतमता प्राप्त की जाती है। जो हमें 3Million चक्र देता है; हम यहां दिए गए सम्मिलन के लिए 4.8M निरीक्षण करते हैं flat_map
(इसलिए इष्टतम का 160%)।
विश्लेषण: याद रखें कि यह वेक्टर के लिए 'रैंडम इंसर्ट' है, इसलिए बड़े पैमाने पर 1 बिलियन साइकिल प्रत्येक इंसर्शन पर डेटा को ऊपर (एक तत्व द्वारा एक तत्व) में आधा शिफ्ट करने के लिए आता है।
3 तत्वों की यादृच्छिक खोज (1 के लिए क्रमबद्ध घड़ियां)
आकार में = 100
आकार में = 10000
यात्रा
आकार 100 से अधिक (केवल मध्यम प्रकार)
आकार 10000 से अधिक (केवल मध्यम प्रकार)
नमक का अंतिम अनाज
अंत में मैं "बेंचमार्किंग 13 Pt1" (सिस्टम आवंटनकर्ता) पर वापस आना चाहता था। हाल ही में किए गए एक प्रयोग में मैं अपने द्वारा विकसित किए गए एक खुले पते के मानचित्र के प्रदर्शन के आसपास कर रहा हूं , मैंने कुछ std::unordered_map
उपयोग मामलों पर विंडोज 7 और विंडोज 8 के बीच 3000% से अधिक के प्रदर्शन अंतर को मापा ( यहां चर्चा की गई )।
जो मुझे उपरोक्त परिणामों के बारे में पाठक को चेतावनी देना चाहता है (वे Win7 पर किए गए थे): आपका लाभ भिन्न हो सकता है।
सादर