2011-04-18 13 views
13

मैं एक बहुत ही हास्यास्पद परिस्थिति में आया जहां एक सामान्य विधि के अंदर एक शून्य प्रकार को शून्य से तुलना करना एक मान प्रकार या संदर्भ प्रकार की तुलना में 234x धीमा है।एक सामान्य विधि पर शून्य करने के लिए एक शून्य विधि की तुलना करने के लिए धीमा क्यों है, जिसमें कोई बाधा नहीं है?

static bool IsNull<T>(T instance) 
{ 
    return instance == null; 
} 

निष्पादन कोड है:: कोड इस प्रकार है

int? a = 0; 
string b = "A"; 
int c = 0; 

var watch = Stopwatch.StartNew(); 

for (int i = 0; i < 1000000; i++) 
{ 
    var r1 = IsNull(a); 
} 

Console.WriteLine(watch.Elapsed.ToString()); 

watch.Restart(); 

for (int i = 0; i < 1000000; i++) 
{ 
    var r2 = IsNull(b); 
} 

Console.WriteLine(watch.Elapsed.ToString()); 

watch.Restart(); 

for (int i = 0; i < 1000000; i++) 
{ 
    var r3 = IsNull(c); 
} 

watch.Stop(); 

Console.WriteLine(watch.Elapsed.ToString()); 
Console.ReadKey(); 

ऊपर कोड के लिए उत्पादन होता है:

00: 00: 00,1879827

00:00: 00.0008779

00: 00: 00.0008532

जैसा कि आप देख सकते हैं, एक शून्य या एक स्ट्रिंग की तुलना में शून्य के लिए एक शून्य से int की तुलना धीमी है 234x धीमी है। अगर मैं सही बाधाओं के साथ एक दूसरे अधिभार जोड़ने के लिए, परिणाम नाटकीय रूप से बदल:

static bool IsNull<T>(T? instance) where T : struct 
{ 
    return instance == null; 
} 

अब परिणाम हैं:

00: 00: ००.०००६०४०

00: 00: 00,0006017

00: 00: 00.0006014

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

+0

ऐसे परिणामों के साथ - सबसे खराब मामले में 1 एम पुनरावृत्तियों के लिए 0.2 सेकंड के तहत, क्या इससे कोई फर्क पड़ता है? – Oded

+1

हां यह करता है यदि आप यह 1 एम एक सेकंड करते हैं। मैं करता हूँ। –

+2

यह न भूलें कि आप लाखों पुनरावृत्तियों की लागत का योग माप रहे हैं * और पहले कॉल * पर कोड को जंप करने की लागत। यदि कोड वास्तव में सस्ता है, क्योंकि यह कोड है, तो जिट लागत जो केवल एक बार होती है वह वास्तव में औसत पर हावी हो सकती है। परीक्षण को दो बार एक ही प्रोग्राम में चलाने में दिलचस्प हो सकता है ताकि दूसरी बार कोड "गर्म" हो। –

उत्तर

5

आप आईएल दो भार के द्वारा उत्पादित की तुलना हैं, तो आप देख सकते हैं कि वहाँ शामिल मुक्केबाजी जाता है: जैसे

पहले दिखता है:

.method private hidebysig static bool IsNull<T>(!!T instance) cil managed 
{ 
    .maxstack 2 
    .locals init (
     [0] bool CS$1$0000) 
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: box !!T 
    L_0007: ldnull 
    L_0008: ceq 
    L_000a: stloc.0 
    L_000b: br.s L_000d 
    L_000d: ldloc.0 
    L_000e: ret 
} 

जबकि दूसरा दिखेगा:

.method private hidebysig static bool IsNull<valuetype ([mscorlib]System.ValueType) .ctor T>(valuetype [mscorlib]System.Nullable`1<!!T> instance) cil managed 
{ 
    .maxstack 2 
    .locals init (
     [0] bool CS$1$0000) 
    L_0000: nop 
    L_0001: ldarga.s instance 
    L_0003: call instance bool [mscorlib]System.Nullable`1<!!T>::get_HasValue() 
    L_0008: ldc.i4.0 
    L_0009: ceq 
    L_000b: stloc.0 
    L_000c: br.s L_000e 
    L_000e: ldloc.0 
    L_000f: ret 
} 

दूसरे मामले में, कंपाइलर जानता है कि प्रकार एक शून्य है इसलिए यह इसके लिए अनुकूलित कर सकता है। पहले मामले में, इसे किसी भी प्रकार, दोनों संदर्भ और मूल्य प्रकारों को संभालना होगा। तो इसे कुछ अतिरिक्त हुप्स के माध्यम से कूदना है।

इंटेल इंटेल की तुलना में तेज़ क्यों है ?, मुझे लगता है कि वहां कुछ जेआईटी अनुकूलन शामिल हैं।

3

बॉक्सिंग और अनबॉक्सिंग वहां बिना आपको पता चल रहा है, और मुक्केबाजी संचालन कुख्यात रूप से धीमे हैं। ऐसा इसलिए है क्योंकि आप पृष्ठभूमि में, शून्य प्रकार के संदर्भ प्रकारों को मूल्य प्रकारों में परिवर्तित कर रहे हैं।

+1

मुझे इसके बारे में पता है। लेकिन फिर, एक int से null की तुलना करने के प्रदर्शन को एक शून्य से की तुलना करने के बराबर नहीं होना चाहिए? –

+0

यह, या कम से कम, यह होना चाहिए। लेकिन आपके बेंचमार्क में मुक्केबाजी संचालन शामिल है। एक लूप के साथ एक और कोशिश करें जो प्रकार int के एक चर की तुलना करता है? * बिना किसी विधि के * 1,000,000 बार, फिर उस शून्य की तुलना में उसी int के साथ तुलना करें। मुझे नहीं पता कि वह मुक्केबाजी करेगा (क्योंकि 'int' प्रकार को पहले' ऑब्जेक्ट 'पर डालना पड़ सकता है) वैसे भी। – Ryan

+1

@ डिएगो: एक इंट को शून्य से तुलना करते समय, हम सीधे रूपांतरण ऑपरेटरों के साथ सौदा करते हैं। तुलना 'समान (int ?, int?)' विधि को कॉल करती है, जहां 'int' और' null' मानों को पूर्ण रूप से परिवर्तित किया जाता है; इस कॉल में कोई मुक्केबाजी नहीं है। (http://blogs.msdn.com/b/kirillosenkov/archive/2008/09/08/why-a-comparison-of-a-value-type-with-null-is-a-warning.aspx देखें) –

14

यहां इसकी जांच करने के लिए आपको क्या करना चाहिए।

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

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

(भविष्य में, आप शायद इस पर विचार करना चाहिए जब प्रदर्शन परीक्षण लेखन। याद रखें कि तो घबराना जब तक पूरी तरह से दूर सब कुछ उस परिणाम का उत्पादन अनुकूलन करने के लिए नि: शुल्क है अगर आप परिणाम का उपयोग नहीं करते हैं, क्योंकि इसका कोई दुष्प्रभाव नहीं है।)

एक बार जब आप असेंबली कोड देख सकते हैं तो आप देखेंगे कि क्या हो रहा है।

मैं इस अपने आप को व्यक्तिगत रूप से जांच नहीं की है, लेकिन बाधाओं अच्छा है कि क्या हो रहा है यह है:

  • पूर्णांक codepath में

    , घबराना को साकार करने के लिए एक बॉक्सिंग पूर्णांक अशक्त नहीं है कि और मोड़ है स्ट्रिंग कोडपैथ में "वापसी झूठी" विधि में विधि

  • स्ट्रिंग कोडपैथ में, जिटर यह महसूस कर रहा है कि शून्य के लिए एक स्ट्रिंग का परीक्षण करना परीक्षण के बराबर है कि स्ट्रिंग में प्रबंधित पॉइंटर शून्य है, इसलिए यह एक एकल निर्देश उत्पन्न कर रहा है जो परीक्षण करता है या नहीं एक रजिस्टर शून्य है।

  • int में? कोडेपैथ, शायद जिटर यह महसूस कर रहा है कि एक int परीक्षण? बहुतायत के लिए मुक्केबाजी int द्वारा पूरा किया जा सकता है? - चूंकि एक बॉक्सिंग नल int एक शून्य संदर्भ है, जो शून्य के विरुद्ध एक प्रबंधित सूचक का परीक्षण करने की पिछली समस्या को कम कर देता है। लेकिन आप मुक्केबाजी की कीमत लेते हैं।

यदि ऐसा है तो जिटर यहां अधिक परिष्कृत हो सकता है और महसूस कर सकता है कि एक int परीक्षण? int के लिए HasValue बूल के उलटा लौटने से शून्य के लिए पूरा किया जा सकता है?

लेकिन जैसा कि मैंने कहा, यह सिर्फ एक अनुमान है। कोड को स्वयं उत्पन्न करें और देखें कि यदि आप रुचि रखते हैं तो यह क्या कर रहा है।

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