|
|
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
Опять не дают мне покоя lock-free алгоритмы, опять немного почитал статьи на Хабре, и опять толком ничего не осилил В первую очередь меня интересует очередь. Я пытался портировать на Delphi такую реализацию очереди (раздел "Пример использования tagged pointers") Реализация не взлетела. Может быть я некорректно её переписал, но вот на что я обратил внимание, и что меня не устроило Смотрите строку D11 ("Читаем значение перед CAS, так как иначе другой dequeue может освободить next") Т.е. получается, может случиться ситуация, когда в одном потоке произойдёт копирование данных в результат, а другой поток будет финализировать эти данные. Если речь идёт о простых типах - Integer, Pointer и т.д. - то проблем нет. Но если происходит работа с Interface, Variant или строки - ошибка гарантирована. В общем основы Очередь представляет собой односвязный список от Head до Tail Dequeue извлекает элементы из Head, Enqueue добавляет элементы в Tail НО. Если Dequeue последнего элемента (Head = Tail) - то обнуляются оба указателя Если Enqueue самого первого элемента (Head = Tail = nil) - то Head тоже инициализируется В общем я целую неделю опробывал разные подходы с разной результативностью Основная проблема возникает тогда, когда мы добавляем элементы и ориентируемся на Tail, а Tail тем временем удаляется в другом потоке В итоге наибольшей стабильности я добился сегодня, когда стал исповедовать правило "кто Tail меняет - тот и задаёт правила" И мои внутренние тесты показали почти идеальный вариант. Только изредка терялся 1 элемент Сейчас я ещё больше упростил код, убрав "Tagged pointer" из Next-тов и FTail. Ошибок стало появляться больше. То ли из-за более интенсивной конкуренции, то ли из-за ABA. Представляю на ваш суд свой код, содержащий ошибки ( есть открытый репозиторий ). Надеюсь, вы свежим взглядом поможете делу P.S. Да, TSyncPointer - это некий "Tagged pointer", 8 байтовая структура, содержащая указатель и счётчик замен (обычно используется для избежания ABA) Код: pascal 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. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 02:26 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
SOFT FOR YOU, Твой код не осилил (не хочу заморачиваться с пониманием новомодных конструкций), но это не важно. У меня к тебе вопрос. Ты уверен, что возможно обойтись без блокировок при добавлении/чтении(взятии в обработку элемента) очереди? Это единственная блокировка, которая нужна, а затраты на нее несравнимо меньше, чем на обработку элемента очереди, зачастую. Я лично не уверен. Впрочем, могу ошибаться - мало ли что можно понимать под очередью. Ну просто по логике. Из разных потоков идет добавление в ОДИН список заданий. Ну как может не быть синхронизации этого добавления (и удаления). ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 02:46 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
YuRock, Уверен :) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 02:56 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
YuRockИз разных потоков идет добавление в ОДИН список заданий. Ну как может не быть синхронизации этого добавления (и удаления). AtomicCmpExchange ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 03:02 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
YuRock, На многопроцессорных системах в большинстве случаев LockFree выигрывает у блокировочных алгоритмов. Блокировка это уровень ядра и это затраты на переключение контекста между потоками. В lockfree такие переключения сведены к минимуму т.к. вся суть в атомарных операциях процессора. Для программ выполняющихся на компьютерах с 1 процессором такие алгоритмы бессмысленны. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 03:07 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
X-CiteYuRock, На многопроцессорных системах в большинстве случаев LockFree выигрывает у блокировочных алгоритмов. Блокировка это уровень ядра и это затраты на переключение контекста между потоками. В lockfree такие переключения сведены к минимуму т.к. вся суть в атомарных операциях процессора. тут очень спорно, та же критическая секция в ядро далеко не сразу не уходит SOFT FOR YOU, на первый взгляд проблема в том, что не отслеживается версионность выделенного участка памяти, элемент может пролететь несколько раз по цепочке контейнер-аллокатор, и это логически будет уже "другой" элемент. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 08:16 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
SOFT FOR YOU, кроме того, надо ещё иногда устранять "гонки" как вариант (мидийная пауза говорят круче) Код: pascal 1. 2. 3. 4. 5. очень существенно ускоряет + проц в пиках не загружается на 100% Код: pascal 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. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 08:31 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
kealon(Ruslan), Версионность есть в FHead. Этого недостаточно? Приведи пример, где может возникнуть ABA ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 10:15 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
kealon(Ruslan), Да, будут бэкофы позже. Но сейчас вопрос не в них, а в корректностям алгоритма ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 10:18 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
kealon(Ruslan), Почему Sleep(1)? Передача неиспользованной части кванта это Sleep(0); ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 10:25 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
SOFT FOR YOUkealon(Ruslan), Версионность есть в FHead. Этого недостаточно? Приведи пример, где может возникнуть ABAя на вскидку не скажу, надо всё тестить, а это очень долго вот это тоже косяк - промежуточное состояние не должно блокировать объект, пользуйся только CAS Код: pascal 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. в догонку: зря ассемблерные вставки наделал, лучше маленько да лучше, TInterlocked из SyncObjs покроет все твои потребности в CAS методы вроде вот этого Код: pascal 1. бесполезны ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 10:47 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
Kazantsev Alexeykealon(Ruslan), Почему Sleep(1)? Передача неиспользованной части кванта это Sleep(0);хз, но так быстрее выходило ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 11:29 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
kealon(Ruslan), Сейчас «ассемблер» используется только при замене значения FHead. Возможно ты прав, имеет смысл использовать стандартный двойной CAS. Но он многократно использовался, нареканий по нему нет Empty. Это простое сравнение Value на nil Sleep. Это типа backoff стратегии. Принято сначала делать ассемблерную команду pause, потом SwitchToThread, потом Sleep(1). Sleep(0) это относительно долгий простой потока. Быстрее он может быть только на синтетических тестах - за счёт минимизации конкуренции. Стандартные блокировочные алгоритмы, по крайней мере на спинлоке - тоже быстрее на синтетических тестах. Самое важное. Чтение или запись - сами по себе атомарные операции, кроме того они значительно быстрее. Поначалу я ВРОДЕ БЫ использовал атомики, но очередь по прежнему выдавала ошибки. Если можешь привести пример, где очередь сработает некорректно - милости прошу. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 12:22 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
На какой конфигурации железа проходит тестирование. Если несколько процессоров, то могу предположить что не хватает барьеров памяти ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 12:56 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
X-Cite, Так лок-фри он без барьеров вроде работает :) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 13:05 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
SOFT FOR YOUSleep. Это типа backoff стратегии. Принято сначала делать ассемблерную команду pause, потом SwitchToThread, потом Sleep(1). Sleep(0) это относительно долгий простой потока Вообще-то, Sleep(0) это эквивалент SwitchToThread, с разницей лишь в том, что SwitchToThread отдаёт предпочтение "голодающему" потоку. А любое ненулевое значение в Sleep может усыпить поток очень на долго. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 13:26 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
SOFT FOR YOUX-Cite, Так лок-фри он без барьеров вроде работает :) Разве?? У каждого процессора свой кеш.. Что будет если 20 процессоров со своими кешами будут непрерывно класть и доставать из очереди элементы. А если это мобильные процессоры, у них слабосвязанный порядок инструкций. P.s. я не вижу в enqueue while true... Разве в строке //подменяем FTail на узел - если 20 нитей будут класть не будет ли потери ftail ??? Вроде как вездедолжен быть while true + cas ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 14:19 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
Ведь суть lockfree это бесконечная попытка чего-либо за конечное число итераций. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 14:20 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
rgreatYuRockИз разных потоков идет добавление в ОДИН список заданий. Ну как может не быть синхронизации этого добавления (и удаления). AtomicCmpExchange А работает эта функция (в винде) уж не через InterlockedExchange ли? А то о ней Майкросовт пишет так: function prevents more than one thread from using the same variable simultaneously. Перевод: функция предотвращает одновременное использование одной и той же переменной несколькими потоками . Какими средствами это достигается - объектами ядра или спец. командами процессора (или ф-циями, которые их вызывают) - я вообще-то не уточнял. Я о логике говорил (ты это слово не процитировал). А смысл один - синхронный доступ к списку заданий. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 15:13 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
авторThis function is implemented using a compiler intrinsic where possible. For more information, see the WinBase.h header file and _InterlockedExchange. This function generates a full memory barrier (or fence) to ensure that memory operations are completed in order. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 18:07 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
Ребят, давайте ближе к теме ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 19:12 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
SOFT FOR YOU, а почему FTail не защищен от ABA? Допустим, поток надолго засыпает в Dequeue прямо перед оператором if (AtomicCmpExchange(FTail, nil, Tail) = Tail) then За то время, пока он спал, очередь переходит в другое состояние, в ней уже 100500 элементов, причем последний сидит по тому же адресу. Этот if успешно выполнится, но из очереди будет взят последний, FTail обнулится и последующие Enqueue будут добавлять в *пустую* с их точки зрения очередь. Усе пропало, шеф )) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.11.2017, 21:55 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
Aleksandr Sharahov, Привет :) Да, наверное ты прав Скажи, а Node.Next должен быть защищён от ABA или нет? Ещё момент. В Enqueue я подменяю FTail. Его обязательно делать с инкрементом счётчика или не обязательно? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.11.2017, 05:13 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
Если говорить о той реализации, какая есть - то конкуренции за Next вообще нет, он просто инициализируется и читается Не таит ли такое допущение каких-то неприятных подводных камней? Но больше всего у меня вопросов по Head В Enqueue я инициализирую FHead-указатель. Данный код переписывает значение, а счётчик оставляет прежним. Как ты думаешь, где-то это может сыграть неприятную роль? И подозреваю я, что ошибка кроется как раз здесь. Когда в Enqueue голова просто инициализируется, а в Dequeue меняется атомиком ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.11.2017, 05:26 |
|
||
|
Lock-free: Доведём очередь до ума
|
|||
|---|---|---|---|
|
#18+
SOFT FOR YOU, море литературы на эту тему, зачем изобретать грабли, на которые уже кто-то наступил. На мой взгляд, чтобы не тратить время впустую имеет смысл соорудить конечный автомат и погонять его как следует, а уже потом пробовать строго доказать корректность. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.11.2017, 11:51 |
|
||
|
|

start [/forum/topic.php?fid=58&msg=39548896&tid=2041445]: |
0ms |
get settings: |
5ms |
get forum list: |
9ms |
check forum access: |
2ms |
check topic access: |
2ms |
track hit: |
175ms |
get topic data: |
6ms |
get forum data: |
2ms |
get page messages: |
48ms |
get tp. blocked users: |
1ms |
| others: | 195ms |
| total: | 445ms |

| 0 / 0 |
