В тази статия ще разгледам различните типове масиви в C#. Разбирането на разликите между типовете масиви ще ви помогне да изберете правилната структура на данните за всеки случай.
Вижте следния код:
Методът MeasureTestA настройва триизмерен масив, преминава през всеки елемент от масива и го задава на стойността „3“.
Методът MeasureTestB прави почти същото, но вместо това използва едномерен масив и изчислява отместването на масива ръчно, като използва променливите на цикъла i, j и k.
Това не би трябвало да има значение, нали? Бихте очаквали, че триизмерните масиви в .NET използват същата логика като това, което изрично съм кодирал в MeasureTestB.
Е, нека да разгледаме:
Видяхте ли, че идва?
Кодът на 3-измерен масив в MeasureTestA е с 31% по-бавен от кода на 1-измерен масив в MeasureTestB!
Какво става тук?
Нека разберем. Ще започнем, като разгледаме кода на триизмерния масив в MeasureTestA. Вътрешният цикъл се компилира до много ефективен междинен език:
Маркираният раздел е мястото, където се случва магията. Методът Array.Set използва индексите i, j и k, за да запише стойността „3“ директно в паметта.
Това са само 6 инструкции с едно извикване на метод. Никак не е зле!
Сега нека да разгледаме вътрешния цикъл на MeasureTestB:
Сега имаме много повече междинни езикови инструкции във вътрешния цикъл. И можете ясно да видите инструкциите mul и add, които ръчно изчисляват отместването на масива от индексите i, j и k.
Но забелязахте ли, че в цикъла няма нито едно извикване на метод?
Вместо това има инструкция за stelem в IL_0033, която записва числото „3“ директно в едномерния масив.
И това е причината за разликата в производителността: .NET runtime има специализирани междинни езикови инструкции за работа с едномерни масиви!
Поради това кодът в MeasureTestB изобщо не трябва да прави извиквания на метод във вътрешния цикъл и това спестява много време.
Така че, ако работите с многоизмерни масиви в горещ цикъл във вашия код, помислете за изравняване на масивите до 1 измерение. Това ще премахне извикване на метод от тялото на вътрешния ви цикъл и ще ускори значително вашия код.
И накрая, нека да разгледаме MeasureTestC.
Този метод също използва едномерен масив, но пренебрегва индексите i, j и k и избягва изчисляването на отместването на масива. Тъй като така или иначе актуализираме масива последователно, този код просто започва от индекс нула и увеличава индекса с единица всеки път.
Ето как изглежда компилираният код:
Това е толкова ефективно, колкото ще постигнем.
Кодът използва инструкцията stelem за запис на числото „3“ директно в едномерния масив и след това увеличава индекса с единица.
Имайте предвид, че никъде няма инструкции за mul или add, защото вече не изчисляваме индекса от i, j и k.
Това ни дава малък тласък на производителността. Кодът в метод C е с 2% по-бърз от метод B.
Това просто показва колко бързи са целочислените умножения на съвременните процесори. Отърваването от 2 mul инструкции в тесен цикъл едва ли прави разлика.
На практика тази крайна оптимизация вероятно не си струва.
И така, какво мислите? Ще преработите ли кода на масива си?
Добавете коментар и ми кажете какво мислите!