एरेस बनाम सूचियों का प्रदर्शन


193

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

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

क्या किसी ने वास्तव में इसे मापा? एक सूची के माध्यम से 6M बार पुनरावृत्ति करना एक सरणी के समान समय लेगा?


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

2
T[]बनाम List<T>एक बड़ा प्रदर्शन अंतर बना सकता है। मैंने .NET 4.0 पर सूचियों से सरणियों में जाने के लिए बस एक अत्यंत (नेस्टेड) ​​लूप गहन अनुप्रयोग को अनुकूलित किया है। मैं 5% से 10% सुधार की उम्मीद कर रहा था, लेकिन 40% से अधिक स्पीडअप हो गया! सूची से सरणी में सीधे स्थानांतरित करने के अलावा कोई अन्य परिवर्तन नहीं। सभी गणना foreachबयानों के साथ की गई थी । मार्क Gravell के जवाब के आधार पर यह की तरह लग रहा foreachसाथ List<T>विशेष रूप से खराब है।
विशेष सॉस

जवाबों:


221

बहुत आसान उपाय ...

तंग-लूप प्रसंस्करण कोड की एक छोटी संख्या में जहां मुझे पता है कि लंबाई तय हो गई है मैं उस अतिरिक्त छोटे सूक्ष्म-अनुकूलन के लिए सरणियों का उपयोग करता हूं; यदि आप अनुक्रमणिका / प्रपत्र के लिए उपयोग करते हैं, तो सरणियाँ मामूली रूप से तेज़ हो सकती हैं - लेकिन IIRC का मानना ​​है कि यह सरणी में डेटा के प्रकार पर निर्भर करता है। लेकिन जब तक आपको माइक्रो-ऑप्टिमाइजेशन की आवश्यकता न हो , इसे सरल रखें और उपयोग करें आदि।List<T>

बेशक, यह केवल तभी लागू होता है जब आप सभी डेटा पढ़ रहे हों; एक शब्दकोश कुंजी-आधारित लुकअप के लिए त्वरित होगा।

यहां "int" का उपयोग करके मेरे परिणाम हैं (यह सत्यापित करने के लिए दूसरा नंबर एक चेकसम है कि वे सभी एक ही काम करते हैं):

(बग को ठीक करने के लिए संपादित)

List/for: 1971ms (589725196)
Array/for: 1864ms (589725196)
List/foreach: 3054ms (589725196)
Array/foreach: 1860ms (589725196)

परीक्षण रिग के आधार पर:

using System;
using System.Collections.Generic;
using System.Diagnostics;
static class Program
{
    static void Main()
    {
        List<int> list = new List<int>(6000000);
        Random rand = new Random(12345);
        for (int i = 0; i < 6000000; i++)
        {
            list.Add(rand.Next(5000));
        }
        int[] arr = list.ToArray();

        int chk = 0;
        Stopwatch watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            int len = list.Count;
            for (int i = 0; i < len; i++)
            {
                chk += list[i];
            }
        }
        watch.Stop();
        Console.WriteLine("List/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            for (int i = 0; i < arr.Length; i++)
            {
                chk += arr[i];
            }
        }
        watch.Stop();
        Console.WriteLine("Array/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in list)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("List/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in arr)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("Array/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        Console.ReadLine();
    }
}

8
दिलचस्प विवरण: यहां बार बार मेरी रिग पर / DEBUG (.net 3.5 sp1): 0.92, 0.80, 0.96, 0.93; जो मुझे बताता है कि ऐरे को अनुकूलित करने के लिए वीएम में कुछ बुद्धिमत्ता है / अन्य मामलों की तुलना में लगभग 10% बेहतर है।
डेविड श्मिट

2
हाँ, सरणी के लिए / के लिए JIT अनुकूलन है। वास्तव में, मैं इस धारणा के तहत था कि इसमें लंबाई का मामला शामिल है (क्योंकि यह जानता है कि यह तय है), इसलिए मैंने इसे पहले बाहर नहीं निकाला था (सूची के विपरीत जहां मैंने किया था)। अद्यतन के लिए धन्यवाद।
मार्क Gravell

2
अजीब। मेरे बहुत समान परीक्षण सरणियों का उपयोग करते समय और फ़ॉरच के बीच कोई महत्वपूर्ण अंतर नहीं दिखाते हैं। एक बेंचमार्क के साथ एक ब्लॉग पोस्ट में पूरी तरह से जांच करेंगे, जो अन्य लोग चला सकते हैं और मेरे लिए परिणाम भेज सकते हैं ...
जॉन स्कीट

1
अगर मैं प्रत्येक परीक्षण के लिए chk1, chk2, chk3, chk4) के लिए एक अलग चर का उपयोग कर नाटकीय रूप से अलग-अलग परिणाम प्राप्त करता हूं। सूची / के लिए = 1303ms, सरणी / के लिए = 433ms। कोई विचार क्यों?
जॉन

4
जॉन द्वारा स्कीट के ब्लॉग में उपरोक्त टिप्पणी में उल्लिखित लिंक को तोड़ दिया गया था। नीचे अद्यतन लिंक है। codeblog.jonskeet.uk/2009/01/29/…
जोश

88

सारांश:

  • सरणी का उपयोग करने की आवश्यकता है:

    • तो अक्सर संभव है। यह तेज़ है और समान मात्रा की जानकारी के लिए सबसे छोटी रैम रेंज लेता है।
    • यदि आप आवश्यक कोशिकाओं की सटीक गिनती जानते हैं
    • यदि डेटा सरणी में सहेजा गया है तो <85000 b (पूर्णांक डेटा के लिए 85000/32 = 2656 तत्व)
    • यदि उच्च रैंडम एक्सेस गति की आवश्यकता है
  • सूची का उपयोग करने की आवश्यकता है:

    • यदि सूची के अंत में कोशिकाओं को जोड़ने की आवश्यकता है (अक्सर)
    • यदि सूची के आरंभ / मध्य में सेल जोड़ने की आवश्यकता है (NOT OFTEN)
    • यदि डेटा सरणी में सहेजा गया है तो <85000 b (पूर्णांक डेटा के लिए 85000/32 = 2656 तत्व)
    • यदि उच्च रैंडम एक्सेस गति की आवश्यकता है
  • लिंक्डलिस्ट को उपयोग करने की आवश्यकता है:

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

अधिक जानकारी:

रंग अर्थ

ऐरे बनाम लिस्ट बनाम लिंक्ड सूची

बहुत अधिक विवरण:

https://stackoverflow.com/a/29263914/4423545


मैं आपके दावे से थोड़ा भ्रमित हूं कि सूची प्रस्तुत करना अपेक्षाकृत तेज़ है लेकिन प्रविष्टि धीमी है। सम्मिलन भी रैखिक समय है, और प्रीपेंड की तुलना में औसतन 50% तेज है।
माइक मैरीनोव्स्की

1
@MikeMarynowski सी # सूची में एरे के चारों ओर आवरण है। इसलिए प्रविष्टि की सूची के मामले में आपके पास कुछ समय के लिए ही रैखिक समय होगा। इस प्रणाली के बाद नया एक बड़ा सरणी बनाएगा और पुराने से आइटम कॉपी करने के लिए समय की आवश्यकता होगी।
एंड्रयू

एक ही बात prepends के साथ।
माइक मेरीनोस्की

एक प्रीपेन्ड ऑपरेशन सिर्फ एक इंसर्ट है। प्रदर्शन के मामले में यह सबसे खराब केस इंसर्ट है, इसलिए यदि इंसर्ट धीमा है, तो प्रीपेंड भी धीमा है।
माइक मैरीनोव्स्की

दोनों सम्मिलित और प्रस्तुत करना O (n) (amortized) है। प्रीपेंड एक सम्मिलित है, लेकिन यह सबसे धीमा संभव सम्मिलित है क्योंकि इसे सूची में सभी वस्तुओं को एक स्थान पर ले जाना है। रैंडम लोकेशन में एक इंसर्ट को केवल उन चीजों को ऊपर ले जाना पड़ता है जो इंसर्शन पॉइंट की तुलना में अधिक इंडेक्स पर होते हैं, इसलिए औसतन 50% आइटम।
19

26

मुझे लगता है कि प्रदर्शन काफी समान होगा। जब आप सूची में आइटम जोड़ते हैं, तो एक सूची बनाम एक सरणी का उपयोग करते समय ओवरहेड शामिल होता है, और जब सूची में सरणी का आकार बढ़ाना होता है जो आंतरिक रूप से उपयोग कर रहा है, तो सरणी की क्षमता तक पहुंच जाती है।

मान लीजिए कि आपके पास 10 की क्षमता के साथ एक सूची है, तो 11 वें तत्व को जोड़ने के लिए सूची की क्षमता बढ़ जाएगी। आप सूची की क्षमता को उन वस्तुओं की संख्या पर प्रारंभिक करके प्रदर्शन प्रभाव को कम कर सकते हैं जो इसे धारण करेंगे।

लेकिन, यह पता लगाने के लिए कि किसी सूची पर पुनरावृत्ति करना किसी सरणी पर चलना जितना तेज़ है, आप इसे बेंचमार्क क्यों नहीं करते?

int numberOfElements = 6000000;

List<int> theList = new List<int> (numberOfElements);
int[] theArray = new int[numberOfElements];

for( int i = 0; i < numberOfElements; i++ )
{
    theList.Add (i);
    theArray[i] = i;
}

Stopwatch chrono = new Stopwatch ();

chrono.Start ();

int j;

 for( int i = 0; i < numberOfElements; i++ )
 {
     j = theList[i];
 }

 chrono.Stop ();
 Console.WriteLine (String.Format("iterating the List took {0} msec", chrono.ElapsedMilliseconds));

 chrono.Reset();

 chrono.Start();

 for( int i = 0; i < numberOfElements; i++ )
 {
     j = theArray[i];
 }

 chrono.Stop ();
 Console.WriteLine (String.Format("iterating the array took {0} msec", chrono.ElapsedMilliseconds));

 Console.ReadLine();

मेरे सिस्टम पर; सरणी पर पुनरावृति 33msec लिया; सूची में पुनरावृत्ति 66msec लिया।

ईमानदार होने के लिए, मुझे उम्मीद नहीं थी कि बदलाव इतना ही होगा। इसलिए, मैंने अपना पुनरावृत्ति एक लूप में रखा है: अब, मैं 1000 बार दोनों पुनरावृत्ति निष्पादित करता हूं। परिणाम हैं:

सूची को पुनरावृत्त करते हुए 67146 मिसे लिया गया, एरे को 40821 मिसे लिया गया

अब, परिवर्तन अब और बड़ा नहीं है, लेकिन फिर भी ...

इसलिए, मैंने .NET रिफलेक्टर शुरू कर दिया है, और सूची वर्ग के अनुक्रमणक का प्राप्तकर्ता, इस तरह दिखता है:

public T get_Item(int index)
{
    if (index >= this._size)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException();
    }
    return this._items[index];
}

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


हाय फ्रेडरिक, धन्यवाद! आप कैसे समझाएंगे कि आपकी सूची ने सरणियों का समय दोगुना कर दिया। वह नहीं जो आप उम्मीद करेंगे। क्या आपने तत्वों की संख्या बढ़ाने की कोशिश की?
बोअज

1
इसे वापस नहीं किया जाएगा। यदि सूचकांक सीमा से बाहर था, तो पहले से ही एक अपवाद को फेंक दें? .NET के पास यह अतिरिक्त जाँच क्यों होती है जब अंतिम परिणाम इसके साथ या उसके बिना समान होता है?
जॉन मर्सीर

@ जॉन मेरिकर सूची के आकार (वर्तमान में सम्‍मिलित मदों की संख्‍या) के विरूद्ध है, जो अलग है और संभवत: _items सरणी की क्षमता से कम है। हर अतिरिक्त के लिए पुन: आवंटन की आवश्यकता नहीं होने से भविष्य की वस्तुओं को जल्दी जोड़ने के लिए सरणी को अतिरिक्त क्षमता के साथ आवंटित किया गया है।
त्रसवी

21

अगर आपको बस एक ही मूल्य मिल रहा है (लूप में नहीं) तो दोनों बाउंड चेकिंग करते हैं (आप प्रबंधित कोड याद रखें) यह सिर्फ सूची है जो इसे दो बार करता है। नोट बाद में देखें कि यह एक बड़ी बात नहीं है।

यदि आप अपना स्वयं का उपयोग कर रहे हैं (int int i = 0; i <x। [लंबाई / गणना]; i ++) तो मुख्य अंतर इस प्रकार है:

  • सरणी:
    • चेकिंग सीमा हटा दी जाती है
  • सूचियाँ
    • सीमा जाँच की जाती है

यदि आप फोरच का उपयोग कर रहे हैं तो मुख्य अंतर निम्नानुसार है:

  • सरणी:
    • पुनरावृत्ति को प्रबंधित करने के लिए कोई ऑब्जेक्ट आवंटित नहीं किया गया है
    • चेकिंग सीमा हटा दी जाती है
  • एक चर के माध्यम से सूची को जाना जाता है।
    • पुनरावृति प्रबंधन चर का आवंटन किया जाता है
    • सीमा जाँच की जाती है
  • IList होने के लिए ज्ञात एक चर के माध्यम से सूची।
    • पुनरावृति प्रबंधन चर ढेर आवंटित किया गया है
    • सीमा की जाँच की जाती है और यह भी देखा जाता है कि सूची के मूल्यों को फॉरेस्ट के दौरान बदला नहीं जा सकता है जबकि सरणी का हो सकता है।

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


11

[ यह सवाल भी देखें ]

मैंने वास्तविक यादृच्छिक संख्याओं का उपयोग करने के लिए मार्क के उत्तर को संशोधित किया है और वास्तव में सभी मामलों में एक ही काम करते हैं।

परिणाम:

         foreach के लिए
ऐरे: 1575ms 1575ms (+ 0%)
सूची: 1630ms 2627ms (+ 61%)
         (+ 3%) (+ 67%)

(चेकसम: -1000038876)

VS 2008 SP1 के तहत रिलीज़ के रूप में संकलित। Q6600@2.40GHz पर डिबगिंग के बिना चल रहा है। .NET 3.5 SP1।

कोड:

class Program
{
    static void Main(string[] args)
    {
        List<int> list = new List<int>(6000000);
        Random rand = new Random(1);
        for (int i = 0; i < 6000000; i++)
        {
            list.Add(rand.Next());
        }
        int[] arr = list.ToArray();

        int chk = 0;
        Stopwatch watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            int len = list.Count;
            for (int i = 0; i < len; i++)
            {
                chk += list[i];
            }
        }
        watch.Stop();
        Console.WriteLine("List/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            int len = arr.Length;
            for (int i = 0; i < len; i++)
            {
                chk += arr[i];
            }
        }
        watch.Stop();
        Console.WriteLine("Array/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in list)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("List/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in arr)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("Array/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);
        Console.WriteLine();

        Console.ReadLine();
    }
}

यह अजीब है - मैंने बस आपका सटीक कोड चलाया है, कमांड लाइन (3.5SP1) से / o + / डिबग के साथ बनाया है और मेरे परिणाम हैं: सूची / के लिए: 1524; सरणी / के लिए: 1472; सूची / foreach: 4128; सरणी / foreach: 1484।
जॉन स्कीट

आप कहते हैं कि यह रिलीज के रूप में संकलित किया गया था - क्या मैं सिर्फ यह जांच सकता हूं कि आपने इसे डिबग करने के बजाय इसे चलाया था? मूर्खतापूर्ण प्रश्न, मुझे पता है, लेकिन मैं अन्यथा परिणाम की व्याख्या नहीं कर सकता ...
जॉन स्कीट

2

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


2

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


2

यहाँ एक है जो शब्दकोशों, IEnumerable का उपयोग करता है:

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

static class Program
{
    static void Main()
    {
        List<int> list = new List<int>(6000000);

        for (int i = 0; i < 6000000; i++)
        {
                list.Add(i);
        }
        Console.WriteLine("Count: {0}", list.Count);

        int[] arr = list.ToArray();
        IEnumerable<int> Ienumerable = list.ToArray();
        Dictionary<int, bool> dict = list.ToDictionary(x => x, y => true);

        int chk = 0;
        Stopwatch watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            int len = list.Count;
            for (int i = 0; i < len; i++)
            {
                chk += list[i];
            }
        }
        watch.Stop();
        Console.WriteLine("List/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            for (int i = 0; i < arr.Length; i++)
            {
                chk += arr[i];
            }
        }
        watch.Stop();
        Console.WriteLine("Array/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in Ienumerable)
            {
                chk += i;
            }
        }

        Console.WriteLine("Ienumerable/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in dict.Keys)
            {
                chk += i;
            }
        }

        Console.WriteLine("Dict/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);


        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in list)
            {
                chk += i;
            }
        }

        watch.Stop();
        Console.WriteLine("List/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in arr)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("Array/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);



        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in Ienumerable)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("Ienumerable/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in dict.Keys)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("Dict/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        Console.ReadLine();
    }
}

2

तत्वों की संख्या बढ़ाकर क्षमता जोड़ने का प्रयास न करें।

प्रदर्शन

List For Add: 1ms
Array For Add: 2397ms

    Stopwatch watch;
        #region --> List For Add <--

        List<int> intList = new List<int>();
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 60000; rpt++)
        {
            intList.Add(rand.Next());
        }
        watch.Stop();
        Console.WriteLine("List For Add: {0}ms", watch.ElapsedMilliseconds);
        #endregion

        #region --> Array For Add <--

        int[] intArray = new int[0];
        watch = Stopwatch.StartNew();
        int sira = 0;
        for (int rpt = 0; rpt < 60000; rpt++)
        {
            sira += 1;
            Array.Resize(ref intArray, intArray.Length + 1);
            intArray[rpt] = rand.Next();

        }
        watch.Stop();
        Console.WriteLine("Array For Add: {0}ms", watch.ElapsedMilliseconds);

        #endregion

मुझे एक सरणी का आकार मिलता है 60k बार धीमी गति से होने जा रहा है ... निश्चित रूप से वास्तविक दुनिया में उपयोग हालांकि, दृष्टिकोण बस यह जांचना होगा कि आपको कितने अतिरिक्त स्लॉट की आवश्यकता है, इसे लंबाई + 60k में बदल दें, और फिर आवेषण के माध्यम से ज़िप करें।
तोब्रिअंद

यदि आप हर बार आपको अधिक स्थान की आवश्यकता होती है, तो आकार बदलना दोगुना होता है। मैंने पाया कि ऐसा करने में लगभग एक ही समय लगता है क्योंकि यह प्रारंभिक घोषणा के बाद इसे एक बार फिर से आकार देने का काम करता है। यह आपको एक सूची और एक सरणी की अधिकांश गति का लचीलापन देता है।
user1318499

2

मुझे चिंता थी कि अन्य उत्तरों में तैनात बेंचमार्क अभी भी कंपाइलर को लूप्स को ऑप्टिमाइज़ करने, खत्म करने या मर्ज करने के लिए छोड़ देंगे, इसलिए मैंने एक लिखा है कि:

  • उपयोग किए गए अप्रत्याशित इनपुट (यादृच्छिक)
  • कंसोल पर मुद्रित परिणाम के साथ एक परिकलित चलाता है
  • प्रत्येक पुनरावृत्ति इनपुट डेटा को संशोधित करता है

नतीजा यह है कि एक प्रत्यक्ष सरणी में ILL में लिपटे सरणी तक पहुंच की तुलना में लगभग 250% बेहतर प्रदर्शन है:

  • 1 बिलियन ऐरे एक्सेस: 4000 मि
  • 1 बिलियन सूची एक्सेस: 10000 एमएस
  • 100 मिलियन ऐरे एक्सेस: 350 मि
  • 100 मिलियन सूची एक्सेस: 1000 एमएस

यहाँ कोड है:

static void Main(string[] args) {
  const int TestPointCount = 1000000;
  const int RepetitionCount = 1000;

  Stopwatch arrayTimer = new Stopwatch();
  Stopwatch listTimer = new Stopwatch();

  Point2[] points = new Point2[TestPointCount];
  var random = new Random();
  for (int index = 0; index < TestPointCount; ++index) {
    points[index].X = random.NextDouble();
    points[index].Y = random.NextDouble();
  }

  for (int repetition = 0; repetition <= RepetitionCount; ++repetition) {
    if (repetition > 0) { // first repetition is for cache warmup
      arrayTimer.Start();
    }
    doWorkOnArray(points);
    if (repetition > 0) { // first repetition is for cache warmup
      arrayTimer.Stop();
    }

    if (repetition > 0) { // first repetition is for cache warmup
      listTimer.Start();
    }
    doWorkOnList(points);
    if (repetition > 0) { // first repetition is for cache warmup
      listTimer.Stop();
    }
  }

  Console.WriteLine("Ignore this: " + points[0].X + points[0].Y);
  Console.WriteLine(
    string.Format(
      "{0} accesses on array took {1} ms",
      RepetitionCount * TestPointCount, arrayTimer.ElapsedMilliseconds
    )
  );
  Console.WriteLine(
    string.Format(
      "{0} accesses on list took {1} ms",
      RepetitionCount * TestPointCount, listTimer.ElapsedMilliseconds
    )
  );

}

private static void doWorkOnArray(Point2[] points) {
  var random = new Random();

  int pointCount = points.Length;

  Point2 accumulated = Point2.Zero;
  for (int index = 0; index < pointCount; ++index) {
    accumulated.X += points[index].X;
    accumulated.Y += points[index].Y;
  }

  accumulated /= pointCount;

  // make use of the result somewhere so the optimizer can't eliminate the loop
  // also modify the input collection so the optimizer can merge the repetition loop
  points[random.Next(0, pointCount)] = accumulated;
}

private static void doWorkOnList(IList<Point2> points) {
  var random = new Random();

  int pointCount = points.Count;

  Point2 accumulated = Point2.Zero;
  for (int index = 0; index < pointCount; ++index) {
    accumulated.X += points[index].X;
    accumulated.Y += points[index].Y;
  }

  accumulated /= pointCount;

  // make use of the result somewhere so the optimizer can't eliminate the loop
  // also modify the input collection so the optimizer can merge the repetition loop
  points[random.Next(0, pointCount)] = accumulated;
}

0

चूंकि सूची <> आंतरिक रूप से सरणियों का उपयोग करता है, इसलिए मूल प्रदर्शन समान होना चाहिए। दो कारण, सूची थोड़ी धीमी क्यों हो सकती है:

  • सूची में एक तत्व को देखने के लिए, सूची का एक तरीका कहा जाता है, जो अंतर्निहित सरणी में दिखता है। इसलिए आपको वहां एक अतिरिक्त विधि की आवश्यकता है। दूसरी ओर संकलक इसे पहचान सकता है और "अनावश्यक" कॉल को दूर कर सकता है।
  • कंपाइलर कुछ विशेष अनुकूलन कर सकता है यदि वह सरणी के आकार को जानता है, कि वह अज्ञात लंबाई की सूची के लिए नहीं कर सकता है। यह कुछ प्रदर्शन सुधार ला सकता है यदि आपके पास केवल आपकी सूची में कुछ तत्व हैं।

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


0

चूंकि मेरे पास एक समान प्रश्न था, इससे मुझे तेज शुरुआत मिली।

मेरा सवाल थोड़ा और अधिक विशिष्ट है, 'एक प्रतिवर्ती सरणी कार्यान्वयन के लिए सबसे तेज़ तरीका क्या है'

मार्क ग्रेवेल द्वारा किया गया परीक्षण बहुत कुछ दिखाता है, लेकिन बिल्कुल समय का उपयोग नहीं करता है। उनकी टाइमिंग में एरे के साथ-साथ लूपिंग भी शामिल है। चूँकि मैं भी एक तीसरी विधि के साथ आया था जिसे मैं परीक्षण करना चाहता था, एक 'डिक्शनरी', बस तुलना करने के लिए, मैंने हिस्ट टेस्ट कोड बढ़ाया।

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

'नंगे' टाइमिंग और 'ओवरहेड इंडेडेड' टाइमिंग के बीच का अंतर मुझे 'स्ट्रक्चर एक्सेस' टाइमिंग का संकेत देता है।

लेकिन यह समय कितना सही है? परीक्षण के दौरान खिड़कियां शीर के लिए कुछ समय खिसकाएंगी। मुझे समय के बारे में कोई जानकारी नहीं है, लेकिन मुझे लगता है कि यह समान रूप से परीक्षण के दौरान और msec के दसियों के क्रम में वितरित किया गया है जिसका अर्थ है कि समय के लिए सटीकता +/- 100 मिसे या इसके क्रम में होनी चाहिए। थोड़ा मोटा अनुमान? वैसे भी एक व्यवस्थित कतरनी त्रुटि का एक स्रोत है।

इसके अलावा, परीक्षण 'डीबग' मोड में किए गए थे, जिसमें कोई इष्टतम नहीं था। अन्यथा कंपाइलर वास्तविक परीक्षण कोड को बदल सकता है।

इसलिए, मुझे दो परिणाम मिलते हैं, एक निरंतर, 'सी (') 'के लिए, और एक चिन्हित' (n) 'और' dt 'एक्सेस के लिए एक अंतर बताता है कि वास्तविक पहुँच में कितना समय लगता है।

और यह परिणाम हैं:

          Dictionary(c)/for: 1205ms (600000000)
          Dictionary(n)/for: 8046ms (589725196)
 dt = 6841

                List(c)/for: 1186ms (1189725196)
                List(n)/for: 2475ms (1779450392)
 dt = 1289

               Array(c)/for: 1019ms (600000000)
               Array(n)/for: 1266ms (589725196)
 dt = 247

 Dictionary[key](c)/foreach: 2738ms (600000000)
 Dictionary[key](n)/foreach: 10017ms (589725196)
 dt = 7279

            List(c)/foreach: 2480ms (600000000)
            List(n)/foreach: 2658ms (589725196)
 dt = 178

           Array(c)/foreach: 1300ms (600000000)
           Array(n)/foreach: 1592ms (589725196)
 dt = 292


 dt +/-.1 sec   for    foreach
 Dictionary     6.8       7.3
 List           1.3       0.2
 Array          0.2       0.3

 Same test, different system:
 dt +/- .1 sec  for    foreach
 Dictionary     14.4   12.0
       List      1.7    0.1
      Array      0.5    0.7

समय की त्रुटियों पर बेहतर अनुमान लगाने के साथ (समय की कटौती के कारण व्यवस्थित माप त्रुटि को कैसे हटाया जाए?) परिणामों के बारे में अधिक कहा जा सकता है।

ऐसा लगता है कि लिस्ट / फ़ॉरचेज़ की सबसे तेज़ पहुंच है लेकिन ओवरहेड इसे मार रहा है।

List / for और List / foreach के बीच का अंतर स्टैंग है। शायद कुछ नकदी शामिल है?

इसके अलावा, यदि आप forलूप या लूप का उपयोग करते हैं तो यह किसी ऐरे से एक्सेस के लिए कोई मायने नहीं रखता foreach। समय के परिणाम और इसकी शुद्धता परिणाम को 'तुलनीय' बनाती है।

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

यहाँ संशोधित परीक्षण कोड है।

Dictionary<int, int> dict = new Dictionary<int, int>(6000000);
List<int> list = new List<int>(6000000);
Random rand = new Random(12345);
for (int i = 0; i < 6000000; i++)
{
    int n = rand.Next(5000);
    dict.Add(i, n);
    list.Add(n);
}
int[] arr = list.ToArray();

int chk = 0;
Stopwatch watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    int len = dict.Count;
    for (int i = 0; i < len; i++)
    {
        chk += 1; // dict[i];
    }
}
watch.Stop();
long c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("         Dictionary(c)/for: {0}ms ({1})", c_dt, chk);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    int len = dict.Count;
    for (int i = 0; i < len; i++)
    {
        chk += dict[i];
    }
}
watch.Stop();
long n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("         Dictionary(n)/for: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    int len = list.Count;
    for (int i = 0; i < len; i++)
    {
        chk += 1; // list[i];
    }
}
watch.Stop();
c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("               List(c)/for: {0}ms ({1})", c_dt, chk);

watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    int len = list.Count;
    for (int i = 0; i < len; i++)
    {
        chk += list[i];
    }
}
watch.Stop();
n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("               List(n)/for: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    for (int i = 0; i < arr.Length; i++)
    {
        chk += 1; // arr[i];
    }
}
watch.Stop();
c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("              Array(c)/for: {0}ms ({1})", c_dt, chk);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    for (int i = 0; i < arr.Length; i++)
    {
        chk += arr[i];
    }
}
watch.Stop();
n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("Array(n)/for: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in dict.Keys)
    {
        chk += 1; // dict[i]; ;
    }
}
watch.Stop();
c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("Dictionary[key](c)/foreach: {0}ms ({1})", c_dt, chk);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in dict.Keys)
    {
        chk += dict[i]; ;
    }
}
watch.Stop();
n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("Dictionary[key](n)/foreach: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in list)
    {
        chk += 1; // i;
    }
}
watch.Stop();
c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("           List(c)/foreach: {0}ms ({1})", c_dt, chk);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in list)
    {
        chk += i;
    }
}
watch.Stop();
n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("           List(n)/foreach: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in arr)
    {
        chk += 1; // i;
    }
}
watch.Stop();
c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("          Array(c)/foreach: {0}ms ({1})", c_dt, chk);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in arr)
    {
        chk += i;
    }
}
watch.Stop();
n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("Array(n)/foreach: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

0

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

प्रकार: List<double[]>

समय: 00: 00: 05.1861300

प्रकार: List<List<double>>

समय: 00: 00: 05.7941351

प्रकार: double[rows * columns]

समय: 00: 00: 06.0547118

कोड चलाना:

int rows = 10000;
int columns = 10000;

IMatrix Matrix = new IMatrix(rows, columns);

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();


for (int r = 0; r < Matrix.Rows; r++)
    for (int c = 0; c < Matrix.Columns; c++)
        Matrix[r, c] = Math.E;

for (int r = 0; r < Matrix.Rows; r++)
    for (int c = 0; c < Matrix.Columns; c++)
        Matrix[r, c] *= -Math.Log(Math.E);


stopwatch.Stop();
TimeSpan ts = stopwatch.Elapsed;

Console.WriteLine(ts.ToString());

मुझे लगता है कि हम कुछ शीर्ष पायदान पर हैं हार्डवेयर त्वरित मैट्रिक्स कक्षाएं जैसे .NET टीम ने System.Numerics.Vectorsकक्षा के साथ किया है !

सी # इस क्षेत्र में थोड़ा और काम करने के साथ सबसे अच्छी एमएल भाषा हो सकती है!

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