Методы оптимизации кода
http://itw66.ru/blog/c_plus_plus/13.html
А вы какие еще способы оптимизации кода знаете? (я не говорю про оптимизацию алгоритмов. Речь идет про код вообще)
x = array[ ++i ];
Это ж две разные операции будет, разве нет?
п.с. имхо, такая экономия на спичках нужна в исключительно редких случаях, ибо, зачастую, узкое место сидит именно в кривом алгоритме.
Оптимизация алгоритма - это наиболее важная оптимизация. Но иногда нужно делать такие системы, в который каждая строчка кода может быть решающей. Тогда и нужно понимать подобные принципы.
Допустим идет разработка системы частиц (particle system). Каждая частица будет апдейтиться в каждом кадре. Если частиц 10000 то любая лишняя операция будет сказываться на производительности. А если добавить несколько if то можно вообще ее убить.
К stl этот пример не относится. Он показывает, что процессор одновременно может делать разные операции, например обращаться к памяти и проводить арифметические операции. Операции ++iter и iter++ это вызовы функций, где может быть написано что угодно. Так что они не имеют отношения к примеру в статье.
вообще то я про то, что в каноническом варианте, при использовании постинкремента создается копия инкрементируемого объекта (сохранение старого значения), что для итераторов затратнее
x = array[ ++i ];
Это ж две разные операции будет, разве нет?
п.с. имхо, такая экономия на спичках нужна в исключительно редких случаях, ибо, зачастую, узкое место сидит именно в кривом алгоритме.
Проблема в том, что чтобы там процессор параллельно не выполнял:
1. Операции не эквивалентны
2. Оптимизатор в компиляторе, задача которгого заниматься именно этим, так же никто не отменял
А в общем, это не те "оптимизации", "которые должен знать каждый профессиональный программист".
http://itw66.ru/blog/c_plus_plus/571.html - Эффективный код на С++
http://itw66.ru/blog/c_plus_plus/491.html - Методы оптимизации памяти
2. Объявляйте переменные там, где они будут использоваться, давая им наименьшую область видимости. Это способствует выполнению пункта 1.
и как будет быстрее так
int a,b;
for(int x=0;x<100500;x+=1)
{
int tmp;
tmp=a;
a=b;
b=tmp;
}
или так
int a,b,tmp;
for(int x=0;x<100500;x+=1)
{
tmp=a;
a=b;
b=tmp;
}
?
6. Используйте int вместо char, short для локальных переменных и для переменных структур, если нет цели минимизировать потребление памяти. int будет работать быстрее. При использовании переменных char и short, они будут конвертироваться в int во всех арифметических операциях.
Схренали? Так только у мелкософта typedef int BOOL; typedef int LONG; да и то у них typedef char CHAR; typedef short SHORT;.
10. Используйте конструкцию if() вместо if-else везде, где это возможно. Помните, что ветвление — очень дорогая операция (точнее дорогая операция jump, т.к. она ломает конвейер команд процессора). В случае if-else, прыжок происходит в любом случае, а в случае команды if только в одном.
Как то сильно сомнения вызывает...
Да и вообще, где же пруф? Недостаточно того факта, что теоретически оно должно работать быстрее.
Важно понимать, что эта оптимизация должна быть портабельной. Также стоит заметить, что оптимизирующий компилятор может инвертировать Вашу оптимизацию своей. ;) Таким образом, где-то вы получите прирост в 0.2%, а где-то потеряете 25%. Кому это нужно?
Программер должен оптимизировать вручную только то, что является для компилятора недостижимым. А что касается адресации, распределения памяти для автоматических объектов и т.д., то тут лучше положиться на автоматизированные средства, которые могут учитывать особенности целевой архитектуры при выполнении оптимизации.
Взять хоть Ваш пример п.9 отсюда:
#include <ctime>
int array[10000];
void func1() {
for( int i = 0; i < 10000; ++i )
array++;
}
void func2() {
int *p = array;
for( int i = 0; i < 10000; ++i, ++p )
++*p;
}
int main( int argc, char **argv )
{
clock_t elps1, elps2;
clock_t stamp = clock();
for( int i = 0; i < 100000; ++i )
func1();
elps1 = clock() - stamp;
stamp = clock();
for( int i = 0; i < 100000; ++i )
func2();
elps2 = clock() - stamp;
stamp = clock();
for( int i = 0; i < 100000; ++i )
func2();
elps2 += clock() - stamp;
stamp = clock();
for( int i = 0; i < 100000; ++i )
func1();
elps1 += clock() - stamp;
stamp = clock();
std::cout << elps1 << ":" << elps2 << std::endl;
return 0;
}
Получается поровну. Попробуйте у себя, может у Вас в 10 быстрее будет? ;)
Важно заметить: я не утверждаю, что оба варианта гарантированно выполняются с одинаковой скоростью. Я утверждаю, что эта оптимизация сильно поганит код, ухудшая его читабельность, а результат может быть положительным, отрицательным или отсутствовать.
В AMD SOG (для amd64) пишут:
C allows the use of either the array operator ([]) or pointers to access the elements of an array.
However, the use of pointers in C makes work difficult for optimizers in C compilers. Without
detailed and aggressive pointer analysis, the compiler has to assume that writes through a pointer can
write to any location in memory, including storage allocated to other variables. (For example, *p and
*q can refer to the same memory location, while x[0] and x[2] cannot.) Using pointers causes
aliasing, where the same block of memory is accessible in more than one way. Using array notation
makes the task of the optimizer easier by reducing possible aliasing.
А выводы делайте сами. Не наступайте на грабли, на которых побывали ступни многих программеров. Делайте свою работу, а оптимизатору не мешайте делать свою.
P.S. Разве что, как хобби.
Не понял комментария. Я написал, что сложить два числа типа int будет быстрее чем сложить числа типа char, short. Это по тому, что прежде чем складывать числа их придется достать из памяти. Получение char дольше чем int, т.к. придется память выравнивать и отсекать соседние байты, вместо того, чтобы просто получить данные из ячейки по адресу.
Не думаю, что подобные оптимизации нужно вообще делать, если код будет запускаться под разные платформы. На PC многие вещи компилятор сам сделает, а на PS3 например куча своих особенностей.Поэтому я и написал, что подобные вещи нужно применять тогда, когда это необходимо.
Простой пример: есть статья как проводилась оптимизация в игре Uncharted2. Берется цикл, который выполняется за 280ms и реорганизацией кода + побитовыми операциями + разверткой цикла + векторизацией некоторых данных доводится до 34 милисекунд, а после переводится на асемблер и выполняется 7ms. Про асемблер я нечего не говорил, но и без него оптимизация в 8 раз. Понятно, что сразу такое не написать. Это была оптимизация уже конкретного кода под конкретную платформу.
Те приемы которые я описал это информация к сведению. Если вы столкнетесь, с необходимостью оптимизации, то статьи подскажет вам некоторые варианты реорганизации кода, которые могут помочь. Некоторые пункты в статьях подходят как best practice (вроде инициализации переменных класса в списке инициализации, а не в теле конструктора. Но нужно проверять это все индивидуально для платформы, окружения и расположения данных в памяти.
1) В теории компилятор должен сам использовать регистры соответствующего размера - al ax eax rax.
2) Если программист создает переменную char то врядли собирается в ней хранить число, так что советовать ему хранить любые данные в Int это неправильно.
3) А теперь мини проверка:
#include <ctime>
#include <string.h>
int array[10000];
short array2[10000];
char array3[10000];
void func1() {
for( int i = 0; i < 10000; ++i )
array+=0x1000000;
}
void func2() {
for( int i = 0; i < 10000; ++i )
array2+=0x100;
}
void func3() {
for( int i = 0; i < 10000; ++i )
array3+=0x1;
}
int main( int argc, char **argv )
{
clock_t elps1, elps2, elps3;
clock_t stamp = clock();
memset(&array,0,sizeof(int)*10000);
memset(&array2,0,sizeof(short)*10000);
memset(&array3,0,sizeof(char)*10000);
for( int i = 0; i < 100000; ++i )
func1();
elps1 = clock() - stamp;
stamp = clock();
for( int i = 0; i < 100000; ++i )
func2();
elps2 = clock() - stamp;
stamp = clock();
for( int i = 0; i < 100000; ++i )
func3();
elps3 = clock() - stamp;
stamp = clock();
std::cout <<"Int :"<<elps1 << " Short:" <<elps2<<" Char:"<<elps3<<std::endl;
return 0;
}
Результат:
Char лишь немного медленнее, а short даже быстрее чем int.
Если Ваши статьи основаны на каких-то других, хорошо бы включать авторство:
1. Например, вот очень похоже на Ваше.
2. Может быть мне одному кажется, что что-то уже было разжевано в советах от Мейерса?!
Дальше не хочу копать, надеюсь на честность ТС.