
Неблокирующие алгоритмы 31 11 2014.pptx
- Количество слайдов: 27
СПБГПУ Кафедра “Прикладная математика” Неблокирующие алгоритмы Доклад на семинаре по специальности Студент гр. 43601/2 Виктор Кацман 02. 12. 2014
План доклада ● История ● Идеи и проблемы ● Классификация ● Примеры ● Производительность 2
Введение ● Разделяемый ресурс - ресурс, совместно используемый несколькими процессами ● Критический ресурс - разделяемый ресурс, который в каждый момент времени может быть доступен не более чем одному процессу ● Взаимное исключение – форма синхронизации, предотвращает одновременный доступ к ресурсу более чем одним потоком ● Задачи: ○ Распределение ресурсов между процессами ○ Взаимное исключение доступа к критическому ресурсу ➢ Писатель не имеет доступа к ресурсу, занятому другим процессом ➢ Читатель всегда считывает корректные значения 3
Введение ● Решения: ○ Блокировка процесса, требующего критический ресурс, занятый другим ресурсом ■ Сохранение контекста процесса и его восстановление занимает много времени ■ Возможна инверсия приоритетов ■ Время работы процесса может быть очень долгим ■ Возникают тупики (deadlocks) ○ Неблокирующая синхронизация – совместное использование ресурсов потоками идет за счет атомарных операций и специальных механизмов ■ Позволяет исправить перечисленные недостатки блокирующих алгоритмов 4
История ● До 1970 года - идеи ● 1970 год - появление CAS (IBM 370) ● 1973 год - появление первой мильтипрограмной ОС UNIX ● 1980 - 1990 годы - появление Macontosh ОС, появление Windows - повышение интереса к неблокирующим алгоритмам, появление их классификации ● 1990 - 2000 годы - выявление ABA проблемы, повышение практического использования неблокирующих синхронизаций ● 2000 - 2010 годы - активная разработка неблокирующих структур данных 5
● Поток, пытаясь получить доступ к критическому ресурсу, проверяет флаг занятости. Если ресурс недоступен, поток через некоторое время снова пытается получить доступ к ресурсу o Для проверки флага занятости можно использовать CAS ● Эффективнее блокировок при малонагруженном критическом ресурсе ● Модификация: между попытками получить доступ к ресурсу, поток выполняет некоторую полезную работу Без блокировок С блокировками Идеи. Аналог блокировки Поток 1 Поток 2 Работа вне критической секции Переключение контекста Работа с критическим ресурсом Бесполезная работа 6
Идеи. Изменение с проверкой ● Сначала выполняем критический участок, а потом проверяем, не был ли он изменен за это время другим процессом ● Алгоритм: 1. Прочесть значение критической переменной и сохранить его в регистре 1 2. Изменить его (выполнить критический участок) и поместить его в регистр 2 3. Сравнить значение в памяти с регистром 1 и обновить его, если оно не было изменено за это время другим потоком, если было – повторить процедуру (с шага 1) - Для «одновременной» замены и обновления используем CAS ● Проблема: значение в памяти могло сначала измениться, а потом принять прежнее значение 7
Идеи. Работа с копией ● Все объекты конструируются как динамический набор узлов, соединенных указателями ● Для каждого объекта есть указатель, через который осуществляется исключительный доступ ● Алгоритм для обновления разделяемых данных: 1. 2. 3. 4. new Подготовить копию объекта Обновить копию Заменить указатель на объект указателем на копию через CAS операцию Если произошел конфликт, то удалить текущую копию и перейти к шагу 1. 8
Идеи. Использование разных буферов ● Обобщение работы с копиями: o Объект копируется в разные буферы o Операции обновления перенаправляются в разные буферы o Буфер с активным значением активируется через сменный указатель ● Минус: непонятно, какой из буферов содержит активное значение ➢ Ситуация: первое обновление началось раньше второго и закончилось позже. Если активное значение содержит буфер: ○ последним обновившийся - в итоге останется первое обновление ○ последним начавший обновляться - первое обновление потеряется Компромисс: можно при записи присваивать ей приоритет 9
Идеи. Упорядочивание чтения и записи ● Чтение и запись проходят в строго определенном порядке o Пример: § Запись происходит справа-налево, а чтение слева-направо § Мы можем точно сказать, что читающий поток сначала прочтет все не измененные символы, затем символы, измененные только одним писателем, потом двумя и т. д. читатель писатель 0100011110011011 o Альтернатива атомарным инструкциям и мгновенным обновлениям ресурсов § Выигрыш по времени 10
Идеи. Анонсирование операции (1) 1. Каждый поток анонсирует информацию (помещает её в некоторую очередь) о том, что он собирается делать с критическим ресурсом 2. Если это не противоречит “планам” других потоков, уже анонсировавших информацию, поток приступает к работе с критическим ресурсом 3. Если противоречит, то поток ждет. В некоторых реализациях поток помогает процессу, работающему с критическим ресурсом в настоящий момент 4. Когда очередь подходит, поток начинает работу с критическим ресурсом. Возможно, другие потоки из очереди ему помогают 11
Идеи. Анонсирование операции (2) ➢ Пример: ○ Критический ресурс разделен на три части А, Б и В ○ Первый поток анонсирует, что он будет обновлять только часть А, получает доступ к критическому ресурсу и начинает работу ○ Второй поток анонсирует, что он хочет обновить части Б и В (прибавить к каждому числу по 1), получает доступ к критическому ресурсу и начинает работу ○ Третий поток анонсирует, что он хочет обновить часть Б, попадает в очередь и начинает помогать второму потоку (сам прибавляет 1 к еще не обновленным числам) ○ Второй поток заканчивает работу, третий - начинает ● Метод эффективен при четком разделении работы потока на шаги, а критического ресурса на непересекающиеся части 12
ABA-проблема ● Одно и тоже значение в некоторой ячейке, прочитанное потоком в разные моменты времени часто считается признаком отсутствия изменений ● После первого прочтения/установки значения в ячейке потоком А, поток В может поменять это значение, затем восстановить его (так же оно может восстановиться другим потоком). В этом случае во второй раз поток А прочтет потом то же самое значение и сочтет, что значение в ячейке не изменилось ➢ Пример (неблокирующий стек на списках): ○ Считаем, что стек не изменился, если не изменился указатель на первый элемент. ○ Второй поток сделал А = pop(); B = pop(); push(A); - Первый элемент снова А. 13
Решения ABA-проблемы ● Хранить вместе с указателем некоторое целое число (например, номер версии). Для атомарной замены можно использовать CAS двойной ширины (dw. CAS) или двойной (DCAS) ● Хранить число ссылок на объект и удалять его (освобождать память) только тогда, когда все потоки перестанут его использовать ● Хранить опасные указатели (hazard pointers) - указатели, хранящие ссылки на объекты, которые сейчас используются и которые нельзя удалить 14
Технология RCU (Read-Copy-Update) ● Читатель - поток, выполняющий любую операцию, кроме удаления данных. Изменения данных он может производить через атомарные операции, параллельно с чтением. ● Писатель - поток, удаляющий данные. В момент удаления данные не должны никем использоваться. ● Период отсрочки (grace period) - период времени с момента исключения писателем элемента из структуры, до момента когда все читатели, начавшие работу с критическим ресурсом до писателя закончат её. 15
Технология RCU. Пример Читатель 1 Потоки Читатель 2 Читатель 3 чтение удаление чтение Читатель 4 Писатель чтение период отсрочки Ждем завершения уже начатых чтений чтение обновление Время 16
Уровни синхронизации ● Без препятствий (obstruction-free) - поток окончит работу с ресурсом за конечное число шагов при отсутствии конкуренции со стороны других потоков wait-free ● Без блокировок (lock-free) - хотя бы один поток завершит работу с ресурсом за конечное число шагов ● Без ожиданий (wait-free) - любой поток завершит работу с ресурсом за конечное число шагов lock-free obstruction-free 17
Требования к очереди Очередь без блокировок. Задача ● Каждый записанный элемент должен либо быть прочитан, либо находиться в очереди ● Если очередь стала пустой, то число чтений из очереди равно числу записей в нее ● Если элемент А записан раньше, чем элемент В, то элемент А должен быть прочитан раньше, чем элемент В ● Хотя бы один из потоков, пытающихся прочитать элемент из очереди или добавить его, должен закончить операцию 18
Очередь без блокировок. Реализация(1) инициализация: Q->head Q->tail next node = new_node(); node->next = NULL; Q->head = Q->tail = node; node enqueue (val): Q->head null Q->tail E 1: node = new_node(); node->val = val; node->next = NULL; E 2: while (true) { tail = Q->tail; next = tail->next; E 3: if (tail == Q->tail) { E 4: else { CAS (&Q->tail, next) } next if (next == NULL) { if (CAS (&tail->next, node)) { break } } E 5: tail E 6: } E 7: } CAS (&Q->tail, node) next node null 19
Очередь без блокировок. Реализация(2) dequeue (pval): Q->head Q->tail D 1: while (true) { D 2: head = Q->head; tail = Q->tail; next = head->next; D 3: if (head == Q->head) { D 4: head tail next if (head == tail) { D 5: if (next == NULL) { return false } D 6: CAS (&Q->tail, next) } D 7: else { D 8: *pval = next->val D 9: if (CAS (&Q->head, next)) { break } } D 10: } Q->head Q->tail node next node null D 11: } free (head); return true 20
Очередь без препятствий ● Любой поток при отсутствии конкуренции должен закончить работу с элементом Без блокировок: enqueue (val): Только без препятствий: enqueue (val): E 1: node = new_node(); node->val = val; ------------------------------------- node->next = NULL; E 2: while (true) { tail = Q->tail; next = tail->next; E 3: if (tail == Q->tail) { E 4. 1: if (next == NULL) { CAS (&Q->tail, node) E 3: if (tail == Q->tail) { E 4. 2: E 4. 3: if (Q->tail == node) break } E 4. 4: else { tail->next = null } } if (next == NULL) { if (CAS (&tail->next, node)) { break } } E 5: else { CAS (&Q->tail, next) } if (CAS (&tail->next, node)) { E 4. 5: else { Q->tail = tail } } E 5: else { CAS (&Q->tail, next) } E 6: } E 7: } CAS (&Q->tail, node) E 7: } 21
Требования к очереди Очередь без ожиданий. Задача ● Каждый записанный элемент должен либо быть прочитан, либо находиться в очереди ● Если очередь стала пустой, то число чтений из очереди равно числу записей в нее ● Если элемент А записан раньше, чем элемент В, то элемент А должен быть прочитан раньше, чем элемент В ● Все потоки, пытающиеся прочитать элемент из очереди или добавить его, должен закончить операцию 22
Очередь без ожиданий. Реализация 23
Библиотека ● C++ библиотека lock-free структур данных и методов безопасного освобождения памяти для этих структур. ● Содержит o Алгоритмы SRM - примитивы неблокирующей синхронизации. Среди них, в том числе разновидности hazard pointer, RCU o Объект GC - объектно-ориентированная надстройка над внутренней реализацией SRM o lock-free контейнеры o Настройки, позволяющие, например, выбирать различные стратегии действий, устанавливать аллокатор, используемый для создания элементов контейнера, указывать предикаты сравнений ● Другие библиотеки: java. util. concurent, boost: : lockfree 24
Скорость работы ● График зависимости времени работы 100 000 вставок и удалений в очередь от числа потоков: ● Атомарная инструкция - трудоемкая операция ● Для индивидуальной задачи может быть придуман более быстрый алгоритм 25
Выводы ● Неблокирующие алгоритмы позволяют: ○ Решить проблему возникновения тупиков ○ Сильно ускорить работу многопоточных приложений и “хорошо распараллеливаемых” алгоритмов при индивидуальном подходе к задаче ● Не имеет смысла применение неблокирующих алгоритмов к: ○ Задачам с редкими обращениями к критическим ресурсам ○ Приложениям с малым числом потоков ● Чаще всего используются неблокирующие алгоритмы “без блокировок”, но не “без ожиданий” ● При оценке трудоемкости многопоточных приложений необходимо учитывать число вероятных блокировок и атомарных операций 26
Литература ● ● ● M. Michel, L. Scott “Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms” Rochester University, 1996 M. Fomitchev “Lock-free linked lists and skip lists”, York University, 2003 M. Herlihy “Obstruction-Free Synchronization: Double-Ended Queries as an Example”, Brown University, 2003 M. Herlihy, N. Shavit, “The Art of Multiprocessor Programming”, Morgan Kaufmann, 2008, стр. 425 -431, 543 -546 Э. Уильямс, “Параллельное программирование на С++ в действии”, ДМК Пресс, 2012 M. David “A Single-Enqueuer Wait-Free Queue Implementation”, Toronto University, 2005 L. Lamport “Concurent Reading and Writing”, Microsoft Research, 1977 https: //gist. github. com/ujentus/4030262 http: //queue. acm. org/detail. cfm? id=2492433 http: //libcds. sourceforge. net/ http: //habrahabr. ru/company/ifree/blog/196834/ http: //habrahabr. ru/company/ifree/blog/206984/ 27
Неблокирующие алгоритмы 31 11 2014.pptx