В тази статия ще разгледам различните типове масиви в 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 инструкции в тесен цикъл едва ли прави разлика.

На практика тази крайна оптимизация вероятно не си струва.

И така, какво мислите? Ще преработите ли кода на масива си?

Добавете коментар и ми кажете какво мислите!