Dizilerin Performansları ve Listeler


194

Sık sık yinelenen bir tamsayı listesine / dizine sahip olmanız gerektiğini ve son derece sık demek istediğimi varsayalım. Sebepleri değişebilir, ancak yüksek hacimli bir işlemenin en iç döngüsünün kalbinde olduğunu söylüyor.

Genel olarak, boyutları esnekliklerinden dolayı Listeler'i (Liste) kullanmayı tercih ederim. Bunun da ötesinde, msdn dokümantasyonunda Listeler dahili olarak bir dizi kullanır ve bu kadar hızlı performans göstermelidir (Reflector'a hızlı bir bakış bunu onaylar). Yine de, bazı ek yükler söz konusudur.

Bunu gerçekten ölçen oldu mu? Bir liste aracılığıyla 6 milyon kez yineleme yapmak, bir dizi ile aynı zamanı alır mı?


3
Performans sorunları bir yana, Sabit boyutlar için Listelere göre Diziler kullanmayı tercih ediyorum (elbette öğe sayısının değiştirilmesinin gerekli olmadığı durumlarda). Mevcut kodu okurken, bir öğenin işlevde kodu daha aşağı incelemek yerine , bir öğenin sabit boyuta zorlandığını çabucak bilmek yararlı olur .
Warren Stevens

2
T[]vs List<T>büyük bir performans farkı yaratabilir. .NET 4.0'da listelerden dizilere geçmek için son derece (iç içe) döngü yoğun bir uygulamayı optimize ettim. Ben belki% 5 ila% 10 iyileşme bekliyordum ama% 40'ın üzerinde hızlanma var! Doğrudan listeden diziye geçmek dışında başka değişiklik yok. Tüm numaralandırmalar foreachifadelerle yapıldı. Marc Gravell cevabı dayanarak, benziyor foreachile List<T>özellikle kötü.
Özel Sos

Yanıtlar:


221

Ölçümü çok kolay ...

Uzunluğun sabit olduğunu bildiğim sıkı döngü işleme kodunda az sayıda mikro optimizasyon için çok küçük bir dizi için diziler kullanıyorum; dizinleri / form için kullanırsanız diziler marjinal olarak daha hızlı olabilir - ancak IIRC dizideki veri türüne bağlı olduğuna inanır. Ancak mikro optimizasyona ihtiyacınız yoksa , basit tutun ve kullanınList<T> vb.

Tabii ki, bu sadece tüm verileri okuyorsanız geçerlidir; anahtar tabanlı aramalar için sözlük daha hızlı olur.

İşte "int" kullanarak benim sonuçları (ikinci sayı hepsi aynı işi yaptığını doğrulamak için bir sağlama toplamı):

(hatayı düzeltmek için düzenlenmiştir)

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

test teçhizatı dayalı:

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
İlginç ayrıntı: İşte donanımımdaki RELEASE / DEBUG (.net 3.5 sp1): 0.92, 0.80, 0.96, 0.93; bu da VM'de Array / for döngüsünü diğer durumlardan yaklaşık% 10 daha iyi optimize etmek için bazı istihbarat olduğunu söylüyor.
David Schmitt

2
Evet, dizi / için JIT optimizasyonu var. Aslında, Uzunluk vakasını içerdiğine dair bir izlenim altındaydım (sabit olduğunu bildiğinden), bu yüzden neden ilk çıkarmadım (yaptığım listenin aksine). Güncelleme için teşekkürler.
Marc Gravell

2
Tuhaf. Çok benzer testlerim diziler kullanılırken for ile foreach arasında anlamlı bir fark olmadığını gösteriyor. Bir blog gönderisinde diğer insanların çalıştırabileceği bir ölçütle iyice araştıracak ve bana sonuçları gönderecek ...
Jon Skeet

1
Her test için chk için farklı bir değişken kullanıyorsanız (chk1, chk2, chk3, chk4) önemli ölçüde farklı sonuçlar elde ediyorum. Liste / = 1303ms, Dizi / = 433ms. Neden herhangi bir fikir?
Jon

4
Yukarıdaki yorumda Jon tarafından Jon Skeet'in bloguna bağlantı kesildi. Aşağıda güncellenen bağlantı bulunmaktadır. codeblog.jonskeet.uk/2009/01/29/…
Josh DeLong

88

Özet:

  • Dizi kullanmanız gerekir:

    • Mümkün olduğunca sık. Hızlıdır ve aynı miktarda bilgi için en küçük RAM aralığını alır.
    • Tam olarak gereken hücre sayısını biliyorsanız
    • Veri <85000 b dizisine kaydedilmişse (tamsayı verileri için 85000/32 = 2656 öğe)
    • Gerekirse yüksek Rasgele Erişim hızı
  • Liste kullanmanız gerekir:

    • Listenin sonuna hücre eklemek gerekirse (genellikle)
    • Listenin başına / ortasına hücre eklemek gerekirse (OFTEN DEĞİL)
    • Veri <85000 b dizisine kaydedilmişse (tamsayı verileri için 85000/32 = 2656 öğe)
    • Gerekirse yüksek Rasgele Erişim hızı
  • LinkedList şunları kullanmalıdır:

    • Listenin başına / ortasına / sonuna hücre eklemek gerekirse (genellikle)

    • Gerekirse yalnızca sıralı erişim (ileri / geri)

    • BÜYÜK öğeleri kaydetmeniz gerekiyorsa, ancak öğe sayısı düşükse.

    • Bağlantılar için ek bellek kullandığından, büyük miktarda öğe için kullanmayın.

      LinkedList'e ihtiyacınız olduğundan emin değilseniz - GEREKMEZ.


Daha fazla detay:

renk anlamı

Dizi vs Liste vs Bağlantılı Liste

Çok daha fazla ayrıntı:

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


Listenin başlangıcının nispeten hızlı, ancak ekleme yavaş olduğu iddiasıyla biraz kafam karıştı. Yerleştirme ayrıca doğrusal bir zamandır ve ortalamaya göre ortalama% 50 daha hızlıdır.
Mike Marynowski

1
C # listesindeki @MikeMarynowski Array'ın etrafını sarar. Listeye ekleme yapılması durumunda sadece bir noktaya kadar doğrusal zamanınız olacaktır. Bu sistem yeni bir büyük dizi oluşturacak ve eskisinden öğeleri kopyalamak için zamana ihtiyaç duyacaktır.
Andrew

Aynı şey prepends ile.
Mike Marynowski

Başa ekleme işlemi yalnızca 0'da bir kesici uçtur. Performans açısından en kötü durumda kesici uçtur, bu nedenle kesici uç yavaşsa, önayar daha da yavaştır.
Mike Marynowski

Hem ekleme hem de başa ekleme O (n) (itfa edilmiş). Başa eklenen bir ektir, ancak listedeki TÜM öğeleri bir nokta yukarı taşımak zorunda olduğu için mümkün olan en yavaş ektir. Rastgele bir konumdaki bir kesici uç yalnızca ekleme noktasından daha yüksek bir dizinde olan öğeleri yukarı taşımak zorundadır, bu nedenle ortalama olarak öğelerin% 50'si.
Mike Marynowski

26

Performansın oldukça benzer olacağını düşünüyorum. Bir Listeye karşı bir Array kullanırken kullanılan ek yük, listeye öğe eklediğinizde IMHO ve listenin dahili olarak kullandığı dizinin boyutunu artırması gerektiğinde, dizinin kapasitesine ulaşıldığında.

10 Kapasiteye sahip bir Listeniz olduğunu varsayalım, ardından 11. öğeyi eklemek istediğinizde Liste kapasitesini artıracaktır. Listenin Kapasitesini tutacağı öğe sayısına başlatarak performans etkisini azaltabilirsiniz.

Ancak, bir Liste üzerinden yineleme işleminin bir dizi üzerinden yineleme yapmak kadar hızlı olup olmadığını anlamak için neden karşılaştırmıyorsunuz?

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();

Sistemimde; dizi üzerinden yineleme 33 msn sürdü; liste üzerinden yineleme 66 msn sürdü.

Dürüst olmak gerekirse, varyasyonun bu kadar olacağını ummamıştım. Yani, yinelememi bir döngüye koydum: şimdi, her iki yinelemeyi 1000 kez yürütüyorum. Sonuçlar:

Liste yineleme 67146 msn aldı dizinin yineleme 40821 msn aldı

Şimdi, varyasyon artık o kadar büyük değil, ama yine de ...

Bu nedenle, .NET Reflector'ı başlattım ve List sınıfının dizinleyicisinin alıcısı şöyle görünüyor:

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

Gördüğünüz gibi, List'in dizinleyicisini kullandığınızda Liste, dahili dizinin sınırlarının dışına çıkıp çıkmadığınızı kontrol eder. Bu ek kontrolün bir maliyeti vardır.


Merhaba Frederik, Teşekkürler! Listenizin dizilerin iki katı zaman aldığını nasıl açıklarsınız? Ne beklediğiniz değil. Eleman sayısını artırmaya çalıştınız mı?
Boaz

1
This._items [dizin] döndürmez; dizin aralık dışındaysa bir istisna atar mı? Nihai sonuç aynı veya onsuz aynı olduğunda neden .NET bu ek denetimi yapar?
John Mercier

@John Mercier, kontrol _items dizisinin kapasitesinden farklı ve muhtemelen daha az olan listenin Boyutuna (şu anda içerilen öğe sayısı) karşıdır. Dizi, her bir ekleme için yeniden tahsis edilmesine gerek kalmadan gelecekteki öğelerin daha hızlı eklenmesi için aşırı kapasiteye ayrılmıştır.
Trasvi

21

eğer sadece bir döngüden tek bir değer alıyorsanız (bir döngüde değil) o zaman her ikisi de sınırları kontrol eder (yönetilen kod hatırlıyorsunuz demektir) sadece liste iki kez yapar. Bunun neden büyük bir olasılık olmadığını öğrenmek için daha sonra notlara bakın.

(İnt int i = 0; i <x. [Uzunluk / Sayım]; i ++) için kendi başınızı kullanıyorsanız, temel fark aşağıdaki gibidir:

  • Dizi:
    • sınır kontrolü kaldırıldı
  • Listeler
    • sınır kontrolü yapılır

Her foreach kullanıyorsanız, temel fark aşağıdaki gibidir:

  • Dizi:
    • yinelemeyi yönetmek için hiçbir nesne tahsis edilmemiştir
    • sınır kontrolü kaldırıldı
  • Liste olarak bilinen bir değişken üzerinden liste.
    • yineleme yönetimi değişkeni yığın tahsis edilir
    • sınır kontrolü yapılır
  • IList olduğu bilinen bir değişken üzerinden liste.
    • yineleme yönetimi değişkeni yığın olarak ayrılır
    • sınır kontrolü de yapılır. Listenin değerleri foreach sırasında değiştirilemezken diziler değiştirilebilir.

Sınır kontrolü genellikle önemli değildir (özellikle derin bir boru hattı ve dal tahmini ile bir cpu üzerindeyseniz - bu günlerin çoğu için norm), ancak bunun yalnızca bir profiliniz size bir sorun olup olmadığını söyleyebilir. Kodunuzun yığın ayırmalarından kaçındığınız kısımlarındaysanız (iyi örnekler kitaplıklar veya hashcode uygulamalarında), değişkenin IList değil List olarak yazıldığından emin olmak bu tuzaktan kaçınacaktır. Her zaman olduğu gibi önemli.


11

[ Ayrıca bu soruya bakın ]

Marc'ın cevabını gerçek rastgele sayıları kullanacak şekilde değiştirdim ve aslında her durumda aynı işi yaptım.

Sonuçlar:

         foreach için
Dizi: 1575ms 1575ms (+% 0)
Liste: 1630ms 2627ms (+% 61)
         (+% 3) (+% 67)

(Sağlama toplamı: -1000038876)

VS 2008 SP1 altında Sürüm olarak derlenmiştir. Q6600@2.40GHz, .NET 3.5 SP1'de hata ayıklamadan çalışıyor.

Kod:

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();
    }
}

Bu garip - Komut satırından (3.5SP1) / o + / debug- ile oluşturulan tam kodunuzu çalıştırıyorum ve sonuçlarım: list / for: 1524; dizi / için: 1472; Liste / Foreach: 4128; dizi / foreach: 1484.
Jon Skeet

Bunun sürüm olarak derlendiğini söylüyorsunuz - hata ayıklamak yerine çalıştırdığınızı kontrol edebilir miyim? Aptalca bir soru, biliyorum, ama sonuçları başka türlü açıklayamam ...
Jon Skeet

2

Ölçümler güzel, ancak iç döngünüzde tam olarak ne yaptığınıza bağlı olarak önemli ölçüde farklı sonuçlar elde edeceksiniz. Kendi durumunuzu ölçün. Çoklu iş parçacığı kullanıyorsanız, bu tek başına önemsiz bir etkinliktir.


2

Gerçekten de, döngü içinde bazı karmaşık hesaplamalar yaparsanız, dizi indeksleyicisinin liste indeksleyicisine göre performansı o kadar küçük olabilir ki, sonunda farketmez.


2

İşte Sözlükler, IEnumerable kullanan biri:

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

Eleman sayısını artırarak kapasite eklemeye çalışmayın.

Verim

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 kez yavaş yavaş olacak bir dizi yeniden boyutlandırma olsun ... Elbette, gerçek dünya kullanımında, sadece kaç ekstra yuva ihtiyacınız olduğunu kontrol etmek, uzunluğu + 60k yeniden boyutlandırmak ve daha sonra ekler zip olacaktır.
tobriand

Daha fazla alana ihtiyacınız olduğunu her bulduğunuzda boyutu iki katına çıkarırsanız bir diziyi yeniden boyutlandırmak çok hızlıdır. İlk bildirimden sonra sadece bir kez yeniden boyutlandırdığı için bunu yapmakla aynı zamanın geldiğini gördüm. Bu size bir listenin esnekliğini ve bir dizinin hızının çoğunu verir.
user1318499

2

Diğer cevaplarda yayınlanan Benchmarks hala derleyici optimizasyon, ortadan kaldırmak veya birleştirmek için yer bırakacağından endişeliydim, bu yüzden yazdım:

  • Kullanılan öngörülemeyen girişler (rastgele)
  • Hesaplanan sonucu konsola yazdırılmış olarak çalıştırır
  • Her tekrarda giriş verilerini değiştirir

Doğrudan bir dizinin, bir IList'e sarılmış bir diziye erişimden yaklaşık% 250 daha iyi performansa sahip olması sonucu:

  • 1 milyar dizi erişim: 4000 ms
  • 1 milyar liste erişimi: 10000 ms
  • 100 milyon dizi erişim: 350 ms
  • 100 milyon liste erişimi: 1000 ms

İşte kod:

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

Liste <> dizileri dahili olarak kullandığından, temel performans aynı olmalıdır. Listenin neden biraz daha yavaş olabileceğinin iki nedeni:

  • Listedeki bir öğeyi aramak için alttaki dizide arama yapan bir Liste yöntemi çağrılır. Bu yüzden orada ek bir yöntem çağrısı gerekir. Öte yandan derleyici bunu tanıyabilir ve "gereksiz" çağrıyı optimize edebilir.
  • Derleyici, dizinin boyutunu biliyorsa, bilinmeyen uzunlukta bir liste için yapamayacağı bazı özel optimizasyonlar yapabilir. Listenizde yalnızca birkaç öğe varsa, bu durum bazı performans iyileştirmeleri getirebilir.

Sizin için herhangi bir fark yaratıp yaratmadığını kontrol etmek için, yayınlanan zamanlama işlevlerini kullanmayı planladığınız boyut listesine göre ayarlamak ve özel durumunuz için sonuçların nasıl olduğunu görmek en iyisidir.


0

Benzer bir sorum olduğu için bu bana hızlı bir başlangıç ​​yaptı.

Benim sorum biraz daha spesifik, 'refleksif dizi uygulaması için en hızlı yöntem nedir'

Marc Gravell tarafından yapılan test çok şey gösteriyor, ancak tam olarak zamanlamaya erişmiyor. Onun zamanlaması dizi ve listeler üzerinde döngü de içerir. Ayrıca test etmek istediğim üçüncü bir yöntem bulduğumdan, sadece karşılaştırmak için bir 'Sözlük', test kodunu uzattım.

Firts, bir döngü kullanarak bir test yapıyorum, bu da bana döngü dahil belirli bir zamanlama veriyor. Bu, gerçek erişim hariç 'çıplak' bir zamanlamadır. Sonra konu yapısına erişerek bir test yapıyorum, bu bana ve 'genel gider' zamanlaması, döngü ve gerçek erişim sağlıyor.

'Çıplak' zamanlama ve 'baş üstü kapalı' zamanlama arasındaki fark bana 'yapı erişimi' zamanlamasının bir göstergesini veriyor.

Ancak bu zamanlama ne kadar doğrudur? Test sırasında pencereler shure için biraz dilimleme yapar. Zaman dilimleme hakkında hiçbir bilgim yok, ancak test sırasında ve onlarca msn sırasına eşit olarak dağıtıldığını varsayıyorum, bu da zamanlamanın doğruluğunun +/- 100 msn kadar olması gerektiği anlamına geliyor. Biraz kaba bir tahmin mi? Her neyse, sistematik bir ölçüm hatası kaynağı.

Ayrıca, testler hiçbir optimizasyon olmadan 'Hata Ayıklama' modunda yapıldı. Aksi takdirde derleyici gerçek test kodunu değiştirebilir.

Böylece, biri sabit, '(c)' olarak işaretlenmiş ve '' n '' olarak işaretlenmiş erişim için iki sonuç alıyorum ve 'dt' farkı bana gerçek erişimin ne kadar zaman aldığını anlatıyor.

İşte sonuçlar:

          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

Zamanlama hataları hakkında daha iyi tahminlerle (zaman dilimlemeden dolayı sistematik ölçüm hatasının nasıl kaldırılacağı?) Sonuçlar hakkında daha fazla şey söylenebilir.

List / foreach en hızlı erişime sahip gibi görünüyor, ancak ek yükü öldürüyor.

List / for ve List / foreach arasındaki fark sabittir. Belki biraz para kazanılır?

Ayrıca, bir diziye erişim için bir fordöngü veya döngü kullanmanız önemli değildir foreach. Zamanlama sonuçları ve kesinliği sonuçları 'karşılaştırılabilir' hale getirir.

Bir sözlük kullanmak çok yavaş, ben sadece bunu düşündüm çünkü sol tarafta (dizinleyici) ben tamsayıların bir seyrek listesi var ve bu testlerde kullanılan bir aralık değil.

İşte değiştirilmiş test kodu.

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

Bazı kısa testlerde, ikisinin bir kombinasyonunu makul derecede yoğun Matematik dediğim şeyde daha iyi olmak için buldum:

Tür: List<double[]>

Zaman: 00: 00: 05.1861300

Tür: List<List<double>>

Zaman: 00: 00: 05.7941351

Tür: double[rows * columns]

Zaman: 00: 00: 06.0547118

Kodu Çalıştırma:

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());

Keşke .NET Ekibi Sınıfı gibi bazı üst düzey Donanım Hızlandırılmış Matris Sınıfları olsaydı System.Numerics.Vectors!

C # bu alanda biraz daha fazla çalışma ile en iyi ML Dili olabilir!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.