c# - निरर्थक तुलना और "अगर" असाइनमेंट से पहले




.net if-statement (5)

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

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

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

निम्नलिखित नमूना आवेदन इस प्रभाव को दर्शाता है जिसमें लिखने की स्थिति से पहले चेक भी शामिल है:

 if (tmp1 != checkValue)  // set only if not equal to checkvalue
 {
    values[i] = checkValue;
 }

यहाँ पूर्ण कोड है:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        const int N = 500_000_000;
        int[] values = new int[N]; // 2 GB
        for (int nThreads = 1; nThreads < Environment.ProcessorCount; nThreads++)
        {
            SetArray(values, checkValue: 1, nTimes: 10, nThreads: nThreads);
            SetArray(values, checkValue: 2, nTimes: 10, nThreads: nThreads);
            SetArrayNoCheck(values, checkValue: 2, nTimes: 10, nThreads: nThreads);
        }
    }

    private static void SetArray(int[] values, int checkValue, int nTimes, int nThreads)
    {
        List<double> ms = new List<double>();

        for (int k = 0; k < nTimes; k++)  // set array values to 1
        {
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = 1;
            }

            var sw = Stopwatch.StartNew();
            Action acc = () =>
            {
                int tmp1 = 0;
                for (int i = 0; i < values.Length; i++)
                {
                    tmp1 = values[i];
                    if (tmp1 != checkValue)  // set only if not equal to checkvalue
                    {
                        values[i] = checkValue;
                    }
                }
            };

            Parallel.Invoke(Enumerable.Repeat(acc, nThreads).ToArray());  // Let this run on 3 cores

            sw.Stop();
            ms.Add(sw.Elapsed.TotalMilliseconds);
            //  Console.WriteLine($"Set {values.Length * 4 / (1_000_000_000.0f):F1} GB of Memory in {sw.Elapsed.TotalMilliseconds:F0} ms. Initial Value 1. Set Value {checkValue}");
        }
        string descr = checkValue == 1 ? "Conditional Not Set" : "Conditional Set";
        Console.WriteLine($"{descr}, {ms.Average():F0}, ms, nThreads, {nThreads}");

    }

    private static void SetArrayNoCheck(int[] values, int checkValue, int nTimes, int nThreads)
    {
        List<double> ms = new List<double>();
        for (int k = 0; k < nTimes; k++)  // set array values to 1
        {
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = 1;
            }

            var sw = Stopwatch.StartNew();
            Action acc = () =>
            {
                for (int i = 0; i < values.Length; i++)
                {
                        values[i] = checkValue;
                }
            };

            Parallel.Invoke(Enumerable.Repeat(acc, nThreads).ToArray());  // Let this run on 3 cores

            sw.Stop();
            ms.Add(sw.Elapsed.TotalMilliseconds);
            //Console.WriteLine($"Unconditional Set {values.Length * 4 / (1_000_000_000.0f):F1} GB of Memory in {sw.Elapsed.TotalMilliseconds:F0} ms. Initial Value 1. Set Value {checkValue}");
        }
        Console.WriteLine($"Unconditional Set, {ms.Average():F0}, ms, nThreads, {nThreads}");
    }
}

यदि आप इसे चलाते हैं तो आपको मान मिलते हैं जैसे:

// Value not set
Set 2.0 GB of Memory in 439 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 420 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 429 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 393 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 404 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 395 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 419 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 421 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 442 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 422 ms. Initial Value 1. Set Value 1
// Value written
Set 2.0 GB of Memory in 519 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 582 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 543 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 484 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 523 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 540 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 552 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 527 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 535 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 581 ms. Initial Value 1. Set Value 2

इसके परिणामस्वरूप 22% तेज प्रदर्शन होता है जो उच्च प्रदर्शन संख्या क्रंचिंग परिदृश्यों में महत्वपूर्ण हो सकता है।

प्रश्न का उत्तर देने के लिए जैसा कि लिखा गया था:

यदि आप केवल मेमोरी थ्रेडेड है, तो आप स्टेटमेंट निकाल सकते हैं। यदि कई थ्रेड एक ही या पास के डेटा पर काम कर रहे हैं तो गलत शेयरिंग हो सकती है जो आपको सीए तक खर्च कर सकती है। मेमोरी एक्सेस प्रदर्शन का 20%।

अपडेट 1 मैंने अधिक परीक्षण चलाए हैं और क्रॉस कोर चिट चैट दिखाने के लिए एक चार्ट बनाया है। यह एक सरल सेट ( बिना शर्त सेट ) दिखाता है क्योंकि यह टिप्पणीकार फ्रैंक हॉपकिंस द्वारा नोट किया गया था। सशर्त नहीं सेट में वह समाहित होता है जो कभी मान सेट नहीं करता है। और अंतिम लेकिन कम से कम कंडिशनल सेट मानों को निर्धारित स्थिति में सेट करेगा।

यहाँ उदाहरण है:

if(value != ageValue) {
  ageValue = value;
}

मेरा मतलब है, यदि हम एक चर का मान दूसरे को देते हैं, तो हमें यह जांचने की आवश्यकता होगी कि क्या उनके पास वैसे ही मूल्य हैं?

यह मुझे भ्रमित करता है। यहाँ व्यापक संदर्भ है:

private double ageValue;
public double Age {
  get {
    return ageValue;
  }

  set {
    if(value != ageValue) {
      ageValue = value;
    }
  }
}

एक winforms नियंत्रण में हमने एक विशिष्ट रंग के लिए BackgroundColor सेट किया था:

myControl.BackgroundColor = Color.White

विशिष्ट परिस्थितियों में यह एक तंग पाश में हो सकता है और एक जमे हुए यूआई को जन्म दे सकता है। कुछ प्रदर्शन विश्लेषण के बाद हमने पाया कि यह कॉल जमे हुए यूआई का कारण था और इसलिए हमने इसे बस इसमें बदल दिया:

if (myControl.BackgroundColor != Color.White)
    myControl.BackgroundColor = Color.White

और हमारे उपकरण का प्रदर्शन वापस ट्रैक पर था (और फिर हमने तंग लूप के कारण को समाप्त कर दिया)।

इसलिए यह जाँच हमेशा निरर्थक नहीं होती है। खासकर यदि लक्ष्य एक संपत्ति है जो सेटर के भीतर अधिक है तो बस एक बैकिंग स्टोर पर मूल्य लागू करना है।


प्रदर्शन कोई बड़ी बात नहीं है, बस आपके तर्क की जरूरत पर निर्भर करता है।


मैं वास्तव में इस तरह सामान कोडित है, अलग-अलग कारणों से। वे थोड़े कठिन हैं समझाने के लिए, तो मेरे साथ सहन करो।

मुख्य बात यह है कि आप एक नया संदर्भ सेट नहीं करते हैं यदि संदर्भ का मान तार्किक रूप से पूर्व संदर्भ के मूल्य के बराबर है। उपरोक्त टिप्पणियों में, उपयोगकर्ताओं ने इस परिदृश्य की अप्रियता की आलोचना की है - और इससे निपटने के लिए अप्रिय है - लेकिन अभी भी अनिवार्य रूप से मामलों में आवश्यक है।

मैं इस तरह से उपयोग के मामलों को विभाजित करने की कोशिश करूंगा:

  1. मान एक अमूर्त डेटा प्रकार है, जहां आपके पास समान तार्किक मान का प्रतिनिधित्व करने वाले अलग-अलग निर्मित उदाहरण हो सकते हैं।

    • यह गणित के कार्यक्रमों में बहुत कुछ होता है, जैसे कि गणितज्ञ, जहां आप आदिम संख्याओं का उपयोग नहीं कर सकते हैं, जिससे आप एक ही प्रतिनिधित्व करने के लिए अलग-अलग वस्तुओं के साथ समाप्त हो सकते हैं।
  2. value का संदर्भ एक कैशिंग तर्क के लिए उपयोगी है।

    • अमूर्त संख्याओं का उपयोग करते समय यह भी पॉप अप कर सकता है। उदाहरण के लिए, यदि आप प्रोग्राम के अन्य हिस्सों को किसी संदर्भ के बारे में कैश्ड डेटा की उम्मीद करते हैं, तो आप इसे तार्किक रूप से समकक्ष संदर्भ के साथ बदलना नहीं चाहते हैं, क्योंकि यह कहीं और उपयोग किए गए कैश को अमान्य कर देगा।
  3. आप एक प्रतिक्रियाशील मूल्यांकनकर्ता का उपयोग कर रहे हैं, जहां एक नया मान सेट करने से अद्यतनों की श्रृंखला-प्रतिक्रिया हो सकती है।

    • वास्तव में यह कैसे और क्यों संदर्भ के आधार पर भिन्न होता है।

बड़ी वैचारिक बात यह है कि, कुछ मामलों में, आपके पास अलग-अलग संदर्भों में समान तार्किक मान हो सकता है, लेकिन आप दो बड़े कारणों के लिए पतित संदर्भों की संख्या को कम करने का प्रयास करना चाहते हैं:

  1. एक ही लॉजिकल वैल्यू को कई बार स्टोर करने से अधिक मेमोरी हो जाती है।

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

एक और यादृच्छिक उदाहरण के लिए, .NET का कचरा संग्रहकर्ता " जनरेशनल " है , जिसका अर्थ है कि यह जाँचने में अधिक प्रयास करता है कि क्या नया होने पर एक मूल्य एकत्र किया जा सकता है। यदि आप अधिमानतः पुराने संदर्भ को बनाए रखते हैं, तो कचरा संग्रहकर्ता को लाभ का अनुभव हो सकता है, क्योंकि यह अधिक विशेषाधिकार प्राप्त पीढ़ी में है, जिससे नए संदर्भ में कचरा जल्दी एकत्रित हो सके।

एक अन्य उपयोग का मामला, फिर से अमूर्त डेटा प्रकारों के साथ है, जहां आपके पास आलसी-मूल्यांकन किए गए गुण हो सकते हैं। उदाहरण के लिए, .IsRational कि आपके पास एक abstract class Number है, जिसमें गुण .IsRational , .IsEven , इत्यादि जैसे गुण हैं। फिर, हो सकता है कि आप उन लोगों की तुरंत गणना न करें, बल्कि परिणामों की कैशिंग करते हुए उन्हें ऑन-डिमांड जेनरेट करें। इस तरह के परिदृश्य में, आप पुराने Number को उसी तार्किक मान के साथ बनाए रखना पसंद कर सकते हैं क्योंकि उनके पास अधिक सामान हो सकता है, जबकि एक नए value में इससे जुड़ी कम जानकारी हो सकती है, भले ही यह तार्किक रूप से हो ==

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


if है, if निरीक्षण पर, निरर्थक नहीं । यह शेष कार्यान्वयन पर निर्भर करता है। ध्यान दें कि C # में != ओवरलोड किया जा सकता है, जिसका अर्थ है कि मूल्यांकन के दुष्प्रभाव हो सकते हैं। फ़ुथर्मोर, चेक किए गए चर को गुणों के रूप में लागू किया जा सकता है, जिसका मूल्यांकन पर दुष्प्रभाव भी हो सकता है।






if-statement