Должны ли связанные реализации, основанные на CompareExchange, использовать SpinWait? - PullRequest
6 голосов
/ 04 ноября 2019

Ниже приведена реализация метода блокировки на основе Interlocked.CompareExchange.

Желательно ли в этом коде использовать вращение SpinWait перед повторением?

public static bool AddIfLessThan(ref int location, int value, int comparison)
{
    int currentValue;
    do
    {
        currentValue = location; // Read the current value
        if (currentValue >= comparison) return false; // If "less than comparison" is NOT satisfied, return false
    }
    // Set to currentValue+value, iff still on currentValue; reiterate if not assigned
    while (Interlocked.CompareExchange(ref location, currentValue + value, currentValue) != currentValue);
    return true; // Assigned, so return true
}

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

Скажем, что два потока участвуют в гонке для выполнения этого метода, и первый поток сразу же завершается успешно, тогда как второй потокизначально не вносит изменений и должен повторить. Без других претендентов, возможно ли вообще потерпеть неудачу второму потоку во второй попытке ?

Если второй поток примера не может завершиться неудачей во второй попытке, то что мы можем получитьс SpinWait? Срезать несколько циклов в маловероятном случае, когда сотня нитей мчится, чтобы выполнить метод?

Ответы [ 2 ]

2 голосов
/ 04 ноября 2019

Две темы просто не тема для обсуждения SpinWait. Но этот код не говорит нам, сколько потоков может фактически конкурировать за ресурс, и при относительно большом количестве потоков использование SpinWait может стать полезным. В частности, при большем количестве потоков виртуальная очередь потоков, которые пытаются успешно получить ресурс, становится длиннее, а те потоки, которые в конечном итоге обслуживаются, имеют хорошие шансы превысить свой временной интервал, выделенный планировщиком, который, в свою очередь, можетпривести к более высокой загрузке ЦП и может повлиять на выполнение других запланированных потоков даже с более высоким приоритетом. SpinWait имеет хороший ответ на эту ситуацию, установив некоторый верхний предел разрешенных вращений, после которого будет выполняться переключение контекста. Таким образом, это разумный компромисс между необходимостью делать дорогостоящий системный вызов для запуска переключения контекста и неконтролируемым использованием ЦП в пользовательском режиме, который в определенных ситуациях может повлиять на выполнение других потоков.

2 голосов
/ 04 ноября 2019

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

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

...