Pull to refresh

Обзор докладов C++ Russia Piter 2019

Reading time5 min
Views3K
В совместной магистратуре ИТМО и JetBrains мы просим студентов, которые отправляются на конференцию, написать отчёт с обзором докладов.
Публикуем один из таких отчётов о конференции C++ Russia Piter 2019. Автор — студент 2 курса магистратуры Артём Хорошев.



В начале ноября я посетил конференцию cpp-russia-piter, ниже я расскажу о докладах, которые мне запомнились.

Роман Русяев: Исключения C++ через призму компиляторных оптимизаций


Интересный доклад, в котором спикер на примере LLVM рассуждал на тему zero cost исключений в современном С++.

LLVM IR представляет программу как control-flow граф. В узлах графа расположены блоки инструкций, которые необходимо выполнить. В конце каждого блока присутствует терминатор, который передает управление следующему блоку. Терминатором может быть условный переход в другой блок, инструкция возврата или же специальная инструкция invoke, которая обладает семантикой вызова функции, и в случае успешного завершения переводит поток управления в один блок, а в случае исключения, указывает блок в который нужно перейти для его обработки. Инструкции в пределах блока обладают свойством, что если мы попали в блок, то мы выполним все инструкции, либо мы вообще не попадем в этот блок. Существуют оптимизации, которые могут работать только в пределах одного блока. В случае малых блоков они будут располагать меньшим контекстом, как следствие хуже производить оптимизации.
Докладчик рассказал, как современные компиляторы умеют преобразовывать инструкции invoke в инструкции call, которые уже не являются терминальными, и, как следствие, дают компилятору больше пространства для оптимизаций. Но, чтобы не надеяться на компилятор, можно самим написать у функции noexcept (если это корректно), чтобы быть уверенными, что компилятор выполнит все оптимизации.

(слайды доклада)

Максим Хижинский: Жилье комфорт-класса для акторов и хендлеров


Докладчик ставил перед собой цель избавиться от ряда проблем, связанных с параллельным программированием:

  • разделяемые данные,
  • context switching,
  • synchronizing,
  • частое создание поток на лету для кратковременных нужд.

В итоге спикер предложил разбивать свою программу на компоненты, время жизни которых будет равно времени жизни программы, и компоненты помещать в “апартменты”, число которых должно быть равно числу потоков. Сами компоненты, как следствие, однопоточные, и общение между компонентами должно происходить через message passing. Я соглашусь, что это решает проблемы, но ценой того, что всю настройку своей программы мы должны произвести на момент компиляции. Как минимум, надо на момент компиляции равномерно распределить компоненты по “апартментам”, что в дальнейшем не дает системе перебалансироваться в зависимости от нагрузки. В итоге, по моему мнению, само решение выглядит не достаточно гибким.

(слайды доклада)

Николай Белобородов: Применение slab-аллокаторов в высоконагруженных сетевых приложениях


Название говорит само за себя. Спикер рассказал, как они существенно увеличили быстродействие системы, используя slab аллокатор. Slab аллокатор оперирует несколькими сущностями:

  • slab — непрерывный участок памяти (обычно фиксированного размера), который разбивается на участки одинакового размера. Эти участки используются для хранения объектов одинакового размера,
  • cache — список slab’ов с одинаковым разбиением,
  • slab allocator — набор кешей.

Благодаря такому построению, объекты одинакового размера хранятся локально. Деаллокация устроена как пометка того, что конкретный участок слаба может быть переиспользован. Это позволяет избежать фрагментацию памяти.

Из такого определения slab аллокатора становится понятно, что он хорошо подходит для выделения освобождения объектов, размер которых лежит в ограниченном интервале. Например, выделяя каждый раз все больший и больший размер, будет создаваться новый кеш, старые кеши переиспользоваться не будут.

Докладчик рассказал, что из-за этого им пришлось отказаться от некоторых контейнеров, в пользу других. Например вектор был заменен на список, hashmap на дерево, но тем не менее, выигрыш в производительности всё равно был получен.

(слайды доклада)

Антон Полухин: C++ трюки из Такси


Доклады Антона Полухина всегда интересны, а решения, которые он предлагает, выглядят качественно. В этот раз Антон показал, как можно улучшить паттерн pimpl с точки зрения динамической аллокации. Для этого необходимо разместить хранилище для объекта-имплементации в самом объекте. Напоминаю, что классический паттерн pimpl выглядит следующим образом:

// Foo.h
struct Foo {
    Foo();

private:
    struct Foo_impl; //forward declaration
    std::unique_ptr<Foo_impl> impl;
};

// Foo.cpp 
//implementation
struct Foo::Foo_impl {
};

Хотим избавится от динамической аллокации, для этого подготовим место заранее прямо в объекте Foo:
// Foo.h
struct Foo {
    Foo();

private:
    struct Foo_impl; //forward declaration
    std::aligned_storage_t<sizeof(Foo_impl), alignof(Foo_impl)> impl;
};

// Foo.cpp 
//implementation
struct Foo::Foo_impl {
}

Такой способ работать не будет, так как у нас нет полной информации о типе Foo_impl в Foo.h и будет получена ошибка компиляции. Единственное решение, которое остается — угадать размер хранилища заранее.

// Foo.h
struct Foo {
    Foo();

private:
    struct Foo_impl; //forward declaration
    constexpr std::size_t kImplSize = 32;
    constexpr std::size_t kImplAlign = 8;
    std::aligned_storage_t<kImplSize, kImplAlign> impl;
};

// Foo.cpp 
//implementation
struct Foo::Foo_impl {
}

Но необходимо добавить проверку, что размер все таки правильный. Это нужно, так как попытка разместить объект в неподходящем для него буфере — UB.

// Foo.h
struct Foo {
    Foo();
    ~Foo();

private:
    constexpr std::size_t kImplSize = 32;
    constexpr std::size_t kImplAlign = 8;
    struct Foo_impl; //forward declaration
    std::aligned_storage_t<kImplSize, kImplAlign> impl;
};

// Foo.cpp 
//implementation
struct Foo::Foo_impl {
}

struct Foo::~Foo() {
    static_assert(kImplSize==sizeof(Foo_impl),"Size and sizeof(T) mismatch");
    static_assert(kImplAlign==alignof(kImplAlign),"Alignment and alignof(T) mismatch");

    // call destructor of Foo_impl
}

Делаем проверку в cpp файле, и если что-то указано неверно, завершаемся с ошибкой компиляции, и выводим правильный размер структуры, чтобы программист со второй попытки точно смог угадать.

Антон показал, как сделать свою библиотеку сериализации в различные форматы удобной, всего лишь не забыв про особенность ADL: при наличии шаблонных параметров у аргументов функции, функция будет искаться в пространстве имен параметров этих аргументов.

(слайды доклада)

Eric Niebler: A unifying abstraction for async in C++


Интересный доклад, в котором рассмотрены проблемы асинхронных абстракций в существующем стандарте языка: почему future и promise медленные, и можем ли мы спроектировать библиотеку так, чтобы избежать этих оверхедов. У разработчиков в facebook, кажется, получилось достойное решение https://github.com/facebookexperimental/libunifex

(слайды доклада)

Дмитрий Кожевников и Андрей Давыдов: Два доклада про модули


В программе было два подряд идущих доклада про модули. После прослушивания обоих докладов стало понятно, что модули еще не готовы к использованию. Меня это немного расстроило, так как я в принципе не интересовался как реализуется эта новая фича языка, и думал, что выйдет С++20 и сразу будет готова к использованию. К сожалению, оказалось, это не так.

(слайды докладов: 1, 2)

Заключение


Прошедшая конференция порадовала интересными примерами использования известных особенностей языка. Большое количество докладов было о фишках из следующего стандарта — С++20. Это, безусловно, очень пригодится всем разработчикам на С++.
Tags:
Hubs:
Total votes 7: ↑6 and ↓1+5
Comments5

Articles

Information

Website
www.jetbrains.com
Registered
Founded
Employees
501–1,000 employees
Location
Россия