В каких случаях структуры данных без блокировок работают быстрее, чем основанные на блокировках?

Сейчас я читаю книгу C++ Concurrency in Action автора Энтони Уильямс, и существует несколько реализаций структур данных без блокировок. В начале главы о структурах данных без блокировок в книге Энтони пишет:

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

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

В каких случаях структуры данных без блокировки являются более оптимальными и должны быть предпочтительными?


person bobeff    schedule 27.10.2016    source источник
comment
Какие, сколько данных? Как долго версии блокировки были заблокированы?   -  person doctorlove    schedule 27.10.2016
comment
нужно предпочесть? Должен - очень сильно сказано   -  person UKMonkey    schedule 27.10.2016
comment
Мне нравятся структуры данных без блокировок, потому что они помогают избежать взаимоблокировок мьютексов. Они будут медленнее, когда будет много конфликтов, как и со спин-блокировками.   -  person brian beuning    schedule 27.10.2016
comment
Вкратце: основанная на блокировке хороша до тех пор, пока ни один поток не сможет получить блокировку. Когда это происходит, это в значительной степени гарантированное переключение контекста, что очень плохо. Если существует вероятность того, что несколько потоков одновременно будут обращаться к одному и тому же критическому разделу (много конфликтов), то безблокировка будет работать лучше.   -  person sbabbi    schedule 27.10.2016
comment
Помимо других вариантов использования, они могут быть полезны для данных, предназначенных в основном для чтения. С чем-то вроде RCU читателям никогда не придется ждать или даже выполнять атомарная операция вообще. Бремя синхронизации может быть полностью возложено на авторов.   -  person Peter Cordes    schedule 27.10.2016


Ответы (5)


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

  • Конкуренция должна быть высокой
  • Ядер ЦП должно быть достаточно, чтобы вращающийся поток мог работать непрерывно (в идеале должен быть закреплен на собственном ядре)
person SergeyA    schedule 27.10.2016
comment
Я думаю, что эти два условия уравновешивают друг друга. Если бы у нас было достаточно ядер процессора, у нас было бы меньше конфликтов. - person Ankush G; 28.01.2021
comment
@AnkushG не совсем так, почему? - person SergeyA; 28.01.2021

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

person Donghui Zhang    schedule 27.10.2016
comment
@ Donghui Zhang Вы правы, это сильно зависит от количества потоков и размера данных, хранящихся в каждом узле. Я протестировал 1000 потоков чтения и записи, и все выглядит иначе в пользу дизайна без блокировки. - person bobeff; 27.10.2016

это зависит от вероятности столкновения.

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

Но если у вас есть большой контейнер и 2 потока будут обращаться к контейнеру в разных областях, очень вероятно, что столкновения не будет. Например: один поток изменяет первый элемент контейнера, а другой поток — последний элемент. В этом случае вероятность повторной попытки очень мала, поэтому тут лучше использовать lock-free.

Другая проблема со свободой от блокировок — спин-блокировки (интенсивное использование памяти), общая производительность атомарных переменных и некоторые ограничения на переменные.

Например, если у вас есть ограничение x == y, которое должно быть истинным, вы не можете использовать атомарные переменные для x и y, потому что вы не можете изменить обе переменные одновременно, в то время как lock() удовлетворит ограничение

person Domso    schedule 27.10.2016

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

Проблема в том, что конструкции без замков могут быть сложными и требуют значительного объема проектирования, чтобы быть надежными; в то время как мьютекс довольно тривиален (в сравнении), а при отладке может быть еще сложнее. По этой причине люди, как правило, предпочитают сначала использовать мьютексы, а затем мигрировать на блокировку позже, как только будет доказано, что конкуренция является узким местом.

person UKMonkey    schedule 27.10.2016
comment
Конструкция мьютекса очень редко, если вообще когда-либо, превосходит беззамковую. Я не думаю, что это совсем так. Похоже, основано исключительно на теории, а не на опыте. Во-первых, безблокировка на самом деле не является блокировкой, блокировка выполняется на аппаратном уровне. Второй замок не означает без задержек - person Slava; 27.10.2016
comment
Если создание структуры данных без блокировки означает, что обычно простые операции теперь требуют последовательности атомарных операций, структура данных с легкими конфликтами может на самом деле выиграть от использования блокировок. - person Peter Cordes; 27.10.2016
comment
Я видел это много раз: академические статьи с неблокируемыми структурами данных (например, для приоритетной очереди) — это просто огромная трата времени по сравнению с облегченными мелкозернистыми блокировками на уровне пользователя, основанными на реализации. - person Anton; 27.10.2016

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

person Ankush G    schedule 28.01.2021