Является ли Interlocked.Increment(ref x)
быстрее или медленнее, чем x++
для ints и longs на разных платформах?
Выполнение блокировки.
Ответ 1
Он медленнее, так как он заставляет действие происходить атомарно и действует как барьер памяти, устраняя способность процессора переупорядочивать доступ к памяти вокруг инструкции.
Вы должны использовать Interlocked.Increment, когда вы хотите, чтобы действие было атомарным в состоянии, которое может быть разделено между потоками - оно не предназначено для полной замены для x ++.
Ответ 2
По нашему опыту, InterlockedIncrement() и др. в Windows являются довольно значительными последствиями. В одном примере мы смогли устранить блокировку и вместо этого использовать ++/-. Это позволило сократить время работы от 140 секунд до 110 секунд. Мой анализ заключается в том, что блокировка принудительно перемещает память в другую сторону (иначе, как могли бы другие ядра увидеть это?). Чтение/запись кеша L1 составляет около 10 тактов, но память читает/записывает больше, чем 100.
В этом примере я оценил количество операций приращения/декремента около 1 миллиарда. Так что на 2 ГГц CPU это что-то вроде 5 секунд для ++/- и 50 секунд для блокировки. Распространяйте разницу между несколькими потоками, а ее значение составляет около 30 секунд.
Ответ 3
Подумайте об этом на мгновение, и вы поймете, что вызов Increment
не может быть быстрее, чем простое приложение оператора инкремента. Если бы это было так, то реализация оператора инкремента компилятора вызовет Increment
внутренне, и они будут выполнять то же самое.
Но, как вы можете видеть, тестируя его для себя, они не выполняют то же самое.
Два варианта имеют разные цели. Используйте оператор инкремента в целом. Используйте Increment
, когда вам нужна операция, чтобы быть атомарной, и вы уверены, что все остальные пользователи этой переменной также используют блокированные операции. (Если они не все сотрудничают, то это действительно не помогает.)
Ответ 4
Это медленнее. Тем не менее, это самый эффективный общий способ, который я знаю для обеспечения безопасности потоков для скалярных переменных.
Ответ 5
Мое тестирование производительности:
volatile: 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);
});
Ответ 6
Он всегда будет медленнее, потому что он должен выполнять блокировку шины CPU, а не только обновлять регистр. Однако современные процессоры достигают почти производительности регистра, поэтому он пренебрежимо мал даже в режиме реального времени.