I have a questionable coding practice.
When I need to iterate through a small list of items whose count limit is under 32000
, I use Int16
for my i variable type instead of Integer
. I do this because I assume using the Int16
is more efficient than a full blown Integer
.
Am I wrong? Is there no effective performance difference between using an Int16
vs an Integer
? Should I stop using Int16
and just stick with Integer
for all my counting/iteration needs?
1. Int16 is used to represents 16-bit signed integers. Int32 is used to represents 32-bit signed integers . Int64 is used to represents 64-bit signed integers.
In theory the int16 is going to be slower than the int32 as it will be cast up to that before the comparison is made.
Use int64 and friends for data The types int8 , int16 , int32 , and int64 (and their unsigned counterparts) are best suited for data. An int64 is the typical choice when memory isn't an issue. In particular, you can use a byte , which is an alias for uint8 , to be extra clear about your intent.
The Int16 can store both types of values including negative and positive between the ranges of -32768 to +32767. Int32: This Struct is used to represents 32-bit signed integer.
Int16: This Struct is used to represents 16-bit signed integer. The Int16 can store both types of values including negative and positive between the ranges of -32768 to +32767. Int32: This Struct is used to represents 32-bit signed integer.
All of the int types here are signed integer values which have varying sizes Int16: 2 bytes Int32 and int: 4 bytes Int64 : 8 bytes
The Int32 can store both types of values including negative and positive between the ranges of -2147483648 to +2147483647. Int64: This Struct is used to represents 64-bit signed integer. The Int64 can store both types of values including negative and positive between the ranges of -9,223,372,036,854,775,808 to +9, 223,372,036,854,775,807
You should almost always use Int32
or Int64
(and, no, you do not get credit by using UInt32
or UInt64
) when looping over an array or collection by index.
The most obvious reason that it's less efficient is that all array and collection indexes found in the BCL take Int32
s, so an implicit cast is always going to happen in code that tries to use Int16
s as an index.
The less-obvious reason (and the reason that arrays take Int32
as an index) is that the CIL specification says that all operation-stack values are either Int32
or Int64
. Every time you either load or store a value to any other integer type (Byte
, SByte
, UInt16
, Int16
, UInt32
, or UInt64
), there is an implicit conversion operation involved. Unsigned types have no penalty for loading, but for storing the value, this amounts to a truncation and a possible overflow check. For the signed types every load sign-extends, and every store sign-collapses (and has a possible overflow check).
The place that this is going to hurt you most is the loop itself, not the array accesses. For example take this innocent-looking loop:
for (short i = 0; i < 32000; i++) {
...
}
Looks good, right? Nope! You can basically ignore the initialization (short i = 0
) since it only happens once, but the comparison (i<32000
) and incrementing (i++
) parts happen 32000 times. Here's some pesudo-code for what this thing looks like at the machine level:
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:
There are 3 conversions in there that are run 32000 times. And they could have been completely avoided by just using an Int32
or Int64
.
Update: As I said in the comment, I have now, in fact written a blog post on this topic, .NET Integral Data Types And You
According to the below reference, the runtime optimizes performance of Int32 and recommends them for counters and other frequently accessed operations.
From the book: MCTS Self-Paced Training Kit (Exam 70-536): Microsoft® .NET Framework 2.0—Application Development Foundation
Chapter 1: "Framework Fundamentals"
Lesson 1: "Using Value Types"
Best Practices: Optimizing performance with built-in types
The runtime optimizes the performance of 32-bit integer types (Int32 and UInt32), so use those types for counters and other frequently accessed integral variables.
For floating-point operations, Double is the most efficient type because those operations are optimized by hardware.
Also, Table 1-1 in the same section lists recommended uses for each type. Relevant to this discussion:
Int16 may actually be less efficient because the x86 instructions for word access take up more space than the instructions for dword access. It will depend on what the JIT does. But no matter what, it's almost certainly not more efficient when used as the variable in an iteration.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With