Optimization for Intel Atom on the Fingers

I will begin, perhaps, with the obvious (to the left of this text). The image shown here is pretty well known.
It shows the size of the Intel Atom processor compared to rice grain. And I will demonstrate to you literally “on fingers” simple and, I hope, tips for optimizing software for Intel Atom useful for C / C ++ programmers.
In general, the only open official source of optimization wisdom for Intel processors - the Intel® 64 and IA-32 Architectures Optimization Reference Manual - contains an entire chapter (# 13) on Atom. Numerous optimization tips are given there ... but only for those who write in assembler. Here is a typical example:
"Assembly / Compiler Coding Rule 4. -For Intel Atom processors, place a MOV instruction between a flag producer instruction and a flag consumer instruction that would have incurred a two-cycle delay. This will prevent partial flag dependency. "
You understand everything? Great!
But the number of Atoms in the universe is constantly growing, and the number of writing software in assembler is decreasing, so many can only
- Multithreading, more precisely, dual-threading, by the number of logical cores. On Atom, very efficient Hyper Threading (available on most Atom models). So if you parallelize your code into two threads, you can count on a performance gain of 30-50% (against the expected 15-20% on Intel desktop architectures).
- Memory alignment. When aligning memory by 16 bytes, it is realistic to get 10% of the gain in an application that actively allocates and copies memory.
- A serious performance threat is the repeated invocation of short (small) functions. If possible, such functions should be either combined or forced to be embedded (inline). The performance gain from such simple optimization on serious applications can reach 20%! Short functions can be hidden in used libraries (for example, mathematical ones), as well as in shared Linux objects (PIC code). By the way, the next file will also be short if bar = 0.
void foo () {
if (bar) {
/ * do something necessary, long and complicated * /
}
} - The cache in Atom is small, so the locality of access to data is especially relevant here, i.e. if possible, do not “jump” over arrays, but go around them sequentially; unite in a structure what is often used and not load the cache with access to dead data
souls. - Atom works very slowly with double data. About 5 times slower than with float! Moreover, both in scalar and vector instructions (SSE). So, if possible, discard double precision.
- Atom is also slow in fission. It’s better not to divide at all, but if you have to, then be aware that unsigned division is faster than signed, 8-bit faster than 16-bit, which, of course, faster than 32-bit. The Intel compiler has a special flag for lowering accuracy = accelerating the “-no-prec-div” divide. And yet - there is only one division unit in the processor, it is shared by all threads, so this can become a bottleneck.
- Working with float (even in the scalar case) is faster through vector instructions (or intrinsics).
The “–fpmath = sse” flag of the gcc compiler generates code from x87 to sse. The Intel compiler does the same
automatically. - And finally, compilers. Here are the recommended flags for compiling under Atom. In addition to the accelerations described above, the Intel compiler optimizes the code at the micro-instruction level (Intel engineers honestly studied the manual given at the beginning :))
gcc <4.5: -march = core2 -mtune = generic -fpmath = sse –O3 [–ffast-math]
gcc> = 4.5: -march = atom –fpmath = sse -03 [-flto] [–ffast-math]
icc <11.1: -xL –O3 –ipo [-fno-alias] [-no-prec-div]
icc> = 11.1 : -xSSE3_ATOM –ipo [–ansi-alias] [-no-prec-div]
Tips are given simply in the form of recipes, without explanation why this is so. But the standard answer is “So