LINQ

2012-06-14 12 views
23

में कुल बनाम प्रदर्शन प्रदर्शन के योग को खोजने के तीन अलग-अलग कार्यान्वयन IENumerable < int> स्रोत स्रोत के 10,000 पूर्णांक के समय के साथ नीचे दिए गए समय के साथ नीचे दिए गए हैं।LINQ

source.Aggregate(0, (result, element) => result + element); 

3 एमएस

source.Sum(c => c); 

लेता है 12 एमएस

source.Sum(); 

1 एमएस

मैं सोच रहा हूँ लेता है क्यों दूसरा कार्यान्वयन में चार गुना अधिक पहले एक से महंगा है लेता है । यह तीसरे कार्यान्वयन के समान नहीं होना चाहिए।

+0

आपकी परीक्षा स्थितियां क्या हैं? –

+5

आपको इन बार कैसे मिला? आपने परिणामों को कितनी बार कोशिश की? –

+0

मैंने डॉटट्रेस का उपयोग करके इसे प्रोफाइल किया। मैंने इसे एक बार भाग लिया, लेकिन तीन रन स्वतंत्र हैं। – Gopal

उत्तर

68

नोट: मेरा कंप्यूटर चल रहा है .Net 4.5 आरसी, इसलिए यह संभव है कि मेरे परिणाम इस से प्रभावित हों।

एक विधि को निष्पादित करने में लगने वाले समय को मापना आमतौर पर बहुत उपयोगी नहीं होता है। इसे आसानी से जेआईटी संकलन जैसी चीजों से प्रभावित किया जा सकता है, जो असली कोड में वास्तविक बाधा नहीं हैं। इस वजह से, मैंने प्रत्येक विधि को निष्पादित करने के लिए 100 × (डिबगर संलग्न किए बिना रिलीज मोड में) मापा। मेरे परिणाम हैं:

  • Aggregate(): 9 एमएस
  • Sum(lambda): 12 एमएस
  • Sum(): 6 एमएस

तथ्य यह है कि Sum() सबसे तेज है आश्चर्य की बात नहीं है: यह एक सरल पाश शामिल बिना किसी प्रतिनिधि आमंत्रण के, जो वास्तव में तेज़ है। Sum(lambda) और Aggregate() के बीच का अंतर लगभग उतना ही प्रमुख नहीं है जितना आपने मापा था, लेकिन यह अभी भी वहां है। इसके लिए क्या कारण हो सकता है? के दो तरीकों के लिए decompiled कोड पर नजर डालते हैं:

public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func) 
{ 
    if (source == null) 
     throw Error.ArgumentNull("source"); 
    if (func == null) 
     throw Error.ArgumentNull("func"); 

    TAccumulate local = seed; 
    foreach (TSource local2 in source) 
     local = func(local, local2); 
    return local; 
} 

public static int Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector) 
{ 
    return source.Select<TSource, int>(selector).Sum(); 
} 

आप देख सकते हैं, Aggregate() एक पाश का उपयोग करता है लेकिन Sum(lambda)Select(), जो बारी में एक इटरेटर का उपयोग करता है का उपयोग करता है। और एक इटरेटर का उपयोग करना मतलब है कि कुछ ओवरहेड है: इटेटरेटर ऑब्जेक्ट बनाना और (शायद अधिक महत्वपूर्ण) प्रत्येक आइटम के लिए एक और विधि आमंत्रण।

आइए सत्यापित करें कि Select() का उपयोग कर वास्तव में दो बार हमारे अपने Sum(lambda) लेखन, एक बार Select(), जो ढांचे से Sum(lambda) रूप में एक ही व्यवहार करना चाहिए, और का उपयोग करके कारण है चलो एक बार Select() का उपयोग किए बिना:

public static int SlowSum<T>(this IEnumerable<T> source, Func<T, int> selector) 
{ 
    return source.Select(selector).Sum(); 
} 

public static int FastSum<T>(this IEnumerable<T> source, Func<T, int> selector) 
{ 
    if (source == null) 
     throw new ArgumentNullException("source"); 
    if (selector == null) 
     throw new ArgumentNullException("selector"); 

    int num = 0; 
    foreach (T item in source) 
     num += selector(item); 
    return num; 
} 

मेरे माप इस बात की पुष्टि है कि मैं क्या सोचा:

  • SlowSum(lambda): 12 एमएस
  • FastSum(lambda): 9 एमएस
+0

बहुत अंतर्दृष्टि। विस्तृत प्रतिक्रिया के लिए धन्यवाद। – Gopal

+0

यह एसओ पर देखे गए सबसे अच्छे उत्तरों में से एक है, पूरी तरह से समझाया गया और अच्छी तरह से तैयार किया गया। (स्पष्ट रूप से +1) – RichK

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