2011-11-13 10 views
8

हाय यह ऐसा कुछ है जो वास्तव में मुझे परेशान कर रहा है और मुझे उम्मीद है कि किसी के पास मेरा जवाब होगा। मैं ref (और out) पढ़ रहा हूं और मैं यह पता लगाने की कोशिश कर रहा हूं कि क्या मैं ref एस का उपयोग करके अपना कोड धीमा कर रहा हूं। क्योंकि मेरी आँखों कोसमान प्रकार लौटने की बजाय रेफरी का उपयोग करने के लिए प्रदर्शन लागत?

void AddToInt(ref int original, int add){ original+=add; } // 1st parameter gets the result 

साथ

int AddToInt(int original, int add){ return original+add; } 

इस

AddToInt(ref _value, _add); 

आसान है पढ़ सकते हैं और इस

_value = AddToInt(_value, _add); 

मैं से कोड करने के लिए: आमतौर पर मैं की तरह कुछ का स्थान ले लेगा पता है कि मैं सह पर क्या कर रहा हूं एक मूल्य लौटने के विरोध में, ref का उपयोग कर। हालांकि, प्रदर्शन कुछ है जो मैं गंभीरता से लेता हूं, और जब आप रेफरी का उपयोग करते हैं तो जाहिर है कि डिफ्रेंसिंग और क्लीनअप बहुत धीमा है।

मैं जानना चाहते हैं क्या क्यों प्रत्येक पोस्ट मैंने पढ़ा है कहते हैं वहाँ बहुत कुछ स्थानों पर आप आमतौर पर, जब यह एक ref (मैं जानता हूँ कि उदाहरण काल्पनिक हैं, लेकिन मुझे आशा है कि आप विचार प्राप्त) से होकर गुजरेगा है मुझे लगता है कि ref उदाहरण छोटा, साफ और अधिक सटीक है।

मुझे यह भी जानना अच्छा लगेगा कि क्यों ref वास्तव में एक मूल्य प्रकार लौटने से धीमा है - मेरे लिए यह मुझे प्रतीत होता है, अगर मैं इसे वापस करने से पहले फ़ंक्शन वैल्यू को संपादित करने जा रहा था, तो यह तेज़ होगा वास्तविक चर को संदर्भित करने के लिए इसे उस चर के उदाहरण के विपरीत स्मृति के रूप में साफ़ करने से पहले इसे संपादित करने के लिए।

+3

मैं आमतौर पर शैलीगत कारणों के लिए एक मूल्य लौटने पसंद करते हैं, के बाद से मैं पक्ष प्रभाव मुक्त कार्यों की तरह। – CodesInChaos

+0

आप प्रदर्शन लागत को समझने की कोशिश कर रहे हैं? एक प्रोफाइलर से पूछो, हमसे मत पूछो! – sehe

+0

पहला मामला पढ़ने में आसान है क्योंकि आपने तदनुसार अपनी विधि का नाम दिया है। कुछ _ _value = जोड़ें (_value, _add); 'या '_value = SumOf (_value, _add);' मेरे लिए अधिक पठनीय है। – nawfal

उत्तर

10

मुख्य समय है कि "रेफरी के लिए" के रूप में प्रदर्शन जब कुछ बहुत ही असामान्य मामलों पर चर्चा कर रहा है एक ही sentance में प्रयोग किया जाता है, एक्सएनए परिदृश्यों में उदाहरण जहां जीसी के साथ समस्याओं से बचने के लिए खेल "ऑब्जेक्ट्स" को कक्षाओं के बजाए स्ट्रक्चर द्वारा काफी आम तौर पर दर्शाया जाता है (जिसका एक्सएनए पर असमान प्रभाव पड़ता है)।यह करने के लिए उपयोगी हो जाता है:

  • ढेर पर एक बड़े struct कई बार प्रतिलिपि बनाने से रोकने
  • वजह से एक struct प्रतिलिपि परिवर्तनशील (XNA structs सामान्य अभ्यास के खिलाफ, आमतौर पर परिवर्तनशील हैं)
  • एक गुजर अनुमति देने के लिए डेटा हानि को रोकने के एक एक सरणी सीधे, बजाय में struct कभी

अन्य सभी मामलों में में कॉपी बाहर और वापस, "रेफरी" और अधिक सामान्यतः एक अतिरिक्त पक्ष प्रभाव के साथ जुड़ा हुआ है, आसानी से वापसी वी में व्यक्त नहीं अल्यू (उदाहरण के लिए मॉनीटर। ट्राइंटर देखें)।

यदि आपके पास XNA/struct एक जैसा परिदृश्य नहीं है, और कोई अजीब साइड इफेक्ट नहीं है, तो बस वापसी मान का उपयोग करें। अधिक विशिष्ट होने के अलावा (जो स्वयं में मूल्य है), इसमें कम डेटा गुजरना शामिल हो सकता है (और int उदाहरण के लिए x64 पर रेफरी से छोटा है), और कम डेरफ़्रेंसिंग की आवश्यकता हो सकती है।

अंत में, वापसी दृष्टिकोण अधिक बहुमुखी है; आप स्रोत को अपडेट करना नहीं चाहते हैं। कंट्रास्ट:

// want to accumulate, no ref 
x = Add(x, 5); 

// want to accumulate, ref 
Add(ref x, 5); 

// no accumulate, no ref 
y = Add(x, 5); 

// no accumulate, ref 
y = x; 
Add(ref y, x); 

मुझे लगता है कि पिछले कम से कम स्पष्ट है (अन्य "रेफरी" के साथ इसके पीछे एक करीब) और रेफरी उपयोग भी कम स्पष्ट भाषाओं जहां यह स्पष्ट नहीं है (उदाहरण के लिए VB) है ।

+0

धन्यवाद, यह बताता है कि मैं कहां करता हूं और रेफरी का उपयोग नहीं करना चाहता हूं और क्यों! सबसे अच्छा आपने मुझे यह देखने के लिए चीजों का एक गुच्छा दिया है कि मैं अन्यथा नहीं होता। – user1044057

+1

रेफरी का एक महत्वपूर्ण लाभ, हालांकि, आपके उदाहरण का संदर्भ देना: यदि कुछ जोड़ने जैसे किसी विधि के संदर्भ में कुछ पारित किया गया है, तो लॉक-फ्री, गैर-अवरुद्ध, परमाणु, थ्रेड-सुरक्षित में अद्यतन करने के तरीके के लिए यह संभव हो सकता है फैशन। तो यदि दो धागे एक साथ "जोड़ें" विधि को कॉल करते हैं जैसा कि दूसरे उदाहरण में दिखाया गया है, तो x में दस जोड़े होंगे। इसके विपरीत, यदि पहले या उदाहरण में दिखाए गए अनुसार एक या दोनों थ्रेड अपडेट किए गए हैं, तो एक थ्रेड दूसरे द्वारा किए गए अपडेट को पूर्ववत कर सकता है। – supercat

+0

@supercat ऐसा करने के लिए उन्हें अभी भी इंटरलाक्ड आदि का उपयोग करने की आवश्यकता होगी ... –

3

पहला, परेशान नहीं है कि ref का उपयोग धीमा या तेज है या नहीं। यह समयपूर्व अनुकूलन है। 99.9 999% मामलों में आप एक परिस्थिति में नहीं चलेगा जिससे यह प्रदर्शन बोल्टनेक्ट का कारण बन जाएगा।

दूसरा, ref का उपयोग करने के विपरीत गणना मूल्य के रूप में गणना के परिणाम को वापस करने से सी-जैसी भाषाओं की सामान्य 'funcional' प्रकृति की वजह से preffered है। यह बयान/कॉल की बेहतर श्रृंखला की ओर जाता है।

+16

मैं हमेशा इन "यह समयपूर्व अनुकूलन" उत्तरों को "मुझे नहीं पता" के रूप में व्याख्या करता है। – Rauhotz

+2

वास्तव में मुझे * पता है * और पता है कि इसे कैसे मापें। हालांकि, समय बर्बाद करने के लिए इसका कोई मतलब नहीं है क्योंकि हम प्रति सीपीयू चक्रों के अंतर के बारे में बात कर रहे हैं। –

+4

मुझे गैर प्रदर्शन प्रदर्शन क्षेत्रों में कुछ कोडिंग शैलियों/डिज़ाइन/आर्किटेक्चर के बीच चयन करते समय हमेशा मुख्य मामले के रूप में प्रदर्शन का उपयोग करते हुए (काम करने वाले) देवताओं को पसंद नहीं है। बहुत कुछ होता है। – Guillaume86

2

मुझे यहां ओन्ड्रेज से सहमत होना है। एक स्टाइलिस्ट व्यू से, यदि आप ref के साथ सबकुछ गुजरना शुरू करते हैं तो आप आखिरकार देवताओं के साथ काम करना समाप्त कर देंगे जो इस तरह के एपीआई को डिजाइन करने के लिए आपको परेशान करना चाहते हैं!

बस विधि से सामान वापस लौटें, void पर वापस आने वाली आपकी विधियों में से 100% नहीं है। आप जो कर रहे हैं वह बहुत ही अशुद्ध कोड का कारण बन जाएगा और आपके देवताओं पर काम करने वाले अन्य देवताओं को भ्रमित कर सकता है। यहां प्रदर्शन पर स्पष्टता का पक्ष लें, क्योंकि आप वैसे भी ऑप्टोमाइज़ेशन में ज्यादा लाभ नहीं उठाएंगे।

जांच इस अतः पोस्ट: C# 'ref' keyword, performance

और जॉन स्कीट से इस अनुच्छेद: http://www.yoda.arachsys.com/csharp/parameters.html

2

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

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

public void Quaternion.GetRollPitchYaw(ref double roll, ref double pitch, ref double yaw){ 
    roll = something; 
    pitch = something; 
    yaw = something; 
} 

यह उन भाषाओं में एक सुंदर मौलिक पैटर्न है जिनके पास पॉइंटर्स का अप्रतिबंधित उपयोग है। सी/सी ++ में आप अक्सर कक्षाओं और सरणी के साथ पॉइंटर्स के रूप में मूल्य के आधार पर प्राइमेटिव को पारित करते देखते हैं। सी # सिर्फ विपरीत है इसलिए 'रेफरी' ऊपर की तरह स्थितियों में आसान है।

जब आप एक वेरिएबल पास करते हैं तो आप रेफरी द्वारा फ़ंक्शन में अपडेट करना चाहते हैं, तो आपको अपना परिणाम देने के लिए केवल 1 लेखन ऑपरेशन आवश्यक है। हालांकि मूल्यों को वापस करते समय, आप सामान्य रूप से फ़ंक्शन के अंदर कुछ चर को लिखते हैं, इसे वापस कर देते हैं, फिर इसे फिर से गंतव्य चर में लिखते हैं। डेटा के आधार पर, यह अनावश्यक ओवरहेड जोड़ सकता है। वैसे भी, ये मुख्य चीजें हैं जिन्हें मैं आमतौर पर रेफरी कीवर्ड का उपयोग करने से पहले विचार करता हूं।

कभी-कभी रेफरी थोड़ा सा तेज़ होता है जब सी # में इसका उपयोग किया जाता है लेकिन प्रदर्शन के लिए गोटो औचित्य के रूप में इसका उपयोग करने के लिए पर्याप्त नहीं होता है।

यहां दिए गए कोड का उपयोग करके 7 साल पुरानी मशीन पर मुझे यह पता चला है कि 100 डिग्री स्ट्रिंग को रेफरी और वैल्यू द्वारा अपडेट किया जा रहा है।

आउटपुट:

पुनरावृत्तियों: 10000000 ByRef: 165ms byval: 417ms

private void m_btnTest_Click(object sender, EventArgs e) { 

    Stopwatch sw = new Stopwatch(); 

    string s = ""; 
    string value = new string ('x', 100000); // 100k string 
    int iterations = 10000000; 

    //----------------------------------------------------- 
    // Update by ref 
    //----------------------------------------------------- 
    sw.Start(); 
    for (var n = 0; n < iterations; n++) { 
     SetStringValue(ref s, ref value); 
    } 
    sw.Stop(); 
    long proc1 = sw.ElapsedMilliseconds; 

    sw.Reset(); 

    //----------------------------------------------------- 
    // Update by value 
    //----------------------------------------------------- 
    sw.Start(); 
    for (var n = 0; n < iterations; n++) { 
     s = SetStringValue(s, value); 
    } 
    sw.Stop(); 
    long proc2 = sw.ElapsedMilliseconds; 

    //----------------------------------------------------- 
    Console.WriteLine("iterations: {0} \nbyref: {1}ms \nbyval: {2}ms", iterations, proc1, proc2); 
} 

public string SetStringValue(string input, string value) { 
    input = value; 
    return input; 
} 

public void SetStringValue(ref string input, ref string value) { 
    input = value; 
} 
+0

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

+0

सभी फेंकने के परिणाम सही हैं, कार्य दो कार्यों पर समान नहीं हैं। दरअसल एक समारोह कुछ वापस आ जाता है, दूसरा नहीं। अकेले रेफरी कीवर्ड गैर रेफरी कीवर्ड से तुलना करता है तेज़ नहीं होता है, लेकिन धीमा नहीं होने पर लगभग वही होता है। सही उपाय करने के लिए एक ही क्रिया के साथ दो कार्य करें और केवल 'रेफरी' को बदलना चाहिए ... – Aristos

संबंधित मुद्दे