Вопрос или проблема
Мне нужно добавить две барьеры памяти в мой код, чтобы предотвратить переупорядочивание моего кода как компилятором, так и процессором.
Вот так:
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();
должно быть выполнено после всех остальных.
Мои вопросы:
- Приходит ли
std::system_clock::now()
с барьером памяти по умолчанию (это syscall)? Значит, явный барьер не нужен? - Должен ли я использовать std::atomic_thread_fence или std::atomic_signal_fence здесь (моя программа однопоточная)?
Ответ или решение
Вопрос о том, как использовать барьеры памяти с функцией std::system_clock::now()
, требует тщательного анализа, чтобы обеспечить правильность последовательности выполнения операций в вашей программе. Давайте ответим на ваши вопросы по порядку.
-
Необходимость явного барьера памяти для
std::system_clock::now()
:
Стандарт C++ не гарантирует, что любые вызовы функций, такие какstd::system_clock::now()
, имеют встроенные барьеры памяти. Хотяstd::system_clock::now()
может выполнять системные вызовы, которые, в свою очередь, могут включать операции синхронизации (в зависимости от реализации), вы не можете полагаться на это поведение. Поэтому, если вашей целью является жесткое упорядочение операций, то лучше добавить явные барьеры памяти. Вы можете использоватьstd::atomic_thread_fence()
илиstd::atomic_signal_fence()
для достижения этой цели, в зависимости от того, какие гарантии вам нужны. - Использование
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()
.
Применение этих методов обеспечит корректное выполнение вашего кода в различных ситуациях, сводя к минимуму риски, связанные с переупорядочиванием операций.