Оптимизация программного кода. Основные возможности оптимизации кода программистом и компилятором

Иногда бывает сложно решить, какую конструкцию лучше использовать i++ или ++i, либо выбрать между конструкцией if-else и switch. В этой статье, написанной специально для сообщества iT works, представлены наиболее реальные средства оптимизации кода, которые должен знать каждый профессиональный программист.

Некоторые считают, что времена оптимизации на уровне кода прошли навсегда, однако это не так. Сейчас существует множество платформ в которых нет таких могущественных компиляторов как в Microsoft Visual Studio. Например шейдерные языки (hlsl, glsl) или код для CUDA, PlayStation3, SPU или мобильные платформы. В зависимости от организации кода, может в десятки раз отличаться его эффективность иногда из-за неэффективности компилятора, на чаще из-за доступа к памяти.

Программируя для разных платформ изучите возможности вашего компилятора и особенности архитектуры процессора (если вы пишите для конкретной консоли). Проведите тесты производительности разных вариантов оптимизации. Часто сложно предполагать, какие именно способы будут наиболее эффективными. А эта статья подскажет разные приемы, которые могут вам помочь. Однако не следует оптимизировать слепо, без предварительного анализа и профилирования. Помните, что преждевременная оптимизация - это зло.

Если вы являетесь программистом в VS под Windows, то скорее всего со многими описанными приемами оптимизации компилятор эффективно справится. Обратите внимание на пункты работы с памятью, а так же я рекомендую ознакомиться с техникой Data oriented design . Некоторые советы по ее использования ищите в статье .

Итак, начнем:

1. Используйте векторизацию данных и векторные команды их обработки (например SSE в CPU или упаковывайте данные если используете шейдеры или CUDA). Это позволит использовать SIMD (Single Instruction, Multiple Data) архитектуру, что значительно повысит скорость вычислений. Если вы решите использовать этот метод, то не забывайте про выравнивание данных в памяти.

2. Эффективнее складывать и умножать в перемешку, чем сначала все сложить, а потом все умножить. Это происходит от того, что сложение и умножение выполняются разными модулями процессора и могут выполняться одновременно.
int a,b,c,k,m,n,t,f1,f2,f3,g1,g2,g3; a = b + с; k = m + n; t = a + k; f1 = f2 * f3; g1 = g2 * g3; Менее эффективно чем: a = b + с; f1 = f2 * f3; k = m + n; g1 = g2 * g3; t = a + k;

3. Нет разницы по скорости в работе с float и double при сложении и умножении. Они выполняются за равное число тактов процессора и используют одни и те же регистры. При делении и извлечении корня, float работает быстрее. Однако если вы будете использовать большие объемы данных, то за счет кеша быстрее будет работать тот тип, который занимает меньше памяти (т.е. float), поэтому в общем случае именно его предпочтительно использовать. Выбирать double имеет смысл когда нужна большая точность.

4.
Допустим есть. const float a = 100.0f; float some1 = some3 * 1.0f / a; float some2 = some4 * 1.0f / a; более эффективно написать не: const float a_inv = 1.0f / a; some1 = some3 * a_inv; some2 = some4 * a_inv; а так: some1 = some3 * (1.0f / a); some2 = some4 * (1.0f / a); Почему это будет эффективнее? Операторы с равным приоритетом выполняются последовательно. Это значит, что будет выполнено сначала умножение, а затем деление. Если же обрамить операцию деления в скобки, то ее выполнит компилятор, а в реальном времени будет выполняться только операция умножения. Что качается отличий варианта 3 от варианта 2, то в 3-ем варианте не создается дополнительной переменной, нет нужны глядя на код думать о том, что это за переменная. А эффективность 2-го и 3-го варианты будет одинаковой.

5. На больших объемах данных и вычислений на них float выгоднее чем double (из за cache miss"ов, см. пункт 3).

6.
a, b - любое выражение Func1, Func2 - функции, которые вызовутся для вычисления условия if(a && b) - нужно первым ставить менее вероятное условие, для большей эффективности if(a || b) - нужно первым ставить более вероятное условие, для большей эффективности if(Func1() && Func2()) - нужно ставить более быстрый оператор первым

7.
void Func(int* a) { int b = 10; Следующие строки одинаковые по эффективности (по времени выполнения): b = 100; *a = 100; } Это происходит по тому, что доступ к стековой переменной осуществляется по указателю на стек. Тоже идет разыменование указателя.

8. Если есть большой массив структур, то нужно делать размер его элементов равным степени двойки. Тогда проход по такому массиву будет значительно быстрее (в 4 -6 раз), за счет выравнивания указателя на каждую структуру в массиве.

9.
int a[ 1000 ]; for(int i =0; i= b ? -1: 0); Можно заменить на: 1. int x = (b - a) >> 31; 2. int x = (b - a) & 0x80000000;

25.
i32 iIndex; Условие: if(iIndex < 0 && iIndex >= iSize) Можно заменить таким: if((u32)iIndex >= iSize) Условие: if(i >= min && i

Загрузка...
Top