एक फॉरेस्ट लूप के अंदर या बाहर एक चर की घोषणा: जो तेज / बेहतर है?


92

इनमें से कौन सा तेज / बेहतर है?

यह वाला:

List<User> list = new List<User>();
User u;

foreach (string s in l)
{
    u = new User();
    u.Name = s;
    list.Add(u);
}

या यह एक:

List<User> list = new List<User>();

foreach (string s in l)
{
    User u = new User();
    u.Name = s;
    list.Add(u);
}

मेरा नौसिखिया-विकासशील कौशल मुझे बताता है कि पहला बेहतर है, लेकिन मेरा एक दोस्त मुझे गलत बताता है, लेकिन मुझे एक अच्छा कारण नहीं दे सका कि दूसरा क्यों बेहतर है।

क्या प्रदर्शन में कोई अंतर है?

जवाबों:


112

प्रदर्शन-वार दोनों उदाहरण समान IL में संकलित किए गए हैं, इसलिए कोई अंतर नहीं है।

दूसरा बेहतर है, क्योंकि यह अधिक स्पष्ट रूप से आपके इरादे को व्यक्त करता है यदि uकेवल लूप के अंदर उपयोग किया जाता है।


10
ध्यान दें कि अगर चर को लैम्ब्डा एक्सप्रेशन या अनाम डेलीगेट द्वारा कैप्चर किया जाता है, तो अंतर होता है ; बाहरी परिवर्तनशील जाल देखें ।
dtb

क्या आप बता सकते हैं कि दोनों एक ही IL में संकलित क्यों हैं? मुझे पूरा यकीन है कि जावास्क्रिप्ट की तरह फ़ंक्शन के शीर्ष तक सी # घोषित नहीं करता है।
स्टाइलफेल

4
@styfle यहां आपके प्रश्न का उत्तर है।
डेविड शेरेट

स्टैक ओवरफ्लो लिंक के बाद अधिक विस्तृत उत्तर प्रदान करते हैं: 1) जॉन हैना और 2) स्ट्रिपिंगवर्यर
user3613932

14

किसी भी मामले में, सबसे अच्छा तरीका है कि एक कंस्ट्रक्टर का उपयोग किया जाए जो एक नाम लेता है ... या, अन्यथा, घुंघराले बालों वाली नोटरी का शोषण करें:

foreach (string s in l)
{
    list.Add(new User(s));
}

या

foreach (string s in l)
{
    list.Add(new User() { Name = s });
}

या इससे भी बेहतर, LINQ:

var list = l.Select( s => new User { Name = s});

अब, जबकि आपका पहला उदाहरण, कुछ मामलों में, अप्रतिस्पर्धी रूप से तेज़ हो सकता है, दूसरा बेहतर है क्योंकि यह अधिक पठनीय है, और कंपाइलर चर को छोड़ सकता है (और इसे पूरी तरह से छोड़ सकता है) क्योंकि यह foreachस्कोप के दायरे से बाहर नहीं है।


6
दिन की नेक्रोफिलिक टिप्पणी: "या इससे भी बेहतर, LINQ"। यकीन है कि यह कोड की एक पंक्ति है, और यह हमें डेवलपर्स के रूप में अच्छा महसूस कराता है। लेकिन चार लाइन संस्करण एफएआर अधिक समझ में आता है, और इस तरह बनाए रखने योग्य है।
ऑस्कर ऑस्टेगार्ड

5
मुश्किल से। LINQ संस्करण के साथ मुझे पता है कि मैं जो कर रहा हूं वह अपरिवर्तनीय है, और यह हर तत्व पर काम कर रहा है।
टोरेक

6

एक घोषणा के कारण किसी भी कोड को निष्पादित नहीं किया जाता है, इसलिए यह प्रदर्शन का मुद्दा नहीं है।

दूसरा वह है जो आप का मतलब है, और यदि आप इसे दूसरे तरीके से करते हैं, तो आप बेवकूफ बनाने की संभावना कम है, इसलिए इसका उपयोग करें। हमेशा छोटे दायरे में चर घोषित करने की कोशिश करें।

और इसके अलावा, Linq का उपयोग करने का बेहतर तरीका है:

List<User> users = l.Select(name => new User{ Name = name }).ToList();

2
मुझे लाइन से प्यार है "हमेशा छोटे दायरे में चर घोषित करने का प्रयास करें।" मुझे लगता है कि एक एकल पंक्ति प्रश्न का बहुत अच्छी तरह से उत्तर दे सकती है।
मंजूर

5

जब भी आपके पास प्रदर्शन के बारे में कोई प्रश्न होता है, तो केवल एक ही उपाय होता है - अपने परीक्षण के चारों ओर एक लूप चलाएं और उसे समय दें।

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

ओह, देर हो चुकी है और मुझे लगता है कि मैं सिर्फ यह कहना चाहूंगा कि इस तरह की चिंता मत करो या इस तरह के विवरण में फंस जाओ।


टिप के लिए thx, मुझे लगता है कि मैं कुछ अन्य सामान के बारे में सोच रहा हूँ, जो मुझे विस्मयकारी रूप से घायल कर रहा है: D
मार्कस

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

1

दूसरा बेहतर है। आपके पास प्रत्येक पुनरावृत्ति में एक नया उपयोगकर्ता होने का अर्थ है।


1

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


मुझे पूरा यकीन है कि सीएलआर लूप के प्रत्येक पुनरावृत्ति पर "एक नया चर" आवंटित नहीं करता है।
dtb

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

16
एक विधि में सभी स्थानीय चर (शीर्ष-स्तर या एक लूप में नेस्टेड) ​​IL में विधि-स्तरीय चर के लिए संकलित किए जाते हैं। विधि निष्पादित होने से पहले चर के लिए स्थान आवंटित किया जाता है, न कि जब सी # में घोषणा के साथ एक शाखा पहुंचती है।
dtb

1
@dtb क्या आपके पास इस दावे का स्रोत है?
स्टाइलफेल

1

इस परिदृश्य में, दूसरा संस्करण बेहतर है।

सामान्य तौर पर, यदि आपको केवल पुनरावृत्ति के शरीर के भीतर मूल्य तक पहुंचने की आवश्यकता है, तो दूसरा संस्करण चुनें। दूसरी ओर, यदि कुछ अंतिम स्थिति है तो चर लूप के शरीर से परे होगा, तो घोषित करें फिर पहले संस्करण का उपयोग करें।




0

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

namespace Test
{
  class Foreach
  {
    string[] names = new[] { "ABC", "MNL", "XYZ" };

    void Method1()
    {
      List<User> list = new List<User>();
      User u;

      foreach (string s in names)
      {
        u = new User();
        u.Name = s;
        list.Add(u);
      }
    }

    void Method2()
    {

      List<User> list = new List<User>();

      foreach (string s in names)
      {
        User u = new User();
        u.Name = s;
        list.Add(u);
      }
    }
  }

  public class User { public string Name; }
}

मैंने सीआईएल को सत्यापित किया लेकिन यह समान नहीं है।

यहां छवि विवरण दर्ज करें

इसलिए मैंने कुछ तैयार किया जो मैं बहुत बेहतर परीक्षण करना चाहता था।

namespace Test
{
  class Loop
  { 

    public TimeSpan method1 = new TimeSpan();
    public TimeSpan method2 = new TimeSpan();

    Stopwatch sw = new Stopwatch();

    public void Method1()
    {
      sw.Restart();

      C c;
      C c1;
      C c2;
      C c3;
      C c4;

      int i = 1000;
      while (i-- > 0)
      {
        c = new C();
        c1 = new C();
        c2 = new C();
        c3 = new C();
        c4 = new C();        
      }

      sw.Stop();
      method1 = method1.Add(sw.Elapsed);
    }

    public void Method2()
    {
      sw.Restart();

      int i = 1000;
      while (i-- > 0)
      {
        var c = new C();
        var c1 = new C();
        var c2 = new C();
        var c3 = new C();
        var c4 = new C();
      }

      sw.Stop();
      method2 = method2.Add(sw.Elapsed);
    }
  }

  class C { }
}

इस मामले में भी दूसरा तरीका हमेशा जीत रहा था लेकिन फिर मैंने सीआईएल को सत्यापित कर दिया कि कोई अंतर नहीं है।

यहां छवि विवरण दर्ज करें

मैं सीआईएल-रीडिंग गुरु नहीं हूं, लेकिन मुझे कोई घोषणा का मुद्दा दिखाई नहीं देता है। जैसा कि पहले ही बताया गया था कि घोषणा आवंटन नहीं है, इसलिए इस पर कोई प्रदर्शन जुर्माना नहीं है।

परीक्षा

namespace Test
{
  class Foreach
  {
    string[] names = new[] { "ABC", "MNL", "XYZ" };

    public TimeSpan method1 = new TimeSpan();
    public TimeSpan method2 = new TimeSpan();

    Stopwatch sw = new Stopwatch();

    void Method1()
    {
      sw.Restart();

      List<User> list = new List<User>();
      User u;

      foreach (string s in names)
      {
        u = new User();
        u.Name = s;
        list.Add(u);
      }

      sw.Stop();
      method1 = method1.Add(sw.Elapsed);
    }

    void Method2()
    {
      sw.Restart();

      List<User> list = new List<User>();

      foreach (string s in names)
      {
        User u = new User();
        u.Name = s;
        list.Add(u);
      }

      sw.Stop();
      method2 = method2.Add(sw.Elapsed);
    }
  }

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