क्या सतह पर दिखने की तुलना में लिनिक अधिक कुशल है?


13

अगर मैं कुछ ऐसा लिखूं:

var things = mythings
    .Where(x => x.IsSomeValue)
    .Where(y => y.IsSomeOtherValue)

क्या यह इस प्रकार है:

var results1 = new List<Thing>();
foreach(var t in mythings)
    if(t.IsSomeValue)
        results1.Add(t);

var results2 = new List<Thing>();
foreach(var t in results1)
    if(t.IsSomeOtherValue)
        results2.Add(t);

या इस तरह से काम करने वाले कवर्स के नीचे कुछ जादू है:

var results = new List<Thing>();
foreach(var t in mythings)
    if(t.IsSomeValue && t.IsSomeOtherValue)
        results.Add(t);

या यह पूरी तरह से पूरी तरह से अलग कुछ है?


4
आप इसे ILSpy में देख सकते हैं।
ChaosPandion

1
यह पहले लेकिन दूसरे ChaosPandion के जवाब की तुलना में दूसरे उदाहरण की तरह है कि ILSpy आपका दोस्त है।
माइकल

जवाबों:


27

LINQ क्वेरी आलसी हैं । इसका मतलब है कि कोड:

var things = mythings
    .Where(x => x.IsSomeValue)
    .Where(y => y.IsSomeOtherValue);

बहुत कम करता है। मूल गणना योग्य ( mythings) केवल गणना की जाती है जब परिणामी गणना योग्य ( things) का सेवन किया जाता है, उदाहरण के लिए एक foreachलूप .ToList(), या .ToArray()

यदि आप कॉल करते हैं things.ToList(), तो यह आपके बाद वाले कोड के लगभग बराबर है, शायद एन्यूमरेटर्स से कुछ (आमतौर पर नगण्य) ओवरहेड।

इसी तरह, यदि आप एक फॉरेस्ट लूप का उपयोग करते हैं:

foreach (var t in things)
    DoSomething(t);

यह प्रदर्शन के समान है:

foreach (var t in mythings)
    if (t.IsSomeValue && t.IsSomeOtherValue)
        DoSomething(t);

प्रगणकों के लिए आलस्य दृष्टिकोण के कुछ प्रदर्शन लाभ (जैसा कि सभी परिणामों की गणना करने और उन्हें एक सूची में संग्रहीत करने के लिए विरोध किया जाता है) यह है कि यह बहुत कम मेमोरी का उपयोग करता है (क्योंकि एक समय में केवल एक परिणाम संग्रहीत होता है) और इसका कोई महत्वपूर्ण प्रभाव नहीं है -सुंदर लागत।

यदि गणना करने योग्य केवल आंशिक रूप से गणना की जाती है, तो यह विशेष रूप से महत्वपूर्ण है। इस कोड पर विचार करें:

things.First();

LINQ को जिस तरह से लागू किया गया है, mythingsवह केवल पहले तत्व तक सीमित होगा जो आपकी शर्तों से मेल खाता है। यदि वह तत्व सूची में जल्दी है, तो यह ओ (एन) के बजाय एक विशाल प्रदर्शन को बढ़ावा देने वाला हो सकता है (जैसे ओ (1))।


1
LINQ और उपयोग करने वाले समतुल्य कोड के बीच एक प्रदर्शन अंतर foreachयह है कि LINQ डेलीगेट इनवोकेशन का उपयोग करता है, जिसमें कुछ ओवरहेड होते हैं। यह महत्वपूर्ण हो सकता है जब स्थितियां बहुत जल्दी निष्पादित होती हैं (जो वे अक्सर करते हैं)।
svick

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

Linq के आलसी मूल्यांकन का एक बुरा सीमा यह है कि इस तरह के तरीकों के अलावा ToListया किसी गणना के "स्नैपशॉट" लेने का कोई तरीका नहीं है ToArray। यदि ऐसा कुछ ठीक से बनाया गया था IEnumerable, तो किसी भी पहलू को "स्नैपशॉट" करने के लिए एक सूची पूछना संभव होगा जो भविष्य में सब कुछ उत्पन्न किए बिना बदल सकता है।
सुपरैट

7

निम्नलिखित कोड:

var things = mythings
    .Where(x => x.IsSomeValue)
    .Where(y => y.IsSomeOtherValue);

आलस के बराबर है, आलसी मूल्यांकन के कारण, कुछ भी नहीं होगा।

var things = mythings
    .Where(x => x.IsSomeValue)
    .Where(y => y.IsSomeOtherValue)
    .ToList();

अलग है, क्योंकि मूल्यांकन लॉन्च किया जाएगा।

प्रत्येक वस्तु mythingsको पहले दिया जाएगा Where। यदि यह पास हो जाता है, तो इसे दूसरे को दिया जाएगा Where। यदि यह गुजरता है, तो यह आउटपुट का हिस्सा होगा।

तो यह इस तरह दिखता है:

var results = new List<Thing>();
foreach(var t in mythings)
{
    if(t.IsSomeValue)
    {
        if(t.IsSomeOtherValue)
        {
            results.Add(t);
        }
    }
}

7

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

के केवल आपके फोन की कल्पना करते हैं ToListपर things

Enumerable.Whereरिटर्न का कार्यान्वयन ए Enumerable.WhereListIterator। जब आप Whereउस WhereListIterator(उर्फ चेनिंग- Whereकॉल) पर कॉल करते हैं , तो आप कॉल नहीं करते हैं Enumerable.Where, लेकिन Enumerable.WhereListIterator.Where, जो वास्तव में विधेय (उपयोग Enumerable.CombinePredicates) को जोड़ती है ।

तो यह अधिक पसंद है if(t.IsSomeValue && t.IsSomeOtherValue)


"रिटर्न ए एन्युमरेबल.हेयरलीस्टिएटर" ने इसे मेरे लिए क्लिक किया। शायद एक बहुत ही सरल अवधारणा है, लेकिन यह कि मैं ILSpy के साथ अनदेखी कर रहा था। धन्यवाद
कंडीशनिंगर

यदि आप गहराई से विश्लेषण में अधिक रुचि रखते हैं तो जॉन स्कीट का इस अनुकूलन को फिर से लागू करना देखें ।
सर्व करें

1

नहीं, यह समान नहीं है। आपके उदाहरण thingsमें एक है IEnumerable, जो इस बिंदु पर अभी भी केवल एक पुनरावृत्ति है, एक वास्तविक सरणी या सूची नहीं है। इसके अलावा चूंकि thingsइसका उपयोग नहीं किया जाता है, लूप का मूल्यांकन कभी भी नहीं किया जाता है। प्रकार, Linq निर्देशों द्वारा IEnumerableतत्वों yield-ed के माध्यम से पुनरावृति करने और उन्हें और अधिक निर्देशों के साथ संसाधित करने की अनुमति देता है, जिसका अर्थ है कि आपके पास वास्तव में केवल एक लूप है।

लेकिन जैसे ही आप एक निर्देश जोड़ते हैं .ToArray()या .ToList(), आप वास्तविक डेटा संरचना के निर्माण का आदेश दे रहे हैं, इस प्रकार आपकी श्रृंखला में सीमाएँ डाल रहा है।

इससे संबंधित SO प्रश्न देखें: /programming/2789389/how-do-i-implement-inumable

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