Домашняя страница Эффективный код на С++
Публикация
Отменить

Эффективный код на С++

Вторая статья по оптимизации кода на С++. В статье рассмотрены некоторые рекомендации, позволяющие сделать ваш код более эффективным.

Первую можно найти тут.

  1. Минимизируйте количество локальных переменных. Тогда компилятор сможет их хранить в регистрах, а не на стеке.

  2. Объявляйте переменные там, где они будут использоваться, давая им наименьшую область видимости. Это способствует выполнению пункта 1.

1
2
3
4
5
6
7
8
void compute(const char* name)
{
   if (!name)
   {
      A a; // переменная будет жить до конца условия, но не до конца функции
      ...
   }
}
  1. Не передавайте в функцию большое количество параметров. Большое количество параметров будет хранится на стеке, а не в регистрах. Если нужно передать большую структуру воспользуйтесь указателем (ссылкой), но не передавайте по значению.

  2. Используйте список инициализации конструктора, вместо присвоения значений в его теле. Так поля будут инициализироваться один раз. Это наиболее ощутимо, если эта переменная - класс. Тогда в первом случае вызовется только конструктор, а во втором конструктор и функция.

Пишите так:

1
2
3
4
5
SomeClass::SomeClass(int val1, int val2)
   : m_val1( val1 )
   , m_val2( val2 )
{
}

Вместо:

1
2
3
4
5
SomeClass::SomeClass(int val1, int val2)
{
   m_val1 = val1;
   m_val2 = val2;
}
  1. Если нужно чтобы функция вернула структуру размером более 4 байт, то передайте эту структуру в функцию по указателю или ссылке. Пусть функция инициализирует ее поля через указатель.

Вместо:

1
2
3
4
5
struct getData()
{
   Struct st;....
   return st;
}

Писать так:

1
2
3
4
5
void getData(struct& st) // ну или по указателю передать
{
   st.value = ...;
   ...
}
  1. Используйте int вместо char, short для локальных переменных и для переменных структур, если нет цели минимизировать потребление памяти. int будет работать быстрее. При использовании переменных char и short, они будут конвертироваться в int во всех арифметических операциях.

  2. Делайте настолько простой конструктор, насколько возможно. Не забывайте, что он будет вызываться при любом создании объекта. Если объект часто создается в промежуточных вычислениях, то лучше, чтобы конструктор был мал. Например делая структуру Vector3 не нужно в конструкторе по умолчанию инициализировать поля x,y,z нулями. Сделайте для этого лучше отдельный конструктор, а конструктор по умолчанию оставьте пустым. Это приблизит вашу структуру к POD типу и сделает более эффективными операции, где не явно создаются новые объекты.

  3. Не создавайте возвращаемого результата в функции, у которой возвращаемое значение никогда не будет использоваться. Компилятор не знает, будет ли оно использоваться и всегда будет возвращать значение. Например не делайте тип bool возвращаемым значением функции, если не собираетесь его обрабатывать.

  4. Не делайте функции виртуальными без необходимости. Вызов виртуальной функции значительно дороже вызова обычной функции, поэтому не нужно делать ее виртуальной предполагая, что кому-нибудь понадобится изменить ее поведение в будущем. Если такая необходимость возникнет, то можно будет легко сделать функцию виртуальной в будущем.

  5. Если функция класса не зависит от его переменных (использует только входные параметры), делайте ее статической. Это позволит компилятору эффективнее оптимизировать код.

  6. Используйте конструкцию if() вместо if-else везде, где это возможно. Помните, что ветвление - очень дорогая операция (точнее дорогая операция jump, т.к. она ломает конвейер команд процессора). В случае if-else, прыжок происходит в любом случае, а в случае команды if только в одном.

  7. Примеры рефакторинга кода. Второй вариант эффективнее первого. Эти преобразования стоит иметь в виду и использовать только при серьезной оптимизации кода. Помните, что преждевременная оптимизация - зло.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
До: x = y % 32;
После: x = y & 31;

До: x = y * 8;
После: x = y << 3;

До: x = y / w + z / w;
После: x = (y + z) / w;

До:  if (a == b && c == d && e == f) {...}
После: if (((a-b) | (c-d) | (e-f)) == 0) {...}

До:  if ((x & 1) || (x & 4)) {...}
После: if (x & 5) {...}

До:  if (x >= 0 && x < 8 && y >= 0 && y < 8) {...}
После: if (((unsigned)(x | y)) < 8) {...}

До:  if ((x == 1) || (x == 2) || (x == 4) || (x == 8) || ...)
После: if (x & (x-1) == 0 && x != 0)

До:  if ((x == 2) || (x == 3) || (x == 5) || (x == 7) || (x == 11) || (x == 13) || (x == 17) || (x == 19)) {...}
После: if ((1 << x) & ((1 << 2) | (1 << 3) | (1 << 5) | (1 << 7) | (1 << 11) | (1 << 13) | (1 << 17) | (1 << 19))) {...}

До:  #define abs(x) (((x)>0)?(x):-(x))
После:  static i32 abs( i32 x )
        {
             i32 y = x >> 31;
             return ( x ^ y ) - y;
        }


До:  int a[3][3][3]; int b[3][3][3];
   for (i = 0; i < 3; i++)
      for (j = 0; j < 3; j++)
         for (k = 0; k < 3; k++)
            b[i][j][k] = a[i][j][k];
После:  struct 3DArrayType
        {
            int elem[3][3][3];
        };
        3DArrayType a,b;
        b = a;


До:  for (i = 0; i < 3; i++)
       for (j = 0; j < 3; j++)
         for (k = 0; k < 3; k++)
            a[i][j][k] = 0;
После: memset(a, 0, sizeof(a));


До:  code, code, code, code ...
После: think, code, think, code ...

Комментарии

mrdekk, 27 дек. 2011, 13:47

  1. что за пункт 27? видимо опечатка?
  1. а кто будет освобождать память?

FiloXSee, 27 дек. 2011, 17:07

  1. да, опечатка.
  1. Я имел в виду конечно не возвращать указатель, а передать указатель на объект в качестве параметра функции и инициализировать поля структуры через него.
Публикация защищена лицензией CC BY 4.0 .