c# - .NET में, कौन सा लूप तेजी से चलता है, 'के लिए' या 'foreach'?




performance for-loop (20)

सी # / वीबी.नेट / .NET में, कौन सा लूप तेजी से चलता है, या foreach ?

जब से मैंने पढ़ा है कि लूप के for एक foreach लूप की तुलना में तेज़ी से काम करता है, मुझे लगता है कि यह सभी संग्रह, जेनेरिक संग्रह, सभी सरणी आदि के लिए सच था।

मैंने Google को खराब कर दिया और कुछ लेख पाए, लेकिन उनमें से अधिकतर असंगत हैं (लेखों पर टिप्पणियां पढ़ें) और खुले अंत में।

आदर्श क्या होगा प्रत्येक परिदृश्य सूचीबद्ध है और इसके लिए सबसे अच्छा समाधान है।

उदाहरण के लिए (यह एक उदाहरण है कि यह कैसे होना चाहिए):

  1. 1000+ तारों की एक सरणी को पुन: सक्रिय करने के for - foreach मुकाबले बेहतर है
  2. IList (गैर सामान्य) तारों पर पुनरावृत्ति के लिए - foreach लिए बेहतर है

वेब पर कुछ संदर्भ पाए गए हैं:

  1. Emmanuel Schanzer द्वारा मूल ग्रैंड पुराना लेख
  2. कोडप्रोजेक्ट फॉरएच बनाम के लिये
  3. ब्लॉग - foreach करने या foreach करने के लिए, यह सवाल है
  4. एएसपी.नेट मंच - बनाम foreach for नेट 1.1 सी #

[संपादित करें]

इसके पठनीयता पहलू के अलावा, मैं वास्तव में तथ्यों और आंकड़ों में रूचि रखता हूं। ऐसे अनुप्रयोग हैं जहां प्रदर्शन अनुकूलन का अंतिम मील निचोड़ा हुआ है।


"क्या कोई तर्क है जिसका उपयोग मैं उसे समझाने में मदद के लिए कर सकता हूं कि लूप का उपयोग करने के लिए स्वीकार्य है?"

नहीं, अगर आपका मालिक आपको यह बताए जाने के स्तर पर माइक्रोमैनेजिंग कर रहा है कि प्रोग्रामिंग भाषा किस प्रकार उपयोग करने के लिए तैयार है, तो वास्तव में आप कुछ भी नहीं कह सकते हैं। माफ़ कीजिये।


TechEd 2005 पर जेफरी रिक्टर:

"मैं वर्षों से सीखने आया हूं सी # कंपाइलर मूल रूप से मेरे लिए झूठा है।" .. "यह कई चीजों के बारे में है।" .. "जब आप एक फोरच लूप करते हैं ..." .. "... यह आपके द्वारा लिखे गए कोड की एक छोटी सी रेखा है, लेकिन सी # कंपाइलर यह असाधारण करने के लिए बाहर निकलता है। यह एक आखिर में ब्लॉक करें / अंत में अवरुद्ध करें, आखिरकार ब्लॉक में यह आपके चर को एक आईडीस्पोजेबल इंटरफ़ेस में रखता है, और अगर कास्ट प्राप्त होता है तो यह उस पर निपटान विधि कहता है, लूप के अंदर यह वर्तमान संपत्ति और MoveNext विधि को लूप के अंदर बार-बार कॉल करता है, वस्तुओं को कवर के नीचे बनाया जा रहा है। बहुत से लोग foreach का उपयोग करते हैं क्योंकि यह बहुत आसान कोडिंग है, करना बहुत आसान है .. ".." प्रदर्शन के संदर्भ में foreach बहुत अच्छा नहीं है, अगर आप वर्ग का उपयोग करके संग्रह पर पुनरावृत्त करते हैं ब्रैकेट नोटेशन, बस इंडेक्स कर रहा है, यह बहुत तेज़ है, और यह ढेर पर कोई ऑब्जेक्ट नहीं बनाता है ... "

ऑन-डिमांड वेबकास्ट: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US


एक के for गति में अंतर - और एक foreach -loop छोटे हैं जब आप सरणी, सूचियों, आदि जैसे सामान्य संरचनाओं के माध्यम से लूपिंग कर रहे हैं, और संग्रह पर LINQ क्वेरी कर रहे हैं लगभग हमेशा धीमा है, हालांकि यह लिखने के लिए अच्छा है! जैसा कि अन्य पोस्टर ने कहा, अतिरिक्त प्रदर्शन के मिलीसेकंड की बजाय अभिव्यक्ति के लिए जाएं।

अब तक यह नहीं कहा गया है कि जब एक foreach लूप संकलित किया जाता है, तो इसे संकलन के आधार पर संकलित किया जाता है जो इसे चालू कर रहा है। इसका अर्थ यह है कि जब आप सुनिश्चित नहीं हैं कि कौन सा लूप उपयोग करना है, तो आपको foreach लूप का उपयोग करना चाहिए - यह संकलित होने पर आपके लिए सबसे अच्छा पाश उत्पन्न करेगा। यह भी अधिक पठनीय है।

foreach लूप के साथ एक और महत्वपूर्ण लाभ यह है कि यदि आपका संग्रह कार्यान्वयन बदलता है (उदाहरण के लिए एक int array से List<int> ) तो आपके foreach लूप को किसी भी कोड परिवर्तन की आवश्यकता नहीं होगी:

foreach (int i in myCollection)

उपर्युक्त वही है चाहे आपका संग्रह किस प्रकार का हो, जबकि आपके लूप के लिए, यदि आप myCollection array को किसी array से List बदलते हैं तो निम्नलिखित नहीं बनाए जाएंगे:

for (int i = 0; i < myCollection.Length, i++)

ऐसे मामलों में जहां आप वस्तुओं के संग्रह के साथ काम करते हैं, foreach बेहतर है, लेकिन यदि आप एक संख्या में वृद्धि करते हैं, तो लूप के लिए बेहतर है।

ध्यान दें कि अंतिम मामले में, आप कुछ ऐसा कर सकते हैं:

foreach (int i in Enumerable.Range(1,10))...

लेकिन यह निश्चित रूप से बेहतर प्रदर्शन नहीं करता है, वास्तव में इसके लिए तुलना में खराब प्रदर्शन है।


किसी भी समय प्रदर्शन पर तर्क होते हैं, आपको केवल एक छोटा परीक्षण लिखना होगा ताकि आप अपने मामले का समर्थन करने के लिए मात्रात्मक परिणामों का उपयोग कर सकें।

स्टॉपवॉच क्लास का उपयोग करें और सटीकता के लिए कुछ मिलियन बार दोहराएं। (यह लूप के बिना मुश्किल हो सकता है):

using System.Diagnostics;
//...
Stopwatch sw = new Stopwatch()
sw.Start()
for(int i = 0; i < 1000000;i ++)
{
    //do whatever it is you need to time
}
sw.Stop();
//print out sw.ElapsedMilliseconds

फिंगर्स ने इस शो के नतीजों को पार किया कि अंतर नगण्य है, और आप शायद सबसे अधिक बनाए रखने योग्य कोड में जो भी परिणाम कर सकते हैं


चाहे foreach से तेज़ है, वास्तव में बिंदु के अलावा है। मुझे गंभीरता से संदेह है कि दूसरे पर एक चुनने से आपके प्रदर्शन पर महत्वपूर्ण प्रभाव पड़ेगा।

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

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



ज्यादातर मामलों में वास्तव में कोई फर्क नहीं पड़ता है।

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

कोई तर्क दे सकता है कि कोड में जादू संख्याएं दिखने लगती हैं तो लूप को बनाए रखने के लिए थोड़ा और मुश्किल हो सकता है। आपको लूप का उपयोग करने में सक्षम न होने पर नाराज होने का अधिकार होना चाहिए और लूप को प्रतिबंधित करने के बजाय एक उपनिवेश बनाने के लिए एक संग्रह बनाना या लैम्ब्डा का उपयोग करना है।


दोनों लगभग उसी तरह से चलेंगे। दोनों का उपयोग करने के लिए कुछ कोड लिखें, फिर उसे आईएल दिखाएं। यह तुलनीय computations दिखाया जाना चाहिए, जिसका मतलब प्रदर्शन में कोई अंतर नहीं है।


निम्नलिखित निष्कर्षों के साथ, पैट्रिक स्मैचिया ने पिछले महीने इस बारे में ब्लॉग किया था:

  • सूची में लूप के लिए सूची में फोरच लूप की तुलना में 2 गुना अधिक सस्ता है।
  • सरणी पर लूपिंग सूची पर लूपिंग से लगभग 2 गुना सस्ता है।
  • नतीजतन, फोरैच का उपयोग करके सूची पर लूपिंग से 5 गुना सस्ता है (जो मुझे विश्वास है, हम सब क्या करते हैं)।

मुझे foreach लूप मिला जो एक List माध्यम से तेज़ हो रहा था । नीचे मेरे परीक्षण परिणाम देखें। नीचे दिए गए कोड में मैं समय मापने के लिए लूप के for अलग-अलग आकार 100, 10000 और 100000 का array दोहराता हूं।

private static void MeasureTime()
    {
        var array = new int[10000];
        var list = array.ToList();
        Console.WriteLine("Array size: {0}", array.Length);

        Console.WriteLine("Array For loop ......");
        var stopWatch = Stopwatch.StartNew();
        for (int i = 0; i < array.Length; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("Array Foreach loop ......");
        var stopWatch1 = Stopwatch.StartNew();
        foreach (var item in array)
        {
            Thread.Sleep(1);
        }
        stopWatch1.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List For loop ......");
        var stopWatch2 = Stopwatch.StartNew();
        for (int i = 0; i < list.Count; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch2.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List Foreach loop ......");
        var stopWatch3 = Stopwatch.StartNew();
        foreach (var item in list)
        {
            Thread.Sleep(1);
        }
        stopWatch3.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds);
    }

UPDATED

@jgauffin सुझाव के बाद मैंने @johnskeet कोड का उपयोग किया और पाया कि array साथ लूप के for निम्नलिखित से तेज है,

  • सरणी के साथ Foreach पाश।
  • सूची के साथ लूप के लिए।
  • सूची के साथ Foreach पाश।

नीचे मेरे परीक्षण परिणाम और कोड देखें,

private static void MeasureNewTime()
    {
        var data = new double[Size];
        var rng = new Random();
        for (int i = 0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }
        Console.WriteLine("Lenght of array: {0}", data.Length);
        Console.WriteLine("No. of iteration: {0}", Iterations);
        Console.WriteLine(" ");
        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with Array: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (var i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with Array: {0}", sw.ElapsedMilliseconds);
        Console.WriteLine(" ");

        var dataList = data.ToList();
        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < dataList.Count; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in dataList)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds);
    }

मेरा अनुमान है कि यह 99% मामलों में शायद महत्वपूर्ण नहीं होगा, तो आप सबसे उपयुक्त (जैसे समझने / बनाए रखने में सबसे आसान) के बजाय तेज़ी से क्यों चुनेंगे?


यह मज़ाकीय है। फॉर-लूप, प्रदर्शन-वार या अन्य पर प्रतिबंध लगाने के लिए कोई अनिवार्य कारण नहीं है।

प्रदर्शन बेंचमार्क और अन्य तर्कों के लिए जॉन स्कीट ब्लॉग देखें।


यह वह लूप के अंदर होता है जो परफॉर्मेंस को प्रभावित करता है, वास्तविक लूपिंग निर्माण नहीं (मान लेना कि आपका मामला गैर-तुच्छ है)।


यह हमेशा करीब होगा। एक सरणी के लिए, कभी-कभी थोड़ा तेज होता है, लेकिन foreach अधिक अभिव्यक्तिपूर्ण है, और LINQ, आदि प्रदान करता है। आम तौर पर, foreach साथ छड़ी।

इसके अतिरिक्त, कुछ परिदृश्यों में foreach अनुकूलित किया जा सकता है। उदाहरण के लिए, एक लिंक्ड सूची इंडेक्सर द्वारा भयानक हो सकती है, लेकिन यह भविष्यवाणी से जल्दी हो सकती है। असल में, मानक LinkedList<T> इस कारण से एक सूचकांक भी प्रदान नहीं करता है।


लूप के for foreach loops को पसंद करने के बहुत अच्छे कारण हैं । यदि आप foreach लूप का उपयोग कर सकते हैं, तो आपका बॉस सही है कि आपको चाहिए।

हालांकि, हर पुनरावृत्ति एक सूची में आसानी से एक सूची में नहीं जा रहा है। अगर वह मना कर रहा है, हाँ यह गलत है।

अगर मैं तुम थे, तो मैं क्या करूँगा अपने सभी प्राकृतिक लोप्स को रिकर्सन में बदल देता है । वह उसे सिखाएगा, और यह आपके लिए भी एक अच्छा मानसिक अभ्यास है।


सबसे पहले, दिमित्री के जवाब का प्रतिवाद। सरणी के लिए, सी # कंपाइलर मोटे तौर पर foreach के लिए एक ही कोड उत्सर्जित करता है क्योंकि यह लूप के बराबर होगा। यह बताता है कि इस बेंचमार्क के लिए, परिणाम मूल रूप से वही हैं:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 1000000;
    const int Iterations = 10000;

    static void Main()
    {
        double[] data = new double[Size];
        Random rng = new Random();
        for (int i=0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }

        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j=0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds);
    }
}

परिणाम:

For loop: 16638
Foreach loop: 16529

इसके बाद, प्रमाणीकरण कि संग्रह प्रकार के बारे में ग्रेग का बिंदु महत्वपूर्ण है - उपरोक्त में List<double> में सरणी बदलें, और आपको मूल रूप से अलग-अलग परिणाम मिलते हैं। न केवल यह सामान्य रूप से धीमा है, लेकिन इंडेक्स तक पहुंचने से पूर्वानुमान काफी धीमा हो जाता है। ऐसा कहकर, मैं अभी भी हमेशा लूप के लिए फोरच पसंद करता हूं जहां यह कोड को सरल बनाता है - क्योंकि पठनीयता लगभग हमेशा महत्वपूर्ण होती है, जबकि माइक्रो-ऑप्टिमाइज़ेशन शायद ही कभी होता है।


foreach loops loops के मुकाबले अधिक विशिष्ट इरादा प्रदर्शित करता है

foreach लूप का उपयोग करके आपके कोड का उपयोग करने वाले किसी को भी दिखाया जाता है कि आप संग्रह में अपनी जगह के बावजूद संग्रह के प्रत्येक सदस्य को कुछ करने की योजना बना रहे हैं। यह भी दिखाता है कि आप मूल संग्रह को संशोधित नहीं कर रहे हैं (और यदि आप कोशिश करते हैं तो अपवाद फेंकता है)।

foreach का दूसरा लाभ यह है कि यह किसी भी IEnumerable पर काम करता है, जहां केवल IList लिए समझ में आता है, जहां प्रत्येक तत्व वास्तव में एक सूचकांक है।

हालांकि, अगर आपको किसी तत्व की अनुक्रमणिका का उपयोग करने की आवश्यकता है, तो निश्चित रूप से आपको लूप का उपयोग करने की अनुमति दी जानी चाहिए। लेकिन अगर आपको किसी इंडेक्स का उपयोग करने की आवश्यकता नहीं है, तो कोई आपके कोड को अव्यवस्थित कर रहा है।

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


I wouldn't expect anyone to find a "huge" performance difference between the two.

I guess the answer depends on the whether the collection you are trying to access has a faster indexer access implementation or a faster IEnumerator access implementation. Since IEnumerator often uses the indexer and just holds a copy of the current index position, I would expect enumerator access to be at least as slow or slower than direct index access, but not by much.

Of course this answer doesn't account for any optimizations the compiler may implement.


It seems a bit strange to totally forbid the use of something like a for loop.

There's an interesting article here that covers a lot of the performance differences between the two loops.

I would say personally I find foreach a bit more readable over for loops but you should use the best for the job at hand and not have to write extra long code to include a foreach loop if a for loop is more appropriate.





for-loop