2010-02-14 17 views
30

किसी को सूची में कहां और ढूँढें के बीच कोई गति अंतर पता है। मुझे पता है कि IENumerable का हिस्सा कहां है और FindAll सूची का हिस्सा है, मैं बस उत्सुक हूं कि तेज़ क्या है।सी # FindAll वीएस जहां स्पीड

+0

[FindAll बनाम एक्सटेंशन-विधि] के संभावित डुप्लिकेट (http://stackoverflow.com/questions/1531702/findall-vs-where-extension-method) –

उत्तर

45

सूची < टी > कक्षा की FindAll विधि वास्तव में एक नई सूची वस्तु बनाती है, और इसके परिणाम जोड़ती है। IEnumerable < टी > के लिए कहाँ विस्तार विधि बस किसी मौजूदा सूची से अधिक पुनरावृति जाएगा और बनाने या कुछ भी जोड़ने के बिना मिलान परिणामों के एक गणन उपज (प्रगणक खुद के अलावा अन्य।)

एक छोटा सेट को देखते हुए, दो की संभावना होगा तुलनात्मक रूप से प्रदर्शन करते हैं। हालांकि, एक बड़ा सेट दिया गया है, जहां FindAll को बेहतर प्रदर्शन करना चाहिए, क्योंकि परिणामों को शामिल करने के लिए बनाई गई नई सूची को अतिरिक्त परिणाम रखने के लिए गतिशील रूप से बढ़ना होगा। FindAll का मेमोरी उपयोग भी तेजी से बढ़ने लगेगा क्योंकि मेल खाने वाले परिणामों की संख्या बढ़ जाती है, जहां निरंतर न्यूनतम मेमोरी उपयोग (अपने आप में ... और जहां भी आप परिणामों के साथ करते हैं उसे छोड़कर)।

+19

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

+5

@ जोनहाना: शुरुआत में मैंने सोचा कि मैं सहमत हूं, मुझे वास्तव में यकीन नहीं है। क्या आपके पास कोई संदर्भ है जो इंगित करता है। ToList() एक धीमी गति से है। FindAll()? कॉलिंग। एक क्वेरी पर टॉलिस्ट() ** ** हो सकता है कि संख्यात्मक पुनरावृत्ति हो, और इसके लिए इसकी स्मृति दक्षता को बनाए रखना चाहिए। इतना ही नहीं, जहां इटरेटर इस तरह के मामलों में FindAll से बेहतर प्रदर्शन कर सकता है, वहां कुछ आंतरिक कार्यान्वयन भी सही आकार (स्मृति आवंटन) की सूची बनाने में सक्षम हो सकते हैं। मैं विशेष रूप से असहमत नहीं हूं, हालांकि एक ठोस संदर्भ होना अच्छा होगा जो FindAlls लाभ को स्पष्ट करता है। – jrista

+1

यह उत्तर गलत है। @Wiory देखें जो वास्तव में मापने के लिए परेशान है। –

3

.FindAll() तेजी से हो, यह सूची के आकार को जानने और पहले से ही for लूप के साथ आंतरिक सरणी के माध्यम से लूपिंग का लाभ उठाता है। .Where() को इस मामले में एक गणक (एक सीलबंद ढांचे वर्ग WhereIterator कहा जाता है) को आग लगाना है और एक ही नौकरी को कम विशिष्ट तरीके से करना है।

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

+1

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

+0

कॉलिंग के मामले में जहां कोई ऐरे या सूची पर, दो अतिरिक्त आंतरिक इटरेटर कक्षाएं हैं, WhereArrayIterator और WhereListIterator, जो उन दो मामलों के लिए अनुकूलित हैं। आम तौर पर बोलते हुए, कॉलिंग FindAll को कॉल करने से कहीं अधिक कुशल होना चाहिए। – jrista

+2

@jrista - I ** पूरी तरह ** ** में मामला ढेर चूक गया।कहां() 'ओवरलोड लोड उन, धन्यवाद! उस कोड को देखकर मैं सहमत हूं, जहां सबसे खराब प्रदर्शन किया जाना चाहिए, लेकिन लगभग हमेशा बेहतर होना चाहिए। इसके अलावा, अगर दूसरों को शिक्षित करने के लिए अतिरिक्त समय लेने वाले लोगों के लिए एसओ बेकार होगा, उदा। आप और ये टिप्पणियां, मुझे कुछ सिखाए जाने के लिए +1। –

4

WhereFindAll से बहुत तेज है। कोई फर्क नहीं पड़ता कि सूची कितनी बड़ी है, Where बिल्कुल वही समय लेता है।

बेशक Where बस एक क्वेरी बनाता है। यह वास्तव में कुछ भी नहीं करता है, FindAll के विपरीत जो एक सूची बनाता है।

+5

यह तकनीकी रूप से सच हो सकता है, लेकिन मुझे लगता है कि यह स्पष्ट है कि ओपी वास्तव में परिणाम की गणना करने के संदर्भ में प्रदर्शन के बारे में पूछ रहा है, न कि नग्न विधि कॉल स्वयं। –

-3

jrista से उत्तर इंद्रियां बनाता है। हालांकि, नई सूची एक ही वस्तु को जोड़ती है, इस प्रकार केवल मौजूदा वस्तुओं के संदर्भ में बढ़ रही है, जो धीमी नहीं होनी चाहिए। जब तक 3.5/लिंक एक्सटेंशन संभव हो, वैसे भी जहां कहीं भी बेहतर रहता है। 2.0

6

के साथ सीमित होने पर FindAll अधिक समझ में आता है FindAll स्पष्ट रूप से धीमा है, क्योंकि इसे एक नई सूची बनाने की आवश्यकता है।

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

मैंने छोटे परीक्षण लिखे, बस इसे कंसोल ऐप प्रोजेक्ट में पेस्ट करें। यह समय/युक्तियों को मापता है: फ़ंक्शन निष्पादन, परिणाम संग्रह पर संचालन ('असली' उपयोग के perf को प्राप्त करने के लिए, और यह सुनिश्चित करने के लिए कि संकलक अप्रयुक्त डेटा इत्यादि को अनुकूलित नहीं करेगा। - मैं सी # के लिए नया हूं और नहीं पता है कि यह अभी तक कैसे काम करता है, क्षमा करें)।

नोटिस: जहां Iumerable() तत्वों की नई सूची बनाता है, प्रत्येक मापा फ़ंक्शन। मैं कुछ गलत कर रहा हूं, लेकिन स्पष्ट रूप से पुनरावृत्त करने के लिए आईन्यूमेरेबल को पुनरावृत्ति सूची से ज्यादा समय लगता है।

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Diagnostics; 

namespace Tests 
{ 

    public class Dummy 
    { 
     public int Val; 
     public Dummy(int val) 
     { 
      Val = val; 
     } 
    } 
    public class WhereOrFindAll 
    { 
     const int ElCount = 20000000; 
     const int FilterVal =1000; 
     const int MaxVal = 2000; 
     const bool CheckSum = true; // Checks sum of elements in list of resutls 
     static List<Dummy> list = new List<Dummy>(); 
     public delegate void FuncToTest(); 

     public static long TestTicks(FuncToTest function, string msg) 
     { 
      Stopwatch watch = new Stopwatch(); 
      watch.Start(); 
      function(); 
      watch.Stop(); 
      Console.Write("\r\n"+msg + "\t ticks: " + (watch.ElapsedTicks)); 
      return watch.ElapsedTicks; 
     } 
     static void Check(List<Dummy> list) 
     { 
      if (!CheckSum) return; 
      Stopwatch watch = new Stopwatch(); 
      watch.Start(); 

      long res=0; 
      int count = list.Count; 
      for (int i = 0; i < count; i++)  res += list[i].Val; 
      for (int i = 0; i < count; i++)  res -= (long)(list[i].Val * 0.3); 

      watch.Stop(); 
      Console.Write("\r\n\nCheck sum: " + res.ToString() + "\t iteration ticks: " + watch.ElapsedTicks); 
     } 
     static void Check(IEnumerable<Dummy> ieNumerable) 
     { 
      if (!CheckSum) return; 
      Stopwatch watch = new Stopwatch(); 
      watch.Start(); 

      IEnumerator<Dummy> ieNumerator = ieNumerable.GetEnumerator(); 
      long res = 0; 
      while (ieNumerator.MoveNext()) res += ieNumerator.Current.Val; 
      ieNumerator=ieNumerable.GetEnumerator(); 
      while (ieNumerator.MoveNext()) res -= (long)(ieNumerator.Current.Val * 0.3); 

      watch.Stop(); 
      Console.Write("\r\n\nCheck sum: " + res.ToString() + "\t iteration ticks :" + watch.ElapsedTicks); 
     } 
     static void Generate() 
     { 
      if (list.Count > 0) 
       return; 
      var rand = new Random(); 
      for (int i = 0; i < ElCount; i++) 
       list.Add(new Dummy(rand.Next(MaxVal))); 

     } 
     static void For() 
     { 
      List<Dummy> resList = new List<Dummy>(); 
      int count = list.Count; 
      for (int i = 0; i < count; i++) 
      { 
       if (list[i].Val < FilterVal) 
        resList.Add(list[i]); 
      }  
      Check(resList); 
     } 
     static void Foreach() 
     { 
      List<Dummy> resList = new List<Dummy>(); 
      int count = list.Count; 
      foreach (Dummy dummy in list) 
      { 
       if (dummy.Val < FilterVal) 
        resList.Add(dummy); 
      } 
      Check(resList); 
     } 
     static void WhereToList() 
     { 
      List<Dummy> resList = list.Where(x => x.Val < FilterVal).ToList<Dummy>(); 
      Check(resList); 
     } 
     static void WhereIEnumerable() 
     { 
      Stopwatch watch = new Stopwatch(); 
      IEnumerable<Dummy> iEnumerable = list.Where(x => x.Val < FilterVal); 
      Check(iEnumerable); 
     } 
     static void FindAll() 
     { 
      List<Dummy> resList = list.FindAll(x => x.Val < FilterVal); 
      Check(resList); 
     } 
     public static void Run() 
     { 
      Generate(); 
      long[] ticks = { 0, 0, 0, 0, 0 }; 
      for (int i = 0; i < 10; i++) 
      { 
       ticks[0] += TestTicks(For, "For \t\t"); 
       ticks[1] += TestTicks(Foreach, "Foreach \t"); 
       ticks[2] += TestTicks(WhereToList, "Where to list \t"); 
       ticks[3] += TestTicks(WhereIEnumerable, "Where Ienum \t"); 
       ticks[4] += TestTicks(FindAll, "FindAll \t"); 
       Console.Write("\r\n---------------"); 
      } 
      for (int i = 0; i < 5; i++) 
       Console.Write("\r\n"+ticks[i].ToString()); 
     } 

    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      WhereOrFindAll.Run(); 
      Console.Read(); 
     } 
    } 
} 

परिणाम (टिक्स) - checksum (परिणामों पर कुछ कार्यों), मोड सक्षम: रिहाई डिबगिंग (Ctrl + F5) के बिना:

  • 16222276 (के लिए -> सूची)
  • 17151121 (foreach -> सूची)
  • 4741494 (जहां -> सूची)
  • 27122285 (जहां -> ienum)
  • 18 821,571 (findall -> सूची)

checksum अक्षम (सब पर लौटे सूची का उपयोग नहीं):

  • 10885004 (के लिए -> सूची)
  • 11221888 (foreach -> सूची)
  • 18688433 (जहां -> सूची)
  • 1075 (जहां -> ienum)
  • 13720243 (findall -> सूची)

वास्तविक परिणाम प्राप्त करने के लिए आपके परिणाम थोड़ा अलग हो सकते हैं, आपको अधिक पुनरावृत्तियों की आवश्यकता है।

+0

आपके परीक्षण ठीक हैं। वे दिखाते हैं कि LINQ तंत्र सीधे सूची में परिचालन करने से धीमा है। कोई आश्चर्य नहीं। आपका "1075 (कहां -> ienum)" उसमें गलत है जहां परिणामस्वरूप तत्वों को घुमाने के बिना वास्तव में कभी भी ऐसा नहीं किया जाएगा! –

+1

क्षमा करें कार्लो, लेकिन वह कहां-> ienum कार्यान्वयन में भी "चेक()" विधि को बुलाता है। चेक() सभी संग्रहों को पुन: सक्रिय करता है, इसलिए उसके परिणाम पूरी तरह मान्य हैं। नतीजतन, यह मेरा जवाब भी सही बनाता है ... वह उत्तर जिसे आपने "मृत गलत" कहा था। – jrista

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