Неблокирующий алгоритм - Non-blocking algorithm

В Информатика, алгоритм называется неблокирующий если сбой или приостановка любой нить не может вызвать сбой или приостановку другого потока;[1] для некоторых операций эти алгоритмы представляют собой полезную альтернативу традиционным блокирующие реализации. Неблокирующий алгоритм без блокировки если есть гарантированный общесистемный прогресс, и без ожидания если также гарантируется прогресс по потоку. «Неблокирование» использовалось как синоним «без блокировки» в литературе до введения в 2003 году свободы от препятствий.[2]

Слово «неблокирующий» традиционно использовалось для описания телекоммуникационные сети который может маршрутизировать соединение через набор реле, «без необходимости переупорядочивать существующие вызовы», см. Сеть Clos. Кроме того, если телефонная станция «исправна, она всегда может установить соединение», см. неблокирующий переключатель минимального охвата.

Мотивация

Традиционный подход к многопоточному программированию заключается в использовании замки синхронизировать доступ к общим Ресурсы. Примитивы синхронизации, такие как мьютексы, семафоры, и критические разделы - это все механизмы, с помощью которых программист может гарантировать, что определенные участки кода не будут выполняться одновременно, если это повредит структуры разделяемой памяти. Если один поток пытается получить блокировку, которая уже удерживается другим потоком, поток будет блокироваться, пока блокировка не освободится.

Блокировка потока может быть нежелательной по многим причинам. Очевидная причина заключается в том, что пока поток заблокирован, он не может ничего сделать: если заблокированный поток выполнял высокоприоритетный или в реальном времени задача, было бы крайне нежелательно останавливать ее выполнение.

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

В отличие от алгоритмов блокировки, неблокирующие алгоритмы не страдают этими недостатками и, кроме того, безопасны для использования в обработчики прерываний: хотя вытесненный поток не может быть возобновлен, прогресс все еще возможен без этого. Напротив, глобальные структуры данных, защищенные взаимным исключением, не могут быть безопасно доступны в обработчике прерывания, поскольку вытесняемый поток может быть тем, который удерживает блокировку, но это можно легко исправить, замаскировав запрос прерывания во время критического участка.[3]

Для повышения производительности можно использовать структуру данных без блокировки. Структура данных без блокировки увеличивает количество времени, затрачиваемого на параллельное выполнение, а не на последовательное выполнение, повышая производительность на многоядерный процессор, потому что доступ к общей структуре данных не требует сериализации, чтобы оставаться согласованным.[4]

Выполнение

За некоторыми исключениями, неблокирующие алгоритмы используют атомный читать-изменять-писать примитивы, которые должно обеспечивать оборудование, наиболее примечательным из которых является сравнить и поменять местами (CAS). Критические разделы почти всегда реализуются с использованием стандартных интерфейсов над этими примитивами (в общем случае критические секции будут блокироваться, даже если они реализованы с этими примитивами). До недавнего времени все неблокирующие алгоритмы приходилось писать «изначально» с базовыми примитивами для достижения приемлемой производительности. Однако возникающая область программная транзакционная память обещает стандартные абстракции для написания эффективного неблокирующего кода.[5][6]

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

Кроме того, некоторые неблокирующие структуры данных достаточно слабы, чтобы их можно было реализовать без специальных атомарных примитивов. Эти исключения включают:

  • одиночный читатель одиночный писатель кольцевой буфер ФИФО, с размером, который равномерно делит переполнение одного из доступных беззнаковых целочисленных типов, может безусловно быть реализовано безопасно используя только барьер памяти
  • Чтение-копирование-обновление с одним писателем и любым количеством читателей. (Читатели не ждут; писатель обычно свободен от блокировок, пока ему не понадобится освободить память).
  • Чтение-копирование-обновление с несколькими авторами и любым количеством читателей. (Читатели не ждут; несколько писателей обычно сериализуются с блокировкой и не свободны от препятствий).

Некоторые библиотеки внутренне используют безблокировочные методы,[7][8][9] но трудно написать правильный код без блокировки.[10][11][12][13]

Ждать-свобода

Свобода ожидания - это самая надежная неблокирующая гарантия прогресса, сочетающая гарантированную общесистемную пропускную способность с голодание -Свобода. Алгоритм не требует ожидания, если каждая операция имеет ограничение на количество шагов, которые алгоритм предпримет до завершения операции.[14]Это свойство критически важно для систем реального времени, и всегда полезно иметь, если стоимость производительности не слишком высока.

Был показан в 1980-х годах.[15] что все алгоритмы могут быть реализованы без ожидания, и многие преобразования из последовательного кода, называемые универсальные конструкции, были продемонстрированы. Однако полученная производительность в целом не соответствует даже наивным схемам блокировки. С тех пор несколько работ улучшили характеристики универсальных конструкций, но все же их характеристики намного ниже, чем у блочных конструкций.

В нескольких статьях исследовалась сложность создания алгоритмов без ожидания. Например, было показано[16] что широко доступные атомные условный примитивы CAS и LL / SC, не может обеспечить безостановочную реализацию многих распространенных структур данных без линейного роста затрат памяти по количеству потоков.

Но на практике эти нижние границы не представляют собой реального препятствия, поскольку расходование строки кэша или гранулы эксклюзивного резервирования (до 2 КБ на ARM) хранилища на поток в общей памяти не считается слишком дорогостоящим для практических систем (обычно количество логически требуется сохранить - это слово, но физически операции CAS в одной и той же строке кэша будут конфликтовать, а операции LL / SC в одной и той же грануле эксклюзивного резервирования будут конфликтовать, поэтому физически необходимый объем хранилища[нужна цитата ] лучше).

До 2011 года алгоритмы без ожидания были редкостью как в исследованиях, так и на практике. Однако в 2011 году Коган и Петранк[17] представил здание очереди без ожидания на CAS примитивный, обычно доступный на обычном оборудовании. Их конструкция расширила очередь Майкла и Скотта без блокировки,[18] это эффективная очередь, часто используемая на практике. Последующий доклад Когана и Петранк[19] предоставил метод для ускорения алгоритмов без ожидания и использовал этот метод, чтобы сделать очередь без ожидания практически такой же быстрой, как и его аналог без блокировки. Следующая статья Тимната и Петранк[20] предоставил автоматический механизм для генерации структур данных без ожидания из структур без блокировки. Таким образом, для многих структур данных теперь доступны реализации без ожидания.

Блокировка-свобода

Отсутствие блокировки позволяет отдельным потокам зависать, но гарантирует пропускную способность всей системы. Алгоритм является свободным от блокировок, если, когда потоки программы выполняются в течение достаточно длительного времени, по крайней мере один из потоков достигает прогресса (для некоторого разумного определения прогресса). Все алгоритмы без ожидания являются свободными от блокировок.

В частности, если один поток приостановлен, то алгоритм без блокировки гарантирует, что оставшиеся потоки все еще могут работать. Следовательно, если два потока могут бороться за одну и ту же блокировку мьютекса или спин-блокировку, тогда алгоритм будет нет без блокировки. (Если мы приостановим один поток, удерживающий блокировку, второй поток будет заблокирован.)

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

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

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

Правильная параллельная поддержка, как правило, является наиболее сложной частью алгоритма без блокировок и часто требует больших затрат для выполнения: не только замедляется вспомогательный поток, но и благодаря механике разделяемой памяти поток, которому оказывается помощь, также замедляется. , если он все еще работает.

Свобода препятствий

Свобода от препятствий - это самая слабая естественная неблокирующая гарантия прогресса. Алгоритм является свободным от препятствий, если в любой момент единственный поток, выполняемый изолированно (то есть со всеми блокирующими потоками, приостановленными) в течение ограниченного числа шагов, завершит свою работу.[14] Все алгоритмы без блокировок не имеют препятствий.

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

Некоторые алгоритмы, работающие без препятствий, используют пару «маркеров согласованности» в структуре данных. Процессы, считывающие структуру данных, сначала считывают один маркер согласованности, затем считывают соответствующие данные во внутренний буфер, затем считывают другой маркер, а затем сравнивают маркеры. Данные согласованы, если два маркера идентичны. Маркеры могут быть неидентичными, когда чтение прерывается другим процессом, обновляющим структуру данных. В таком случае процесс сбрасывает данные во внутреннем буфере и пытается снова.

Смотрите также

Рекомендации

  1. ^ Гётц, Брайан; Пайерлс, Тим; Блох, Джошуа; Bowbeer, Джозеф; Холмс, Дэвид; Ли, Дуг (2006). Параллелизм Java на практике. Река Аппер Сэдл, Нью-Джерси: Аддисон-Уэсли. п.41. ISBN  9780321349606.
  2. ^ Herlihy, M .; Luchangco, V .; Мойр, М. (2003). Синхронизация без препятствий: двусторонние очереди в качестве примера (PDF). 23-е Международная конференция по распределенным вычислительным системам. п. 522.
  3. ^ Батлер В. Лэмпсон; Дэвид Д. Ределл (Февраль 1980 г.). «Опыт работы с процессами и мониторами в Mesa». Коммуникации ACM. 23 (2): 105–117. CiteSeerX  10.1.1.142.5765. Дои:10.1145/358818.358824.
  4. ^ Гийом Марсе и Карл Кингсфорд.«Быстрый подход без блокировок для эффективного параллельного подсчета появления k-мер» Биоинформатика (2011) 27 (6): 764-770.Дои:10.1093 / биоинформатика / btr011"Счетчик медузы мер".
  5. ^ Харрис, Тим; Фрейзер, Кейр (26 ноября 2003 г.). «Языковая поддержка для облегченных транзакций» (PDF). Уведомления ACM SIGPLAN. 38 (11): 388. CiteSeerX  10.1.1.58.8466. Дои:10.1145/949343.949340.
  6. ^ Харрис, Тим; Marlow, S .; Peyton-Jones, S .; Херлихи, М. (15–17 июня 2005 г.). «Транзакции составной памяти». Материалы симпозиума ACM SIGPLAN 2005 г. по принципам и практике параллельного программирования, PPoPP '05: Чикаго, Иллинойс. Нью-Йорк, штат Нью-Йорк: ACM Press. С. 48–60. Дои:10.1145/1065944.1065952. ISBN  978-1-59593-080-4.
  7. ^ libcds - C ++ библиотека безблокирующих контейнеров и схема безопасного восстановления памяти
  8. ^ liblfds - Библиотека lock-free структур данных, написанная на C
  9. ^ Комплект для параллелизма - Библиотека C для проектирования и реализации неблокирующей системы
  10. ^ Херб Саттер. «Код без блокировки: ложное чувство безопасности». Архивировано из оригинал на 2015-09-01.
  11. ^ Херб Саттер. «Написание кода без блокировки: исправленная очередь». Архивировано из оригинал на 2008-12-05.
  12. ^ Херб Саттер. «Написание обобщенной параллельной очереди».
  13. ^ Херб Саттер. "Проблема с замками".
  14. ^ а б Энтони Уильямс."Безопасность: выключено: Как не прострелить себе ногу с помощью атомики C ++".2015.p. 20.
  15. ^ Херлихи, Морис П. (1988). Невозможность и универсальность результатов без ожидания синхронизации. Proc. 7-й ежегодный симпозиум ACM. по принципам распределенных вычислений. С. 276–290. Дои:10.1145/62546.62593. ISBN  0-89791-277-2.
  16. ^ Фич, Вера; Хендлер, Дэнни; Шавит, Нир (2004). О внутренней слабости примитивов условной синхронизации. Proc. 23-й ежегодный симпозиум ACM по принципам распределенных вычислений (PODC). С. 80–87. Дои:10.1145/1011767.1011780. ISBN  1-58113-802-4.
  17. ^ Коган, Алексей; Петранк, Эрез (2011). Очереди без ожидания с множеством объектов очереди и удаления из очереди (PDF). Proc. 16-я конференция ACM SIGPLAN Symp. по принципам и практике параллельного программирования (PPOPP). С. 223–234. Дои:10.1145/1941553.1941585. ISBN  978-1-4503-0119-0.
  18. ^ Майкл, Магед; Скотт, Майкл (1996). Простые, быстрые и практичные неблокирующие и блокирующие параллельные алгоритмы очереди. Proc. 15-й ежегодный симпозиум ACM. по принципам распределенных вычислений (PODC). С. 267–275. Дои:10.1145/248052.248106. ISBN  0-89791-800-2.
  19. ^ Коган, Алексей; Петранк, Эрез (2012). Метод создания быстрых структур данных без ожидания. Proc. 17-й ACM SIGPLAN Symp. по принципам и практике параллельного программирования (PPOPP). С. 141–150. Дои:10.1145/2145816.2145835. ISBN  978-1-4503-1160-1.
  20. ^ Тимнат, Шахар; Петранк, Эрез (2014). Практическое моделирование без ожидания структур данных без блокировки. Proc. 17-й ACM SIGPLAN Symp. по принципам и практике параллельного программирования (PPOPP). С. 357–368. Дои:10.1145/2692916.2555261. ISBN  978-1-4503-2656-8.

внешняя ссылка