Эффективные барьеры памяти

У меня многопоточное приложение, где каждый поток имеет переменную целочисленного типа. Эти переменные увеличиваются во время выполнения программы. В некоторых точках кода нить сравнивает свою переменную подсчета с той, которая имеет другие потоки.

Теперь, поскольку известно, что потоки, запущенные на многоядерном процессоре, могут выполняться не в порядке, поток может не читать ожидаемые значения счетчика других потоков. Для решения этой проблемы одним из способов является использование атомной переменной, такой как std:: atomic < > of С++ 11. Однако выполнение забора памяти при каждом приращении счетчиков значительно замедлит работу программы.

Теперь то, что я хочу сделать, это то, что когда поток собирается читать другой счетчик потоков, только тогда создается забор памяти, и в памяти в этой точке обновляются обновления всех потоков. Как это можно сделать на С++. Я использую Linux и g++.

Ответ 1

Стандартная библиотека С++ 11 включает поддержку заборов в <atomic> с помощью std::atomic_thread_fence.

Вызов этого вызывает полный забор:

std::atomic_thread_fence(std::memory_order_seq_cst);

Если вы хотите испустить только покупку или только забор, вы можете использовать std:memory_order_acquire и std::memory_order_release.

Ответ 2

Есть x86 intrinsics, которые соответствуют барьерам памяти, которые вы можете использовать самостоятельно. Заголовок Windows имеет макрос барьера памяти, поэтому вы должны найти что-то эквивалентное для Linux.

Ответ 3

Для этой цели вы можете использовать boost:: asio:: strand. Создайте обработчик, ответственный за чтение счетчика. Этот обработчик может быть вызван из нескольких потоков. Вместо прямого вызова обработчика, оберните его внутри boost:: asio:: strand. Это гарантирует, что обработчик не может одновременно вызываться несколькими потоками.

http://www.boost.org/doc/libs/1_35_0/doc/html/boost_asio/tutorial/tuttimer5.html

Надеюсь, я правильно понял вопрос.

Ответ 4

Мое предложение было бы иметь функцию collectTimers() в классе более высокого уровня, которая может запросить каждый поток для своего счетчика (через queue/msg). Таким образом, обновление таймеров не задерживается, но сбор таймеров немного медленнее.

Это работает, только если у вас есть какой-то механизм связи между потоками.

Ответ 5

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

Это сделает его очень эффективным и простым. Просто предложение.

Ответ 6

Вы можете попробовать что-то вроде конструкции счетчика ограничений по сигналу в Secion 4.4.3 http://mirror.nexcess.net/kernel.org/linux/kernel/people/paulmck/perfbook/perfbook.2011.08.28a.pdf

Этот вид конструкции может устранить атомарные операции из fastpath (увеличивая счетчик потоков). Независимо от сложности, стоит вам решать, конечно.