Здравствуйте, andrey.desman, Вы писали:
AD>Нет, конечно, не утверждал, но учитывая вышенаписанное, ты скорее сам исходники смотрел не шибко внимательно.
Да, просмотрел. В случае если, таки, необходимо засыпать, используется WaitForSingleObjectEx и поток уходит в ядро. Вся остальная реализация в user-mode. Т.е. все достаточно не плохо под Винду . Некоторые другие вещи в CRT меня ужасали, бывало.
Здравствуйте, okman, Вы писали:
O>Здравствуйте, AlexGin, Вы писали:
AG>>while(lck.test_and_set(std::memory_order_acquire)) [b]// здесь, возможно, придется подождать!
O>Спинлок в такой имплементации — это вообще, как по мне, плохая идея.
Вот, я тоже об этом подумал. Т.к если есть сначала дешевый test в цикле, то шину и кеш не грузим,
а затем по факту освообждения переходим к шагу два с попыткой атомарно захватить. Если не успели — возвращаемся на дешевый цикл чтения.
O>Во-первых, постоянно теребить глобальную переменную с xchg/cmpxchg и блокировкой шины — не есть гуд O>(bus traffic и все такое). Как минимум, тут надо применять стратегию не "test and set", а O>"test test and set" + какую-то разгрузку в виде yield/pause/Sleep/etc.
А такое можно реализовать легковесно в user mode не отдавая контекст ядру/планировщику?
O>Во-вторых, на однопроцессорной машине крутиться в спин-цикле бессмысленно, так как лок никто в O>это время не освободит.
O>Ну и в-третьих, если между захватом и освобождением лока поток будет вытеснен, получится O>очень некрасивая ситуация по отношению к другим потокам, которые ждут его освобождения. O>Особенно если лок не гарантирует порядок захвата. Так легко и систему завесить, особенно если O>такой спинлок применять где-нибудь в критических местах — драйверы, всякие системные O>обработчики и т.д. Встречал на практике.
Любопытный вопрос. А какие практики используются вместо этого в ядре?
AG>Взято отсюда: AG>http://anki3d.org/spinlock
AG>Теперь предположим, что в результате некоторой ситуации все ядра моего CPU ждут на while в методе lock. AG>Да, случай практически не вероятный, но всё-таки — полностью НЕ исключаемый. AG>Загрузка всех секций CPU будет под 100% (здесь я даже не спрашиваю насчет полезноти/бесполезности данной вычислительной работы). AG>Спрошу только одно — сможет ли пользователь хоть как-то взаимодействовать с ОС? Хоть бы для того, чтобы снять эту зависшую задачу?
Ну а зачем так делать? Вот вам принципы из практики на счет производительности, чтобы с многопоточностью все было ок:
1. Как не странно. не разделяйте данные. Если можно, всегда делайте локальные копии данных для каждого потока.
2. Старайтесь все данные через блокировки передавать пакетировано и как можно быстрее. В идеале, все должно сводится к обмену указателей на массив объектов.
3. Spin-Lock дает выигрыш только если количество итераций достаточно мало. В противном случае выгоднее уйти в ядро и не тратить циклы CPU.
Здравствуйте, barney, Вы писали:
B>А такое можно реализовать легковесно в user mode не отдавая контекст ядру/планировщику?
Наверное, ничего лучше инструкции pause пока не придумали.
B>Любопытный вопрос. А какие практики используются вместо этого в ядре?
Конкретно ядро Windows перед захватом спинлока задирает IRQL до DISPATCH_LEVEL, это предотвращает вытеснение
потока планировщиком (хотя это не исключает, например, приход прерывания, в результате чего код все равно
будет временно вытеснен). Ну и вместо обычных спинлоков рекомендуется применять спинлоки с очередями на
стеке (in-stack queued spinlock) — там каждый поток вместо глобальной переменной теребит переменную у
себя в стеке, ну и гарантируется порядок захвата спинлока потоками (т.е. убирается проблема "starvation").
Про linux не в курсе, я его видел только "на картинках"
В режиме пользователя запретить вытеснение потока нельзя. Думаю, в режиме пользователя по этой
причине никаких спинлоков быть не должно.
Здравствуйте, AlexGin, Вы писали:
N>>Уточните, под какой ОС такое происходит. AG>Последний раз видел такую ситуацию — примерно пять лет назад под Windows XP.
Хммм...
N>>Честно, у меня как-то не наблюдалось. Но у меня уже лет 20 что сервера, что десктопы/лаптопы почти сплошь Linux и FreeBSD.
AG>А разве в мире Linux что-то принцыпиально иначе? Разве не будет такой же ситуации? AG>Если на каждом из ядер крутится свой поток, и не даёт возможности "вклиниться" диспетчеру потоков, то там должно быть аналогично.
С чего это вдруг "должно" быть?
Регулярно приходят таймерные прерывания (или при NOHZ режиме их можно заказывать на конкретные моменты).
Диспетчер отслеживает потребление процессора нитями и делает, чтобы каждая нить из желающих получала долю процессора, максимально близко пропорциональную некоторому числу, зависящему от приоритета.
Кроме этого, есть система временного буста диспетчерского приоритета для нитей, которые до того находились в спячке достаточное время — это помогает типовым интерактивным задачам, которые реагируют на события от пользователя и спят в остальное время; это делается на каком-то варианте token bucket.
Если нить сама не отдаёт процессор, то её по таймерному прерыванию или даже по любому сисколлу могут вытеснить принудительно. Диспетчер всегда имеет право вклиниться и переключить активную нить на любом ядре, кроме сверхкоротких периодов пребывания в kernel land между точками выхода в user land, ожиданий или явных yield'ов.
И мне просто дико слышать, что где-то иначе, тем более что "должно" быть иначе — это ж какой, извините, каменный век в алгоритмах?
(Нет, конечно, не всё так однозначно, я сама дочь офицера есть realtime priority и ещё хитрые фишки того же плана — но вряд ли какой безумец сделает на них thread pool. И, наоборот, есть шейперы, которые позволяют урезать права)
AG>Предположим, что у нас обычный офисный комп с Win10/Win7. AG>У меня есть приложение, которое (выполняя определенную работу) — дико тормозит все мои пользовательские действия. AG>Что я могу сделать, как пользователь? Что могу сделать, если приложение сторонее и я НЕ имею его исходных кодов?
Вот был бы Linux — я бы сказал, как сделать, чтобы это приложение не брало более, например, 40% CPU, если есть другие желающие — конкретно, через cgroup/cpu.shares. А с Windows... боюсь, что тут ровно как в знаменитом лозунге про спасение утопающих...
Здравствуйте, reversecode, Вы писали:
R>коллективный разум это когда решается какая то проблема доселе не известная или не имеющая решения R>а как устроены ОС описано в гугле на каждом шагу, плюс куча книг и исходников R>это называется по другому, — научите меня бесплатно
А сам то ты здесь наверно ошиваешься не для того чтобы учить/учиться, а флеймить ради?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, okman, Вы писали:
O>Спинлок в такой имплементации — это вообще, как по мне, плохая идея.
Если этим спинлоком защищать только условные Get/Set для некоторого (общего для потоков) значения, то идея вроде как хорошая.
Но, конечно же, при использовании его есть риск завесить приложение, если вынесли в него некие сложные действия.
O>Во-первых, постоянно теребить глобальную переменную с xchg/cmpxchg и блокировкой шины — не есть гуд O>(bus traffic и все такое). Как минимум, тут надо применять стратегию не "test and set", а O>"test test and set" + какую-то разгрузку в виде yield/pause/Sleep/etc.
+100500
Вот именно о разгрузке в виде ::Sleep — я и сообщал здесь выше!
В виде Sleep или WaitingFor... ввести ожидание, чтобы передать управление диспетчеру потоков.
O>Во-вторых, на однопроцессорной машине крутиться в спин-цикле бессмысленно, так как лок никто в O>это время не освободит.
Да, однопроцессорных сейчас уже давно нет, но когда-то (лет 15 назад) как-то же работала многопоточка?
O>Ну и в-третьих, если между захватом и освобождением лока поток будет вытеснен, получится O>очень некрасивая ситуация по отношению к другим потокам, которые ждут его освобождения.
Вероятность того, что поток будет вытеснен, если внутри этого лока — только один вызов гетера/сетера — фактически нулевая.
O>Особенно если лок не гарантирует порядок захвата. Так легко и систему завесить, особенно если O>такой спинлок применять где-нибудь в критических местах — драйверы, всякие системные O>обработчики и т.д. Встречал на практике.
+100500
Да, если внутрь этого лока вносить что-то длинное, то такое бывает. Я когда-то также сталкивался с этим.
Но здесь — вся ответственность на разработчике
Каких-либо механизмов, способных разрулить проблему IMHO не существует.
Кроме, разве что сделать ожидание в локе — с прокачкой сообщений ОС, и с вызовом Sleep...
Здравствуйте, Videoman, Вы писали:
AG>>Теперь предположим, что в результате некоторой ситуации все ядра моего CPU ждут на while в методе lock. AG>>Да, случай практически не вероятный, но всё-таки — полностью НЕ исключаемый. AG>>Загрузка всех секций CPU будет под 100% (здесь я даже не спрашиваю насчет полезноти/бесполезности данной вычислительной работы). AG>>Спрошу только одно — сможет ли пользователь хоть как-то взаимодействовать с ОС? Хоть бы для того, чтобы снять эту зависшую задачу?
V>Ну а зачем так делать? Вот вам принципы из практики на счет производительности, чтобы с многопоточностью все было ок: V>1. Как не странно. не разделяйте данные. Если можно, всегда делайте локальные копии данных для каждого потока.
Есть немалая вероятность, что когда-либо эти данные окажутся рассинхронизированы (будут иметь различные значения, вместо одного).
V>2. Старайтесь все данные через блокировки передавать пакетировано и как можно быстрее. В идеале, все должно сводится к обмену указателей на массив объектов.
+100500
Это бесспорно так.
V>3. Spin-Lock дает выигрыш только если количество итераций достаточно мало. В противном случае выгоднее уйти в ядро и не тратить циклы CPU.
+100500
Здесь также согласен.
Здравствуйте, lpd, Вы писали:
lpd>Здравствуйте, AlexGin, Вы писали:
AG>Да, но есть риск получить систему полностью загруженной, когда она перестанет отзываться на действия пользователя. AG>Вполне возможно, что в цикле рабочего потока ::Sleep(100) — не вызовет катастрофических задержек. AG>Однако, вызвав диспетчер потоков позволит пользователю как-то общаться с машиной, когда запущена твоя вычислительная задача. AG>В противном случае — компьютер становиться страшно тормознутым и практически не управляемым
lpd>Лучше уменьшить приоритет вычислительной задаче через системный менеджер процессов, например, или из кода.
Так получится, что уменьшение приоритета даст примерно тот же эффект (если не хуже),
в плане снижения производительности, что тот же самый ::Sleep
Здравствуйте, AlexGin, Вы писали:
AG>Вероятность того, что поток будет вытеснен, если внутри этого лока — только один вызов гетера/сетера — фактически нулевая.
Смысл в том, что она все равно не нулевая.
У нас в софте были такие зависания, оказалось там была схожая реализация лока с циклом на CMPXCHG.
Код под локом — всего несколько процессорных инструкций, но все равно иногда случалось, что
он вытеснялся и система висла (потому что за лок конкурировали другие важные потоки системы).
Когда переделали на стандартные виндовые spinlock/pushlock — все заработало.
AG>Каких-либо механизмов, способных разрулить проблему IMHO не существует. AG>Кроме, разве что сделать ожидание в локе — с прокачкой сообщений ОС, и с вызовом Sleep...
Ну в ядре, как я уже писал, можно на время запретить переключение контекстов, ядерные
спинлоки так и работают (повышая IRQL до соответствующего уровня, где шедулер не работает).