Производительность Interlocked.Increment
Является Interlocked.Increment(ref x)
быстрее или медленнее, чем x++
для целых и длинных на разных платформах?
6 ответов
Он медленнее, поскольку заставляет действие выполняться атомарно и действует как барьер памяти, устраняя способность процессора переупорядочивать доступ к памяти вокруг инструкции.
Вы должны использовать Interlocked.Increment, когда хотите, чтобы действие было атомарным в состоянии, которое может быть разделено между потоками - оно не предназначено для полной замены x++.
По нашему опыту, InterlockedIncrement() и др. Для Windows оказывают довольно значительное влияние. В одном примере мы смогли устранить блокировку и использовать вместо нее ++/-. Одно это уменьшило время пробега со 140 до 110 секунд. Мой анализ состоит в том, что блокировка вызывает возврат памяти (иначе как другие ядра могли бы видеть это?). Чтение / запись в кэш-памяти L1 составляет около 10 тактов, но чтение / запись в памяти больше похоже на 100.
В этом примере я оценил число операций увеличения / уменьшения примерно в 1 миллиард. Таким образом, для процессора с частотой 2 ГГц это примерно 5 секунд для ++/- и 50 секунд для блокировки. Распределите разницу по нескольким потокам и приблизите ее к 30 секундам.
Подумайте об этом на мгновение, и вы поймете Increment
Вызов не может быть быстрее, чем простое применение оператора приращения. Если бы это было так, то реализация компилятором оператора приращения вызвала бы Increment
внутренне, и они будут выполнять то же самое.
Но, как вы можете убедиться, проверив это на себе, они не работают одинаково.
Два варианта имеют разные цели. Обычно используйте оператор приращения. использование Increment
когда вам нужно, чтобы операция была атомарной, и вы уверены, что все другие пользователи этой переменной также используют взаимосвязанные операции. (Если они не все сотрудничают, то это не очень помогает.)
Это медленнее. Тем не менее, это наиболее эффективный из всех известных мне способов обеспечения безопасности потоков для скалярных переменных.
Он всегда будет медленнее, потому что он должен выполнять блокировку шины ЦП, а не просто обновлять регистр. Однако современные процессоры достигают почти регистровой производительности, поэтому она незначительна даже при обработке в реальном времени.
Мой тест производительности:
летучие: 65 174 400
блокировка: 62 428 600
заблокировано: 113 248 900
TimeSpan span = TimeSpan.FromSeconds(5);
object syncRoot = new object();
long test = long.MinValue;
Do(span, "volatile", () => {
long r = Thread.VolatileRead(ref test);
r++;
Thread.VolatileWrite(ref test, r);
});
Do(span, "lock", () =>
{
lock (syncRoot)
{
test++;
}
});
Do(span, "interlocked", () =>
{
Interlocked.Increment(ref test);
});