Облаков Константин
Разработчик группы обработки свежего контента
Lock-free in practice:
RealTime-Server
• Введение
• Мотивировка
• Традиционный подход к lock-free
• Read Copy Update
• Версионный подход
• Большой пример
• Заключение
План
• Введение
• Мотивировка
• Традиционный подход к lock-free
• Read Copy Update
• Версионный подход
• Большой пример
• Заключение
План
• Mutex (global, grained, etc.)
• Atomic memory transactions (hardware)
• Lock-free / wait-free
Многие ядра = синхронизация:
• Введение
• Мотивировка
• Традиционный подход к lock-free
• Read Copy Update
• Версионный подход
• Большой пример
• Заключение
План
1.Свежесть бывает только одна
2.Нужно доносить информацию консистентно
3.Производительность нужна всем
4.Время ответа ограничено
5.Операций чтения намного больше записи
RealTime-Server
• Введение
• Мотивировка
• Традиционный подход к lock-free
• Read Copy Update
• Версионный подход
• Большой пример
• Заключение
План
1.Найти lock-free реализацию всех структур
2.Заменить стандартные
Традиционный подход к lock-free
• Самодостаточные lock-free структуры
слишком сложны и специфичны
• Куча лишних операций
• Консистентность всё равно отсутствует
Не надо так!
Куча лишних операций:
ЯДРО
Контроль
памяти
Регистрация
действий
Циклы CAS
Куча лишних операций:
Общая консистентность отсутсвует:
Не связаны!
• Введение
• Мотивировка
• Традиционный подход к lock-free
• Read Copy Update
• Версионный подход
• Большой пример
• Заключение
План
• Если данные только для чтения
• Если данные меняются атомарно
• Если данные в единоличном владении
Когда синхронизация не нужна
Read: Всё до чего можно добраться по
указателям – читаемо и консистентно
Copy: Перед изменением данные копируются
Update: Откопированные данные можно
обновлять
Но только один пишущий поток 
Строим на основе этого систему
16
- Копировать всё?
- Да!
17
- Это дорого!
- А вот и нет …
18
• Введение
• Мотивировка
• Традиционный подход к lock-free
• Read Copy Update
• Версионный подход
• Большой пример
• Заключение
План
19
• Копировать память всего процесса – дорого!
• Каждый процесс не владеет памятью
напрямую – есть прослойка PageTable
• Если откопировать только прослойку –
будет видимость полного копирования
• Надо применять copy-on-write для страниц
Как работает системный вызов fork()
20
• В. дерево – переиспользуем поддеревья
• В. стэк – переиспользуем общую часть
• В. хэш – переиспользуем чанки
– атомарно добавляем записи
• при открытой адресации сначала пишем данные, затем ключ и (опционально)
флаг готовности, если ключ не атомарен
• при списковом разрешении коллизий используем атомарность указателей
– старые записи просто маркируем старыми, не удаляя
– при необходимости в запись добавляем номер версии хэша
– слишком заполненный хэш копируем в новое место
• Прочее …
Обобщение – версионные структуры
21
Дерево
22
Дерево
23
Дерево
24
Стек
25
Стек
26
Стек
27
Хэш
28
Хэш
29
Хэш
30
Объединение структур
Старая версия Новая версия
на 99.9% те же
31
Указатель на корневую версию
Старая версия Новая версия
32
Указатель на корневую версию
Старая версия Новая версия
33
Указатель на корневую версию
Старая версия Новая версия
Когда удалить??
34
1.Надо регистрировать читателей!
2.Надо просматривать список читающих
3.Никем не читаемую не текущую версию
можно чистить
4.Если всё владение идёт через умные
указатели со счётчиком ссылок – удаление
подструктур произойдёт автомагически!
5.Схема накладывает свой отпечаток на
читающий поток - нужен цикл переопроса
Когда удалить старую версию?
35
Регистрация читателей
Зарезервированные ячейки
Счётчик занятых
36
Регистрация читателей
Зарезервированные ячейки
Счётчик занятых Нет запрета на
очистку старых
версий!
37
Регистрация читателей
Зарезервированные ячейки
Счётчик занятых Нет запрета на
очистку старых
версий!
38
Регистрация читателей
C
Зарезервированные ячейки
Счётчик занятых Версию C и
более поздние
удалять нельзя!
39
Регистрация читателей
D C
Зарезервированные ячейки
Счётчик занятых Версию C и
более поздние
удалять нельзя!
40
Регистрация читателей
D C
Зарезервированные ячейки
Счётчик занятых Версию C и
более поздние
удалять нельзя!
41
Регистрация читателей
D C F
Зарезервированные ячейки
Счётчик занятых Версию C и
более поздние
удалять нельзя!
42
Регистрация читателей
C F
Зарезервированные ячейки
Счётчик занятых Версию C и
более поздние
удалять нельзя!
43
Регистрация читателей
F
Зарезервированные ячейки
Счётчик занятых Версию F и
более поздние
удалять нельзя!
44
Регистрация читателей
G F
Зарезервированные ячейки
Счётчик занятых Версию F и
более поздние
удалять нельзя!
Переиспользуем ячейки в потоке!
45
1.Получаем ячейку, если пока нет
2.Читаем текущий указатель на версию
3.Записываем в свою ячейку
4.Снова читаем указатель
5.Если не совпал – на шаг 3
WARNING: Это не spin lock!
Если указатель меняется – в системе
происходит прогресс
Цикл переопроса при регистрации
46
• Храним порядковый номер версии (>0)
• В ячейке хранить не указатель, а номер
• Читатетель записывает номер версии в
ячейку, затем читает указатель на версию
и сразу работает с ней
Надо потребовать, что переполнения
счётчика или не поисходит, или происходит
за “достаточно большое время”
Как можно сделать wait-free
47
1.Пишем в ячейку “пусто”
И всё!
Освобождение версии
48
• Введение
• Мотивировка
• Традиционный подход к lock-free
• Read Copy Update
• Версионный подход
• Большой пример
• Заключение
План
49
• “Документ” – набор 1000 случайных чисел
• “Поиск” – ищем количество уникальных
документов, содержащих число из
заданного диапазона [from, to)
• Запросы на добавление документов
• Запросы на поиск (не более 1000 тредов)
• Wait-free!
Условия примера RealTime-Server
50
Класс элемента RCU-стека
class Item {
int Data;
shared_ptr<Item> NextItem;
public:
Item(int data, shared_ptr<Item> nextItem)
: Data(data), NextItem(nextItem) { }
const Item* Next() const {
return NextItem.get();
}
int GetData() const {
return Data;
}
};
51
Класс итератора по RCU-стеку
class const_iterator {
const Item* Current;
public:
const_iterator(const Item* current = nullptr)
: Current(current) { }
const_iterator& operator++() {
Current = Current->Next();
return *this;
}
bool operator!=(const const_iterator& other) const {
return Current != other.Current;
}
int operator*() const {
return Current->GetData();
}
};
52
Класс RCU-стека
class RCU_Stack {
class Item;
shared_ptr<Item> First;
public:
void push(int data) {
shared_ptr<Item>(
new Item(data, First)).swap(First);
}
class const_iterator;
const_iterator begin() const {return First.get();}
const_iterator end() const { return nullptr; }
};
53
Класс ноды дерева
class Node {
int Key;
shared_ptr<Node> Left;
shared_ptr<Node> Right;
RCU_Stack Stack;
public:
Node(int key, int data) : Key(key) {
Stack.push(data);
}
void add_node_data(int i, int data);
void find_docs(int from, int to, set<int>* docs);
static void make_copy(
shared_ptr<Node>& node
);
};
54
Класс ноды дерева
void Node::add_node_data(int i, int data) {
if (i == Key) {
Stack.push(data);
} else if (i < Key && Left) {
make_copy(Left);
Left->add_node(i);
} else if (i > Key && Right) {
make_copy(Right);
Right->add_node(i);
} else {
shared_ptr<Node> new_child(new Node(i));
if (i < Key) Left = new_child;
else Right = new_child;
}
}
55
Класс ноды дерева
void Node::find_docs(
int from, int to, set<int>* docs) {
if (from <= Key && Key < to) {
for (auto doc : Stack)
docs->insert(doc);
}
if (from < Key && Left) {
Left->find_docs(from, to, docs);
}
if (Key < to && Right) {
Right->find_docs(from, to, docs);
}
}
56
Класс ноды дерева
void Node::make_copy(shared_ptr<Node>& node) {
if (node.use_count() > 1) {
shared_ptr<Node> new_node(new Node(*node));
node = new_node;
}
}
57
Класс RCU-дерева
class RCU_Tree {
class Node;
shared_ptr<Node> Root;
public:
void add_node_data(int i, int data);
void find_docs(
int from, int to, set<int>* docs);
};
58
Класс RCU-дерева
void RCU_Tree::add_node_data(int i, int data) {
Node::make_copy(Root);
if (Root) {
Root->add_node_data(i, data);
return;
}
shared_ptr<Node> new_root(
new Node(i, data));
Root = new_root;
}
59
Класс RCU-дерева
void RCU_Tree::find_docs(
int from, int to, set<int>* docs)
{
if (Root) {
Root->find_docs(from, to, docs);
}
}
60
Класс версии
struct Version {
RCU_Tree Tree;
};
61
Класс очереди версий (part 1)
class Versions_queue {
using version_ptr = shared_ptr<Version>;
using version_info = pair<version_ptr, int>;
atomic<int> Client_id;
atomic<Version*> Current;
atomic<int> Current_version_id;
array<atomic<int>, 1000> Client_version_ids;
queue<version_info> Queue;
...
};
62
Класс очереди версий (part 2)
class Versions_queue {
...
void cleanup_unused_versions() {
int max_client_id = Client_id;
auto begin = Client_version_ids.begin();
auto end = begin + max_client_id;
int min_ver_id = Current_version_id;
for (auto it = begin; it != end; ++it) {
int client_ver_id = *it;
if (client_ver_id && min_ver_id > cient_ver_id)
min_ver_id = client_ver_id;
}
while (!Queue.empty() &&
Queue.front().second < min_ver_id) Queue.pop();
}
...
};
63
Класс очереди версий (part 3)
class Versions_queue {
...
public:
Versions_queue()
: Client_id(0), Current_version_id(1) {
for (auto& version : Client_version_ids)
version = 0;
version_ptr new_version(new Version);
Queue.push(new_version);
Current = new_version.get();
}
int get_client_id() {
return Client_id++;
}
...
};
64
Класс очереди версий (part 4)
class Versions_queue {
...
public:
...
Version* get_current_version(int client_id) {
int version_id = Current_version_id;
Client_version_ids[client_id] = version_id;
return Current;
}
...
};
65
Класс очереди версий (part 5)
class Versions_queue {
...
public:
...
Version* create_new_version() {
cleanup_unused_versions();
version_ptr ret(new Version(*Current));
Queue.push(
version_info(ret, Current_version_id + 1));
return ret.get();
}
...
};
66
Класс очереди версий (part 6)
class Versions_queue {
...
public:
...
void release_current_version(int client_id) {
Client_version_ids[client_id] = 0;
}
void release_new_version(Version* version) {
Current = version;
++Current_version_id;
}
};
67
Класс сессии на запись
class Write_session {
Versions_queue* Queue;
Version* New_version;
public:
Write_session(Versions_queue* queue)
: Queue(queue)
, New_version(Queue->create_new_version()) { }
~Write_session() {
Queue->release_new_version(New_version);
}
Version* operator->() {
return New_version;
}
};
68
Класс сессии на чтение
class Read_session {
Versions_queue* Queue;
int Client_id;
Version* Current_version;
public:
Read_session(Versions_queue* queue, int client_id)
: Queue(queue)
, Client_id(client_id)
, Current_version(Queue->get_current_version(Client_id))
{ }
~Read_session() {
Queue->release_current_version(Client_id);
}
Version* operator->() {
return Current_version;
}
};
69
Использование сессии на запись
for (int doc = 0; doc < 1000; ++doc) {
Write_session session(&Queue);
for (int j = 0; j < 1000; ++j) {
session->Tree.add_node_data(
random_number(), doc);
}
}
70
Использование сессии на чтение
int sum = 0;
int client_id = Queue.get_client_id();
for (int i = 0; i < 7200; ++i) {
set<int> docs;
Read_session session(&Queue, client_id);
for (int j = 0; j < 1000; ++j) {
int from = random_number();
int to = from + 10;
session->Tree.check_node(from, to, &docs);
}
sum += docs.size();
}
71
Замеряем время работы треда
0
5
10
15
20
25
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
Write
Read
Количество ядер
72
• Введение
• Мотивировка
• Традиционный подход к lock-free
• Read Copy Update
• Версионный подход
• Большой пример
• Заключение
План
73
• Lock-free позволяет достичь максимальной
производительности на многих ядрах
• Общие алгоритмы lock-free пока ещё сложны
• Конкретные подслучаи доступны для
реализации любому разработчику
• Предложенный подход позволяет легко
сделать lock-free RealTime-Server в модели
Writer + N x Reader
• Подход разграничивает Write / Read – можно
использовать message passing для Write и т.д.
Заключение
Константин Облаков
Разработчик группы
обработки свежего контента
kostik@yandex-team.ru
Спасибо за
внимание!

More Related Content

PDF
Григорий Демченко — Асинхронное программирование и сопрограммы
PPTX
Асинхронность и сопрограммы
PDF
Цена ошибки
PDF
C++ STL & Qt. Занятие 05.
PDF
Для чего мы делали свой акторный фреймворк и что из этого вышло?
PDF
Parallel STL
PPTX
Практика использования Dependency Injection
PPTX
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...
Григорий Демченко — Асинхронное программирование и сопрограммы
Асинхронность и сопрограммы
Цена ошибки
C++ STL & Qt. Занятие 05.
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Parallel STL
Практика использования Dependency Injection
Александр Тарасенко, Использование python для автоматизации отладки С/C++ код...

What's hot (20)

PPTX
Asynchrony and coroutines
PDF
Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)
PDF
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
PPTX
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
PDF
C++ STL & Qt. Занятие 10.
PDF
Hunting for a C++ package manager
PDF
Использование юнит-тестов для повышения качества разработки
PDF
Павел Довгалюк, Обратная отладка
PPTX
Применение фреймворка GStreamer в системе видеонаблюдения
PDF
Многопоточное программирование на C#, путевые заметки
PDF
Архитектура. Доступноять программных систем.
PPTX
Александр Фокин, Рефлексия в C++
PDF
PDF
Полный цикл тестирования React-приложений, Алексей Андросов и Наталья Стусь
PDF
JavaScript. Event Loop and Timers (in russian)
PDF
Борис Сазонов, RAII потоки и CancellationToken в C++
PDF
Java tricks for high-load server programming
PPTX
Multithreading in java past and actual
PPTX
Bytecode
PDF
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Asynchrony and coroutines
Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
C++ STL & Qt. Занятие 10.
Hunting for a C++ package manager
Использование юнит-тестов для повышения качества разработки
Павел Довгалюк, Обратная отладка
Применение фреймворка GStreamer в системе видеонаблюдения
Многопоточное программирование на C#, путевые заметки
Архитектура. Доступноять программных систем.
Александр Фокин, Рефлексия в C++
Полный цикл тестирования React-приложений, Алексей Андросов и Наталья Стусь
JavaScript. Event Loop and Timers (in russian)
Борис Сазонов, RAII потоки и CancellationToken в C++
Java tricks for high-load server programming
Multithreading in java past and actual
Bytecode
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Ad

Viewers also liked (20)

PDF
Конкурентные ассоциативные контейнеры
PDF
Multithreading done right
PDF
Better Code: Concurrency
PDF
High quality library from scratch
PDF
Debugging and Profiling C++ Template Metaprograms
PDF
Конверсия управляемых языков в неуправляемые
PDF
Address/Thread/Memory Sanitizer
PDF
Дмитрий Копляров , Потокобезопасные сигналы в C++
ODP
Антон Полухин. C++17
PDF
С++ without new and delete
PPTX
Gor Nishanov, C++ Coroutines – a negative overhead abstraction
PDF
Антон Бикинеев, Reflection in C++Next
PPTX
Григорий Демченко, Универсальный адаптер
PPTX
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
PDF
Clang tidy
PDF
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
PPTX
Алексей Кутумов, C++ без исключений, часть 3
PDF
Догнать и перегнать boost::lexical_cast
PPTX
Фитнес для вашего кода: как держать его в форме
PPTX
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
Конкурентные ассоциативные контейнеры
Multithreading done right
Better Code: Concurrency
High quality library from scratch
Debugging and Profiling C++ Template Metaprograms
Конверсия управляемых языков в неуправляемые
Address/Thread/Memory Sanitizer
Дмитрий Копляров , Потокобезопасные сигналы в C++
Антон Полухин. C++17
С++ without new and delete
Gor Nishanov, C++ Coroutines – a negative overhead abstraction
Антон Бикинеев, Reflection in C++Next
Григорий Демченко, Универсальный адаптер
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Clang tidy
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Алексей Кутумов, C++ без исключений, часть 3
Догнать и перегнать boost::lexical_cast
Фитнес для вашего кода: как держать его в форме
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
Ad

Similar to Практика Lock-free. RealTime-сервер (20)

PDF
Оптимизация программ для современных процессоров и Linux, Александр Крижановс...
PPT
Как построить высокопроизводительный Front-end сервер (Александр Крижановский)
PPTX
Multiprocessor Programming Intro (lecture 3)
PDF
Разработка высокопроизводительных серверных приложений для Linux/Unix (Алекса...
PDF
20090222 parallel programming_lecture01-07
PDF
Что особенного в СУБД для данных в оперативной памяти / Константин Осипов (Ta...
PDF
Антон Потапов — С++ контейнеры и многопоточность: вместе или врозь?
PDF
Nonblocking algorithms/CAS/Atomics by Alexey Fyodorov
PDF
Конкурентные ассоциативные контейнеры
PPTX
Multiprocessor Programming Intro (lecture 2)
ODP
GetDev.NET: Снова Эрланг
PDF
Atomics, CAS and Nonblocking algorithms
PDF
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
PDF
Purely practical data structures
PDF
Евгений Лазин. Неизменяемая структура данных HAMT для создания БД в памяти
PDF
20100307 virtualization igotti_lecture04
PDF
Помоги ближнему, или Как потоки помогают друг другу
PDF
Помоги ближнему, или Как потоки помогают друг другу
PDF
Синхронизация данных Peer-To-Peer
PDF
Дмитрий Жестилевский — Yet another threading framework: асинхронная разработк...
Оптимизация программ для современных процессоров и Linux, Александр Крижановс...
Как построить высокопроизводительный Front-end сервер (Александр Крижановский)
Multiprocessor Programming Intro (lecture 3)
Разработка высокопроизводительных серверных приложений для Linux/Unix (Алекса...
20090222 parallel programming_lecture01-07
Что особенного в СУБД для данных в оперативной памяти / Константин Осипов (Ta...
Антон Потапов — С++ контейнеры и многопоточность: вместе или врозь?
Nonblocking algorithms/CAS/Atomics by Alexey Fyodorov
Конкурентные ассоциативные контейнеры
Multiprocessor Programming Intro (lecture 2)
GetDev.NET: Снова Эрланг
Atomics, CAS and Nonblocking algorithms
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
Purely practical data structures
Евгений Лазин. Неизменяемая структура данных HAMT для создания БД в памяти
20100307 virtualization igotti_lecture04
Помоги ближнему, или Как потоки помогают друг другу
Помоги ближнему, или Как потоки помогают друг другу
Синхронизация данных Peer-To-Peer
Дмитрий Жестилевский — Yet another threading framework: асинхронная разработк...

More from Platonov Sergey (20)

PPTX
Евгений Зуев, С++ в России: Стандарт языка и его реализация
PPT
Евгений Крутько, Многопоточные вычисления, современный подход.
PDF
Тененёв Анатолий, Boost.Asio в алгоритмической торговле
PPTX
Павел Беликов, Опыт мигрирования крупного проекта с Windows-only на Linux
PDF
Дмитрий Кашицын, Вывод типов в динамических и не очень языках II
PDF
Дмитрий Кашицын, Вывод типов в динамических и не очень языках I
PDF
QML\Qt Quick на практике
PDF
Визуализация автомобильных маршрутов
PDF
Функциональный микроскоп: линзы в C++
PDF
C++ exceptions
PPTX
Как мы уменьшили количество ошибок в Unreal Engine с помощью статического ана...
PDF
HPX: C++11 runtime система для параллельных и распределённых вычислений
PPTX
Ranges calendar-novosibirsk-2015-08
PDF
Использование maven для сборки больших модульных c++ проектов на примере Odin...
PDF
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
PDF
One definition rule - что это такое, и как с этим жить
PDF
DI в C++ тонкости и нюансы
PPTX
Аскетичная разработка браузера
PDF
Concepts lite
PDF
Денис Кормалев Метаобъектная система Qt
Евгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Крутько, Многопоточные вычисления, современный подход.
Тененёв Анатолий, Boost.Asio в алгоритмической торговле
Павел Беликов, Опыт мигрирования крупного проекта с Windows-only на Linux
Дмитрий Кашицын, Вывод типов в динамических и не очень языках II
Дмитрий Кашицын, Вывод типов в динамических и не очень языках I
QML\Qt Quick на практике
Визуализация автомобильных маршрутов
Функциональный микроскоп: линзы в C++
C++ exceptions
Как мы уменьшили количество ошибок в Unreal Engine с помощью статического ана...
HPX: C++11 runtime система для параллельных и распределённых вычислений
Ranges calendar-novosibirsk-2015-08
Использование maven для сборки больших модульных c++ проектов на примере Odin...
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
One definition rule - что это такое, и как с этим жить
DI в C++ тонкости и нюансы
Аскетичная разработка браузера
Concepts lite
Денис Кормалев Метаобъектная система Qt

Практика Lock-free. RealTime-сервер