Author Topic: Why Shorts Aren't Typically Used  (Read 1710 times)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Why Shorts Aren't Typically Used
« on: March 20, 2010, 08:18:27 PM »
Variables declared as short are not often used, at least not on x86 computers, and with perhaps good reason. The reason is a bit subtle, but let me explain. Keep in mind though, that this only really applies to x86 based computers, but that's most everything these days that we would actually call a computer.

First, here are the basic data types in C/C++:
char - 1 byte - 8 bits - unsigned range: 0..255 - signed range: -128..127
short - 2 bytes - 16 bits - unsigned range: 0..65536 - signed range: -32768..32767
int - 4 bytes - 32 bits - unsigned range: 0..4294967295 - signed range: -2147483648..2147483647

Now for the interesting subtlety. When accessing data from a 32 bit data segment the default access size is either 8 or 32 bits. Instructions have an operand size bit to distinguish between the two cases. If the data segment was 16 bit, then the default access sizes would be 8 or 16 bits. Now, if you want to do a 16 bit data access in a 32 bit segment, or a 32 bit data access in a 16 bit segment, the instruction needs an operand size prefix byte.

Since Windows runs in 32 bit mode (unless you have a 64 bit version), every time you access a "short" value, you need that operand size prefix. Thus, to both read and write (or write and then read) a short value, you've increased the size of the code segment by 2 bytes (1 for the read, 1 for the write) than if you had used an int (or a char). Since these extra bytes are in the instruction stream, they are likely to slow down processing slightly. The short of course needs 2 bytes less in the data segment than an int, but that's assuming no alignment requirements.

In reality, alignment requirements will quite likely insert padding bytes around short or char values, so 4 bytes will be used up to store any of the basic data types. It depends though on other surrounding variables. The alignment is done for speed reasons mostly. Data accesses are generally 32 bits at a time, in blocks that are aligned on 32 bit boundaries. If you want to read 8 bits (1 byte), then you read it's containing block and ignore the other bytes. If you read 16 bits (2 bytes), and both bytes fit in the same 32 bit block, it will again read 32 bits, and ignore the ones it doesn't need. But if those 2 bytes overlap two 32 bit blocks, then both blocks must be read, and then the bits of interest from each 32 bit block are combined to form the 16 bit value. The remaining 48 bits are ignored. The same thing happens for 32 bit reads if they are not perfectly aligned on a 32 bit boundary. Hence, for speed reasons, the compiler will try to avoid having variables overlap a 32 bit boundary. If you declare a char followed by an int, then 3 padding bytes will be added after the char so the int will be aligned properly. If you declare a short followed by an int, then 2 padding bytes will be added after the short so the int will align properly.

It's not all bad though, since you don't always need padding bytes to maintain alignment. For instance, if you declare two short variables followed by an int, then everything will be naturally aligned and will not need any padding bytes. (Both shorts are stored in the same 32 bit block). Similarly, you can declare two char values, followed by a short value, followed by an int, and everything will be aligned. Heck, you could even (potentially) declare a char, followed by a short, followed by a char, followed by an int, and none of them will overlap a 32 bit boundary, although, it's rare to see a short value that starts on an odd address. Usually, compilers will try to align data on a boundary the size of the data, so 1 byte values are usually byte aligned, 2 byte values are usually 2 byte aligned, and 4 byte values are usually 4 byte aligned. This tends to extend to larger data types which are now being supported more and more by CPUs.

Now on an x86 you can usually read data on non-aligned boundaries (with the slower two step read), but there are a few exceptions. Some instructions, mostly vector processing ones, expect a large pack of data aligned on a boundary the size of the pack of data, otherwise the processor raises and exception. You can also enforce this on normal data accesses, perhaps to ensure all code is constructed with proper alignment for speed reasons. The stack usually has this enforced. Hence, any variables stored on the stack will need to be properly aligned, or accessing them will caused an exception to be raised, and likely terminate your app. The stack is where all the local variables and function parameters get stored. Hence, any variables declared local to a function will have padding and alignment rules applied to them. Values passed to a function will also expand out to 4 bytes as they are pushed onto the stack.

So what does all this mean? Well, there isn't really a whole lot of good reason to use shorts in most cases. The point of a variable is to store a value, and retrieve it later for use. Since that implies at least two data accesses, you can expect the instruction stream to expand by 2 bytes or more if you use shorts, slowing down processing, and at best saving 2 bytes in a data segment, but not likely.

The major reasons for using shorts that I can see are:
1) You need to be compatible with existing code that uses shorts.
2) You have an array:
     a) That has a fair number of elements (save 2 bytes per element over using int)
     b) That isn't not accessed from many places relative to the number of elements (waste 1 byte for every access)


Of course all of this ignores other alignment factors, such as code or data segment alignment. If the data takes up just a little beyond a typical 4KB memore page, then it needs to use a whole new page, while the code segment might be far short of it's next boundary. Or perhaps you're concerned with a typical 16 byte function size alignment, so the code for loops can fit nicely in a cache line. Or perhaps you're concerned about the 512 byte alignment typically found for disk sectors, and hence the executable file on disk. OF course keeping track of all those things is somewhat hard to do, and you likely won't be aware of those values as compilers won't tell you about them under most circumstances. Besides, those concerns are statistically less likely to be an issue than the 4 byte data alignment size. In "short", it's probably best to avoid shorts.
« Last Edit: March 20, 2010, 08:19:13 PM by Hooman »