Тема :: доход против темы :: onSpinWait - PullRequest
8 голосов
/ 09 мая 2019

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

Есть ответы типа this , которые в основном говорят:

Уступление также было полезно для занятого ожидания ...

Я не могу согласиться с ними по той простой причине, что ForkJoinPool использует Thread::yield внутри, и это довольно недавнее дополнение в мире jdk.

Меня по-настоящему беспокоит то, что в jdk используются такие слова (StampledLock::tryDecReaderOverflow):

    else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
        Thread.yield();
    else
        Thread.onSpinWait();
    return 0L;

Так что, похоже, бывают случаи, когда один из них предпочтительнее другого.И нет, у меня нет фактического примера, где мне это может понадобиться - единственный, который я на самом деле использовал, был Thread::onSpinWait, потому что 1) я был занят ожиданием 2) имя в значительной степени самоочевидно, что я должен использовал его в спине занятых.

1 Ответ

6 голосов
/ 09 мая 2019

При блокировке потока есть несколько стратегий на выбор: спин, wait() / notify() или их комбинация. Чистое вращение переменной - это стратегия с очень низкой задержкой, но она может истощить другие потоки, которые борются за процессорное время. С другой стороны, wait() / notify() освободит ЦП для других потоков, но может стоить тысячи циклов ЦП с задержкой при планировании / планировании потоков.

Так, как мы можем избежать чистого вращения, а также накладных расходов, связанных с планированием и расписанием заблокированного потока?

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

Последнее добавление - Thread.onSpinWait(), которое вставляет специфичные для архитектуры инструкции, чтобы подсказать процессору, что поток находится в цикле вращения. На x86 это, вероятно, инструкция PAUSE, на aarch64 это инструкция YIELD.

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

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

...