मैं यहां सामान्य ज्ञान के खिलाफ जा रहा हूं std::copy
जिसमें थोड़ा, लगभग अगोचर प्रदर्शन नुकसान होगा। मैंने सिर्फ एक परीक्षण किया और पाया कि असत्य होना: मैंने एक प्रदर्शन अंतर पर ध्यान दिया। हालांकि, विजेता था std::copy
।
मैंने C ++ SHA-2 कार्यान्वयन लिखा था। मेरे परीक्षण में, मेरे पास सभी चार SHA-2 संस्करणों (224, 256, 384, 512) और 300 बार लूप का उपयोग करके 5 तार हैं। मैं Boost.timer का उपयोग करके समय को मापता हूं। यह 300 लूप काउंटर मेरे परिणामों को पूरी तरह से स्थिर करने के लिए पर्याप्त है। मैंने प्रत्येक memcpy
संस्करण और संस्करण के बीच बारी-बारी से 5 बार परीक्षण किया std::copy
। मेरा कोड यथासंभव बड़ी मात्रा में डेटा को हथियाने का लाभ उठाता है (कई अन्य कार्यान्वयन char
/ के साथ काम करते हैं char *
, जबकि मैं T
/ के साथ काम करता हूं / T *
(जहां T
उपयोगकर्ता के कार्यान्वयन में सबसे बड़ा प्रकार है जिसमें अतिप्रवाह व्यवहार होता है), इसलिए तीव्र मेमोरी एक्सेस मेरे एल्गोरिथ्म के प्रदर्शन का सबसे बड़ा प्रकार केंद्रीय हो सकता है। ये मेरे परिणाम हैं:
SHA-2 परीक्षणों को पूरा करने के लिए समय (सेकंड में)
std::copy memcpy % increase
6.11 6.29 2.86%
6.09 6.28 3.03%
6.10 6.29 3.02%
6.08 6.27 3.03%
6.08 6.27 3.03%
Std की गति में कुल औसत वृद्धि :: memcpy पर प्रतिलिपि: 2.99%
मेरा संकलक Fedora 16 x86_64 पर 4.6.3 है। मेरे अनुकूलन झंडे हैं -Ofast -march=native -funsafe-loop-optimizations
।
मेरे SHA-2 कार्यान्वयन के लिए कोड।
मैंने अपने एमडी 5 कार्यान्वयन पर एक परीक्षण चलाने का फैसला किया। परिणाम बहुत कम स्थिर थे, इसलिए मैंने 10 रन बनाने का फैसला किया। हालाँकि, मेरे पहले कुछ प्रयासों के बाद, मुझे एक परिणाम मिला कि एक रन से लेकर दूसरे तक अलग-अलग तरीके से, इसलिए मैं अनुमान लगा रहा हूं कि कुछ प्रकार की ओएस गतिविधि चल रही थी। मैंने शुरू करने का फैसला किया।
एक ही संकलक सेटिंग्स और झंडे। MD5 का केवल एक संस्करण है, और यह SHA-2 की तुलना में तेज़ है, इसलिए मैंने 5 सेट स्ट्रिंग्स के समान सेट पर 3000 लूप किए।
ये मेरे अंतिम 10 परिणाम हैं:
एमडी 5 परीक्षणों को पूरा करने के लिए समय (सेकंड में)
std::copy memcpy % difference
5.52 5.56 +0.72%
5.56 5.55 -0.18%
5.57 5.53 -0.72%
5.57 5.52 -0.91%
5.56 5.57 +0.18%
5.56 5.57 +0.18%
5.56 5.53 -0.54%
5.53 5.57 +0.72%
5.59 5.57 -0.36%
5.57 5.56 -0.18%
एसटीडी की गति में कुल औसत कमी :: मेमकीपी पर प्रतिलिपि: 0.11%
मेरे एमडी 5 कार्यान्वयन के लिए कोड
ये परिणाम बताते हैं कि कुछ अनुकूलन है जो मेरे SHA-2 परीक्षणों में उपयोग की जाने वाली std :: copy है std::copy
जो मेरे MD5 परीक्षणों में उपयोग नहीं हो सकता है। SHA-2 परीक्षणों में, दोनों सरणियों को एक ही फ़ंक्शन में बनाया गया था जिसे std::copy
/ कहा जाता है memcpy
। मेरे एमडी 5 परीक्षणों में, फ़ंक्शन में से एक को फ़ंक्शन पैरामीटर के रूप में पारित किया गया था।
मैंने यह देखने के लिए थोड़ा और परीक्षण किया कि मैं std::copy
फिर से तेज करने के लिए क्या कर सकता हूं । उत्तर सरल निकला: लिंक समय अनुकूलन चालू करें। ये एलटीओ के साथ मेरे परिणाम हैं (विकल्प-जीपीएल में जीसीसी):
एमडीएफ परीक्षण के साथ -फ्लोटो के पूरा होने में समय (सेकंड में)
std::copy memcpy % difference
5.54 5.57 +0.54%
5.50 5.53 +0.54%
5.54 5.58 +0.72%
5.50 5.57 +1.26%
5.54 5.58 +0.72%
5.54 5.57 +0.54%
5.54 5.56 +0.36%
5.54 5.58 +0.72%
5.51 5.58 +1.25%
5.54 5.57 +0.54%
एसटीडी की गति में कुल औसत वृद्धि :: मेमेकपी पर प्रतिलिपि: 0.72%
सारांश में, उपयोग करने के लिए प्रदर्शन जुर्माना नहीं लगता है std::copy
। वास्तव में, एक प्रदर्शन लाभ प्रतीत होता है।
परिणामों की व्याख्या
तो क्यों std::copy
एक प्रदर्शन को बढ़ावा दे सकता है ?
सबसे पहले, मैं इसे किसी भी कार्यान्वयन के लिए धीमा होने की उम्मीद नहीं करूंगा, जब तक कि इनलाइनिंग का अनुकूलन चालू नहीं हो जाता। सभी संकलक आक्रामक रूप से इनलाइन करते हैं; यह संभवतः सबसे महत्वपूर्ण अनुकूलन है क्योंकि यह कई अन्य अनुकूलन को सक्षम करता है। std::copy
(और मुझे संदेह है कि सभी वास्तविक विश्व कार्यान्वयन करते हैं) यह पता लगाते हैं कि तर्क तुच्छ रूप से प्रतिलिपि योग्य हैं और यह स्मृति क्रमबद्ध रूप से रखी गई है। इसका मतलब है कि सबसे खराब स्थिति में, जब memcpy
कानूनी है, तो std::copy
कोई बुरा प्रदर्शन नहीं करना चाहिए। std::copy
उस डिफ़र के तुच्छ कार्यान्वयन को memcpy
आपके कंपाइलर के मानदंडों को पूरा करना चाहिए "गति या आकार के लिए अनुकूलन करते समय इसे हमेशा इनलाइन करें"।
हालाँकि, std::copy
इसकी अधिक जानकारी भी रखता है। जब आप कॉल करते हैं std::copy
, तो फ़ंक्शन प्रकार बरकरार रहता है। memcpy
पर काम करता है void *
, जो लगभग सभी उपयोगी जानकारी देता है। उदाहरण के लिए, यदि मैं किसी सरणी में गुजरता हूं, तो std::uint64_t
कंपाइलर या लाइब्रेरी कार्यान्वयनकर्ता 64-बिट संरेखण का लाभ उठाने में सक्षम हो सकता है std::copy
, लेकिन ऐसा करना अधिक कठिन हो सकता है memcpy
। एल्गोरिदम के कई कार्यान्वयन इस तरह काम करते हैं पहले रेंज के शुरू में अनलग्ड हिस्से पर काम करते हैं, फिर गठबंधन वाले हिस्से, फिर अंत में अनलगइन किए गए हिस्से को। यदि यह सब संरेखित होने की गारंटी है, तो कोड सरल और तेज हो जाता है, और सही करने के लिए आपके प्रोसेसर में शाखा भविष्यवक्ता के लिए आसान हो जाता है।
समय से पहले अनुकूलन?
std::copy
एक दिलचस्प स्थिति में है। मुझे उम्मीद है कि यह memcpy
किसी भी आधुनिक अनुकूलन कंपाइलर के मुकाबले कभी-कभी धीमा और कभी तेज हो सकता है । इसके अलावा, कुछ भी जो आप कर सकते हैं memcpy
, आप कर सकते हैं std::copy
। memcpy
बफ़र्स में किसी भी ओवरलैप की अनुमति नहीं देता है, जबकि std::copy
एक दिशा में ओवरलैप का समर्थन करता है ( std::copy_backward
ओवरलैप की दूसरी दिशा के साथ)। memcpy
केवल, संकेत पर काम करता है std::copy
किसी भी iterators पर काम करता है ( std::map
, std::vector
, std::deque
, या मेरे स्वयं के कस्टम प्रकार)। दूसरे शब्दों में, आपको बस std::copy
उस समय का उपयोग करना चाहिए जब आपको आस-पास डेटा की मात्रा की प्रतिलिपि बनाने की आवश्यकता हो।
char
कार्यान्वयन के आधार पर हस्ताक्षर या अहस्ताक्षरित किया जा सकता है। यदि बाइट्स की संख्या> = 128 हो सकती है, तोunsigned char
अपने बाइट सरणियों के लिए उपयोग करें। ((int *)
कलाकारों को भी सुरक्षित किया जाएगा(unsigned int *)
।)