Подтвердить что ты не робот

Уменьшается ли атомный декремент дороже, чем приращение?

В своем Blog Herb Sutter пишет

[...], потому что увеличивает счетчик ссылок на интеллектуальные указателиобычно можно оптимизировать так же, как обычный прирост в оптимизированной реализации shared_ptr - просто обычная инструкция приращения, и никаких заграждений в сгенерированном коде.

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

Текст посвящен реализации shared_ptr, и я не уверен, что его замечание относится только к этому или, как правило, к делу. Из его формулировки я понимаю, что это вообще.

Но когда я думаю об этом, я могу только думать о "более дорогом декременте", когда сразу следует if(counter==0), что, вероятно, имеет место с shared_ptr.

Поэтому я задаюсь вопросом, является ли атомная операция ++counter (обычно) всегда быстрее чем --counter, или просто потому, что используется if(--counter==0)... с shared_ptr?

4b9b3361

Ответ 1

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

Я не знаю ни одной архитектуры, где --counter (или counter--, если мы говорим о простых типах данных, таких как int, char и т.д.), медленнее, чем ++counter или counter++.

Ответ 2

Он обсуждает это более подробно где-то, я думаю, в его атомном < > оружии. В основном это касается того, где нужны ограждения памяти в случае использования shared_ptr, а не какое-либо внутреннее свойство атомных приращений против декрементов.

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

Ответ 3

Проблема Sutter говорит о том, что приращение счетчика ссылок не требует каких-либо последующих действий для правильности. Вы принимаете ненулевой счетчик ссылок на другой ненулевой счет, поэтому никаких дальнейших действий не требуется. Однако декремент требует последующих действий для правильности. Десятичное значение принимает ненулевой счетчик ссылок либо с ненулевым, либо с нулевым счетчиком ссылок, и если ваш декремент счетчика ссылок равен нулю, вам необходимо выполнить действие --- в частности, освободить ссылочный объект. Эта динамика декремента и действия требует большей согласованности, как на уровне забора (поэтому освобождение не переупорядочивается с некоторыми другими чтениями/записью на другом ядре, которые перезаписываются логикой управления памятью/памятью процессора) и на уровня компилятора (поэтому компилятор не переупорядочивает операции вокруг декремента, которые могут привести к переупорядочению чтения/записи вокруг потенциального освобождения).

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