Hiệu suất của Mảng so với Danh sách


191

Giả sử bạn cần có một danh sách / mảng số nguyên mà bạn cần lặp lại thường xuyên, và ý tôi là cực kỳ thường xuyên. Lý do có thể khác nhau, nhưng nói rằng nó nằm ở trung tâm của vòng lặp bên trong nhất của quá trình xử lý âm lượng lớn.

Nói chung, người ta sẽ chọn sử dụng Danh sách (Danh sách) do tính linh hoạt của chúng về kích thước. Trên hết, các khiếu nại tài liệu msDN Danh sách sử dụng một mảng bên trong và sẽ thực hiện nhanh như vậy (một cái nhìn nhanh với Reflector xác nhận điều này). Không bao giờ, có một số chi phí liên quan.

Có ai thực sự đo lường điều này? lặp đi lặp lại 6 triệu lần thông qua một danh sách có cùng thời gian với một mảng không?


3
Các vấn đề về hiệu suất sang một bên, tôi thích sử dụng Mảng hơn Danh sách cho kích thước cố định của chúng (tất nhiên trong trường hợp không cần thay đổi số lượng vật phẩm). Khi đọc mã hiện có, tôi thấy hữu ích khi nhanh chóng biết rằng một mục bị buộc phải có kích thước cố định, thay vì phải kiểm tra mã sâu hơn trong hàm.
Warren Stevens

2
T[]so với List<T>có thể làm cho một sự khác biệt hiệu suất lớn. Tôi chỉ tối ưu hóa một ứng dụng chuyên sâu vòng lặp cực kỳ (lồng nhau) để chuyển từ danh sách sang mảng trên .NET 4.0. Tôi đã mong đợi có thể cải thiện 5% đến 10% nhưng đã tăng tốc hơn 40%! Không có thay đổi nào khác ngoài việc chuyển trực tiếp từ danh sách sang mảng. Tất cả các liệt kê đã được thực hiện với các foreachtuyên bố. Dựa trên câu trả lời của Marc Gravell, có vẻ như foreachvới List<T>đặc biệt xấu.
Sốt đặc biệt

Câu trả lời:


221

Rất dễ đo ...

Trong một số ít mã xử lý vòng lặp chặt chẽ mà tôi biết độ dài được cố định, tôi sử dụng các mảng cho phần tối ưu hóa siêu nhỏ đó; mảng có thể nhanh hơn một chút nếu bạn sử dụng bộ chỉ mục / cho biểu mẫu - nhưng IIRC tin rằng nó phụ thuộc vào loại dữ liệu trong mảng. Nhưng trừ khi bạn cần tối ưu hóa vi mô, giữ cho nó đơn giản và sử dụng, List<T>v.v.

Tất nhiên, điều này chỉ áp dụng nếu bạn đang đọc tất cả dữ liệu; một từ điển sẽ nhanh hơn cho việc tra cứu dựa trên khóa.

Đây là kết quả của tôi bằng cách sử dụng "int" (số thứ hai là tổng kiểm tra để xác minh tất cả họ đã làm cùng một công việc):

(chỉnh sửa để sửa lỗi)

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

dựa trên giàn thử:

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
Chi tiết thú vị: Đây là thời gian ĐÁNG TIN CẬY / DEBUG trên giàn khoan của tôi (.net 3.5 sp1): 0.92, 0.80, 0.96, 0.93; cho tôi biết rằng có một số thông minh trong VM để tối ưu hóa vòng lặp Array / for tốt hơn khoảng 10% so với các trường hợp khác.
David Schmitt

2
Có, có tối ưu hóa JIT cho mảng / cho. Trên thực tế, tôi đã có ấn tượng rằng nó bao gồm trường hợp Độ dài (vì nó biết nó đã được sửa), do đó tại sao tôi không rút nó ra trước (không giống như Danh sách tôi đã làm). Cảm ơn các cập nhật.
Marc Gravell

2
Kỳ dị. Các thử nghiệm rất giống nhau của tôi cho thấy không có sự khác biệt đáng kể giữa for và foreach khi sử dụng mảng. Sẽ điều tra kỹ lưỡng trong một bài đăng trên blog với điểm chuẩn mà người khác có thể chạy và gửi cho tôi kết quả cho ...
Jon Skeet

1
Tôi nhận được kết quả khác nhau đáng kể nếu sử dụng một biến khác nhau cho chk cho mỗi thử nghiệm (chk1, chk2, chk3, chk4). Danh sách / for = 1303ms, Mảng / for = 433ms. Bất cứ ý tưởng tại sao?
Jon

4
Liên kết được đề cập trong nhận xét trên của Jon đến blog của Jon Skeet đã bị hỏng. Dưới đây là liên kết cập nhật. codeblog.jonskeet.uk/2009/01/29/ Mạnh
Josh DeLong

88

Tóm lược:

  • Mảng cần sử dụng:

    • Vì vậy, thường xuyên nhất có thể. Nó nhanh và chiếm phạm vi RAM nhỏ nhất cho cùng một lượng thông tin.
    • Nếu bạn biết chính xác số lượng tế bào cần thiết
    • Nếu dữ liệu được lưu trong mảng <85000 b (85000/32 = 2656 phần tử cho dữ liệu số nguyên)
    • Nếu cần tốc độ truy cập ngẫu nhiên cao
  • Danh sách cần sử dụng:

    • Nếu cần để thêm các ô vào cuối danh sách (thường)
    • Nếu cần để thêm các ô vào đầu / giữa danh sách (KHÔNG PHẢI)
    • Nếu dữ liệu được lưu trong mảng <85000 b (85000/32 = 2656 phần tử cho dữ liệu số nguyên)
    • Nếu cần tốc độ truy cập ngẫu nhiên cao
  • LinkedList cần sử dụng:

    • Nếu cần để thêm các ô vào đầu / giữa / cuối danh sách (thường)
    • Nếu chỉ cần truy cập tuần tự (tiến / lùi)
    • Nếu bạn cần lưu các mục LỚN, nhưng số lượng vật phẩm thấp.
    • Tốt hơn không nên sử dụng cho số lượng lớn các mặt hàng, vì nó sử dụng bộ nhớ bổ sung cho các liên kết.

Thêm chi tiết:

ý nghĩa màu sắc

Mảng vs Danh sách vs Danh sách liên kết

Nhiều chi tiết hơn:

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


Tôi hơi bối rối bởi khẳng định của bạn rằng danh sách trả trước tương đối nhanh nhưng chèn chậm. Chèn cũng là thời gian tuyến tính và trung bình nhanh hơn 50% so với trả trước.
Mike Marynowski

1
@MikeMarynowski trong danh sách c # là trình bao bọc xung quanh Array. Vì vậy, trong trường hợp chèn vào danh sách, bạn sẽ chỉ có thời gian tuyến tính đến một số điểm. Sau đó, hệ thống này sẽ tạo một mảng mới lớn hơn và sẽ cần thời gian để sao chép các mục từ mảng cũ.
Andrew

Điều tương tự với tiền thưởng.
Mike Marynowski

Một thao tác trả trước chỉ là một chèn tại 0. Đây là trường hợp xấu nhất về hiệu năng, vì vậy nếu chèn chậm, thì trả trước thậm chí còn chậm hơn.
Mike Marynowski

Cả chèn và trả trước là O (n) (khấu hao). Một khoản tiền đặt trước là một phần chèn, nhưng đó là phần chèn chậm nhất có thể vì nó phải di chuyển TẤT CẢ các mục trong danh sách. Một vị trí chèn vào một vị trí ngẫu nhiên chỉ phải di chuyển lên các mục có chỉ số cao hơn điểm chèn, vì vậy trung bình 50% các mục.
Mike Marynowski

26

Tôi nghĩ hiệu suất sẽ khá giống nhau. Chi phí liên quan khi sử dụng Danh sách so với Mảng là IMHO khi bạn thêm các mục vào danh sách và khi danh sách phải tăng kích thước của mảng mà nó sử dụng bên trong, khi đạt được dung lượng của mảng.

Giả sử bạn có Danh sách có Dung lượng 10, thì Danh sách sẽ tăng dung lượng khi bạn muốn thêm phần tử thứ 11. Bạn có thể giảm tác động hiệu suất bằng cách khởi tạo Công suất của danh sách theo số lượng mục sẽ giữ.

Nhưng, để tìm hiểu xem việc lặp qua Danh sách có nhanh như lặp qua một mảng không, tại sao bạn không điểm chuẩn nó?

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

Trên hệ thống của tôi; lặp đi lặp lại trên mảng mất 33msec; lặp đi lặp lại trong danh sách mất 66msec.

Thành thật mà nói, tôi đã không mong đợi rằng sự thay đổi sẽ là nhiều như vậy. Vì vậy, tôi đã đặt vòng lặp của mình trong một vòng lặp: bây giờ, tôi thực hiện cả hai lần lặp 1000 lần. Kết quả là:

Lặp lại Danh sách mất 67146 msec Lặp lại mảng mất 40821 msec

Bây giờ, biến thể không còn lớn nữa, nhưng vẫn ...

Do đó, tôi đã khởi động .NET Reflector và getter của trình lập chỉ mục của lớp List, trông như thế này:

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

Như bạn có thể thấy, khi bạn sử dụng bộ chỉ mục của Danh sách, Danh sách sẽ thực hiện kiểm tra xem bạn có đi ra khỏi giới hạn của mảng bên trong hay không. Kiểm tra bổ sung này đi kèm với một chi phí.


Xin chào Frederik, Cảm ơn! Làm thế nào bạn sẽ giải thích rằng danh sách của bạn mất gấp đôi thời gian của các mảng. Không phải những gì bạn mong đợi. Bạn đã cố gắng tăng số lượng các yếu tố?
Boaz

1
Sẽ không trả lại cái này._items [index]; đã ném một ngoại lệ nếu chỉ số nằm ngoài phạm vi? Tại sao .NET có kiểm tra thêm này khi kết quả cuối cùng giống hoặc không có nó?
John Mercier

@John Mercier kiểm tra đối chiếu với Kích thước của danh sách (số mục hiện có), khác với và có thể ít hơn dung lượng của mảng _items. Mảng được phân bổ với dung lượng vượt mức để làm cho việc thêm các mục trong tương lai nhanh hơn bằng cách không yêu cầu phân bổ lại cho mỗi lần thêm.
Trasvi

21

nếu bạn chỉ nhận được một giá trị duy nhất từ ​​một trong hai (không phải trong một vòng lặp) thì cả hai đều kiểm tra giới hạn (bạn đang nhớ mã được quản lý), đó chỉ là danh sách thực hiện hai lần. Xem các ghi chú sau để biết tại sao điều này có thể không phải là một vấn đề lớn.

Nếu bạn đang sử dụng của riêng bạn cho (int int i = 0; i <x. [Độ dài / Đếm]; i ++) thì sự khác biệt chính là như sau:

  • Mảng:
    • kiểm tra giới hạn được loại bỏ
  • Danh sách
    • kiểm tra giới hạn được thực hiện

Nếu bạn đang sử dụng foreach thì sự khác biệt chính là như sau:

  • Mảng:
    • không có đối tượng nào được phân bổ để quản lý việc lặp
    • kiểm tra giới hạn được loại bỏ
  • Danh sách thông qua một biến được gọi là Danh sách.
    • biến quản lý lặp là stack được phân bổ
    • kiểm tra giới hạn được thực hiện
  • Liệt kê thông qua một biến được biết đến là IList.
    • biến quản lý lặp được phân bổ heap
    • Kiểm tra giới hạn cũng được thực hiện Danh sách giá trị có thể không bị thay đổi trong quá trình tìm kiếm trong khi mảng có thể.

Việc kiểm tra giới hạn thường không phải là vấn đề lớn (đặc biệt nếu bạn đang ở trên một cpu có dự đoán chi tiết và đường ống sâu - tiêu chuẩn cho hầu hết những ngày này) nhưng chỉ hồ sơ của bạn mới có thể cho bạn biết nếu đó là vấn đề. Nếu bạn đang ở trong các phần của mã nơi bạn đang tránh phân bổ heap (ví dụ tốt là thư viện hoặc trong triển khai mã băm) thì đảm bảo biến được nhập vì Danh sách không IList sẽ tránh được cạm bẫy đó. Như mọi khi hồ sơ nếu nó quan trọng.


11

[ Xem thêm câu hỏi này ]

Tôi đã sửa đổi câu trả lời của Marc để sử dụng các số ngẫu nhiên thực tế và thực sự làm cùng một công việc trong mọi trường hợp.

Các kết quả:

         foreach
Mảng: 1575ms 1575ms (+ 0%)
Danh sách: 1630ms 2627ms (+ 61%)
         (+ 3%) (+ 67%)

(Tổng kiểm tra: -1000038876)

Được biên dịch dưới dạng Phát hành theo VS 2008 SP1. Chạy mà không cần gỡ lỗi trên Q6600@2.40GHz, .NET 3.5 SP1.

Mã số:

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

Điều đó thật kỳ lạ - Tôi vừa chạy mã chính xác của bạn, được xây dựng từ dòng lệnh (3.5SP1) với / o + / debug- và kết quả của tôi là: list / for: 1524; mảng / cho: 1472; danh sách / foreach: 4128; mảng / foreach: 1484.
Jon Skeet

Bạn nói rằng điều này đã được biên dịch dưới dạng phát hành - tôi có thể kiểm tra xem bạn đã chạy nó chứ không phải gỡ lỗi không? Câu hỏi ngớ ngẩn, tôi biết, nhưng tôi không thể giải thích kết quả khác ...
Jon Skeet

2

Các phép đo là tốt, nhưng bạn sẽ nhận được kết quả khác nhau đáng kể tùy thuộc vào những gì bạn đang thực hiện chính xác trong vòng lặp bên trong của bạn. Đo lường tình hình của riêng bạn. Nếu bạn đang sử dụng đa luồng, thì đó là một hoạt động không tầm thường.


2

Thật vậy, nếu bạn thực hiện một số tính toán phức tạp bên trong vòng lặp, thì hiệu suất của bộ chỉ mục mảng so với bộ chỉ mục danh sách có thể rất nhỏ, cuối cùng, điều đó không thành vấn đề.


2

Đây là một trong đó sử dụng từ điển, 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

Đừng cố thêm dung lượng bằng cách tăng số lượng phần tử.

Hiệu suất

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

Tôi nhận được thay đổi kích thước một mảng 60 nghìn lần sẽ chậm ... Chắc chắn trong cách sử dụng trong thế giới thực, cách tiếp cận sẽ chỉ là kiểm tra xem bạn cần bao nhiêu vị trí bổ sung, thay đổi kích thước của nó thành chiều dài + 60k, sau đó nén qua các phần chèn.
tobriand

Thay đổi kích thước một mảng là rất nhanh nếu bạn tăng gấp đôi kích thước mỗi lần bạn thấy bạn cần thêm dung lượng. Tôi thấy nó dường như mất khoảng thời gian để làm điều đó vì nó chỉ thay đổi kích thước một lần sau khi khai báo ban đầu. Điều đó cho phép bạn linh hoạt một danh sách và hầu hết tốc độ của một mảng.
dùng1318499

2

Tôi đã lo lắng rằng các Điểm chuẩn được đăng trong các câu trả lời khác vẫn sẽ chừa chỗ cho trình biên dịch để tối ưu hóa, loại bỏ hoặc hợp nhất các vòng lặp nên tôi đã viết một câu:

  • Được sử dụng đầu vào không thể đoán trước (ngẫu nhiên)
  • Chạy một tính toán với kết quả được in ra bàn điều khiển
  • Sửa đổi dữ liệu đầu vào mỗi lần lặp lại

Kết quả là một mảng trực tiếp có hiệu suất tốt hơn khoảng 250% so với quyền truy cập vào một mảng được bao bọc trong IList:

  • 1 tỷ lượt truy cập mảng: 4000 ms
  • 1 tỷ lượt truy cập danh sách: 10000 ms
  • 100 triệu lượt truy cập mảng: 350 ms
  • 100 triệu lượt truy cập danh sách: 1000 ms

Đây là mã:

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

Vì Danh sách <> sử dụng các mảng bên trong, nên hiệu suất cơ bản phải giống nhau. Hai lý do, tại sao Danh sách có thể chậm hơn một chút:

  • Để tra cứu một phần tử trong danh sách, một phương thức của List được gọi, nó tìm kiếm trong mảng bên dưới. Vì vậy, bạn cần một phương thức bổ sung gọi đó. Mặt khác, trình biên dịch có thể nhận ra điều này và tối ưu hóa cuộc gọi "không cần thiết".
  • Trình biên dịch có thể thực hiện một số tối ưu hóa đặc biệt nếu nó biết kích thước của mảng, mà nó không thể thực hiện cho một danh sách có độ dài không xác định. Điều này có thể mang lại một số cải tiến hiệu suất nếu bạn chỉ có một vài yếu tố trong danh sách của mình.

Để kiểm tra xem nó có tạo ra sự khác biệt nào cho bạn hay không, có lẽ nên điều chỉnh tốt nhất các chức năng thời gian được đăng thành một danh sách kích thước bạn định sử dụng và xem kết quả cho trường hợp đặc biệt của bạn như thế nào.


0

Vì tôi có một câu hỏi tương tự, điều này đã cho tôi một khởi đầu nhanh chóng.

Câu hỏi của tôi cụ thể hơn một chút, 'phương pháp nhanh nhất để thực hiện mảng phản xạ là gì'

Các thử nghiệm được thực hiện bởi Marc Gravell cho thấy rất nhiều, nhưng không chính xác thời gian truy cập. Thời gian của anh ấy bao gồm các vòng lặp trên các mảng và danh sách là tốt. Vì tôi cũng đã đưa ra một phương pháp thứ ba mà tôi muốn thử nghiệm, một 'Từ điển', chỉ để so sánh, tôi đã mở rộng mã kiểm tra lịch sử.

Fumps, tôi làm một bài kiểm tra bằng cách sử dụng một hằng số, cho tôi một thời gian nhất định bao gồm cả vòng lặp. Đây là thời gian "trần", không bao gồm quyền truy cập thực tế. Sau đó, tôi thực hiện một thử nghiệm với việc truy cập cấu trúc chủ đề, điều này mang lại cho tôi và 'bao gồm cả thời gian, vòng lặp và truy cập thực tế.

Sự khác biệt giữa thời gian 'trần' và 'thời gian không có phí' cho tôi một dấu hiệu về thời gian 'truy cập cấu trúc'.

Nhưng làm thế nào chính xác là thời gian này? Trong các cửa sổ kiểm tra sẽ làm một số thời gian cắt cho shure. Tôi không có thông tin về việc cắt thời gian nhưng tôi cho rằng nó được phân phối đều trong quá trình thử nghiệm và theo thứ tự hàng chục ms có nghĩa là độ chính xác cho thời gian phải theo thứ tự +/- 100 msec hoặc hơn. Một ước tính sơ bộ? Dù sao một nguồn của một lỗi mearure có hệ thống.

Ngoài ra, các thử nghiệm đã được thực hiện trong chế độ 'Gỡ lỗi' mà không tối ưu hóa. Nếu không, trình biên dịch có thể thay đổi mã kiểm tra thực tế.

Vì vậy, tôi nhận được hai kết quả, một cho hằng số, được đánh dấu '(c)' và một cho truy cập được đánh dấu '(n)' và sự khác biệt 'dt' cho tôi biết thời gian truy cập thực tế mất bao lâu.

Và đây là kết quả:

          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

Với ước tính tốt hơn về các lỗi thời gian (làm thế nào để loại bỏ lỗi đo lường hệ thống do cắt thời gian?) Có thể nói nhiều hơn về kết quả.

Có vẻ như List / foreach có quyền truy cập nhanh nhất nhưng chi phí đang giết chết nó.

Sự khác biệt giữa List / for và List / foreach là stange. Có thể một số tiền mặt có liên quan?

Hơn nữa, để truy cập vào một mảng, không quan trọng nếu bạn sử dụng forvòng lặp hoặc foreachvòng lặp. Kết quả thời gian và độ chính xác của nó làm cho kết quả 'có thể so sánh'.

Sử dụng từ điển là chậm nhất, tôi chỉ xem xét nó bởi vì ở phía bên trái (bộ chỉ mục) tôi có một danh sách các số nguyên thưa thớt và không phải là một phạm vi như được sử dụng trong các bài kiểm tra này.

Đây là mã kiểm tra sửa đổi.

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

Trong một số bài kiểm tra ngắn, tôi đã tìm thấy sự kết hợp của cả hai để tốt hơn trong những gì tôi sẽ gọi là Toán học chuyên sâu hợp lý:

Kiểu: List<double[]>

Thời gian: 00: 00: 05.1861300

Kiểu: List<List<double>>

Thời gian: 00: 00: 05.7941351

Kiểu: double[rows * columns]

Thời gian: 00: 00: 06.0547118

Chạy mã:

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

Tôi ước gì chúng ta có một số Lớp Ma trận Tăng tốc Phần cứng hàng đầu như Nhóm .NET đã thực hiện với System.Numerics.VectorsLớp!

C # có thể là Ngôn ngữ ML tốt nhất với nhiều công việc hơn trong lĩnh vực này!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.