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

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

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

Когато програмирате за различни платформи, проучете възможностите на вашия компилатор и характеристиките на архитектурата на процесора (ако пишете за конкретна конзола). Провеждайте тестове за производителност на различни опции за оптимизация. Често е трудно да се предвиди кои методи ще бъдат най-ефективни. И тази статия ще ви каже различни техники, които могат да ви помогнат. Не трябва обаче да оптимизирате на сляпо, без предварителен анализ и профилиране. Не забравяйте, че преждевременната оптимизация е зло.

Ако сте VS програмист на Windows, тогава най-вероятно компилаторът ще се справи ефективно с много от описаните техники за оптимизация. Обърнете внимание на точките за работа с паметта и препоръчвам да се запознаете с технологията Дизайн, ориентиран към данни. Намерете някои съвети за използването му в статията.

И така, нека започнем:

1. Използвайте команди за векторизация на данни и векторна обработка (например SSE в процесора или пакетиране на данни, ако използвате шейдъри или CUDA). Това ще позволи използването на SIMD (Single Instruction, Multiple Data) архитектура, което значително ще увеличи скоростта на изчисленията. Ако решите да използвате този метод, не забравяйте да запазите данните подравнени в паметта.

2. По-ефективно е да събирате и умножавате разбъркано, отколкото първо да добавяте всичко и след това да умножавате всичко. Това се случва, защото събирането и умножението се извършват от различни процесорни модули и могат да се извършват едновременно.
int a,b,c,k,m,n,t,f1,f2,f3,g1,g2,g3; a = b + c; k = m + n; t = a + k; f1 = f2 * f3; g1 = g2 * g3; По-малко ефективен от: a = b + c; f1 = f2 * f3; k = m + n; g1 = g2 * g3; t = a + k;

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

4.
Да кажем, че има. const float a = 100.0f; float some1 = some3 * 1.0f/a; float some2 = some4 * 1.0f/a; по-ефективно е да пишете not: 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 (поради пропуски в кеша, вижте точка 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

Зареждане...
Връх