Interlocked.Increment против блокировки в режиме отладки и выпуска
Я проверял как Interlocked.Increment
а также lock
вести себя на архитектуре моего компьютера, потому что я прочитал следующие строки в этой статье.
Как переписано с Interlocked.Increment, метод должен выполняться быстрее, по крайней мере, на некоторых архитектурах.
Используя следующий код, я убедился, что стоит пересмотреть блокировки в моих проектах.
var watch = new Stopwatch();
var locker = new object();
int counter = 0;
watch.Start();
for (int i = 0; i < 100000000; i++)
{
lock (locker)
{
counter++;
}
}
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalSeconds);
watch.Reset();
counter = 0;
watch.Start();
for (int i = 0; i < 100000000; i++)
{
Interlocked.Increment(ref counter);
}
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalSeconds);
Я получаю стабильные результаты с приблизительными значениями 2,4 с для блокировки и 1,2 с для блокировки. Однако я был удивлен, обнаружив, что запуск этого кода в режиме выпуска повышает значение только для Interlocked примерно до 0,7 с, а время блокировки остается неизменным. Это почему? Как оптимизируется блокировка в режиме разблокировки, когда нет блокировки?
1 ответ
Вы должны посмотреть на сгенерированный машинный код, чтобы увидеть разницу: отладка + Windows + разборка. Версия отладочной сборки вызова Interlocked.Increment():
00FC27AD call 7327A810
Версия выпуска сборки:
025F279D lock inc dword ptr [ebp-24h]
Другими словами, оптимизатор джиттера стал действительно умным в сборке Release и заменил вызов вспомогательной функции на отдельную машинную инструкцию.
Оптимизация просто не становится лучше, чем это. Та же оптимизация не может быть применена к вызову метода Monitor.Enter (), который находится под оператором блокировки, это довольно существенная функция, которая реализована в CLR и не может быть встроенной. Он делает много вещей помимо Interlocked.Increment(), он позволяет операционной системе перепланировать, когда поток блокируется при попытке получить монитор, и поддерживает очередь ожидающих потоков. Это может быть очень важно для обеспечения хорошего параллелизма, но только не в тестовом коде, поскольку блокировка полностью не оспаривается. Остерегайтесь синтетических тестов, которые не приближаются к фактическому использованию.