2014-04-03 7 views
34

द्वारा पारित मूल्य से तेज़ी से गुजरने के द्वारा मैंने दो दृष्टिकोणों के बीच प्रदर्शन की तुलना करने के लिए सी ++ में एक सरल कार्यक्रम बनाया - मूल्य से गुजरना और संदर्भ द्वारा पास करना। असल में संदर्भ द्वारा पारित मूल्य से बेहतर प्रदर्शन किया जाता है।संदर्भ

निष्कर्ष होना चाहिए कि मूल्य से गुजर कम घड़ी-चक्र (निर्देश)

मैं अगर किसी को विस्तार में समझा सकता है क्यों पास मूल्य से कम घड़ी-चक्र की आवश्यकता होती है वास्तव में खुशी होगी की आवश्यकता है।

#include <iostream> 
#include <stdlib.h> 
#include <time.h> 

using namespace std; 

void function(int *ptr); 
void function2(int val); 

int main() { 

    int nmbr = 5; 

    clock_t start, stop; 
    start = clock(); 
    for (long i = 0; i < 1000000000; i++) { 
     function(&nmbr); 
     //function2(nmbr); 
    } 
    stop = clock(); 

    cout << "time: " << stop - start; 

    return 0; 
} 

/** 
* pass by reference 
*/ 
void function(int *ptr) { 
    *ptr *= 5; 
} 

/** 
* pass by value 
*/ 
void function2(int val) { 
    val *= 5; 
} 
+5

आप इसे पढ़ना चाहेंगे: [गति चाहते हैं? मूल्य से गुजरें] (http: // cpp-next।com/archive/200/08/want-speed-pass-by-value /) – Angew

+22

इन दो कार्यों का व्यवहार समान नहीं है, इसलिए यह वास्तव में उचित तुलना नहीं है ... (इसके अलावा, आप एक पॉइंटर पास कर रहे हैं , सी ++ संदर्भ नहीं ...) –

+24

'function2' * कुछ नहीं * है, इसलिए इसे पूरी तरह से मशीन कोड से छोड़ा जा सकता है। –

उत्तर

68

यह पता लगाने का एक अच्छा तरीका है कि डिस्सेप्लोरों की जांच करने के लिए कोई अंतर क्यों है। यहाँ परिणाम मैं दृश्य स्टूडियो 2012

अनुकूलन झंडे के साथ साथ अपने मशीन पर मिल रहे हैं, दोनों कार्यों को एक ही कोड बनाएं: बिना

int main() 
{ 
    clock_t start, stop ; 
    start = clock() ; 
    stop = clock() ; 
    cout << "time: " << stop - start ; 
    return 0 ; 
} 

:

009D1270 57     push  edi 
009D1271 FF 15 D4 30 9D 00 call  dword ptr ds:[9D30D4h] 
009D1277 8B F8    mov   edi,eax 
009D1279 FF 15 D4 30 9D 00 call  dword ptr ds:[9D30D4h] 
009D127F 8B 0D 48 30 9D 00 mov   ecx,dword ptr ds:[9D3048h] 
009D1285 2B C7    sub   eax,edi 
009D1287 50     push  eax 
009D1288 E8 A3 04 00 00  call  std::operator<<<std::char_traits<char> > (09D1730h) 
009D128D 8B C8    mov   ecx,eax 
009D128F FF 15 2C 30 9D 00 call  dword ptr ds:[9D302Ch] 
009D1295 33 C0    xor   eax,eax 
009D1297 5F     pop   edi 
009D1298 C3     ret 

यह करने के लिए मूल रूप से बराबर है अनुकूलन झंडे, आपको शायद अलग-अलग परिणाम मिलेंगे।

समारोह (एक भी अनुकूलन):

00114890 55     push  ebp 
00114891 8B EC    mov   ebp,esp 
00114893 81 EC C0 00 00 00 sub   esp,0C0h 
00114899 53     push  ebx 
0011489A 56     push  esi 
0011489B 57     push  edi 
0011489C 8D BD 40 FF FF FF lea   edi,[ebp-0C0h] 
001148A2 B9 30 00 00 00  mov   ecx,30h 
001148A7 B8 CC CC CC CC  mov   eax,0CCCCCCCCh 
001148AC F3 AB    rep stos dword ptr es:[edi] 
001148AE 8B 45 08    mov   eax,dword ptr [ptr] 
001148B1 8B 08    mov   ecx,dword ptr [eax] 
001148B3 6B C9 05    imul  ecx,ecx,5 
001148B6 8B 55 08    mov   edx,dword ptr [ptr] 
001148B9 89 0A    mov   dword ptr [edx],ecx 
001148BB 5F     pop   edi 
001148BC 5E     pop   esi 
001148BD 5B     pop   ebx 
001148BE 8B E5    mov   esp,ebp 
001148C0 5D     pop   ebp 
001148C1 C3     ret 

function2 (एक भी अनुकूलन)

00FF4850 55     push  ebp 
00FF4851 8B EC    mov   ebp,esp 
00FF4853 81 EC C0 00 00 00 sub   esp,0C0h 
00FF4859 53     push  ebx 
00FF485A 56     push  esi 
00FF485B 57     push  edi 
00FF485C 8D BD 40 FF FF FF lea   edi,[ebp-0C0h] 
00FF4862 B9 30 00 00 00  mov   ecx,30h 
00FF4867 B8 CC CC CC CC  mov   eax,0CCCCCCCCh 
00FF486C F3 AB    rep stos dword ptr es:[edi] 
00FF486E 8B 45 08    mov   eax,dword ptr [val] 
00FF4871 6B C0 05    imul  eax,eax,5 
00FF4874 89 45 08    mov   dword ptr [val],eax 
00FF4877 5F     pop   edi 
00FF4878 5E     pop   esi 
00FF4879 5B     pop   ebx 
00FF487A 8B E5    mov   esp,ebp 
00FF487C 5D     pop   ebp 
00FF487D C3     ret 

क्यों मान द्वारा पारित तेजी से (कोई अनुकूलन मामले में) है?

ठीक है, function() में दो अतिरिक्त mov संचालन हैं। की पहली अतिरिक्त mov आपरेशन पर एक नज़र लेते हैं:

001148AE 8B 45 08    mov   eax,dword ptr [ptr] 
001148B1 8B 08    mov   ecx,dword ptr [eax] 
001148B3 6B C9 05    imul  ecx,ecx,5 

यहाँ हम सूचक dereferencing कर रहे हैं। function2() में, हमारे पास पहले से ही मूल्य है, इसलिए हम इस चरण से बचते हैं। हम पहले पॉइंटर के पते को रजिस्टर ईएक्स में ले जाते हैं। फिर हम पॉइंटर के मूल्य को रजिस्टर ecx में ले जाते हैं। अंत में, हम मूल्य पांच से गुणा करते हैं। दूसरी अतिरिक्त mov आपरेशन में

आइए नज़र:

001148B3 6B C9 05    imul  ecx,ecx,5 
001148B6 8B 55 08    mov   edx,dword ptr [ptr] 
001148B9 89 0A    mov   dword ptr [edx],ecx 

अब हम पीछे की ओर बढ़ रहे हैं। हमने अभी तक 5 से मूल्य गुणा करना समाप्त कर दिया है, और हमें मान को वापस स्मृति पते में रखना होगा।

क्योंकि function2() को एक सूचक के संदर्भ और संदर्भ के साथ निपटने की ज़रूरत नहीं है, यह इन दो अतिरिक्त mov संचालन को छोड़ देता है।

+2

विस्तृत उत्तर के लिए +1। मैं इसे समय के अनुसार देखता हूं और शायद इसे स्वीकार कर सकता हूं :-) –

+0

यह केवल तभी सच है जब आपके द्वारा मूल्य –

+1

द्वारा किए गए मूल्य के लिए कोई प्रति-निर्माता या विनाशक नहीं है !!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! –

4

मूल्य द्वारा पासिंग अक्सर छोटे प्रकार के लिए बहुत जल्दी है, क्योंकि उनमें से ज्यादातर आधुनिक प्रणालियों (64 बिट) पर सूचक से छोटे हैं। मूल्य से पारित होने पर कुछ अनुकूलन भी हो सकते हैं।

एक सामान्य नियम के रूप में, मूल्य के आधार पर बिल्टिन-प्रकार पास करें।

11

कुछ तर्क करने के लिए: सबसे लोकप्रिय मशीनों में, एक पूर्णांक 32bits है, और एक सूचक 32 या 64bits

तो आपको लगता है कि अधिक से अधिक जानकारी पारित करने के लिए है।

गुणा यह:

एक पूर्णांक आप के लिए है गुणा करने के लिए।

सम्मान सूचक:

एक पूर्णांक एक सूचक आप के लिए है द्वारा बताया गुणा करने के लिए। इसे गुणा करें।

आशा है कि यह काफी स्पष्ट है :)


अब कुछ और विशिष्ट सामग्री के लिए

:

के रूप में यह बताया गया है, अपने द्वारा मूल्य समारोह परिणाम के साथ कुछ भी नहीं करता है, लेकिन दर-सूचक एक वास्तव में स्मृति में परिणाम बचाता है। गरीब सूचक के साथ आप इतने अनुचित क्यों हैं? :((बस मजाक कर रहे हैं)

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

और अंततः (और शायद सबसे महत्वपूर्ण), पॉइंटर्स, मूल्यों या संदर्भों से इसकी गति नहीं होती है। कौन जानता है, आपको एक मशीन मिल सकती है जो पॉइंटर्स के साथ तेज़ है और मूल्यों के साथ कठिन समय लेती है, या ठीक है, ठीक है, हार्डवेयर में कुछ पैटर्न हैं और हम इन सभी धारणाओं को बनाते हैं, सबसे व्यापक रूप से स्वीकार्य लगता है:

संदर्भ द्वारा सरल वस्तुओं को संदर्भित करें और संदर्भ (या सूचक) द्वारा अधिक जटिल लोगों को पास करें (लेकिन फिर, जटिल क्या है? क्या आसान है हार्डवेयर के अनुसार यह समय के साथ बदलता है)

तो हाल ही में मुझे लगता है कि मानक राय बन रही है: मूल्य से गुजरें और संकलक पर भरोसा करें। और यह अच्छा है। कंपाइलर्स का विशेषज्ञता विशेषज्ञता विकास के वर्षों और गुस्से में उपयोगकर्ताओं ने हमेशा बेहतर होने की मांग की है।

4

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

6

जब आप मूल्य से गुजरते हैं, तो आप संकलक को उस इकाई की एक प्रति बनाने के लिए कह रहे हैं जो आप मूल्य से गुजर रहे हैं।

जब आप संदर्भ से गुज़र रहे हैं, तो आप संकलक को बता रहे हैं कि संदर्भ की वास्तविक स्मृति का उपयोग करना चाहिए। कंपाइलर यह नहीं जानता कि आप इसे अनुकूलित करने के प्रयास में कर रहे हैं, या क्योंकि किसी अन्य थ्रेड में संदर्भित मान बदल रहा है (उदाहरण के लिए)। इसे स्मृति के उस क्षेत्र का उपयोग करना है।

संदर्भ द्वारा पास करने का अर्थ प्रोसेसर को उस विशिष्ट मेमोरी ब्लॉक तक पहुंचना है। रजिस्टरों की स्थिति के आधार पर यह सबसे प्रभावी प्रक्रिया हो सकती है या नहीं भी हो सकती है। जब आप संदर्भ से गुज़रते हैं, तो ढेर पर स्मृति का उपयोग किया जा सकता है, जिससे कैश (बहुत तेज़) मेमोरी तक पहुंचने का मौका बढ़ जाता है।

अंत में, आपकी मशीन के आर्किटेक्चर और आपके द्वारा पारित किए जाने वाले प्रकार के आधार पर, संदर्भ वास्तव में आपके द्वारा प्रतिलिपि किए जा रहे मान से बड़ा हो सकता है। 32 बिट पूर्णांक की प्रतिलिपि बनाना 64 बिट मशीन पर संदर्भ पारित करने से कम कॉपी करना शामिल है।

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

जबकि आखिरी बिंदु गैर-तुच्छ है, अंगूठे का एक अच्छा नियम जावा करता है: मूलभूत प्रकारों को मूल्य से, और जटिल प्रकार (कॉन्स) संदर्भ द्वारा पास करें।

11

संदर्भ द्वारा पारित होने के साथ ओवरहेड:

  • मूल्य:

    • प्रत्येक एक्सेस एक भिन्नता की जरूरत है, यानी, वहाँ एक और स्मृति मूल्य द्वारा पढ़ा पारित होने के साथ

    ओवरहेड है स्टैक पर या रजिस्टरों में प्रतिलिपि बनाने की आवश्यकता है

छोटी वस्तुओं के लिए, जैसे एक पूर्णांक, मूल्य से गुजरना तेज़ होगा। बड़ी वस्तुओं के लिए (उदाहरण के लिए एक बड़ी संरचना), प्रतिलिपि बहुत अधिक ओवरहेड पैदा करेगी ताकि संदर्भ द्वारा गुजरना तेजी से हो।

2

अक्सर 32 बिट मेमोरी मैनिपुलेशन निर्देशों को निष्पादित करने के लिए मूल 64 बिट प्लेटफ़ॉर्म पर धीमा होता है, क्योंकि प्रोसेसर को 64 बिट निर्देशों को चलाने की आवश्यकता होती है। यदि यह कंपाइलर द्वारा सही तरीके से किया जाता है, तो 32 बिट निर्देशों को निर्देश कैश पर "जोड़ा" मिलता है, लेकिन यदि 32 बिट पठन को 64 बिट निर्देश के साथ निष्पादित किया जाता है तो 4 अतिरिक्त बाइट भरने के रूप में कॉपी किए जाते हैं और फिर त्याग दिए जाते हैं। संक्षेप में, सूचक आकार से छोटे होने का मतलब यह नहीं है कि यह तेज़ है। यह स्थिति और कंपाइलर पर निर्भर करता है, और पूरी तरह से समग्र प्रकारों को छोड़कर प्रदर्शन के लिए विचार नहीं किया जाना चाहिए जहां मूल्य 1 की परिमाण से सूचक से निश्चित रूप से बड़ा होता है, या ऐसे मामलों में जहां आपको पूर्ण प्रदर्शन के लिए पूर्ण प्रदर्शन की आवश्यकता होती है पोर्टेबिलिटी के संबंध में एक विशेष मंच। संदर्भ या मूल्य से गुजरने के बीच की पसंद केवल इस बात पर निर्भर करती है कि आप कहां से गुजरने वाली प्रक्रिया को संशोधित करने में सक्षम होना चाहते हैं या नहीं। यदि यह 128 बिट्स से छोटे प्रकार के लिए केवल पढ़ने के लिए है, तो मूल्य से गुजरें, यह सुरक्षित है।

5

कल्पना कीजिए कि आप एक फ़ंक्शन में चलते हैं और आपको एक int मूल्य के साथ आना चाहिए। फ़ंक्शन में कोड उस int मान के साथ सामान करना चाहता है।

मूल्य से गुजरना फ़ंक्शन में चलने जैसा है और जब कोई int foo मान मांगता है, तो आप उन्हें केवल उन्हें देते हैं।

संदर्भ द्वारा पास int foo मान के पते के साथ फ़ंक्शन में चल रहा है। अब जब भी किसी को foo के मूल्य की आवश्यकता होती है तो उसे जाना और इसे देखना है। हर कोई निराशाजनक होने के बारे में शिकायत करेगा कि वह सभी भयानक समय को लुभाने वाला है। मैं इस समारोह में अब 2 मिलीसेकंड के लिए रहा हूं और मैंने हजारों बार फू देखा होगा! आपने मुझे पहली जगह क्यों नहीं दिया? आप मूल्य से क्यों नहीं गए?

इस समानता ने मुझे यह देखने में मदद की कि मूल्य से गुजरना क्यों सबसे तेज़ विकल्प है।

+0

इस उत्तर में बहुत अधिक अपवर्त होना चाहिए :) – DoubleK