Достаточно ли std::memory_order_relax для проверки "доступности"?

У меня есть параллельный объект, который может или может содержать указатель на функцию в каждый момент времени. Схема объекта выглядит так:

struct ConcurrentObject{
  //variables
  std::atomic<void(*)()> callback;
} 

поэтому один поток может решить, что он хочет присоединить обратный вызов с этим объектом и передать его вперед:

ConcurrentObject* co = new ConcurrentObject(); //I'm using smart pointers, no worries.
//do some logic
co->callback = someCallback; //void(*)() , this may be difference callback every time

Я получаю этот объект после его модификации и проверяю, доступен ли обратный вызов:

auto co = aquireConcurrentObject();
auto callback = co->callback.load();
if (callback){
    callback()
}

теперь мы знаем, что без указания какого-либо порядка памяти, по умолчанию передается упорядоченная память memory_order_seq_cst что говорит компилятору (в двух словах): "не разбирайте никакие инструкции чтения или записи, чтобы сделать программу быстрее, сохраняйте относительный порядок инструкций, как указано в коде, и делайте его видимым через процессор".

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

Мой вопрос - делает std::memory_order_relaxed достаточно для этого действия?

3 ответа

Решение

Да, вы правы, в вашем примере std::memory_order_relaxed безопасно использовать, потому что ваш код полагается только на тот факт, что обратный вызов является атомарным. Ваш код не подвержен возможному переупорядочению операций с памятью

Порядок в памяти для обращений указателя обратного вызова влияет на "видимость" переменных, используемых обратным вызовом.

Если ваш обратный звонок:

1) похож на constexpr, то есть он не использует ничего, кроме своих аргументов и константных глобальных переменных, или

2) использует только те переменные, которые инициализируются до (до того) возможного использования обратного вызова,

затем с помощью std::memory_order_relaxed нормально и для магазина и для загрузки.

Но если ваш код под //do some logic инициализирует некоторые переменные, используемые обратным вызовом, тогда вы должны использовать по крайней мере std::memory_order_release/std::memory_order_acquire для хранения и загрузки соответственно. В противном случае при выполнении обратного вызова эти переменные могут быть неинициализированными (точнее, это будет гонка данных с точки зрения стандарта C++11, который является неопределенным поведением).

У вас есть способ измерить влияние на производительность? Изменение порядка памяти может выглядеть как хорошая загрузка при загрузке (кажется, что это действительно так), но на самом деле, в большинстве приложений - это не имеет значения.

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

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

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