प्रदर्शन के संदर्भ में क्या std :: memcpy () या std :: copy () का उपयोग करना बेहतर है?


163

क्या memcpyनीचे दिखाए अनुसार उपयोग करना बेहतर है या std::copy()प्रदर्शन के मामले में उपयोग करना बेहतर है ? क्यों?

char *bits = NULL;
...

bits = new (std::nothrow) char[((int *) copyMe->bits)[0]];
if (bits == NULL)
{
    cout << "ERROR Not enough memory.\n";
    exit(1);
}

memcpy (bits, copyMe->bits, ((int *) copyMe->bits)[0]);

ध्यान दें कि charकार्यान्वयन के आधार पर हस्ताक्षर या अहस्ताक्षरित किया जा सकता है। यदि बाइट्स की संख्या> = 128 हो सकती है, तो unsigned charअपने बाइट सरणियों के लिए उपयोग करें। ( (int *)कलाकारों को भी सुरक्षित किया जाएगा (unsigned int *)।)
डैन ब्रेस्लाउ

13
आप उपयोग क्यों नहीं कर रहे हैं std::vector<char>? या के बाद से आप कहते हैं bits, std::bitset?
GMANNICKG

2
वास्तव में, क्या आप मुझे समझा सकते हैं कि क्या (int*) copyMe->bits[0]करता है?
user3728501

4
सुनिश्चित नहीं है कि क्यों कुछ ऐसा लगता है जो प्रदान किए गए इतने महत्वपूर्ण संदर्भ के साथ ऐसा खिलवाड़ था +81 पर, लेकिन हे। @ user3728501 मेरा अनुमान है कि बफर की शुरुआत intअपने आकार को निर्धारित करती है, लेकिन यह कार्यान्वयन-परिभाषित आपदा के लिए एक नुस्खा की तरह लगता है, यहां बहुत सारी चीजें हैं।
अंडरस्कोर_ड

2
वास्तव में, वह (int *)कास्ट सिर्फ शुद्ध अपरिभाषित व्यवहार है, कार्यान्वयन-परिभाषित नहीं। एक कलाकार के माध्यम से टाइप-पिंगिंग करने की कोशिश करना सख्त अलियासिंग नियमों का उल्लंघन करता है और इसलिए पूरी तरह से मानक द्वारा अपरिभाषित है। (इसके अलावा, सी C ++ हालांकि नहीं, आप टाइप-यमक नहीं कर सकते एक के माध्यम से unionया तो।) आप परिवर्तित कर रहे हैं सुंदर बहुत ही अपवाद नहीं है करने के लिए का एक प्रकार char*है, लेकिन भत्ता सममित नहीं है।
अंडरस्कोर_ड

जवाबों:


207

मैं यहां सामान्य ज्ञान के खिलाफ जा रहा हूं 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::copymemcpyबफ़र्स में किसी भी ओवरलैप की अनुमति नहीं देता है, जबकि std::copyएक दिशा में ओवरलैप का समर्थन करता है ( std::copy_backwardओवरलैप की दूसरी दिशा के साथ)। memcpyकेवल, संकेत पर काम करता है std::copyकिसी भी iterators पर काम करता है ( std::map, std::vector, std::deque, या मेरे स्वयं के कस्टम प्रकार)। दूसरे शब्दों में, आपको बस std::copyउस समय का उपयोग करना चाहिए जब आपको आस-पास डेटा की मात्रा की प्रतिलिपि बनाने की आवश्यकता हो।


35
मैं इस बात पर जोर देना चाहता हूं कि इसका मतलब यह नहीं है कि std::copy२.९९% या ०. that२% या ०.१.११% से अधिक तेजी से memcpy, ये समय पूरे कार्यक्रम को निष्पादित करने के लिए है। हालाँकि, मुझे आमतौर पर लगता है कि असली कोड में बेंचमार्क नकली कोड में बेंचमार्क से ज्यादा उपयोगी होते हैं। मेरे पूरे कार्यक्रम को निष्पादन की गति में बदलाव आया। सिर्फ दो नकल योजनाओं के वास्तविक प्रभावों को अलग-अलग किए जाने पर यहां दिखाए गए से अधिक अंतर होगा, लेकिन यह दर्शाता है कि वास्तविक कोड में उनके औसत दर्जे का अंतर हो सकता है।
डेविड स्टोन

2
मैं आपके निष्कर्षों से असहमत होना चाहता हूं, लेकिन परिणाम परिणाम हैं: /। हालांकि एक सवाल (मुझे पता है कि यह बहुत समय पहले था और आपको शोध याद नहीं है, इसलिए आप जिस तरह से सोचते हैं, वैसे ही टिप्पणी करें), आपने शायद विधानसभा कोड में नहीं देखा;
ST3

2
मेरी राय में memcpyऔर std::copyअलग-अलग कार्यान्वयन हैं, इसलिए कुछ मामलों में संकलक आसपास के कोड और एक कोड के एक अभिन्न टुकड़े के रूप में वास्तविक मेमोरी कॉपी कोड का अनुकूलन करता है। यह दूसरे शब्दों में कभी-कभी एक बेहतर होता है, दूसरे में और यहां तक ​​कि दूसरे शब्दों में, जो तय करता है कि समय से पहले या यहां तक ​​कि बेवकूफ अनुकूलन का उपयोग करना है, क्योंकि हर स्थिति में आपको नया शोध करना होगा और, अधिक क्या है, कार्यक्रम आमतौर पर विकसित किए जा रहे हैं, इसलिए बाद में कुछ छोटे बदलावों के कारण दूसरे पर होने वाले लाभ का नुकसान हो सकता है।
ST3

3
@ एसटी 3: मुझे लगता है कि सबसे खराब स्थिति में, std::copyएक तुच्छ इनलाइन फ़ंक्शन है जो memcpyकानूनी होने पर कॉल करता है। बेसिक इनलाइनिंग किसी भी नकारात्मक प्रदर्शन अंतर को समाप्त कर देगी। मैं पोस्ट को अपडेट करूँगा कि std :: copy की प्रतिलिपि अधिक तेज़ क्यों हो सकती है।
डेविड स्टोन

7
बहुत जानकारीपूर्ण विश्लेषण। स्टैड की गति में पुन: कुल औसत में कमी :: मेम्पी पर कॉपी: 0.11% , जबकि संख्या सही है, परिणाम सांख्यिकीय महत्वपूर्ण नहीं हैं। साधनों में अंतर के लिए एक 95% विश्वास अंतराल है (-0.013s, 0.025), जिसमें शून्य शामिल है। जैसा कि आपने बताया कि अन्य स्रोतों और आपके डेटा के साथ भिन्नता थी, आप शायद कहेंगे कि प्रदर्शन समान है। संदर्भ के लिए, अन्य दो परिणाम सांख्यिकीय रूप से महत्वपूर्ण हैं - संभावना है कि जब आप संयोग से इस बार अंतर देखेंगे तो 100 मिलियन (पहले) में 1 और 20,000 (अंतिम) में 1 होगा।
टीओटी

78

सभी compilers मैं एक साधारण का स्थान ले लेगा पता std::copyएक साथ memcpyजब यह उचित है, या और भी बेहतर है, vectorize इतना है कि यह भी तेजी से एक से होगा कॉपी memcpy

किसी भी मामले में: स्वयं का पता लगाएं और पता लगाएं। अलग-अलग कंपाइलर अलग-अलग काम करेंगे, और यह बहुत संभव है कि आप जो पूछते हैं, वह वैसा ही न हो।

संकलक अनुकूलन (पीडीएफ) पर इस प्रस्तुति को देखें ।

यहाँ GOD एक POD प्रकार के साधारण के लिए क्या करता हैstd::copy

#include <algorithm>

struct foo
{
  int x, y;    
};

void bar(foo* a, foo* b, size_t n)
{
  std::copy(a, a + n, b);
}

यहां डिस्सैम्फ़र (केवल -Oअनुकूलन के साथ ), को कॉल दिखा रहा है memmove:

bar(foo*, foo*, unsigned long):
    salq    $3, %rdx
    sarq    $3, %rdx
    testq   %rdx, %rdx
    je  .L5
    subq    $8, %rsp
    movq    %rsi, %rax
    salq    $3, %rdx
    movq    %rdi, %rsi
    movq    %rax, %rdi
    call    memmove
    addq    $8, %rsp
.L5:
    rep
    ret

यदि आप फ़ंक्शन हस्ताक्षर को बदलते हैं

void bar(foo* __restrict a, foo* __restrict b, size_t n)

फिर एक मामूली प्रदर्शन में सुधार के लिए memmoveबन जाता है memcpy। ध्यान दें कि memcpyखुद भारी हो जाएगा।


1
मैं प्रोफाइलिंग कैसे कर सकता हूं। क्या उपकरण का उपयोग करें (विंडोज़ और लिनक्स में)?
user576670

5
@ कोनराड, आप सही हैं। लेकिन memmoveतेज नहीं होना चाहिए - बल्कि, यह धीमा होना चाहिए क्योंकि इसे दो डेटा रेंज ओवरलैप होने की संभावना को ध्यान में रखना है। मुझे लगता है std::copyकि ओवरलैपिंग डेटा की अनुमति देता है, और इसलिए इसे कॉल करना होगा memmove
चार्ल्स साल्विया

2
@ कोनराड: अगर मेम्मोव हमेशा मेमकी से तेज होता, तो मेम्कॉव मेम्मोव कहलाता। क्या std :: copy वास्तव में (यदि कुछ भी) कार्यान्वयन-परिभाषित है, तो लागू हो सकता है, इसलिए कार्यान्वयन का उल्लेख किए बिना बारीकियों का उल्लेख करना उपयोगी नहीं है।
फ्रेड नर्क

1
हालांकि, इस व्यवहार को पुन: पेश करने के लिए एक सरल कार्यक्रम, जीसीसी के तहत -O3 के साथ संकलित मुझे एक दिखाता है memcpy। यह मुझे विश्वास दिलाता है कि जीसीसी चेक है कि क्या मेमोरी ओवरलैप है।
ज्वीरीच

1
@ कोनराड: मानक std::copyएक दिशा में ओवरलैप की अनुमति देता है, लेकिन दूसरे में नहीं। आउटपुट की शुरुआत इनपुट सीमा के भीतर नहीं हो सकती है, लेकिन इनपुट की शुरुआत आउटपुट रेंज के भीतर झूठ बोलने की अनुमति है। यह थोड़ा अजीब है, क्योंकि असाइनमेंट के क्रम को परिभाषित किया गया है, और एक कॉल यूबी हो सकती है, भले ही उन असाइनमेंट का प्रभाव, उस क्रम में परिभाषित किया गया हो। लेकिन मुझे लगता है कि प्रतिबंध वैश्वीकरण अनुकूलन की अनुमति देता है।
स्टीव जेसोप

24

हमेशा का उपयोग करें std::copyक्योंकि memcpyकेवल सी शैली पॉड संरचनाओं के लिए सीमित है, और संकलक की संभावना के लिए कॉल का स्थान ले लेगा std::copyसाथ memcpyकरता है, तो लक्ष्य तथ्य पॉड में हैं।

इसके अलावा, std::copyकई itter प्रकार के साथ इस्तेमाल किया जा सकता है, न केवल संकेत। std::copyकोई प्रदर्शन हानि के लिए अधिक लचीला है और स्पष्ट विजेता है।


आपको पुनरावृत्तियों के आसपास कॉपी क्यों करना चाहिए?
एटमोक्रीटेशन

3
आप पुनरावृत्तियों की प्रतिलिपि नहीं बना रहे हैं, बल्कि दो पुनरावृत्तियों द्वारा परिभाषित सीमा है। उदाहरण के लिए, द्वारा इंगित बफर में (के बीच और सब कुछ ) std::copy(container.begin(), container.end(), destination);की सामग्री की नकल करेंगे । की तरह या shenanigans की आवश्यकता नहीं है । containerbeginenddestinationstd::copy&*container.begin()&container.back() + 1
डेविड स्टोन

16

सिद्धांत रूप में, memcpyएक मामूली , अगोचर हो सकता है , अपरिमेय , प्रदर्शन लाभ हो सकता है, केवल इसलिए कि इसमें जैसी आवश्यकताएं नहीं हैं std::copy। के मैन पेज से memcpy:

ओवरफ्लो से बचने के लिए, दोनों गंतव्य और स्रोत मापदंडों द्वारा इंगित सरणियों का आकार, कम से कम संख्या बाइट्स होगा, और ओवरलैप नहीं होना चाहिए (मेमोरी ब्लॉकों को ओवरलैप करने के लिए, मेमोव एक सुरक्षित दृष्टिकोण है)।

दूसरे शब्दों में, memcpyडेटा ओवरलैप करने की संभावना को अनदेखा कर सकता है। (ओवरलैपिंग सरणियों को पास memcpyकरना अपरिभाषित व्यवहार है।) इसलिए memcpyइस स्थिति के लिए स्पष्ट रूप से जांच करने की आवश्यकता नहीं है, जबकि std::copyजब तक OutputIteratorपैरामीटर स्रोत सीमा में नहीं होता तब तक इसका उपयोग किया जा सकता है। ध्यान दें कि यह कहने के लिए समान नहीं है कि स्रोत रेंज और गंतव्य सीमा ओवरलैप नहीं हो सकती है।

इसलिए चूंकि std::copyकुछ अलग आवश्यकताएं हैं, सिद्धांत रूप में यह होना चाहिए थोड़ा (पर एक चरम जोर देने के साथ थोड़ा धीमी), क्योंकि यह शायद ओवरलैपिंग सी-सरणियों के लिए जाँच करेगा, वरना के लिए सी-सरणियों की नकल प्रतिनिधि memmoveहै, जो प्रदर्शन करने के लिए की जरूरत है जाँच। लेकिन व्यवहार में, आप (और अधिकांश प्रोफाइलर) शायद किसी भी अंतर का पता नहीं लगा पाएंगे।

बेशक, यदि आप POD के साथ काम नहीं कर रहे हैं, तो आप वैसे भी उपयोग नहीं कर सकतेmemcpy


7
यह सच है std::copy<char>। लेकिन std::copy<int>यह मान सकते हैं कि इसके इनपुट इंटिग्रेटेड हैं। इससे बहुत बड़ा फर्क पड़ेगा, क्योंकि यह हर तत्व को प्रभावित करता है। ओवरलैप एक बार की जांच है।
MSalters

2
@MSalters, सच है, लेकिन memcpyमैंने सबसे अधिक कार्यान्वयन के लिए संरेखण और बाइट के बजाए शब्दों को कॉपी करने के प्रयास के लिए जांच देखी है।
चार्ल्स साल्विया

1
std :: copy () ओवरलैपिंग मेमोरी को भी अनदेखा कर सकता है। यदि आप ओवरलैपिंग मेमोरी का समर्थन करना चाहते हैं, तो आपको उपयुक्त स्थितियों में std :: रिवर्स_copy () कॉल करने के लिए तर्क स्वयं लिखना होगा।
साइगॉन

2
एक विपरीत तर्क है जो बनाया जा सकता है: memcpyइंटरफ़ेस के माध्यम से जाने पर यह संरेखण जानकारी खो देता है। इसलिए, बिना memcpyसोचे-समझे शुरुआत और अंत को संभालने के लिए रन-टाइम पर अलाइनमेंट चेक करना पड़ता है। वे चेक सस्ते हो सकते हैं लेकिन वे मुफ्त नहीं हैं। जबकि std::copyइन जांचों से बच सकते हैं और वेक्टर कर सकते हैं। इसके अलावा, संकलक यह साबित कर सकता है कि स्रोत और गंतव्य सरणियाँ ओवरलैप नहीं करते हैं और फिर से उपयोगकर्ता के बीच चयन किए बिना वेक्टर करते हैं memcpyऔर memmove
मैक्सिम Egorushkin

11

मेरा नियम सरल है। यदि आप C ++ का उपयोग कर रहे हैं तो C ++ लाइब्रेरियों को प्राथमिकता दें न कि C :)


40
C ++ को स्पष्ट रूप से C पुस्तकालयों का उपयोग करने की अनुमति देने के लिए डिज़ाइन किया गया था। यह कोई दुर्घटना नहीं थी। अक्सर यह बेहतर है कि सी ++ में मेमरी की तुलना में एसटीडी का उपयोग करें: लेकिन इसका कोई लेना-देना नहीं है कि कौन सी सी है, और इस तरह का तर्क आमतौर पर गलत दृष्टिकोण है।
फ्रेड नर्क

2
@FredNurk आमतौर पर आप C के कमजोर क्षेत्र से बचना चाहते हैं जहां C ++ एक सुरक्षित विकल्प प्रदान करता है।
फिल 1970

@ Phil1970 मुझे यकीन नहीं है कि C ++ इस मामले में ज्यादा सुरक्षित है। हमें अभी भी मान्य पुनरावृत्तियों को पारित करना है जो अधिक नहीं हैं, आदि मुझे लगता है कि सुरक्षित होने के std::end(c_arr)बजाय उपयोग करने में सक्षम c_arr + i_hope_this_is_the_right_number_of elementsहै? और शायद अधिक महत्वपूर्ण बात, स्पष्ट। और वह बिंदु होगा जो मैं इस विशिष्ट मामले में जोर देता हूं: std::copy()अधिक मुहावरेदार, अधिक बनाए रखने योग्य है यदि पुनरावृत्तियों के प्रकार बाद में बदलते हैं, तो वाक्यविन्यास स्पष्ट हो जाता है, आदि
अंडरस्कोर_ड

1
@underscore_d std::copyसुरक्षित है क्योंकि यह POD- प्रकार नहीं होने की स्थिति में पास किए गए डेटा को सही ढंग से कॉपी करता है। memcpyख़ुशी से एक std::stringवस्तु को बाइट द्वारा एक नए प्रतिनिधित्व बाइट को कॉपी करेंगे ।
जेन्स

3

बस एक मामूली जोड़: अनुकूलन के सक्षम या अक्षम होने पर निर्भर करता है के बीच गति अंतर memcpy()और std::copy()काफी भिन्न हो सकता है। जी ++ 6.2.0 और अनुकूलन के बिना memcpy()स्पष्ट रूप से जीतता है:

Benchmark             Time           CPU Iterations
---------------------------------------------------
bm_memcpy            17 ns         17 ns   40867738
bm_stdcopy           62 ns         62 ns   11176219
bm_stdcopy_n         72 ns         72 ns    9481749

जब अनुकूलन सक्षम होते हैं ( -O3), सब कुछ फिर से बहुत अधिक दिखता है:

Benchmark             Time           CPU Iterations
---------------------------------------------------
bm_memcpy             3 ns          3 ns  274527617
bm_stdcopy            3 ns          3 ns  272663990
bm_stdcopy_n          3 ns          3 ns  274732792

बड़ा सरणी कम ध्यान देने योग्य प्रभाव हो जाता है, लेकिन यहां तक ​​कि पर N=1000 memcpy() जब अनुकूलन सक्षम नहीं होते हैं तब लगभग दोगुना तेज होता है।

स्रोत कोड (Google बेंचमार्क की आवश्यकता है):

#include <string.h>
#include <algorithm>
#include <vector>
#include <benchmark/benchmark.h>

constexpr int N = 10;

void bm_memcpy(benchmark::State& state)
{
  std::vector<int> a(N);
  std::vector<int> r(N);

  while (state.KeepRunning())
  {
    memcpy(r.data(), a.data(), N * sizeof(int));
  }
}

void bm_stdcopy(benchmark::State& state)
{
  std::vector<int> a(N);
  std::vector<int> r(N);

  while (state.KeepRunning())
  {
    std::copy(a.begin(), a.end(), r.begin());
  }
}

void bm_stdcopy_n(benchmark::State& state)
{
  std::vector<int> a(N);
  std::vector<int> r(N);

  while (state.KeepRunning())
  {
    std::copy_n(a.begin(), N, r.begin());
  }
}

BENCHMARK(bm_memcpy);
BENCHMARK(bm_stdcopy);
BENCHMARK(bm_stdcopy_n);

BENCHMARK_MAIN()

/* EOF */

18
विकलांगों के साथ प्रदर्शन को मापना अक्षम है ... अच्छी तरह से ... बहुत ज्यादा व्यर्थ ... यदि आप प्रदर्शन में रुचि रखते हैं तो आप अनुकूलन के बिना संकलन नहीं करेंगे।
बोलोव

3
@bolov हमेशा नहीं। डिबग के तहत एक अपेक्षाकृत तेज़ कार्यक्रम कुछ मामलों में महत्वपूर्ण है।
बलूत का फल

2

यदि आपको वास्तव में अधिकतम नकल प्रदर्शन (जो आप नहीं कर सकते हैं) की आवश्यकता है, तो उनमें से किसी का भी उपयोग न करें

वहाँ एक है बहुत कुछ इसके लिए / कोर आप एक से अधिक थ्रेड का उपयोग करने के लिए तैयार करता है, तो और भी अधिक - कि अनुकूलन स्मृति नकल करने के लिए किया जा सकता है। उदाहरण के लिए देखें:

इस यादगार कार्यान्वयन में क्या गायब / उप-इष्टतम है?

प्रश्न और उत्तर में से कुछ ने क्रियान्वयन के कार्यान्वयन या लिंक का सुझाव दिया है।


4
पेडेंट मोड: सामान्य कैविएट के साथ जो " न तो उनमें से उपयोग करें " का अर्थ है यदि आपने साबित कर दिया है कि आपके पास अत्यधिक विशिष्ट स्थिति / आवश्यकता है जिसके लिए न तो आपके कार्यान्वयन द्वारा प्रदान किया गया मानक फ़ंक्शन पर्याप्त तेज़ है ; अन्यथा, मेरी सामान्य चिंता यह है कि जो लोग साबित नहीं हुए हैं कि उनके कार्यक्रम के आमतौर पर अधिक उपयोगी भागों के बजाय समय से पहले कोडिंग को अनुकूलित करने पर उनका अनुकूलन हो जाता है।
अंडरस्कोर_ड

-2

प्रोफाइलिंग यह बयान दिखाती है: std::copy()हमेशा उतना ही तेज memcpy()या उतना ही झूठा होता है।

मेरा सिस्टम:

HP-Compaq-dx7500-Microtower 3.13.0-24- जेनेरिक # 47-Ubuntu SMP शुक्र 2 मई 23:30:00 UTC 2014 x86_64 x86_64 x86_64 GNU / Linux।

gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2

कोड (भाषा: c ++):

    const uint32_t arr_size = (1080 * 720 * 3); //HD image in rgb24
    const uint32_t iterations = 100000;
    uint8_t arr1[arr_size];
    uint8_t arr2[arr_size];
    std::vector<uint8_t> v;

    main(){
        {
            DPROFILE;
            memcpy(arr1, arr2, sizeof(arr1));
            printf("memcpy()\n");
        }

        v.reserve(sizeof(arr1));
        {
            DPROFILE;
            std::copy(arr1, arr1 + sizeof(arr1), v.begin());
            printf("std::copy()\n");
        }

        {
            time_t t = time(NULL);
            for(uint32_t i = 0; i < iterations; ++i)
                memcpy(arr1, arr2, sizeof(arr1));
            printf("memcpy()    elapsed %d s\n", time(NULL) - t);
        }

        {
            time_t t = time(NULL);
            for(uint32_t i = 0; i < iterations; ++i)
                std::copy(arr1, arr1 + sizeof(arr1), v.begin());
            printf("std::copy() elapsed %d s\n", time(NULL) - t);
        }
    }

g ++ -O0 -o test_stdcopy test_stdcopy.cpp

memcpy () प्रोफ़ाइल: मुख्य: 21: अब: 1422969084: 04859 बीता हुआ: 2650 us
std :: कॉपी () प्रोफ़ाइल: मुख्य: 27: अब: 1422969084: 04862 बीत गया: 2745 हमें
memcpy () बीता हुआ 44 s std :: कॉपी :: ( ) बीता हुआ 45 एस

g ++ -O3 -o test_stdcopy test_stdcopy.cpp

memcpy () प्रोफ़ाइल: मुख्य: 21: अब: 1422969601: 04939 बीता हुआ: 2385 us
std :: copy () प्रोफ़ाइल: मुख्य: 28: अब: 1422969601: 04941 बीत गया: 2690 us
memcpy () बीता हुआ 27 s std :: copy () ) बीता हुआ 43 एस

Red Alert ने बताया कि कोड मेमरी से सरणी से सरणी और std :: कॉपी से सरणी से वेक्टर तक का उपयोग करता है। यह तख्तापलट तेजी से यादगार होने का एक कारण है।

चूंकि वहाँ है

v.reserve (sizeof (arr1));

वेक्टर या सरणी की प्रतिलिपि में कोई अंतर नहीं होगा।

दोनों मामलों के लिए सरणी का उपयोग करने के लिए कोड निश्चित है। मेमसीपी अभी भी तेज:

{
    time_t t = time(NULL);
    for(uint32_t i = 0; i < iterations; ++i)
        memcpy(arr1, arr2, sizeof(arr1));
    printf("memcpy()    elapsed %ld s\n", time(NULL) - t);
}

{
    time_t t = time(NULL);
    for(uint32_t i = 0; i < iterations; ++i)
        std::copy(arr1, arr1 + sizeof(arr1), arr2);
    printf("std::copy() elapsed %ld s\n", time(NULL) - t);
}

memcpy()    elapsed 44 s
std::copy() elapsed 48 s 

1
गलत, आपका प्रोफाइलिंग दिखाता है कि किसी सरणी में कॉपी करना वेक्टर में कॉपी होने से अधिक तेज़ है। विषय से परे।
रेड अलर्ट

मैं गलत हो सकता है, लेकिन आपके सही उदाहरण में, मेम्ची के साथ, आप arr1 में arr2 की नकल नहीं कर रहे हैं, जबकि std :: copy के साथ, आप arr1 को arr2 में कॉपी कर रहे हैं? ... आप क्या कर सकते हैं एकाधिक, वैकल्पिक बनाने के लिए? प्रयोगों (एक बार मेम्ची का एक बैच, एक बार एसटीडी का एक बैच :: कॉपी, फिर मेम्कोपी के साथ वापस, आदि, कई बार।)। फिर, मैं समय () के बजाय घड़ी () का उपयोग करूंगा, क्योंकि कौन जानता है कि उस प्रोग्राम के अलावा आपका पीसी क्या कर सकता है। बस मेरे दो सेंट, हालांकि ... :-)
पियरसबल

7
तो, std::copyएक सदिश से एक सरणी पर स्विच करना किसी भी तरह memcpyलगभग दो बार लंबे समय तक बना दिया? यह डेटा अत्यधिक संदिग्ध है। -O3 के साथ gcc का उपयोग करके मैंने आपका कोड संकलित किया है, और उत्पन्न विधानसभा दोनों छोरों के लिए समान है। इसलिए जब आप अपनी मशीन पर निरीक्षण करते हैं तो कोई भी अंतर केवल आकस्मिक है।
रेड अलर्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.