2009-06-17 6 views
13

मैं बस मेरे लिए कुछ अजीब कुछ आया: जब आप एक मान प्रकार पर समान() विधि का उपयोग करते हैं (और यदि यह विधि निश्चित रूप से अतिरंजित नहीं हुई है) तो आपको कुछ बहुत मिलता है धीमी - फ़ील्ड की तुलना की जाती है प्रतिबिंब का उपयोग कर एक से एक! जैसा कि:सी # - वैल्यू टाइप बराबर विधि - संकलक प्रतिबिंब का उपयोग क्यों करता है?

public struct MyStruct{ 
    int i; 
} 

    (...) 

    MyStruct s, t; 
    s.i = 0; 
    t.i = 1; 
    if (s.Equals(t)) /* s.i will be compared to t.i via reflection here. */ 
     (...) 

मेरा प्रश्न: सी # कंपाइलर मूल्य प्रकारों की तुलना करने के लिए एक सरल विधि क्यों नहीं उत्पन्न करता है? की तरह कुछ (MyStruct की परिभाषा में):

public override bool Equals(Object o){ 
     if (this.i == o.i) 
     return true; 
     else 
     return false; 
    } 

संकलक जानता संकलन समय पर MyStruct के क्षेत्र क्या कर रहे हैं, कारण है कि यह क्रम तक इंतजार करता MyStruct क्षेत्रों की गणना करने में?

मेरे लिए बहुत अजीब।

धन्यवाद :)

जोड़ा: क्षमा करें, मैं बस एहसास है कि, ज़ाहिर है, Equals एक भाषा कीवर्ड लेकिन एक क्रम तरीका नहीं है ... संकलक इस विधि से पूरी तरह अनजान है। तो यह प्रतिबिंब का उपयोग करने के लिए यहाँ संवेदना बनाते हैं।

+0

देख "बराबर के मानक कार्यान्वयन का उपयोग करने के लिए, अपने मान प्रकार बॉक्सिंग और संदर्भ प्रकार System.ValueType का एक उदाहरण के रूप में पारित किया जाना चाहिए। The विधि के बराबर है तो प्रदर्शन करने के लिए प्रतिबिंब का उपयोग करता है तुलना। " - msdn.microsoft.com/en-us/library/ff647790.aspx – MrPhil

उत्तर

8

निम्नलिखित mscorlib से decompiled ValueType.Equals विधि है:

public override bool Equals(object obj) 
{ 
    if (obj == null) 
    { 
     return false; 
    } 
    RuntimeType type = (RuntimeType) base.GetType(); 
    RuntimeType type2 = (RuntimeType) obj.GetType(); 
    if (type2 != type) 
    { 
     return false; 
    } 
    object a = this; 
    if (CanCompareBits(this)) 
    { 
     return FastEqualsCheck(a, obj); 
    } 
    FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 
    for (int i = 0; i < fields.Length; i++) 
    { 
     object obj3 = ((RtFieldInfo) fields[i]).InternalGetValue(a, false); 
     object obj4 = ((RtFieldInfo) fields[i]).InternalGetValue(obj, false); 
     if (obj3 == null) 
     { 
      if (obj4 != null) 
      { 
       return false; 
      } 
     } 
     else if (!obj3.Equals(obj4)) 
     { 
      return false; 
     } 
    } 
    return true; 
} 

जब संभव हो, एक बिट-वार तुलना की जाएगी (CanCompareBits और FastEqualsCheck को नोट करें, जिनमें से दोनों को InternalCall के रूप में परिभाषित किया गया है। जेआईटी संभावित रूप से उपयुक्त कोड इंजेक्ट करेगा। डब्ल्यू के रूप में हाय यह बहुत धीमी है, मैं आपको नहीं बता सका।

+0

बस इसका परीक्षण करें। तुम सही हो। :) – SRO

+2

मुझे आश्चर्य है कि अगर कोई रनटाइम किसी भी संरचना के लिए 'बराबर' ओवरराइड को स्वत: उत्पन्न करने के लिए होता है, तो कोई संगतता समस्याएं होती हैं जो पहले से परिभाषित नहीं करती थी: 'बूल बराबर (ऑब्जेक्ट अन्य) {रिटर्न स्ट्रक्चर कॉम्पैयर ।EqualsProc (इसे रेफरी, अन्य); } ', जहां 'इक्वाल्सप्रोक' स्थिर वर्ग 'स्ट्रक्चर कॉम्पैयर ' के भीतर एक स्थिर प्रतिनिधि क्षेत्र था? इस तरह का एक दृष्टिकोण हर वस्तु की तुलना में प्रतिबिंब का उपयोग करने से बचने से बचता है, और मुक्केबाजी चरण से भी बच सकता है। – supercat

9

यह प्रतिबिंब का उपयोग नहीं करता है जब इसे की आवश्यकता नहीं होती है। यह struct के मामले में बस थोड़ा सा मूल्यों की तुलना करें यदि ऐसा हो सकता है। हालांकि, अगर struct सदस्यों में से कोई भी सदस्य (या सदस्यों के सदस्य, किसी भी वंशज) object.Equals ओवरराइड करते हैं और अपना स्वयं का कार्यान्वयन प्रदान करते हैं, जाहिर है, यह वापसी मूल्य की गणना करने के लिए बिट-बाय-बिट तुलना पर भरोसा नहीं कर सकता है।

कारण यह धीमा है कि Equals का पैरामीटर object प्रकार है और मूल्य प्रकारों को object के रूप में माना जाना चाहिए। बॉक्सिंग में ढेर पर स्मृति आवंटित करना और उस स्थान पर मूल्य प्रकार की प्रतिलिपि बनाना शामिल है।

आप मैन्युअल Equals विधि है कि लेता है के लिए एक अधिभार प्रदान कर सकता है अपनी खुद की struct को रोकने के लिए पैरामीटर के रूप में मुक्केबाजी:

public bool Equals(MyStruct obj) { 
    return obj.i == i; 
} 
+4

यह कुछ मामलों में प्रतिबिंब का उपयोग करता है। यदि यह पता लगाता है कि यह केवल परिणामों को मिटा सकता है, तो ऐसा होता है - लेकिन यदि फ़ील्ड में संदर्भ प्रकार (या संदर्भ प्रकार वाले प्रकार) हैं, तो इसे और अधिक दर्दनाक प्रक्रिया करना है। –

+1

जब मैं इसे लिख रहा था, मैंने चारों ओर पढ़ा और मैंने पाया कि कुछ नेट फ्रेमवर्क लेखकों (Cwalina, अब्राम) बस पुष्टि करते हैं कि बराबर मूल्य प्रकारों पर प्रतिबिंब का उपयोग कर रहा है। लेकिन शायद फ्रेमवर्क 2.0 में? – SRO

+2

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

3

एक कंपाइलर जेनरेट फ़ंक्शन का विचार उचित है।

प्रभावों के बारे में सोचते हुए मुझे लगता है कि भाषा डिजाइन टीम ने यह सही किया है। सी ++ से ज्ञात संकलित तरीकों को शुरुआती लोगों के लिए समझना मुश्किल है। देखते हैं क्या स्वत: जनरेट की struct.Equals साथ सी # में क्या होगा:

यह अब के रूप में, .Equals की अवधारणा() सरल है:

  • हर struct inherits ValueType से बराबर होती है।
  • अगर ओवर्रिडन, कस्टम बराबर विधि लागू होती है।

संकलक हमेशा बराबर विधि बनाने हैं, तो हम हो सकता है:

  • हर struct inherits वस्तु से बराबर होती है। (ValueType अब अपना संस्करण लागू करेगा)
  • Object.Equals अब हमेशा होता है (!) ओवरराइड, या तो उत्पन्न संकलक द्वारा विधि के बराबर है या उन कार्यान्वयन द्वारा

अब हमारे struct एक स्वत: जनरेट की ओवरराइड विधि है कि कोड रीडर नहीं देखता है! तो आप कैसे जानते हैं कि बेस विधि ऑब्जेक्ट। एक्वाल्स आपकी संरचना पर लागू नहीं होता है? स्वचालित रूप से संकलक उत्पन्न विधियों के सभी मामलों को सीखकर। और यह सी ++ सीखने वाले बोझों में से एक है।

उपयोगकर्ता को समान रूप से कुशल संरचना छोड़ने और अवधारणाओं को सरल रखने के लिए अच्छा निर्णय मानेंगे, मानक मानक बराबर विधि की आवश्यकता होती है।

उस ने कहा, प्रदर्शन महत्वपूर्ण structs बराबर ओवरराइड करना चाहिए। नीचे दिए गए कोड,

बनाम 53 मिलीसेकंड कारण आभासी बराबर से परहेज है, लेकिन वैसे भी करने के लिए नेट 4.5.1

इस प्रदर्शन लाभ निश्चित रूप से है पर मापा से पता चलता है, इसलिए यदि आभासी Object.Equals होगा कहा जाता है कि लाभ बहुत कम होगा। निष्पादन महत्वपूर्ण मामलों ऑब्जेक्ट को कॉल नहीं करेंगे। हालांकि, इसलिए यहां लाभ लागू होगा।

using System; 
using System.Diagnostics; 

struct A 
{ 
    public int X; 
    public int Y; 
} 

struct B : IEquatable<B> 
{ 
    public bool Equals(B other) 
    { 
     return this.X == other.X && this.Y == other.Y; 
    } 

    public override bool Equals(object obj) 
    { 
     return obj is B && Equals((B)obj); 
    } 

    public int X; 
    public int Y; 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     var N = 100000000; 

     A a = new A(); 
     a.X = 73; 
     a.Y = 42; 
     A aa = new A(); 
     a.X = 173; 
     a.Y = 142; 

     var sw = Stopwatch.StartNew(); 
     for (int i = 0; i < N; i++) 
     { 
      if (a.Equals(aa)) 
      { 
       Console.WriteLine("never ever"); 
      } 
     } 
     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 

     B b = new B(); 
     b.X = 73; 
     b.Y = 42; 
     B bb = new B(); 
     b.X = 173; 
     b.Y = 142; 

     sw = Stopwatch.StartNew(); 
     for (int i = 0; i < N; i++) 
     { 
      if (b.Equals(bb)) 
      { 
       Console.WriteLine("never ever"); 
      } 
     } 
     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 
    } 
} 

भी http://blog.martindoms.com/2011/01/03/c-tip-override-equals-on-value-types-for-better-performance/

+0

यह ध्यान देने योग्य है कि संकलक प्रतिबिंब का उपयोग नहीं करता है; यह बस 'ValueType.Equals' विधि' में वर्चुअल विधि प्रेषण का उपयोग करता है; क्योंकि उस विधि से यह 'वर्ग' वर्ग वर्ग होने की अपेक्षा करता है ['वैल्यू टाइप ', इसके नाम के बावजूद, एक वर्ग] मूल्य को बॉक्स करने की आवश्यकता है। वैचारिक रूप से, यह अच्छा हो सकता है और यदि 'ValueType' परिभाषित किया था एक स्थिर विधि' ValueTypeEquals (सन्दर्भ यह टी, अन्य वस्तु) {ValueTypeComparer .Compare (यह, अन्य रेफरी); 'और सिफारिश की है कि जब संभव compilers फोन है कि वरीयता में आभासी 'बराबर 'विधि के लिए। इस तरह के एक दृष्टिकोण हो सकता है ... – supercat

+0

... रनटाइम को प्रत्येक प्रकार के लिए तुलनात्मक उत्पन्न करने के लिए केवल पहली बार उपयोग किया जाता है, और उसके बाद उस तुलनाकर्ता को बाद में इनवॉक्शंस पर सीधे एक्सेस करें। – supercat

+1

नाइटपिक: 'रेफरेंस एक्वाल्स (नल, ओबीजे)' कॉल तकनीकी रूप से अनावश्यक है क्योंकि 'अभिव्यक्ति अभिव्यक्ति ('obj') शून्य है, तो' अभिव्यक्ति अभिव्यक्ति गलत है] (https://msdn.microsoft.com/en -US/पुस्तकालय/scekt9xw.aspx)। मुझे यकीन है कि यह किसी भी उपयोगी तरीके से बेंचमार्क के परिणामों को प्रभावित नहीं करता है। फिर भी, अगर मैं इससे कोई फर्क नहीं पड़ता, तो मैं इसे अनुकूलित करने के लिए संकलक पर भरोसा नहीं करता। – tne

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