Начну, пожалуй, с очевидного (слева от этого текста). Изображение, приведенное здесь, довольно известно.
Оно демонстрирует размер процессора Intel Atom в сравнении с рисовым зерном. А я продемонстрирую вам буквально «на пальцах» простые и, надеюсь, полезные для программистов на С\С++ советы по оптимизации софта для Intel Atom.
Вообще, единственный открытый официальный источник оптимизационной мудрости для процессоров Intel — это Intel® 64 and IA-32 Architectures Optimization Reference Manual — содержит целую главу (#13), посвященную Atom. Там даются многочисленные советы по оптимизации… но только для тех, кто пишет на ассемблере. Вот типичный пример:
"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."
Вы все поняли? Отлично!
Но число Атомов во вселенной постоянно растет, а число пишущих софт на ассемблере — убывает, то что многим остается только
- Многопоточность, точнее — двухпоточность, по числу логических ядер. На Atom — очень эффективный Hyper Threading (имеется в большинстве моделей Atom). Так что если вы распарараллелите ваш код на два потока, то можете расчитывать на прирост производительности 30-50% ( против ожидаемых 15-20% на десктопных архитектурах Intel).
- Выравнивание памяти. При выравнивании памяти на 16 байт реально получить 10% выигрыша в приложении, активно выделяющем и копирующем память.
- Серьезная угроза производительности — многократный вызов коротких (небольших) функций. По возможности, такие функции надо или объединять, или принудительно встраивать (inline). Выигрыш производительности от такой простейшей оптимизации на серьезных приложениях может достигать 20%! Короткие функции могут прятаться в используемых библиотеках (например, математических), а также в разделяемых объектах Linux (PIC code). Кстати, следующая ф-я тоже будет короткой в случае, если bar =0.
void foo() {
if (bar) {
/* делаем что-то нужное, длинное и сложное */
}
}
- Кэш в Atom небольшой, так что здесь особенно актуальна локальность доступа к данным -т.е. по возможности не «прыгать» по массивам, а обходить их последовательно; объединять в структуру то, что часто используется и не грузить кэш доступом к мертвым
душамданным. - Atom очень медленно работает с данными типа double. Примерно в 5 раз медленнее, чем с float! Причем, как в скалярных, так и векторных инструкциях (SSE). Так что, по возможности, откажитесь от двойной точности.
- Также медленно Atom занимается делением. Лучше вообще не делить, но если приходится, то знайте, что беззнаковое деление быстрее знакового, 8-битное быстрее 16-битного, которое, конечно же, быстрее 32-битного. У компилятора Intel есть специальный флаг для понижения точности = ускорения деленя “-no-prec-div”. И еще — блок деления в процессоре один, он совместно используется всеми потоками, так что это может стать узким местом.
- Работать с float (даже в скалярном случае) быстрее через векторные инструкции (или интринсики).
Флаг “–fpmath=sse” компилятора gcc генерирует код с x87 на sse. Компилятор Intel делает то же самое
автоматически.. - И, наконец, компиляторы. Вот рекоммендуемые флаги для компиляции под Atom. Помимо вышеописанных ускорений, компилятор Intel оптимизирует код на уровне микроинструкций (инженеры Интел честно изучили приведенный в начале мануал :) )
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]
Советы даны просто в форме рецептов, без пояснений, почему это так. Но стандартный ответ звучит как «Так