Барьер памяти с использованием std::system_clock::now()

Вопрос или проблема

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

Вот так:

rec.time_stamp0 = std::system_clock::now();
std::atomic_signal_fence(std::memory_order::release);
/*
  выполняем множество действий...
*/
std::atomic_signal_fence(std::memory_order::acquire);
rec.time_stamp1 = std::system_clock::now();

Здесь я хочу убедиться, что rec.time_stamp0 = std::system_clock::now(); будет выполнено до всех остальных кодов, а rec.time_stamp1 = std::system_clock::now(); должно быть выполнено после всех остальных.

Мои вопросы:

  1. Приходит ли std::system_clock::now() с барьером памяти по умолчанию (это syscall)? Значит, явный барьер не нужен?
  2. Должен ли я использовать std::atomic_thread_fence или std::atomic_signal_fence здесь (моя программа однопоточная)?

Ответ или решение

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

  1. Необходимость явного барьера памяти для std::system_clock::now():
    Стандарт C++ не гарантирует, что любые вызовы функций, такие как std::system_clock::now(), имеют встроенные барьеры памяти. Хотя std::system_clock::now() может выполнять системные вызовы, которые, в свою очередь, могут включать операции синхронизации (в зависимости от реализации), вы не можете полагаться на это поведение. Поэтому, если вашей целью является жесткое упорядочение операций, то лучше добавить явные барьеры памяти. Вы можете использовать std::atomic_thread_fence() или std::atomic_signal_fence() для достижения этой цели, в зависимости от того, какие гарантии вам нужны.

  2. Использование std::atomic_thread_fence или std::atomic_signal_fence в однопоточном режиме:
    В вашем случае, поскольку программа работает в одном потоке, std::atomic_signal_fence() будет достаточным. Это позволит предотвратить переупорядочивание операций компилятором, а также может быть полезно, если вы планируете в будущем развивать код для многопоточной среды. Однако, если ваша программа действительно является однопоточной и вам необходимо жесткое упорядочение всех операций (например, для взаимодействия с аппаратным обеспечением или синхронизацией с الخارجей средой), то лучше использовать std::atomic_thread_fence(), так как он обеспечивает более строгие гарантии.

Вот исправленный вариант вашего кода с предложениями по улучшению:

rec.time_stamp0 = std::system_clock::now();
std::atomic_thread_fence(std::memory_order::release); // Используем thread fence для однопоточной программы
/*
  do many things...
*/
std::atomic_thread_fence(std::memory_order::acquire); // Обеспечиваем упорядочение
rec.time_stamp1 = std::system_clock::now();

Резюме:

  • std::system_clock::now() не гарантирует наличие встроенного барьера памяти, поэтому использование явных барьеров является хорошей практикой.
  • Для однопоточной программы предпочтительно использовать std::atomic_thread_fence(), так как оно предоставляет более строгие гарантии по порядку выполнения операций, чем std::atomic_signal_fence().

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

Оцените материал
Добавить комментарий

Капча загружается...