Уменьшите количество переключений контекста между потоками с одинаковым приоритетом

Я пишу приложение, которое использует стороннюю библиотеку для выполнения тяжелых вычислений.

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

Есть ли способ увеличить «квант времени» всех потоков в процессе, чтобы, например. все потоки с нормальным приоритетом редко переключают контекст (выдают), если они явно не переданы, например, через семафоры?

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

РЕДАКТИРОВАТЬ:

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

  1. Перечислить все потоки с определенным приоритетом (например, нормальный).
  2. Приостановить их всех.
  3. Создайте цикл, который возобновляет/приостанавливает потоки каждый, например. 40 мс и гарантирует, что запущено не больше потоков, чем текущее количество процессоров.

Какие-либо серьезные недостатки с этим подходом? Не знаете, каковы накладные расходы на возобновление/приостановку потока?


person ronag    schedule 31.08.2014    source источник
comment
Являются ли экземпляры lib взаимозависимыми? Если нет, то почему вы не можете избежать перегрузки доступных ядер, запустив столько потоков, сколько ядер, наподобие пула потоков?   -  person Martin James    schedule 31.08.2014
comment
Ну, поскольку количество экземпляров меняется динамически, это сложно. Мне нужно будет инициализировать/повторно инициализировать библиотеку несколько раз, чтобы изменить количество потоков в зависимости от текущей нагрузки.   -  person ronag    schedule 31.08.2014
comment
Если бы вы использовали пул потоков, количество экземпляров библиотеки, запущенных в любой момент времени, было бы таким же, как количество потоков в пуле. Другие экземпляры библиотеки просто стояли бы в очереди, пока потоки пула не стали бы доступны для их обработки. Кажется, нет смысла применять ЦП к экземпляру, если результат перегружается?   -  person Martin James    schedule 31.08.2014
comment
Если это окна, возможно, вы могли бы уменьшить частоту тиков, используя timeBeginPeriod() и timeEndPeriod(), чтобы установить тикер на более высокое значение. Я думаю, что значение по умолчанию составляет 15,625 мс (64 Гц).   -  person rcgldr    schedule 31.08.2014
comment
Действительно ли тикрейт влияет на планирование полностью используемых потоков?   -  person ronag    schedule 31.08.2014
comment
@MartinJames: я не совсем понимаю, как это будет работать. У нас, вероятно, разное понимание проблемы, и я не могу точно указать на нее пальцем.   -  person ronag    schedule 31.08.2014
comment
Если у вас 8 ядер, запустите 8 потоков. 8 потоков извлекают экземпляры библиотеки из очереди блокировки и обрабатывают их по мере их появления. Если в очереди находится более 8 экземпляров, 8 обрабатываются, а остальные помещаются в очередь. Когда поток завершает выполнение экземпляра, он возвращается, чтобы попытаться извлечь другой экземпляр из очереди. Поскольку всегда есть только 8 потоков, не может быть перегрузки, независимо от того, сколько экземпляров отправлено в очередь.   -  person Martin James    schedule 31.08.2014
comment
@MartinJames: Ах, да. В этом есть смысл. Однако это предполагает, что каждый элемент в очереди масштабируется по всем потокам (что может быть не так, и в конечном итоге я бы отказался от подписки). Но да, это решит проблему, как я ее описал.   -  person ronag    schedule 31.08.2014
comment
@MartinJames Вы, вероятно, захотите начать несколько больше, чем 8, если вы почти не уверены, что можете избежать принудительного переключения контекста. В противном случае любое принудительное переключение контекста приведет к недостаточной загрузке ЦП. Ошибки страницы или любая блокирующая операция могут привести к переключению контекста.   -  person David Schwartz    schedule 31.08.2014
comment
Что говорит @DavidSchwartz....   -  person Martin James    schedule 01.09.2014


Ответы (2)


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

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

  1. Завершение задания может занять больше времени, поскольку в период между запуском задания и его завершением над другими заданиями будет выполнено больше работы.

  2. Дополнительные потоки потребляют память для своих стеков и связанной с ними другой информации об отслеживании.

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

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

person David Schwartz    schedule 31.08.2014
comment
Ну ... это зависит от количества данных, которые, возможно, потребуется перезагрузить в основные кеши. Мы не знаем, сколько это может быть из поста OP :( - person Martin James; 31.08.2014
comment
Нет, но люди, разработавшие планировщик, знали, насколько велики основные кэши. Таким образом, они могли легко убедиться, что не было так много принудительных переключений контекста, чтобы перезагрузка основных кэшей была значительной. И, конечно, если они не некомпетентны, они сделали именно это. - person David Schwartz; 31.08.2014
comment
Во всяком случае, в Windows это может быть несущественным - я проверял это. - person Martin James; 31.08.2014
comment
Вероятно, это потому, что у вас было много принудительных переключений контекста. Планировщик не может запустить поток, который не готов к запуску. Непринудительные переключения контекста являются дискреционными. Планировщик делает их только тогда, когда считает, что это выгодно. Вы должны пытаться изменить это поведение только тогда, когда есть какая-то причина, которую вы знаете лучше, чем планировщик. Здесь нет такой причины. - person David Schwartz; 31.08.2014
comment
Хорошие ответы. Однако, насколько мне известно, ОС может довольно часто принудительно переключать контекст, чтобы избежать голодающих потоков. Именно таких принудительных переключений контекста я хотел бы избежать. - person ronag; 31.08.2014
comment
Перегруженные ядра, потоки с одинаковым приоритетом, интенсивно использующие ЦП, без системных вызовов, без операций ввода-вывода. - person Martin James; 31.08.2014
comment
@ronag Это не принудительно. Это дискреционные. ОС будет делать это только тогда, когда у нее есть веские основания полагать, что выгоды перевешивают затраты. - person David Schwartz; 31.08.2014
comment
@ronag - нет, так не бывает. Единственный шанс изменить набор запущенных потоков — это когда он прерывается либо системным вызовом, либо аппаратным прерыванием. - person Martin James; 31.08.2014
comment
@MartinJames: Вау, тогда я явно неправильно понял, как работает планировщик. Нужно будет прочитать об этом еще раз. Я был совершенно уверен, что если у меня есть 100 потоков, выполняющих простой цикл for, подсчитывающий переменную, все потоки будут продвигаться вперед (т. е. самое большее через несколько секунд все переменные будут > 0). - person ronag; 31.08.2014
comment
Это может произойти из-за аппаратных прерываний от таймера. - person Martin James; 31.08.2014
comment
@Мартин Джеймс: Понятно. Как часто это обычно бывает? - person ronag; 31.08.2014
comment
@ronag Через несколько секунд, конечно. Но пара сотен переключений контекста за несколько секунд — это ничто на современном железе. Планировщик не будет переключать контекст по своему усмотрению, если только он не считает, что выгоды перевешивают затраты. И если вы каким-то образом не знаете лучше, чем планировщик, вы должны позволить ему принять это решение без помех. - person David Schwartz; 31.08.2014
comment
@DavidSchwartz: Действительно. Здорово. Что ж, я чувствую, что узнал что-то новое и переоценил библиотеки, основанные на задачах, такие как TBB и Concrt, для крупнозернистого параллелизма. - person ronag; 31.08.2014

Какие-либо серьезные недостатки с этим подходом? Не знаете, каковы накладные расходы на возобновление/приостановку потока?

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

Вышеупомянутое применимо для любой операционной системы, но из почтового тега SO мне кажется, что это было запрошено для системы на базе Microsoft Windows. Теперь, если мы прочитаем о SuspendThread () из MSDN мы получаем следующее:

"Эта функция в первую очередь предназначена для использования отладчиками. Она не предназначена для использования для синхронизации потоков. Вызов SuspendThread в потоке, которому принадлежит объект синхронизации, такой как мьютекс или критический раздел, может привести к в взаимоблокировку, если вызывающий поток пытается получить объект синхронизации, принадлежащий приостановленному потоку".

Итак, рассмотрим сценарий, в котором поток получил какой-то ресурс (неявно т.е. часть не кода... в библиотеке или режиме ядра), и если мы приостановим поток, это приведет к загадочной ситуации взаимоблокировки, поскольку другие потоки этого процесса будут ожидать для этого конкретного ресурса. Дело в том, что мы не уверены (в любое время) в нашей программе, какие ресурсы приобретаются любым запущенным потоком, приостановка/возобновление потока не является хорошей идеей.

person Mantosh Kumar    schedule 31.08.2014