2012-12-20 11 views
5

संभव डुप्लिकेट:
c# Leaner way of initializing int arrayसी # में पूर्णांक सरणी प्रारंभ करने में कैसे

असल में मैं अगर वहाँ

private static int[] GetDefaultSeriesArray(int size, int value) 
    { 
     int[] result = new int[size]; 
     for (int i = 0; i < size; i++) 
     { 
      result[i] = value; 
     } 
     return result; 
    } 
नीचे दिखाया गया है की तुलना में एक अधिक कुशल कोड है जानना चाहते हैं

जहां आकार 10 से 150000 तक भिन्न हो सकता है। छोटे सरणी के लिए कोई मुद्दा नहीं है, लेकिन बेहतर डब्ल्यू होना चाहिए उपरोक्त करने के लिए आह। मैं वीएस -2010 (.NET 4.0)

+0

क्या आप अपनी सरणी के सभी क्षेत्रों में 'मूल्य' सेट कर रहे हैं? – cheesemacfly

+0

हां वह प्रत्येक तत्व को एक गैर-डिफ़ॉल्ट int मान पर सेट कर रहा है - और संभवतः यह माइक्रो ऑप्टिमाइज़ेशन को छोड़कर सबसे तेज़ तरीका है (कोई संदेह नहीं करेगा कि कुछ माइक्रो ऑप्टिमाइज़ेशन तकनीक दिखाएं) –

+1

कोई फर्क नहीं पड़ता कि आप इसे कैसे करते हैं, बिना किसी सरणी को प्रारंभ करना सी # में डिफ़ॉल्ट मूल्य एक ओ (एन) ऑपरेशन है। – Maciej

उत्तर

4

एक तरीका है जिससे आप गति सुधार सकते हैं Array.Copy का उपयोग करके। यह निचले स्तर पर काम करने में सक्षम है जिसमें यह स्मृति के बड़े वर्गों को आवंटित करने वाला थोक है।

असाइनमेंट बैच करके आप सरणी को एक सेक्शन में स्वयं कॉपी कर सकते हैं।

उस पर, बैच स्वयं काफी प्रभावी ढंग से समानांतर हो सकते हैं।

यहां मेरा प्रारंभिक कोड है। 10 मिलियन वस्तुओं के नमूने सरणी के साथ मेरी मशीन (जिसमें केवल दो कोर हैं) पर, मुझे 15% या इतनी गति मिल रही थी। आपको बैच आकार के साथ खेलना होगा (इसे अपने पृष्ठ आकार के गुणकों में रहने के लिए इसे कुशल रखने के लिए प्रयास करें) ताकि आपके पास मौजूद वस्तुओं के आकार को ट्यून किया जा सके। छोटे सरणी के लिए यह आपके कोड के लगभग समान हो जाएगा क्योंकि यह पहले बैच को भरने से पहले नहीं मिलेगा, लेकिन यह उन मामलों में भी (ध्यान से) खराब नहीं होगा।

private const int batchSize = 1048576; 
private static int[] GetDefaultSeriesArray2(int size, int value) 
{ 

    int[] result = new int[size]; 

    //fill the first batch normally 
    int end = Math.Min(batchSize, size); 
    for (int i = 0; i < end; i++) 
    { 
     result[i] = value; 
    } 

    int numBatches = size/batchSize; 

    Parallel.For(1, numBatches, batch => 
    { 
     Array.Copy(result, 0, result, batch * batchSize, batchSize); 
    }); 

    //handle partial leftover batch 
    for (int i = numBatches * batchSize; i < size; i++) 
    { 
     result[i] = value; 
    } 

    return result; 
} 
+0

+1। अच्छा सुझाव। क्या आपको तुलना करने का मौका मिला था कि अकेले सुधार (समानांतर के बिना) क्या है? –

+0

@AlexeiLevenkov कुछ सरल परीक्षणों के करीब यह था। ऐसा कुछ है जिसे इससे अधिक लाभ प्राप्त करने के लिए अच्छी तरह से ट्यून किया जाना चाहिए (उदाहरण के लिए, आपको केवल सही बैच आकार होना चाहिए)। – Servy

8

सी #/सीएलआर गैर-डिफ़ॉल्ट मानों के साथ सरणी को निष्क्रिय करने के तरीके में नहीं बनाया गया है।

आपका कोड उतना ही कुशल है जितना कि आप प्रति आइटम संचालन में माप सकते हैं।

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

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

धीमी लेकिन अधिक कॉम्पैक्ट कोड (जो संभावित रूप से पढ़ने में आसान है) Enumerable.Repeat का उपयोग करना होगा। ध्यान दें कि ToArray बड़े सरणी के लिए आवंटित स्मृति की महत्वपूर्ण मात्रा का कारण बन जाएगा (जो LOH पर आवंटन के साथ भी समाप्त हो सकता है) - High memory consumption with Enumerable.Range?

var result = Enumerable.Repeat(value, size).ToArray(); 
+5

यह काफी थोड़ा * कम कुशल * होगा, * अधिक कुशल * नहीं होगा। – Servy

+0

@ सर्वी: शायद यही कारण है कि एलेक्सी ने कहा कि "आपका कोड उतना ही कुशल है जितना इसे प्राप्त कर सकता है"। –

+0

@ होन्ज़ा ब्रेस्टन वेल, मैं उस कथन से असहमत हूं। इसके अलावा, सवाल एक बेहतर विकल्प के लिए पूछा। यदि आपके पास कोई बेहतर विकल्प नहीं है तो बस जवाब न दें, जैसा कि आप जानते हैं कि कुछ प्रदान करने का विरोध खराब है। – Servy

0
Enumerable.Repeat(value, size).ToArray(); 
-2

कोई पहले से ही उल्लेख किया है, आप इस तरह समानांतर प्रसंस्करण लाभ उठा सकते हैं के रूप में:

int[] result = new int[size]; 
Parallel.ForEach(result, x => x = value); 
return result; 

खेद है कि मैं इस पर प्रदर्शन परीक्षण करने का समय नहीं था (वी.एस. इस पर स्थापित नहीं मशीन) लेकिन यदि आप इसे कर सकते हैं और परिणाम साझा कर सकते हैं तो यह बहुत अच्छा होगा।

संपादित: टिप्पणी के अनुसार, जबकि मैं अभी भी लगता है कि प्रदर्शन वे बराबर हैं के संदर्भ में, आप पाश के लिए समानांतर कोशिश कर सकते हैं: अप पढ़ना Enumerable.Repeat 20 बार की तुलना में धीमी

Parallel.For(0, size, i => result[i] = value); 
+0

यह समानांतर करने का एक प्रभावी तरीका नहीं होगा। यह लगभग निश्चित रूप से खराब होने जा रहा है क्योंकि आप स्मृति लोकता खो देंगे और काम की इकाई बहुत छोटी है। यदि आपने समानांतर उपयोग किया है, कम से कम, यह एक बेहतर शॉट होगा। – Servy

+0

@EDIT: आप हमेशा प्रदर्शन को आसानी से जांच सकते हैं .. तो आपको अनुमान लगाने की आवश्यकता नहीं होगी। –

+1

मैंने इन दोनों के साथ कुछ सरल परीक्षण चलाए और समांतर संस्करणों में लगभग 5x लंबा समय लगा, जो कि मुझे अपेक्षित था। – Servy

0

पाश और केवल एक चीज मैंने पाया है जो अपनी गति में सुधार हो सकता है के लिए ऑप्स मानक

private static int[] GetDefaultSeriesArray(int size, int value) 
{ 
    int[] result = new int[size]; 
    for (int i = 0; i < size; ++i) 
    { 
     result[i] = value; 
    } 
    return result; 
} 

नोट मैं ++ i ++ करने के लिए बदल जाता है। i ++ प्रतियां I, increments i, और मूल मान देता है। ++ मैं केवल बढ़ी हुई मान

1

प्रदर्शन में सुधार करने का एक और तरीका एक सुंदर मूल तकनीक के साथ है: लूप अनोलिंग।

मैंने 20 मिलियन वस्तुओं के साथ एक सरणी शुरू करने के लिए कुछ कोड लिखा है, यह बार-बार किया जाता है और औसत की गणना की जाती है। लूप को अनलॉक किए बिना, इसमें लगभग 44 एमएस लगता है। 10 एमएस की लूप अनलोलिंग के साथ 23 एमएस में प्रक्रिया समाप्त हो गई है।

private void Looper() 
     { 
      int repeats = 100; 
      float avg = 0; 

      ArrayList times = new ArrayList(); 

      for (int i = 0; i < repeats; i++) 
       times.Add(Time()); 

      Console.WriteLine(GetAverage(times)); //44 

      times.Clear(); 

      for (int i = 0; i < repeats; i++)    
       times.Add(TimeUnrolled()); 

      Console.WriteLine(GetAverage(times)); //22 

     } 

private float GetAverage(ArrayList times) 
     { 
      long total = 0; 
      foreach (var item in times) 
      { 
       total += (long)item; 
      } 

      return total/times.Count; 
     } 

     private long Time() 
     { 
      Stopwatch sw = new Stopwatch(); 
      int size = 20000000; 
      int[] result = new int[size]; 
      sw.Start(); 


      for (int i = 0; i < size; i++) 
      { 
       result[i] = 5; 
      } 
      sw.Stop(); 
      Console.WriteLine(sw.ElapsedMilliseconds); 
      return sw.ElapsedMilliseconds; 
     } 

     private long TimeUnrolled() 
     { 
      Stopwatch sw = new Stopwatch(); 
      int size = 20000000; 
      int[] result = new int[size]; 
      sw.Start(); 


      for (int i = 0; i < size; i += 10) 
      { 
       result[i] = 5; 
       result[i + 1] = 5; 
       result[i + 2] = 5; 
       result[i + 3] = 5; 
       result[i + 4] = 5; 
       result[i + 5] = 5; 
       result[i + 6] = 5; 
       result[i + 7] = 5; 
       result[i + 8] = 5; 
       result[i + 9] = 5; 
      } 
      sw.Stop(); 
      Console.WriteLine(sw.ElapsedMilliseconds); 
      return sw.ElapsedMilliseconds; 
     } 
+0

सबसे अच्छा कोड नहीं है, लेकिन यह मेरा बिंदु दिखाता है। सब कुछ यह 50% सुधार है, यदि आप इसे एक बार करते हैं तो ज्यादा नहीं, लेकिन जोड़ता है। – Mataniko

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