Kinerja Array vs. Daftar

194

Katakanlah Anda perlu memiliki daftar / array bilangan bulat yang sering Anda perlukan, dan maksud saya sangat sering. Alasannya mungkin berbeda-beda, tetapi katakanlah itu berada di jantung lingkaran paling dalam dari pemrosesan volume tinggi.

Secara umum, orang akan memilih untuk menggunakan Daftar (Daftar) karena fleksibilitas ukurannya. Selain itu, dokumentasi msdn mengklaim Daftar menggunakan array secara internal dan harus bekerja sama cepatnya (tampilan cepat dengan Reflector mengonfirmasi hal ini). Namun demikian, ada beberapa overhead yang terlibat.

Apakah ada yang benar-benar mengukur ini? akan mengulangi 6M kali melalui daftar akan sama dengan array?

Boas
sumber
3
Selain masalah kinerja, saya lebih suka penggunaan Array dari Daftar karena ukurannya tetap (dalam kasus di mana mengubah jumlah item tidak diperlukan, tentu saja). Saat membaca kode yang ada, saya merasa terbantu dengan cepat mengetahui bahwa suatu item dipaksa memiliki ukuran yang tetap, daripada harus memeriksa kode lebih jauh dalam fungsi.
Warren Stevens
2
T[]vs. List<T>dapat membuat perbedaan kinerja yang besar. Saya baru saja mengoptimalkan aplikasi intensif yang sangat (bersarang) untuk berpindah dari daftar ke array di .NET 4.0. Saya mengharapkan peningkatan 5% hingga 10% tetapi mendapatkan lebih dari 40% peningkatan! Tidak ada perubahan lain selain pindah langsung dari daftar ke array. Semua enumerasi dilakukan dengan foreachpernyataan. Berdasarkan jawaban Marc Gravell, sepertinya foreachdengan List<T>sangat buruk.
Saus Spesial

Jawaban:

221

Sangat mudah diukur ...

Dalam sejumlah kecil kode pemrosesan loop ketat di mana saya tahu panjangnya diperbaiki, saya menggunakan array untuk sedikit tambahan mikro-optimasi; array bisa sedikit lebih cepat jika Anda menggunakan pengindeks / untuk formulir - tetapi IIRC percaya itu tergantung pada jenis data dalam array. Tetapi kecuali Anda perlu mengoptimalkan mikro, tetap sederhana dan gunakanList<T> dll.

Tentu saja, ini hanya berlaku jika Anda membaca semua data; kamus akan lebih cepat untuk pencarian berbasis kunci.

Inilah hasil saya menggunakan "int" (angka kedua adalah sebuah checksum untuk memverifikasi mereka semua melakukan pekerjaan yang sama):

(diedit untuk memperbaiki bug)

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

berdasarkan rig uji:

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();
    }
}
Marc Gravell
sumber
8
Detail menarik: Inilah saatnya RELEASE / DEBUG di rig saya (.net 3.5 sp1): 0.92, 0.80, 0.96, 0.93; yang memberitahu saya bahwa ada beberapa kecerdasan dalam VM untuk mengoptimalkan Array / for loop sekitar 10% lebih baik daripada kasus lainnya.
David Schmitt
2
Ya, ada optimasi JIT untuk array / untuk. Sebenarnya, saya mendapat kesan bahwa itu termasuk kasus Panjang (karena ia tahu itu diperbaiki), maka mengapa saya tidak menariknya dulu (tidak seperti Daftar tempat saya melakukannya). Terima kasih atas pembaruannya.
Marc Gravell
2
Aneh. Tes saya yang sangat mirip tidak menunjukkan perbedaan signifikan antara for dan foreach saat menggunakan array. Akan menyelidiki secara menyeluruh dalam sebuah posting blog dengan patokan yang orang lain dapat menjalankan dan mengirim hasil untuk ...
Jon Skeet
1
Saya mendapatkan hasil yang sangat berbeda jika menggunakan variabel yang berbeda untuk chk untuk setiap tes (chk1, chk2, chk3, chk4). Daftar / untuk = 1303ms, Array / untuk = 433ms. Ada ide mengapa?
Jon
4
Tautan yang disebutkan dalam komentar di atas oleh Jon ke blog Jon Skeet rusak. Di bawah ini adalah tautan yang diperbarui. codeblog.jonskeet.uk/2009/01/29/…
Josh DeLong
88

Ringkasan:

  • Array perlu digunakan:

    • Sesering mungkin. Cepat dan membutuhkan rentang RAM terkecil untuk informasi jumlah yang sama.
    • Jika Anda tahu jumlah pasti sel yang dibutuhkan
    • Jika data disimpan dalam array <85000 b (85000/32 = 2656 elemen untuk data integer)
    • Jika diperlukan kecepatan Akses Acak tinggi
  • Daftar harus digunakan:

    • Jika perlu menambahkan sel ke akhir daftar (sering)
    • Jika perlu menambahkan sel di awal / tengah daftar (BUKAN SERING)
    • Jika data disimpan dalam array <85000 b (85000/32 = 2656 elemen untuk data integer)
    • Jika diperlukan kecepatan Akses Acak tinggi
  • LinkedList perlu menggunakan:

    • Jika perlu menambahkan sel di awal / tengah / akhir daftar (sering)

    • Jika diperlukan hanya akses sekuensial (maju / mundur)

    • Jika Anda perlu menyimpan item BESAR, tetapi jumlah item rendah.

    • Lebih baik tidak menggunakan item dalam jumlah besar, karena menggunakan memori tambahan untuk tautan.

      Jika Anda tidak yakin bahwa Anda memerlukan LinkedList - ANDA TIDAK PERLU.


Keterangan lebih lanjut:

arti warna

Array vs Daftar vs Daftar Tertaut

Lebih banyak detail:

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

Andrew
sumber
Saya sedikit bingung dengan pernyataan Anda bahwa daftar prepend relatif cepat tetapi penyisipan lambat. Penyisipan juga merupakan waktu linier, dan lebih cepat rata-rata 50% daripada prepend.
Mike Marynowski
1
@MikeMarynowski dalam daftar c # adalah pembungkus di sekitar Array. Jadi dalam hal penyisipan ke daftar, Anda akan memiliki waktu linier hanya untuk beberapa titik. Setelah sistem ini akan membuat array baru yang lebih besar dan perlu waktu untuk menyalin item dari yang lama.
Andrew
Hal yang sama dengan prepends.
Mike Marynowski
Operasi prepend hanya sebuah sisipan pada 0. Ini adalah penyisipan kasus terburuk dalam hal kinerja, jadi jika penyisipan lambat, prepend bahkan lebih lambat.
Mike Marynowski
Baik insert maupun prepend adalah O (n) (diamortisasi). Sebuah prepend ADALAH sebuah sisipan, tetapi itu adalah insert yang paling lambat karena ia harus memindahkan SEMUA item dalam daftar satu tempat di atas. Sisipan di lokasi acak hanya harus memindahkan item yang berada di indeks lebih tinggi dari titik penyisipan, jadi 50% dari item rata-rata.
Mike Marynowski
26

Saya pikir kinerjanya akan sangat mirip. Overhead yang terlibat saat menggunakan Daftar vs Array adalah, IMHO ketika Anda menambahkan item ke daftar, dan ketika daftar harus meningkatkan ukuran array yang digunakan secara internal, ketika kapasitas array tercapai.

Misalkan Anda memiliki Daftar dengan Kapasitas 10, maka Daftar akan meningkatkan kapasitasnya setelah Anda ingin menambahkan elemen ke-11. Anda dapat mengurangi dampak kinerja dengan menginisialisasi Kapasitas daftar ke jumlah item yang akan dipegang.

Tetapi, untuk mencari tahu apakah iterasi pada Daftar adalah secepat iterasi pada array, mengapa Anda tidak membandingkannya?

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

Di sistem saya; iterating melalui array mengambil 33msec; iterating atas daftar butuh 66msec.

Sejujurnya, saya tidak berharap bahwa variasinya akan sebesar itu. Jadi, saya telah menempatkan iterasi saya dalam satu lingkaran: sekarang, saya menjalankan iterasi 1000 kali. Hasilnya adalah:

iterating Daftar mengambil 67146 msec iterating array mengambil 40821 msec

Sekarang, variasinya tidak terlalu besar lagi, tetapi masih ...

Oleh karena itu, saya telah memulai .NET Reflector, dan pengambil indekser dari kelas Daftar, terlihat seperti ini:

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

Seperti yang Anda lihat, ketika Anda menggunakan pengindeks Daftar, Daftar melakukan pemeriksaan apakah Anda tidak akan keluar dari batas array internal. Pemeriksaan tambahan ini disertai dengan biaya.

Frederik Gheysels
sumber
Hai Frederik, Terima kasih! Bagaimana Anda menjelaskan bahwa daftar Anda membutuhkan waktu dua kali lipat dari array. Bukan yang Anda harapkan. Apakah Anda mencoba menambah jumlah elemen?
Boaz
1
Tidak akan mengembalikan ini._item [indeks]; sudah melempar pengecualian jika indeksnya di luar jangkauan? Mengapa .NET melakukan pemeriksaan tambahan ini ketika hasil akhirnya sama dengan atau tanpa itu?
John Mercier
@ John Mercier, cek tersebut bertentangan dengan Ukuran daftar (jumlah item yang saat ini terkandung), yang berbeda dengan dan mungkin kurang dari kapasitas array _items. Array dialokasikan dengan kapasitas berlebih untuk membuat penambahan item di masa mendatang lebih cepat dengan tidak memerlukan alokasi ulang untuk setiap penambahan.
Trasvi
21

jika Anda hanya mendapatkan nilai tunggal dari salah satu (tidak dalam satu lingkaran) maka keduanya melakukan pengecekan batas (Anda berada dalam kode yang dikelola ingat) itu hanya daftar melakukannya dua kali. Lihat catatannya nanti untuk mengapa ini mungkin bukan masalah besar.

Jika Anda menggunakan milik Anda untuk (int int = 0; i <x. [Panjang / Hitung]; i ++) maka perbedaan utama adalah sebagai berikut:

  • Himpunan:
    • pemeriksaan batas dihapus
  • Daftar
    • pemeriksaan batas dilakukan

Jika Anda menggunakan foreach maka perbedaan utama adalah sebagai berikut:

  • Himpunan:
    • tidak ada objek yang dialokasikan untuk mengelola iterasi
    • pemeriksaan batas dihapus
  • Daftar melalui variabel yang dikenal sebagai Daftar.
    • variabel manajemen iterasi adalah stack yang dialokasikan
    • pemeriksaan batas dilakukan
  • Daftar melalui variabel yang dikenal sebagai IList.
    • variabel manajemen iterasi dialokasikan tumpukan
    • batas memeriksa dilakukan juga Nilai daftar mungkin tidak diubah selama foreach sedangkan array bisa.

Pengecekan batas seringkali bukan masalah besar (terutama jika Anda menggunakan CPU dengan prediksi mendalam tentang pipa dan cabang - norma untuk sebagian besar hari ini) tetapi hanya profil Anda sendiri yang dapat memberi tahu Anda jika itu merupakan masalah. Jika Anda berada di bagian kode Anda di mana Anda menghindari alokasi tumpukan (contoh yang baik adalah perpustakaan atau dalam implementasi kode hash) maka memastikan variabel diketik sebagai Daftar tidak IList akan menghindari perangkap itu. Seperti biasa profil jika itu penting.

ShuggyCoUk
sumber
11

[ Lihat juga pertanyaan ini ]

Saya telah memodifikasi jawaban Marc untuk menggunakan angka acak aktual dan benar-benar melakukan pekerjaan yang sama dalam semua kasus.

Hasil:

         untuk foreach
Array: 1575ms 1575ms (+ 0%)
Daftar: 1630ms 2627ms (+ 61%)
         (+ 3%) (+ 67%)

(Checksum: -1000038876)

Disusun sebagai Rilis di bawah VS 2008 SP1. Berjalan tanpa debugging pada [email protected], .NET 3.5 SP1.

Kode:

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();
    }
}
David Schmitt
sumber
Aneh - saya baru saja menjalankan kode persis Anda, dibangun dari command line (3.5SP1) dengan / o + / debug- dan hasil saya adalah: daftar / untuk: 1524; array / untuk: 1472; daftar / pendahuluan: 4128; array / foreach: 1484.
Jon Skeet
Anda mengatakan ini dikompilasi sebagai rilis - dapatkah saya memeriksa apakah Anda menjalankannya dan bukan men-debug? Pertanyaan konyol, aku tahu, tapi aku tidak bisa menjelaskan hasil sebaliknya ...
Jon Skeet
2

Pengukurannya bagus, tetapi Anda akan mendapatkan hasil yang sangat berbeda tergantung pada apa yang Anda lakukan persis di lingkaran dalam Anda. Ukur situasi Anda sendiri. Jika Anda menggunakan multi-threading, itu saja adalah aktivitas non-sepele.

Stephan Eggermont
sumber
2

Memang, jika Anda melakukan beberapa perhitungan kompleks di dalam loop, maka kinerja pengindeks array versus pengindeks daftar mungkin sangat kecil, yang pada akhirnya, itu tidak masalah.

Frederik Gheysels
sumber
2

Inilah yang menggunakan Kamus, 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();
    }
}
Travis
sumber
2

Jangan mencoba menambah kapasitas dengan menambah jumlah elemen.

Performa

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
Fatih GÜRDAL
sumber
Saya mendapatkan ukuran array 60k kali akan menjadi lambat ... Tentunya dalam penggunaan dunia nyata, pendekatannya hanya untuk memeriksa berapa banyak slot tambahan yang Anda butuhkan, mengubah ukurannya menjadi panjang + 60k, dan kemudian masukkan melalui sisipan.
tobriand
Mengubah ukuran array sangat cepat jika Anda menggandakan ukuran setiap kali Anda membutuhkan lebih banyak ruang. Saya menemukan bahwa hal itu membutuhkan waktu yang hampir bersamaan karena hanya mengubah ukurannya sekali setelah deklarasi awal. Itu memberi Anda fleksibilitas daftar dan sebagian besar kecepatan array.
user1318499
2

Saya khawatir bahwa tolok ukur yang diposting di jawaban lain masih akan meninggalkan ruang bagi kompiler untuk mengoptimalkan, menghilangkan atau menggabungkan loop jadi saya menulis satu yang:

  • Digunakan input yang tidak dapat diprediksi (acak)
  • Menjalankan perhitungan dengan hasil yang dicetak ke konsol
  • Memodifikasi data input setiap pengulangan

Hasilnya, array langsung memiliki kinerja sekitar 250% lebih baik daripada akses ke array yang dibungkus dengan IList:

  • 1 miliar akses array: 4000 ms
  • Akses 1 miliar daftar: 10.000 ms
  • 100 juta akses array: 350 ms
  • Akses 100 juta daftar: 1000 ms

Berikut kodenya:

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;
}
Cygon
sumber
0

Karena Daftar <> menggunakan array secara internal, kinerja dasar harus sama. Dua alasan, mengapa Daftar mungkin sedikit lebih lambat:

  • Untuk mencari elemen dalam daftar, metode Daftar dipanggil, yang melakukan pencarian dalam array yang mendasarinya. Jadi Anda perlu pemanggilan metode tambahan di sana. Di sisi lain kompiler mungkin mengenali ini dan mengoptimalkan panggilan "tidak perlu".
  • Compiler mungkin melakukan beberapa optimasi khusus jika ia mengetahui ukuran array, yang tidak dapat dilakukan untuk daftar panjang yang tidak diketahui. Ini mungkin membawa beberapa peningkatan kinerja jika Anda hanya memiliki beberapa elemen dalam daftar Anda.

Untuk memeriksa apakah ada bedanya untuk Anda, mungkin lebih baik sesuaikan fungsi waktu yang diposkan ke daftar ukuran yang Anda rencanakan untuk digunakan dan lihat bagaimana hasilnya untuk kasing khusus Anda.

sth
sumber
0

Karena saya memiliki pertanyaan serupa, ini membuat saya memulai dengan cepat.

Pertanyaan saya sedikit lebih spesifik, 'apa metode tercepat untuk implementasi array refleksif'

Pengujian yang dilakukan oleh Marc Gravell menunjukkan banyak, tetapi tidak tepat waktu akses. Waktunya termasuk perulangan di array dan daftar juga. Karena saya juga menemukan metode ketiga yang ingin saya uji, 'Kamus', hanya untuk membandingkan, saya menambah kode tes hist.

Pertama, saya melakukan tes menggunakan konstanta, yang memberi saya waktu tertentu termasuk loop. Ini adalah waktu yang 'telanjang', tidak termasuk akses yang sebenarnya. Kemudian saya melakukan tes dengan mengakses struktur subjek, ini memberi saya dan waktu, termasuk perulangan dan akses aktual.

Perbedaan antara timing 'bare' dan timing 'overhead induced' memberi saya indikasi waktu 'struktur akses'.

Tetapi seberapa akurat waktu ini? Selama pengujian windows akan melakukan beberapa waktu mengiris untuk shure. Saya tidak punya informasi tentang waktu mengiris tetapi saya berasumsi itu didistribusikan secara merata selama pengujian dan dalam urutan puluhan msec yang berarti bahwa ketepatan waktu harus dalam urutan +/- 100 msec atau lebih. Perkiraan yang agak kasar? Pokoknya sumber kesalahan mearure sistematis.

Juga, tes dilakukan dalam mode 'Debug' tanpa optimalisasi. Kalau tidak, kompiler dapat mengubah kode tes yang sebenarnya.

Jadi, saya mendapatkan dua hasil, satu untuk konstanta, bertanda '(c)', dan satu untuk akses ditandai '(n)' dan perbedaannya 'dt' memberi tahu saya berapa banyak waktu yang dibutuhkan akses aktual.

Dan ini hasilnya:

          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

Dengan perkiraan yang lebih baik pada kesalahan waktu (bagaimana menghapus kesalahan pengukuran sistematis karena waktu mengiris?) Lebih banyak yang bisa dikatakan tentang hasilnya.

Sepertinya List / foreach memiliki akses tercepat tetapi overhead membunuhnya.

Perbedaan antara Daftar / untuk dan Daftar / foreach adalah stange. Mungkin ada uang tunai yang terlibat?

Lebih lanjut, untuk akses ke array tidak masalah jika Anda menggunakan forloop atau foreachloop. Hasil waktu dan keakuratannya membuat hasil 'kompatibel'.

Menggunakan kamus adalah yang paling lambat, saya hanya mempertimbangkannya karena di sisi kiri (pengindeks) saya memiliki daftar integer yang jarang dan bukan rentang seperti yang digunakan dalam tes ini.

Berikut adalah kode tes yang dimodifikasi.

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);
PapaAtHome
sumber
0

Dalam beberapa tes singkat saya menemukan kombinasi keduanya lebih baik dalam apa yang saya sebut Matematika intensif:

Tipe: List<double[]>

Waktu: 00: 00: 05.1861300

Tipe: List<List<double>>

Waktu: 00: 00: 05.7941351

Tipe: double[rows * columns]

Waktu: 00: 00: 06.0547118

Menjalankan Kode:

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

Saya berharap kami memiliki beberapa Kelas Matriks Akselerasi Hardware Terkemuka seperti yang dilakukan oleh Tim NET dengan System.Numerics.VectorsKelas!

C # bisa menjadi Bahasa ML terbaik dengan sedikit lebih banyak pekerjaan di bidang ini!

paku berkarat
sumber