First, here is what I understand and think what is true for the question.
Use fast data types for single variables like counters or for
loop indexes. For example:
#define LOOP_COUNT (100U)
uint_fast8_t index;
for(index = 0; index < LOOP_COUNT; index++){
/* Do something */
}
I suppose the most suitable type in here is uint_fast8_t
since index
can never exceed 255 and this will be the fastest implementation for all platforms. If I used unsigned int
instead, it will be fastest in >=16 bits platforms but will be slower in <16 bits platforms as int
is 16 bits minimum by standard. Also, if I used uint8_t
it will be slower on >8 bits platforms as the compiler adds an AND 0xFF
instruction to check overflow for each increment (my ARM7 compiler does that even on full speed optimization). size_t
is also not an option since it can be bigger than the native integer size.
Bad side (?) of this if an overflow is expected for 8 bits, it is not gonna happen. Programmer should check for overflow manually (as he/she should IMHO), which may result in buggy code if forgotten. Also, compiler (and even PC-Lint to my surprise) will not give any warning/issue if LOOP_COUNT "accidentally" set to a value bigger than 255 on >8 bits platforms, but the warning will be generated on an 8 bits platform, which will reduce portability and introduce bugs, but this can be avoided with #if
checks.
Use least data types as much as possible if memory usage is of concern like in arrays or structures. For example:
uint_least8_t array[100];
It is the most portable and efficient way to declare arrays if memory usage is of concern. This type will give a byte array if byte access is possible on the platform, and give the smallest accessible width integer array otherwise. Also, least types can be used in structures if we have arrays of the structure.
Least type can also suffer the problems fast types do, as width of variables can be changed on different platforms for both cases.
Avoid fixed width data types as much as possible as they may not even exist on some platforms, except hardware register access, communication protocol mapping, etc. where we need to know the exact bits of the variable. For example:
typedef struct {
uint8_t flags;
uint8_t length;
uint8_t data[100];
uint16_t crc;
} __attribute__((packed)) package_t;
Usually __attribute__((packed))
(or something similar) should be used to ensure no padding will be inserted for these cases, as this can be a problem by itself.
Now, if my understanding is true, I think least data types are more likely to be used in arrays or structures, fast data types are more likely to be used for single variables and fixed data types are unlikely to be used in order to achieve maximum portability and efficiency. But typing "fast" and "least" every time is not encouraging. So, I think of a type set as follows:
typedef [u]intN_t os_[u|s]exactN_t;
typedef [u]int_fastN_t os_[u|s]N_t;
/* I couldn't come up with a better name */
typedef [u]int_leastN_t os_[u|s]minN_t;
/* These may change */
typedef uint_least8_t os_byte_t;
typedef uint_least16_t os_word_t;
/* ... */
Also, I would be pleased to know how and where you use C99 standard types.
A major feature of C99 is its numerics support, and in particular its support for access to the features of IEEE 754-1985 (also known as IEC 60559) floating-point hardware present in the vast majority of modern processors (defined in "Annex F IEC 60559 floating-point arithmetic").
The ISO C99 standard introduced portable data types through a header file named stdint. h. This header file is included with the compiler and creates data types of a fixed width. A fixed width data type ensures that an 8 bit variable is 8 bits, a 16 bit variable is 16 bits and so on and so forth.
int32_t: signed 32-bit. uint32_t: unsigned 32-bit. int64_t: signed 64-bit. uint64_t: unsigned 64-bit.
Writing highly portable code is hard. Writing highly portable code that is optimal and works correctly is even harder.
For the majority of time, if it is feasible, I would suggest using basic types such as int
, char
, etc, rather than uint8_t
or uint8_fast_t
. The types int
and char
is guaranteed to exist. There is no doubt about that. Of course, SOMETIMES we need a specific behaviour from the code, and that will require a specific type - but that code will most likely break if the system doesn't support that exact type.
For your first example, it is extremely unlikely that you'll get better performance than using int
, unless your code is designed to (also) run on 8-bit processors. On a 16-, 32- or 64-bit processor, the native size will be the fastest for loops (unsigned being slightly better on 64-bit machines, since it doesn't require sign extension).
In your second example, it really only matters if the array is large enough to warrant the space-saving over using either char
or int
or short
or whatever makes sense for the content. On modern machines (including many embedded platforms and even when usign the stack) 400 bytes isn't really that much.
For your third example, obviously, for protocols, you will need to use types that match the protocol definition exactly, or things will go wrong. On platforms that doesn't support the correct type, this will have to be solved in some platform specific way - how you go about it will depend on exactly what the platform actually supports.
So, to answer your concrete questions:
Excessive use of "special type" variables is likely to:
Remember also that performance is often a case of 90% of the time is taken by 10% of the code. Understanding where (under normal usage) your code spends its time is critical. Of course, when porting code to different systems and on different architectures, you may find that the performance bottleneck moves, based on the relationship between processor speed, size of caches and memory speed. A system with high processor speed, but (realtively) small caches can sometimes perform worse than a similar system with lower clock-speed and bigger caches, just as one example.
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