LINQ के कार्यों का क्रम मायने रखता है?


114

मूल रूप से, जैसा कि प्रश्न कहता है ... प्रदर्शन के संदर्भ में LINQ फ़ंक्शन का क्रम मायने रखता है ? जाहिर है परिणाम अभी भी समान होना होगा ...

उदाहरण:

myCollection.OrderBy(item => item.CreatedDate).Where(item => item.Code > 3);
myCollection.Where(item => item.Code > 3).OrderBy(item => item.CreatedDate);

दोनों मुझे एक ही परिणाम लौटाते हैं, लेकिन एक अलग LINQ क्रम में हैं। मुझे लगता है कि कुछ वस्तुओं को अलग करने के परिणाम अलग-अलग होंगे, और मुझे उन लोगों की चिंता नहीं है। मेरी मुख्य चिंता यह जानने में है कि क्या, समान परिणाम प्राप्त करने में, आदेश प्रदर्शन को प्रभावित कर सकता है। और, सिर्फ 2 LINQ कॉल्स पर मैंने नहीं किया (ऑर्डरबाय, कहां), लेकिन किसी भी LINQ कॉल्स पर।


9
बहुत बढ़िया सवाल।
रॉबर्ट एस।

यह और भी अधिक स्पष्ट है कि प्रदाता का अनुकूलन अधिक पेडिक केस के साथ मायने रखता है जैसे var query = myCollection.OrderBy(item => item.Code).Where(item => item.Code == 3);
मार्क हर्ड

1
आप एक दिलचस्प वोट :), दिलचस्प सवालों का गुणगान करते हैं। मैं इस पर विचार करूंगा, जब मैं ईईएफ में अपने Linq को संस्थाओं को लिखूंगा।
जिब्बो

1
@GibboK: अपने LINQ प्रश्नों को "ऑप्टिमाइज़" करने का प्रयास करते समय सावधान रहें (नीचे उत्तर देखें)। कभी-कभी आप वास्तव में किसी भी चीज का अनुकूलन नहीं करते हैं। अनुकूलन का प्रयास करते समय एक प्रोफाइलर उपकरण का उपयोग करना सबसे अच्छा है।
मायरमैन

जवाबों:


147

यह उपयोग में LINQ प्रदाता पर निर्भर करेगा। LINQ ऑब्जेक्ट्स के लिए, जो निश्चित रूप से एक बड़ा अंतर ला सकता है । हम वास्तव में मिल गया है मान लें:

var query = myCollection.OrderBy(item => item.CreatedDate)
                        .Where(item => item.Code > 3);

var result = query.Last();

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

इसकी तुलना उल्टे ऑपरेशन से करें, पहले फ़िल्टरिंग:

var query = myCollection.Where(item => item.Code > 3)
                        .OrderBy(item => item.CreatedDate);

var result = query.Last();

इस बार हम केवल फ़िल्टर किए गए परिणामों को ऑर्डर कर रहे हैं, जो "फ़िल्टर से मेल खाते एक एकल आइटम" के नमूने के मामले में बहुत अधिक कुशल होंगे - समय और स्थान दोनों में।

इससे यह भी फर्क पड़ सकता है कि क्वेरी सही तरीके से निष्पादित होती है या नहीं। विचार करें:

var query = myCollection.Where(item => item.Code != 0)
                        .OrderBy(item => 10 / item.Code);

var result = query.Last();

यह ठीक है - हम जानते हैं कि हम कभी भी 0. से विभाजित नहीं करेंगे। लेकिन यदि हम फ़िल्टरिंग से पहले ऑर्डर करते हैं , तो क्वेरी एक अपवाद फेंक देगी।


2
@Jon Skeet, क्या प्रत्येक LINQ प्रदाता और कार्यों के लिए Big-O के बारे में प्रलेखन है? या यह केवल "प्रत्येक अभिव्यक्ति स्थिति के लिए अद्वितीय है" का मामला है।
माइकेल

1
@michael: यह बहुत स्पष्ट रूप से प्रलेखित नहीं है, लेकिन अगर आप मेरे "एडुलिनक" ब्लॉग श्रृंखला को पढ़ते हैं तो मुझे लगता है कि मैं इसके बारे में उचित विस्तार से बात करता हूं।
जॉन स्कीट

3
@michael: आप इसे यहाँ देख सकते हैं msmvps.com/blogs/jon_skeet/archive/tags/Edulinq/default.aspx
VoodooChild

3
@gdoron: यह वास्तव में स्पष्ट नहीं है कि आपका क्या मतलब है, ईमानदार होना। लगता है कि आप एक नया प्रश्न लिखना चाहते हैं। यह ध्यान रखें कि क्वेरी करने योग्य आपकी क्वेरी की व्याख्या करने की कोशिश नहीं कर रहा है - इसका काम पूरी तरह से आपकी क्वेरी को संरक्षित करना है ताकि कुछ और इसकी व्याख्या कर सके। यह भी ध्यान दें कि LINQ से लेकर ऑब्जेक्ट तक अभिव्यक्ति पेड़ का उपयोग नहीं करते हैं।
जॉन स्कीट

1
@gdoron: मुद्दा यह है कि प्रदाता का काम है, न कि क्वेरी करने योग्य काम। और एंटिटी फ्रेमवर्क का उपयोग करते समय यह बात नहीं होनी चाहिए। यह LINQ के लिए ऑब्जेक्ट्स के लिए मायने रखता है। लेकिन हां, हर तरह से एक और सवाल पूछें।
जॉन स्कीट

17

हाँ।

लेकिन वास्तव में क्या है कि प्रदर्शन का अंतर है कैसे अंतर्निहित अभिव्यक्ति पेड़ LINQ प्रदाता द्वारा मूल्यांकन किया जाता है पर निर्भर करता है।

उदाहरण के लिए, आपकी क्वेरी LINQ-to-XML के लिए दूसरी बार (WHERE क्लॉज़ के साथ) तेज़ी से निष्पादित हो सकती है, लेकिन LINQ-to-SQL के लिए पहली बार तेज़ हो सकती है।

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


5

आपके विशेष उदाहरण में यह प्रदर्शन के लिए एक अंतर बना सकता है

पहली क्वेरी: आपकी OrderByकॉल पूरे स्रोत अनुक्रम के माध्यम से पुनरावृत्त करने की आवश्यकता है , जिसमें उन आइटम शामिल हैं जहां Code3 या उससे कम है। Whereखंड तब भी पुनरावृति करने की जरूरत है पूरे अनुक्रम का आदेश दिया।

दूसरी क्वेरी: Whereकॉल केवल उन वस्तुओं के लिए अनुक्रम को सीमित करता है जहां Code3 से अधिक है। OrderByकॉल तब केवल Whereकॉल द्वारा लौटाए गए अनुक्रम को कम करने की आवश्यकता है ।


3

लिनक-टू-ऑब्जेक्ट्स में:

छँटाई बल्कि धीमी है और O(n)स्मृति का उपयोग करता है। Whereदूसरी ओर अपेक्षाकृत तेज़ है और निरंतर मेमोरी का उपयोग करता है। इसलिए कर रहे हैंWhere पहले करना तेजी से होगा, और बड़े संग्रह के लिए काफी तेज होगा।

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


1

जाहिर है परिणाम अभी भी समान होना होगा ...

ध्यान दें कि यह वास्तव में सच नहीं है - विशेष रूप से, निम्नलिखित दो लाइनें अलग-अलग परिणाम देगी (अधिकांश प्रदाताओं / डेटासेट के लिए):

myCollection.OrderBy(o => o).Distinct();
myCollection.Distinct().OrderBy(o => o);

1
नहीं, मेरा क्या मतलब था कि परिणाम भी अनुकूलन पर विचार करने के लिए समान होना चाहिए। कुछ का "अनुकूलन" करने और एक अलग परिणाम प्राप्त करने का कोई मतलब नहीं है।
माइकल

1

यह ध्यान देने योग्य है कि LINQ क्वेरी का अनुकूलन कैसे करें, इस पर विचार करते समय आपको सावधान रहना चाहिए । उदाहरण के लिए, यदि आप निम्नलिखित करने के लिए LINQ के घोषित संस्करण का उपयोग करते हैं:

public class Record
{
    public string Name { get; set; }
    public double Score1 { get; set; }
    public double Score2 { get; set; }
}


var query = from record in Records
            order by ((record.Score1 + record.Score2) / 2) descending
            select new
                   {
                       Name = record.Name,
                       Average = ((record.Score1 + record.Score2) / 2)
                   };

अगर, किसी भी कारण से, आपने पहले एक चर में औसत स्टोर करके क्वेरी को "अनुकूलित" करने का फैसला किया, तो आपको वांछित परिणाम नहीं मिलेगा:

// The following two queries actually takes up more space and are slower
var query = from record in Records
            let average = ((record.Score1 + record.Score2) / 2)
            order by average descending
            select new
                   {
                       Name = record.Name,
                       Average = average
                   };

var query = from record in Records
            let average = ((record.Score1 + record.Score2) / 2)
            select new
                   {
                       Name = record.Name,
                       Average = average
                   }
            order by average descending;

मुझे पता है कि बहुत से लोग वस्तुओं के लिए घोषणात्मक LINQ का उपयोग नहीं करते हैं, लेकिन यह विचार के लिए कुछ अच्छा भोजन है।


0

यह प्रासंगिकता पर निर्भर करता है। मान लीजिए कि यदि आपके पास कोड = 3 के साथ बहुत कम आइटम हैं, तो अगला आदेश संग्रह के छोटे सेट पर काम करेगा ताकि ऑर्डर प्राप्त किया जा सके।

जबकि यदि आपके पास एक ही CreatedDate के साथ कई आइटम हैं, तो अगला ऑर्डर तिथि तक ऑर्डर प्राप्त करने के लिए संग्रह के बड़े सेट पर काम करेगा।

तो, दोनों ही मामलों में प्रदर्शन में अंतर होगा

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.