Використання сучасного С++ для підвищення продуктивності

У даній статті я хотів би розповісти, як використання засобів сучасних стандартів З++ дозволяє підвищити продуктивність програм без яких-небудь особливих зусиль від програміста.

Ця стаття торкається лише засоби мови, а не конкретні техніки оптимізації (тобто такі речі як локальність пам'яті, платформозависимые оптимізації, lockfree та інше залишаються за бортом).

Ключове слово final
Віртуальний метод класу, оголошений з використанням ключового слова final, не може бути перевизначено спадкоємцями.

class A {
public:
virtual void f() {}
};

class B : public A {
public:
virtual void f() override final {}
};

class C : public B {
public:
virtual void f() override {} // помилка
};

У деяких випадках це дозволяє компілятору уникнути звичайних витрат на виклик віртуальної функції (девіртуалізація).

Приклад:

Код
class A {
public:
virtual void f() = 0;
};

class B1 : public A {
public:
void f() final override;
};

class B2 : public A {
public:
void f() override;
};

void with_final(B1* b) {
b->f();
}

void no_final(B2* b) {
b->f();
}


Асемблерний код (тут і далі: ggc 6.2, -O3 -std=c++14):

Результат
with_final(B1*):
jmp B1::f()
no_final(B2*):
mov rax, QWORD PTR [rdi]
jmp [QWORD PTR [rax]]


Не передавайте розумні покажчики по посиланню
Розумні покажчики повинні використовуватися для визначення терміну життя об'єкта. Не потрібно передавати розумні покажчики по посиланню в метод, якщо він не виконує ніяких операцій з самим об'єктом розумного покажчика, а лише з об'єктом, що зберігаються в ньому.

Приклад:

Код
#include <memory>
void f1(std::unique_ptr<int>& i) {
*i += 1;
}
void f2(int& i) {
i += 1;
}


Результат
f1(std::unique_ptr<int, std::default_delete<int> >&):
mov rax, QWORD PTR [rdi]
add DWORD PTR [rax], 1
ret
f2(int&):
add DWORD PTR [rdi], 1
ret


Використовуйте Rule of zero
Rule of zero свідчить, що для класу, в якому не потрібно явно визначати деструктор, також не потрібно явно визначати конструктори/оператори копіювання/переміщення.

Явне визначення конструктора копіювання забороняє компілятору генерувати конструктор переміщення, що в деяких випадках може значно знизити продуктивність.

Приклад:

#include < string>

class A {
std::string s;
public:
A() = default;
A(const A& a) = default; // конструктор переміщення не буде згенерований 
};

class B {
std::string s;
public:
B() = default;// конструктор копіювання і переміщення згенерований автоматично
};

auto f()
{
return std::make_pair(A(), B());// для А буде викликаний конструктор копіювання для B - переміщення
}

віддавайте перевагу emplace копіювання
Починаючи з С++11 стандартні контейнери дозволяють конструювати елемент безпосередньо всередині контейнера, уникаючи зайвого копіювання.

Використання emplace швидше не тільки простого копіювання, але навіть переміщення.

Не використовуйте shared_ptr, якщо можна обійтися unique_ptr
Використання shared_ptr несе за собою певні витрати. При створенні, копіювання, видалення shared_ptr оновлює зовнішній лічильник посилань на який розташований об'єкт. Також shared_ptr зобов'язаний бути потокобезопасным, що теж може нести за собою відповідні витрати. У той час як виділення і видалення пам'яті з використанням unique_ptr взагалі ніяк не відрізняється від використання ручного управління пам'яттю з використанням new/delete.

Спасибі за увагу!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.