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(), он позволяет операционной системе перепланировать, когда поток блокируется при попытке получить монитор, и поддерживает очередь ожидающих потоков. Это может быть очень важно для обеспечения хорошего параллелизма, но только не в тестовом коде, поскольку блокировка полностью не оспаривается. Остерегайтесь синтетических тестов, которые не приближаются к фактическому использованию.

Другие вопросы по тегам