Увидят ли другие потоки запись в переменную размером с слово «volatile» в разумные сроки?

Когда вы спрашиваете о более конкретном проблема Я обнаружил, что это основная проблема, в которой люди не совсем уверены.

Можно сделать следующие предположения:

  • ЦП действительно использует протокол согласованности кеша, такой как MESI (F) (примеры: x86 / x86_64 и ARMv7mp)
  • Предполагается, что переменная имеет размер, который атомарно записывается / считывается процессором (выровненный и собственный размер слова)
  • Переменная объявлена ​​volatile

Вопросы следующие:

  • Если я напишу переменную в одном потоке, увидят ли это изменение другие потоки?
  • Каков порядок величины таймфрейма, в котором другие потоки увидят изменение?
  • Знаете ли вы об архитектурах, в которых когерентности кэша недостаточно для обеспечения видимости между процессорами и ядрами?

Вопрос НЕ:

  • Насколько безопасно использовать такую ​​переменную?
  • о проблемах с переупорядочиванием
  • об атомике C ++ 11

Это можно рассматривать как дубликат в C / C ++, являются ли изменчивые переменные гарантированно имеющими в конечном итоге согласованную семантику между потоками? и другие подобные вопросы, но я думаю, что ни один из них не имеет тех четких требований в отношении целевой архитектуры, которые приводят к большой путанице в отношении различных предположений.


person Hanno S.    schedule 30.11.2015    source источник
comment
@MartinJames, в многопоточности - по моему опыту - очень сложно писать надежные тесты. Многие вещи работают 99,99% времени, но все равно не работают в определенные сроки. Я задаю этот вопрос, чтобы понять, как это работает в теории, и узнать, какие проблемы я мог вообще не предвидеть.   -  person Hanno S.    schedule 30.11.2015
comment
Полностью согласен с предыдущим комментарием, здесь тестирование не пригодится. Однако связанный вопрос имеет неопределенное поведение, поэтому я не вижу смысла обсуждать вопросы, касающиеся настроек, которые не удаляют неопределенное поведение. Вы говорите, что вопрос НЕ [...], но бессмысленно отвечать на вопрос, в котором говорится, что вопрос не в том, чтобы делать это правильно, я хочу знать производительность неопределенного поведения.   -  person Jonathan Wakely    schedule 30.11.2015
comment
Если вы хотите работать на аппаратном уровне, напишите asm и убедитесь, что вы знаете, что делаете, не пишите C ++ с неопределенным поведением и пытайтесь предсказать, что компилятор сделает с неопределенным кодом.   -  person Jonathan Wakely    schedule 30.11.2015
comment
Я думаю, что вопрос, на который вы ссылаетесь , является дубликатом, хотя обсуждение в комментариях к принятому ответу немного отвлекает. В стандарте говорится, что реализация должна гарантировать, что последнее значение (в порядке модификации), присвоенное атомарной операцией или операцией синхронизации, станет видимым для всех других потоков в течение конечного периода времени. (раздел 1.10 параграф 28); там нет упоминания о volatile.   -  person rici    schedule 30.11.2015
comment
Ваш вопрос общий? volatile для произвольного типа? Или конкретный? Возможно, эта ссылка может предоставить дополнительную информацию: en.cppreference.com/w/cpp/language / cv (это не ответ, но я подумал, что может быть интересно узнать).   -  person Ely    schedule 30.11.2015
comment
Я попытался сделать его достаточно общим для типичного сценария. Мой вопрос касается типов, которые могут быть сохранены / загружены процессором атомарно. Обычно для этого должен подходить тип int.   -  person Hanno S.    schedule 30.11.2015
comment
Дело в том, что атомарность (т.е. не чтение / запись только части ячейки памяти) необходима для правильного многопоточного поведения, но недостаточна. Вам также нужны гарантии непротиворечивости памяти, а volatile не дает таких гарантий. Стандарты C и C ++ ясно говорят, что только атомарные операции дают такие гарантии (которые являются специальными операциями, которые включают любые необходимые барьеры памяти ... использование атомарных операций здесь не означает только частичное чтение / запись! ). См. Ссылки внизу страницы cxx.isvolatileusefulwiththreads.com для получения более подробной информации.   -  person Jonathan Wakely    schedule 02.12.2015
comment
@JonathanWakely: Я знаю это. Но C ++ 11 также поддерживает memory_order_relaxed, который не имеет этих гарантий (только атомарные загрузки / сохранения) - или я здесь ошибаюсь? Мой вопрос в том, соответствует ли использование volatile использованию memory_order_relaxed при использовании с переменными, которые могут храниться / загружаться атомарно.   -  person Hanno S.    schedule 02.12.2015
comment
github.com/datacratic/boost-svn/ blob / master / boost / atomic / detail / (используется для ARM) и github.com/datacratic/boost-svn/blob/master/boost/atomic/detail/, похоже, указывает на то, что, по крайней мере, ребята из команды разработчиков так думают для GCC на ARM и x86. Например магазин реализован как const_cast<volatile storage_type &>(v_) = v;   -  person Hanno S.    schedule 02.12.2015


Ответы (5)


Знаете ли вы об архитектурах, в которых когерентности кэша недостаточно для обеспечения видимости между процессорами и ядрами?

Я не знаю ни одного процессора с несколькими ядрами, у которого есть проблемы с когерентностью кеша. Кто-то может использовать неправильный тип процессора в многопроцессорной плате, например процессор Intel, который имеет то, что Intel называет внешним QPI. отключен, но это вызовет всевозможные проблемы.

Вики-статья о Intel QPI и о том, на каких процессорах он включен или отключен:

http://en.wikipedia.org/wiki/Intel_QuickPath_Interconnect

person rcgldr    schedule 30.11.2015

Если я напишу переменную в одном потоке, увидят ли это изменение другие потоки?

Нет никаких гарантий. Если вы думаете, что он есть, покажите мне, где вы его нашли.

Каков порядок таймфрейма, в котором другие потоки увидят изменение?

Этого не может быть никогда. Нет никаких гарантий.

Знаете ли вы об архитектурах, в которых когерентности кэша недостаточно для обеспечения видимости между процессорами и ядрами?

Это бессвязный вопрос, потому что вы говорите об операциях в коде C ++, который должен быть скомпилирован в код сборки. Даже если у вас есть аппаратные гарантии, применимые к ассемблерному коду, нет никакой гарантии, что эти гарантии «пройдут» до кода C ++.

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

person David Schwartz    schedule 23.01.2016

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

Я могу ошибаться, но вам придется доказать, что я не прав.

person gnasher729    schedule 30.11.2015

Предполагая, что в настоящее время x86 / 64:

Если я напишу переменную в одном потоке, увидят ли это изменение другие потоки?

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

Каков порядок таймфрейма, в котором другие потоки увидят изменение?

Это действительно зависит от того, как вы измеряете. По сути, это будет время задержки памяти = 200 циклов на одном и том же узле NUMA. Примерно двойной на другом узле, на двухузловой коробке. Может отличаться на больших коробках. Если ваша запись будет переупорядочена относительно точки измерения времени, вы можете получить +/- 50 циклов.

Я измерил это несколько лет назад и получил 60-70 нс на приставках с частотой 3 ГГц и вдвое больше на другом узле.

Знаете ли вы об архитектурах, в которых когерентности кэша недостаточно для обеспечения видимости между процессорами и ядрами?

Я думаю, что смысл согласованности кеша - это наглядность. Сказав это, я не уверен, что машины риска Sun имеют такую ​​же когерентность кеша и расслабленную модель памяти, что и x86, поэтому я бы очень тщательно протестировал их. В частности, вам может потребоваться добавить барьеры освобождения памяти, чтобы принудительно сбросить записи в память.

person BitWhistler    schedule 22.01.2016

Учитывая описанные вами предположения, нет никакой гарантии, что запись переменной volatile в одном потоке будет «видна» в другом.

Учитывая это, ваш второй вопрос (о сроках) не применим.

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

На практике на архитектурах, которые требуют выполнения таких инструкций, реализация примитивов синхронизации данных (мьютексы, семафоры, критические секции и т. Д.), Помимо прочего, использует эти инструкции.

В более широком смысле ключевое слово volatile в C ++ вообще не имеет ничего общего с многопоточностью, не говоря уже о когерентности между кешами. volatile в данном потоке выполнения означает необходимость в таких вещах, как выборка и запись переменной, которая не удаляется или не переупорядочивается компилятором (что влияет на оптимизацию). Это не переводится в какие-либо требования к упорядочиванию или синхронизации завершения выборки или записи между потоками выполнения - и такие требования необходимы для согласованности кеша.

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

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

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

person Peter    schedule 23.01.2016