Многопоточная паранойя - PullRequest
29 голосов
/ 03 января 2009

Это сложный вопрос, пожалуйста, внимательно подумайте, прежде чем ответить.

Рассмотрим эту ситуацию. Два потока (читатель и писатель) обращаются к одному глобальному int. Это безопасно? Обычно я отвечаю без мыслей, да!

Однако мне кажется, что Херб Саттер так не считает. В своих статьях об эффективном параллелизме он обсуждает некорректную очередь без блокировки и исправленную версию .

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

мой вопрос; Является ли перезапись записи проблемой на реальном оборудовании? Или многопоточная паранойя просто педантична?
А как насчет классических однопроцессорных систем?
А как насчет более простых процессоров RISC, таких как встроенный power-pc?

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

Ответы [ 9 ]

26 голосов
/ 03 января 2009

Ваша идея осмотра сборки недостаточно хороша; переупорядочение может происходить на аппаратном уровне.

Чтобы ответить на ваш вопрос "это когда-либо проблема на оборудовании для чтения:" Да! На самом деле я сам столкнулся с этой проблемой.

Можно ли обойти проблему с однопроцессорными системами или в других особых случаях? Я бы сказал «нет», потому что через пять лет вам, возможно, понадобится работать на многоядерных процессорах, и тогда найти все эти места будет сложно (невозможно?).

Одно исключение: Программное обеспечение, разработанное для встраиваемых аппаратных приложений, где вы действительно полностью контролируете оборудование. На самом деле я "обманул", как это в тех ситуациях, например, процессор ARM.

9 голосов
/ 03 января 2009

Да, используйте барьеры памяти для предотвращения переупорядочения команд, где это необходимо. В некоторых компиляторах C ++ ключевое слово volatile было расширено для вставки неявных барьеров памяти для каждого чтения и записи - но это не переносимое решение. (Аналогично с интерфейсом Interlocked * win32 API). В Vista даже добавлены некоторые более мелкозернистые API-интерфейсы с блокировкой, которые позволяют указывать семантику чтения или записи.

К сожалению, C ++ имеет настолько свободную модель памяти, что любой подобный код будет в некоторой степени непереносим, ​​и вам придется писать разные версии для разных платформ.

5 голосов
/ 03 января 2009

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

Исправлено было использование инструкции Interlocked, поскольку (в то время) не было никакого способа сказать компилятору просто, кроме барьера записи после назначения.

Нам действительно нужно расширение языка, чтобы разобраться с этим. Использование volatile (если поддерживается компилятором) слишком грубо для случаев, когда вы пытаетесь выжать из куска кода как можно большую производительность.

5 голосов
/ 03 января 2009

Как вы сказали, из-за переупорядочения, выполняемого на уровне кэша или процессора, вам действительно необходим какой-то барьер памяти для обеспечения надлежащей синхронизации, особенно для многопроцессорных систем (и особенно на платформах, отличных от x86). (Мне дано полагать, что однопроцессорные системы не имеют этих проблем, но не цитируйте меня об этом - я, безусловно, более склонен к безопасному обращению и в любом случае к синхронизированному доступу.)

4 голосов
/ 03 января 2009

это когда-нибудь проблема на реальном оборудовании?

Абсолютно, особенно сейчас с переходом на несколько ядер для текущих и будущих процессоров. Если вы зависите от заказанной атомарности при реализации функций в своем приложении и не можете гарантировать это требование с помощью выбранной вами платформы или использования примитивов синхронизации, при условиях все , т. Е. Клиент переходит от одного ядра CPU на многоядерный CPU, тогда вы просто ожидаете возникновения проблемы.

Цитата из упомянутой статьи Херба Саттера (вторая)

Упорядоченные атомарные переменные пишутся по-разному на популярных платформах и средах. Например:

  • volatile в C # / .NET, как в volatile int.
  • volatile или * Atomic * в Java, как в volatile int, AtomicInteger.
  • atomic<T> в C ++ 0x, предстоящий стандарт ISO C ++, как в atomic<int>.

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

3 голосов
/ 03 января 2009

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

Если вы хотите увидеть, как могут быть плохие вещи, ищите научные статьи по модели памяти Java (а также по модели памяти C ++). Учитывая переупорядочение, которое может сделать настоящее аппаратное обеспечение, попытка выяснить, что безопасно на языке высокого уровня, является кошмаром.

2 голосов
/ 17 октября 2010

Нет, это небезопасно, и есть реальное аппаратное обеспечение, которое демонстрирует эту проблему, например, модель памяти в чипе powerpc на xbox 360 позволяет переупорядочивать записи. Это усугубляется отсутствием внутренних барьеров, см. Эту статью на msdn для получения более подробной информации.

1 голос
/ 05 января 2009

Когда я задал вопрос, меня больше всего заинтересовал однопроцессорный powerpc. В одном из комментариев InSciTek Jeff упоминаются инструкции powerpc SYNC и ISYNC. Те, где ключ к окончательному ответу. Я нашел это здесь на сайте IBM.

Статья большая и довольно плотная, но отнять ее нет, она небезопасна. На старых PowerPC оптимизаторы памяти не были достаточно сложными, чтобы вызвать проблемы на однопроцессорных. Однако, новые более агрессивны и могут нарушить даже простой доступ к глобальному int.

1 голос
/ 05 января 2009

Ответ на вопрос «Безопасно ли это» по сути своей неоднозначен.

Это всегда безопасно, даже для двоих, в том смысле, что ваш компьютер не загорается. Это безопасно, в том смысле, что вы всегда получите значение, которое int удерживало когда-то в прошлом, Это небезопасно, в том смысле, что вы можете получить значение, которое будет обновлено другим потоком.

«Атомная» означает, что вы получаете вторую гарантию. Поскольку double обычно не является атомарным, вы можете получить 32 старых и 32 новых бита. Это явно небезопасно.

...