Состояние гонки на x86 - PullRequest
       0

Состояние гонки на x86

24 голосов
/ 08 июля 2011

Может кто-нибудь объяснить это утверждение:

shared variables
x = 0, y = 0

Core 1       Core 2
x = 1;       y = 1;
r1 = y;      r2 = x;

Как можно иметь r1 == 0 и r2 == 0 на процессорах x86?

Источник "Язык параллелизмаБартош Милевски .

Ответы [ 3 ]

27 голосов
/ 08 июля 2011

Проблема может возникнуть из-за оптимизации, включающей переупорядочение инструкций . Другими словами, оба процессора могут назначить r1 и r2 до назначения переменных x и y, если они обнаружат, что это приведет к лучшей производительности. Эту проблему можно решить, добавив барьер памяти , который будет обеспечивать ограничение порядка.

Цитировать слайд-шоу , которое вы упомянули в своем сообщении:

Современные многоядерные / языковые разрывы последовательная согласованность .

Что касается архитектуры x86, лучшим ресурсом для чтения является Руководство разработчика программного обеспечения для архитектуры Intel® 64 и IA-32 (Глава 8.2 Порядок памяти ). Разделы 8.2.1 и 8.2.2 описывают упорядочение памяти, осуществляемое Intel486, Pentium, Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium 4, Intel Процессоры семейства Xeon и P6: модель памяти под названием упорядочение процессоров , в отличие от упорядочение программ ( строгое упорядочение ) более старой архитектуры Intel386 (где чтение и инструкции записи всегда выдавались в порядке их появления в потоке инструкций.

В руководстве описываются многие гарантии заказа модели памяти для процессора (например, Нагрузки не переупорядочиваются при других нагрузках , Хранилища не переупорядочиваются с другими хранилищами , Хранилища не переупорядочиваются со старыми нагрузками и т. д.), но в нем также описывается допустимое правило переупорядочения, которое вызывает состояние гонки в должности ОП:

8.2.3.4 Грузы могут быть переупорядочены с более ранними магазинами в разные Местоположение

С другой стороны, если первоначальный порядок инструкций был переключен:

shared variables
x = 0, y = 0

Core 1       Core 2
r1 = y;      r2 = x;
x = 1;       y = 1;

В этом случае процессор гарантирует, что ситуация r1 = 1 и r2 = 1 не разрешена (из-за 8.2.3.3 Хранилища не переупорядочиваются с более ранней гарантией загрузки ), означая, что эти инструкции никогда не будут переупорядочено в отдельных ядрах.

Чтобы сравнить это с различными архитектурами, ознакомьтесь с этой статьей: Упорядочение памяти в современных микропроцессорах ( это изображение специально). Вы можете видеть, что Itanium (IA-64) делает еще больше переупорядочения, чем архитектура IA-32.

3 голосов
/ 08 июля 2011

На процессорах с более слабой моделью согласованности памяти (таких как SPARC, PowerPC, Itanium, ARM и т. Д.) Указанное выше условие может иметь место из-за отсутствия принудительной когерентности кэша при записи без явной инструкции барьера памяти. Таким образом, в основном Core1 видит запись на x до y, в то время как Core2 видит запись на y до x. В этом случае не требуется полная инструкция заборов: в основном вам нужно будет применять семантику записи или выпуска только в этом сценарии, чтобы все записи были зафиксированы и были видимы всем процессорам, прежде чем произойдут чтения по тем переменным, которые были написано Архитектуры процессоров с такими сильными моделями согласованности памяти, как x86, обычно делают это ненужным, но, как отмечает Гро, сам компилятор может переупорядочивать операции. Вы можете использовать ключевое слово volatile в C и C ++, чтобы предотвратить переупорядочение операций компилятором в данном потоке. Это не означает, что volatile создаст потокобезопасный код, который управляет видимостью операций чтения и записи между потоками ... для этого потребуется барьер памяти. Таким образом, в то время как использование volatile может все еще создавать небезопасный многопоточный код, в данном потоке он будет обеспечивать последовательную согласованность на уровне соответствия машинного кода.

2 голосов
/ 09 июля 2011

Вот почему некоторые говорят: Потоки, которые считаются вредными

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

  • Компилятор знает, что x и y не являются псевдонимами, поэтому заказывать операции не требуется.

  • Процессор знает, что x и y не являются псевдонимами, поэтому он может переупорядочить их по скорости.Хороший пример того, когда это происходит, - когда процессор обнаруживает возможность комбинирования записи .Он может объединить одну запись с другой, если он может сделать это, не нарушая свою модель когерентности.

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

...