Расслабленная атомика и когерентность памяти в отсутствие синхронизации - PullRequest
2 голосов
/ 22 февраля 2020

Я написал базовый планировщик графиков c, который синхронизирует выполнение задачи без ожидания. Поскольку топология графа неизменна, я решил, что все операции Atom c будут упрощены. Однако, когда я узнал больше об аппаратном обеспечении ЦП, я начал беспокоиться о поведении моей структуры данных на платформах с более слабыми моделями памяти (я только протестировал свой код на x86). Вот сценарий, который беспокоит меня:

Поток 1 (T1) и Поток 2 (T2) одновременно обновляют (неатомно) области памяти X и Y соответственно, а затем переходят к выполнению других не связанных задач.

Поток 3 (T3) выбирает зависимую задачу после выполнения T1 и T2, загружает X и Y и суммирует их. Синхронизация получения / выпуска, соединения потоков или блокировки не выполняются, и задача T3 гарантированно будет запланирована после выполнения T1 и T2.

Предполагая, что T1, T2 и T3 запланированы (ОС) на разных ядрах ЦП, мой вопрос: При отсутствии каких-либо ограничений памяти или инструкций, подобных блокировке, T3 гарантированно см. последние значения X и Y? Другой способ задать вопрос: Если вы не вставите ограждение, через какое время после магазина вы сможете выполнить загрузку, или нет никаких гарантий относительно этого?

Меня беспокоит то, что нет никаких гарантий, что ядра, которые выполнили T1 и T2, очистили свои буферы хранения к тому времени, когда ядро ​​T3 попытается загрузить эту информацию . Я склонен думать о гонках данных как о повреждениях данных, которые происходят из-за нагрузки и хранилища (или хранилища и хранилища), происходящего одновременно. Однако я пришел к выводу, что не совсем уверен, что в действительности означает , учитывая распределенную природу процессоров в микромасштабе. Согласно CppRef:

Программа, которая имеет две конфликтующие оценки, имеет гонку данных, если:

  • обе оценки не выполняются в одном потоке или в одном и том же обработчике сигналов, или
  • обе конфликтующие оценки являются атомом c операциями (см. Std :: atomi c) или
  • одна из конфликтующих оценок происходит раньше другой (см. Std :: memory_order)

Похоже, это означает, что любой, использующий мой планировщик графиков, столкнется с гонкой данных (при условии, что они сами не защищают от нее), даже если я могу гарантировать, что T3 не будет работать до T1 и Т2 сделаны. Я еще не наблюдал гонки данных в моих тестах, но я не настолько наивен, чтобы думать, что одних тестов достаточно, чтобы доказать это.

1 Ответ

1 голос
/ 22 февраля 2020

как долго после магазина вы можете выполнить загрузку

ISO C ++ дает нулевые гарантии о сроках. Почти всегда плохая идея полагаться на время / расстояние для правильности.

В этом случае все, что вам нужно, это синхронизация получения / выпуска где-то в самом планировщике, например, T1 и T2 объявляют себя выполненными с использованием release-store и планировщик проверяет это с загрузкой получения.

В противном случае, что это вообще означает, что T3 выполняется после T1 и T2? Если планировщик мог видеть «Я сделал» хранить на ранней стадии, он может начать T3, пока T1 или T2 не сделали все свои хранилища.

Если вы убедитесь, что все в T3 происходит после T1 и T2 (используя загружает, которые «синхронизируются с» хранилищем релизов от каждого из T1 и T2), вам даже не нужно использовать атомы в T1 и T2, только в механизме планировщика.

Получение нагрузки и Магазин релизов относительно дешев по сравнению с seq_cst. На реальном HW seq_cst должен заполнять sh буфер хранилища после хранилища, релиз не делает. x86 делает acq_rel бесплатно.


(И да, тестирование на x86 ничего не доказывает; аппаратная модель памяти в основном acq_rel, поэтому при переупорядочении во время компиляции выбирается какой-то законный порядок, и затем этот порядок выполняется с acq_rel.)


Я не уверен, если запуск потока new гарантирует, что все в этом потоке "происходит после" этой точки в этом потоке. Если это так, то это формально безопасно.

Если нет, то в теории IRIW переупорядочение вызывает беспокойство. (Все потоки, использующие загрузки seq_cst, должны согласовывать глобальный порядок хранилищ seq_cst, но не с более слабыми порядками памяти. На практике PowerP C - это оборудование, которое может делать это в реальной жизни, AFAIK, и только для коротких i sh windows. Будут ли две записи atomi c в разные места в разных потоках всегда рассматриваться в одном и том же порядке другими потоками? . Любой конструктор std::thread будет включать системный вызов и будет длинным достаточно на практике, а также в любом случае задействовать барьеры независимо от того, формально ли это гарантирует ISO C ++.

Если вы не запускаете новый поток, а вместо этого сохраняете флаг для работника: видите, тогда снова достаточно acq / rel; происходит до того, как транзитивно , поэтому A -> B и B -> C подразумевают A -> C.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...