[Из песочницы] Использование современного С++ для повышения производительности

В данной статье я хотел бы рассказать, как использование средств современных стандартов С++ позволяет повысить производительность программ без каких-либо особых усилий от программиста.

Эта статья затрагивает лишь средства языка, а не конкретные техники оптимизации (т.е. такие вещи как локальность памяти, платформозависимые оптимизации, 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 
void f1(std::unique_ptr& i) {
	*i += 1;
}
void f2(int& i) {
	i += 1;
}


Результат
f1(std::unique_ptr >&):
        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 

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.

Спасибо за внимание!

Комментарии (2)

  • 13 октября 2016 в 14:26

    +1

    > Не передавайте умные указатели по ссылке

    И в примере сравнивается «производительность» умного указателя и значения. Тогда уж надо сравнивать код, который генерируется при передаче умного указателя по значению: копирование умного указателя + разыменование.

  • 13 октября 2016 в 14:41

    +1

    Извините, но что здесь современного (уже во всю обсуждается C++17) и в чём повышение производительности? Это ведь просто выжимки из мануалов.
    Предпочитайте emplace копированию

    Здесь по факту просто определение методу emplace.
    Не используйте shared_ptr, если можно обойтись unique_ptr

    ровно как и
    Ключевое слово final

    Просто перевод первых строчек с http://www.cplusplus.com/reference.
    Не передавайте умные указатели по ссылке

    Почему? Если на объект создан указатель, то работа должна происходить с ним в контексте умного указателя. Указатели существуют для того, чтобы создать объект в процессе работы, когда мы не знаем на этапе компиляции параметров конструктора для него/количества экземпляров/тип экземпляра/итд.
    В примере, вы что так что так передадите адрес объекта (размер которого фиксированный). А при передаче во вторую функцию, вам в любом случае придётся разыменовать unique_ptr. В чем профит?

© Habrahabr.ru