Разница между атомарными реализациями decf - PullRequest
5 голосов
/ 05 мая 2019

Я изучал реализацию атомарного подсчета ссылок.

Большинство операций очень согласованы между библиотеками, но я обнаружил удивительное разнообразие в операции «уменьшить счет». (Обратите внимание, что, как правило, единственная разница между общим и слабым decf в том, что называется on_zero(). Исключения отмечены ниже.)

Если есть другие реализации, реализованные в терминах модели C11 / C ++ 11 (что делает MSVC?), Кроме типа «мы используем seq_cst, потому что мы не знаем ничего лучшего», не стесняйтесь редактировать их в.

Большинство примеров изначально были C ++, но здесь я переписал их на C, вставил и нормализовал в соответствии с соглашением >= 1:

#include <stdatomic.h>
#include <stddef.h>
typedef struct RefPtr RefPtr;
struct RefPtr {
    _Atomic(size_t) refcount;
};
// calls the destructor and/or calls free
// on a shared_ptr, this also calls decref on the implicit weak_ptr
void on_zero(RefPtr *);

Из Увеличение примеров intrusive_ptr и openssl :

void decref_boost_intrusive_docs(RefPtr *p) {
    if (atomic_fetch_sub_explicit(&p->refcount, 1, memory_order_release) == 1) {
        atomic_thread_fence(memory_order_acquire);
        on_zero(p);
    }
}

Можно было бы использовать memory_order_acq_rel для операции fetch_sub, но это приводит к ненужным операциям «получения», когда счетчик ссылок еще не достигает нуля и может налагать снижение производительности.

но большинство других ( 1024 * Повышение *, libstdc ++ , libc ++ shared ) сделать что-то еще:

void decref_common(RefPtr *p) {
    if (atomic_fetch_sub_explicit(&p->refcount, 1, memory_order_acq_rel) == 1)
        on_zero(p);
}

Но libc ++ делает что-то другое для слабого счетчика . Любопытно, что это во внешнем исходном файле:

void decref_libcxx_weak(RefPtr *p) {
    if (atomic_load_explicit(&p->refcount, memory_order_acquire) == 1)
        on_zero(p);
    else
        decref_common(p);
}

Тогда возникает вопрос: каковы реальные различия?

Подвопросы: комментарии неправильные? Что делают конкретные платформы (на aarch64 будет ldar дешевле, чем dmb ishld? Также ia64?)? При каких условиях можно использовать более слабые версии (например, если dtor - nop, если удалитель просто free, ...)?

См. Также Подсчет атомных ссылок и Почему необходим барьер захвата перед удалением данных в интеллектуальном указателе с атомным подсчетом ссылок?

1 Ответ

1 голос
/ 05 мая 2019

Выбор libc ++ задокументирован в исходном коде:

ПРИМЕЧАНИЕ: загрузка получения здесь является оптимизацией очень распространенного случая, когда разделяемый указатель разрушается, не имея других спорных ссылок.

кодер libc ++ заметил, что большую часть времени, когда уничтожается последняя shared_ptr, weak_ptr не ссылается на общий объект.Насколько я знаю, и, по крайней мере, на x86, инструкции чтения-изменения-записи гораздо шире, чем инструкции чтения.Таким образом, для наиболее распространенного случая они решили избегать расширенного и неиспользуемого чтения-изменения-записи.Другая реализация стандартной библиотеки не выполняет эту оптимизацию.

...