Вы должны почти всегда использовать Int32
или Int64
(и, нет, вы не получаете кредит при использовании UInt32
или UInt64
) при циклическом просмотре массива или коллекции по индексу .
Наиболее очевидная причина, по которой он менее эффективен, состоит в том, что все индексы массивов и коллекций, найденные в BCL, принимают Int32
с, поэтому неявное приведение всегда происходит в коде, который пытается использовать Int16
с в качестве индекса.
Менее очевидная причина (и причина, по которой массивы принимают Int32
в качестве индекса) состоит в том, что спецификация CIL говорит, что все значения стека операций либо Int32
или Int64
. Каждый раз, когда вы загружаете или сохраняете значение для любого другого целочисленного типа (Byte
, SByte
, UInt16
, Int16
, UInt32
или UInt64
), возникает неявная операция преобразования. Для неподписанных типов штраф за загрузку отсутствует, но для хранения значения это означает усечение и возможную проверку переполнения. Для типов со знаком каждая знак загрузки расширяется, а каждый магазин складывается (и имеет возможную проверку переполнения).
Место, из-за которого вам будет больно больше всего, - это сам цикл, а не доступ к массиву. Например, возьмем этот невинно выглядящий цикл:
for (short i = 0; i < 32000; i++) {
...
}
Хорошо выглядит, верно? Нету! Вы можете игнорировать инициализацию (short i = 0
), поскольку она происходит только один раз, но части сравнения (i<32000
) и увеличения (i++
) происходят 32000 раз. Вот некоторый псевдо-код того, как эта штука выглядит на уровне машины:
Int16 i = 0;
LOOP:
Int32 temp0 = Convert_I16_To_I32(i); // !!!
if (temp0 >= 32000) goto END;
...
Int32 temp1 = Convert_I16_To_I32(i); // !!!
Int32 temp2 = temp1 + 1;
i = Convert_I32_To_I16(temp2); // !!!
goto LOOP;
END:
Есть 3 преобразований, которые выполняются 32000 раз. И их можно было полностью избежать, просто используя Int32
или Int64
.
Обновление: Как я уже сказал в комментарии, я фактически написал сообщение в блоге на эту тему: .NET Integral Data Types And You