Здравствуйте, Alex Alexandrov, Вы писали:
AA>Ох, зря эту ветку подняли... Рекомендую прочитать все перед высказыванием мыслей.
AA>Вкратце:
А если в подробностях ? Может статью на эту тему, а то ощущается некая нехватка собранной и структурирванной информации на эту тему, причём на русском языке (помните главную идеологию RSDN — обширный портал для руско-говорящих программистов )
Вопрос.
Как-то в одной из веток (возможно даже в этой — сейчас уже не найду) проскакивало такое заявление: поскольку явных механизмов установки барьера памяти в C++ нет, то неявным является вызов обычной функции (вроде как компилятор должен обеспечить генерацию такого кода). То есть насколько я понял вызов функции (возможно специально оформленной — естественно inline функций это не касается ) гарантировано заставляет процессор перечитывать даные из памяти и тем самым решается проблема переупорядочивании комманд. Насколько это утверждение близко к действительности, из чего оно следует и можно ли его принять на вооружение, как гарантированное средство? Вызов каких именно функции приводит к таком поведению или всех кроме подставляемых?
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Здравствуйте, Mr. None, Вы писали:
AA>>Вкратце: MN>А если в подробностях ? Может статью на эту тему, а то ощущается некая нехватка собранной и структурирванной информации на эту тему, причём на русском языке (помните главную идеологию RSDN — обширный портал для руско-говорящих программистов )
В ветке были приведены все необходимые ссылки на док-цию Intel, Microsoft, POSIX, etc..
Здравствуйте, MaximE, Вы писали:
ME>Здравствуйте, Mr. None, Вы писали:
AA>>>Вкратце: MN>>А если в подробностях ? Может статью на эту тему, а то ощущается некая нехватка собранной и структурирванной информации на эту тему, причём на русском языке (помните главную идеологию RSDN — обширный портал для руско-говорящих программистов )
ME>В ветке были приведены все необходимые ссылки на док-цию Intel, Microsoft, POSIX, etc..
А где это ообщение... я его найти не могу — тогда мельком просмотрел, а сейчас хочется прочитать по подробнее...
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
[]
> ME>В ветке были приведены все необходимые ссылки на док-цию Intel, Microsoft, POSIX, etc.. > > А где это ообщение... я его найти не могу — тогда мельком просмотрел, а сейчас хочется прочитать по подробнее...
Боюсь, что тебе придется прочитать всю ветку со всеми ссылками.
E>А что ему мешает их вставлять, интересно? Не вставляет — такова реализация. Но барьеры без volatile особого смысла не имеют. Если программа с глубокой оптимизацией и параллелизмом правильно работает без volatile — это либо недостатки компилятора, либо счастливое стечение обстоятельств.
ну. это вы погорячились. по работе постоянно приходится писать программы с глубокой оптимизацией и параллелизмом. и ещё ни разу не пришлось воспользоваться volatile. ибо единственное место, где эти переменные полезны — это всякие там подобия флажков. но это такой бедный и не гибкий механизм синхронизации, что используется очень и очень редко. в моём случае никогда. кроме этого по своему хобби знаю, что и в ядре ОС можно преспокойно обойтись без подобных переменных. просто надо правильно проектировать архитектуру. вот тут, конечно, я не очень понимаю, зачем автору такая переменная понадобилась, но вот распределённый счётчики, который тут упоминались или флажки done — не самое лучшее решение. ибо активное ожидание или подсчёт не каких-то событий, а количеств исполнения инструкции добавления значения — это очевидное зло.
E>То есть, значение выражения является его основным результатом, а отнюдь не побочным эффектом. И ничто не E>У тебя — ерунда, на деле — истина
ME>> На POSIX все барьеры работают без volatile.
E>Что значит "на POSIX"? С каких пор POSIX стал как-то влиять на особенности оптимизации сишного кода? Это, как ты выразился в отношении i86 — стечение обстоятельств.
это не стечение обстоятельств : ). по опыту знаю, что на alpha, powerpc и xscale всё тоже корректно работает. просто это ведь очевидно. если в функцию примитива синхронизации передаётся указатель на переменную, то, какой бы ни была оптимизация в компиляторе, он просто обязан значение этой переменной сохранить в память, а затем уже вызывать функцию. дальнейшее — дело техники и специфичной для неё реализации примитива синхронизации. если же речь о глобальных переменных, то опять же. компилятор си (это особенность языка) не может знать, с какими именно ячейками в глобальной памяти будет работать та или иная функция, особенно, если речь идёт о вызове функции из другого модуля. поэтому все глобальные переменные, в том числе и static (чёрт ведь знает, куда что указывает) опять сохраняются в память. соответсвенно, после вызова функций, обращения к ним включают чтение из памяти.
volatile был полезен, пока человечество искало подходы к многопоточному программированию. но сейчас, imho, от него никакой пользы. одно лишь смущение умов. лишние знания ограничивают свободу творчества. вот человек вместо того, чтобы творить. сидит и разбирается, а что же такое volatile есть.
Здравствуйте, Mr. None, Вы писали:
MN>А если в подробностях ? Может статью на эту тему, а то ощущается некая нехватка собранной и структурирванной информации на эту тему, причём на русском языке (помните главную идеологию RSDN — обширный портал для руско-говорящих программистов )
Я подумаю над этим. Макс, порецензируешь, если что?
MN>Вопрос. MN>Как-то в одной из веток (возможно даже в этой — сейчас уже не найду) проскакивало такое заявление: поскольку явных механизмов установки барьера памяти в C++ нет, то неявным является вызов обычной функции (вроде как компилятор должен обеспечить генерацию такого кода). То есть насколько я понял вызов функции (возможно специально оформленной — естественно inline функций это не касается ) гарантировано заставляет процессор перечитывать даные из памяти и тем самым решается проблема переупорядочивании комманд. Насколько это утверждение близко к действительности, из чего оно следует и можно ли его принять на вооружение, как гарантированное средство? Вызов каких именно функции приводит к таком поведению или всех кроме подставляемых?
Не стоит путать барьеры компилятора и барьеры памяти.
Барьер компилятора не имеет никакого отношения к процессору. Условно говоря, для компилятора регистры процессора являются этаким кэшем нулевого уровня — он помещает в них локальные или глобальные данные для ускорения работы с ними. Одним из основных свойств кэша является время жизни в нем данных, т.е. время жизни соответствия кэшированных данных и их оригинала. Так вот барьер компиляции разрывает эту связь, вынуждая компилятор перезагрузить данные в регистры из памяти. Таким барьером является либо применение спецификатора volatile при объявлении переменной (в этом случае компилятор не имеет права кэшировать эту ячейку в регистре, он может это делать только на малое время и только если машинную инструкцию иным образом исполнить не получается), либо вызов внешней функции, т.е. функции, которая находится в другой единице компиляции и про которую компилятор, таким образом, не знает какие регистры могли измениться во время работы этой функции.
Барьер процессора — совершенно другая штука. Современные процессоры (не x86, но Итаниум и какие-то еще) могут (не могут as in умеют, а могут as in позволяют себе) переупорядочивать инструкции записи в память. Т.е. мы выполняем код
* записать число 1 в ячейку памяти A
* записать число 2 в ячейку памяти B
* записать число 3 в ячейку памяти С
но внешний наблюдатель может увидеть эти обновления в любой из 6 перестановок. Для упорядочивания этого процесса на процессорах с таким свойствами имеются специальные инструкции барьеров. Барьер может быть с одной из следующих семантик: acquire, release или full. Ссылки на материалы по этому есть в ветке.
Оба барьера необходимы для правильной работы в многопроцессорной среде при простом доступе к памяти (без использования примитивов синхронизации или атомарных инструкций — например, в многократно рассмотренном примере с глобальной булевой переменной). Именно поэтому в общем случае просто volatile недостаточно. Хотя есть частные случаи, когда его хватит:
1. На x86, поскольку здесь нет процессорного переупорядочивания.
2. Компилятор может знать о необходимости генерации процессорного барьера вдобавок к своему. Например, у Intel Compiler для Itanium есть опция /Qserialize-volatile. По умолчанию, правда, выключена.
Но самый надежный способ — все-таки примитивы синхронизации и атомарные Interlocked* функции. Как жаль, правда, что в Posix нет аналогов Interlocked*. Понимаю, что дань независимости от конкретных возможностей процессоров, но все равно жаль...
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
It's kind of fun to do the impossible (Walt Disney)
Здравствуйте, Alex Alexandrov, Вы писали:
AA>Здравствуйте, Mr. None, Вы писали:
MN>>А если в подробностях ? Может статью на эту тему, а то ощущается некая нехватка собранной и структурирванной информации на эту тему, причём на русском языке (помните главную идеологию RSDN — обширный портал для руско-говорящих программистов )
AA>Я подумаю над этим. Макс, порецензируешь, если что?
Здравствуйте, Alex Alexandrov, Вы писали:
AA>Здравствуйте, Mr. None, Вы писали:
MN>>А если в подробностях ? Может статью на эту тему, а то ощущается некая нехватка собранной и структурирванной информации на эту тему, причём на русском языке (помните главную идеологию RSDN — обширный портал для руско-говорящих программистов )
AA>Я подумаю над этим. Макс, порецензируешь, если что?
Не вопрос. Но я думаю, лучше об этом попросить Александра Терехова — он реальный перец в этих вопросах.
[]
AA>Но самый надежный способ — все-таки примитивы синхронизации и атомарные Interlocked* функции. Как жаль, правда, что в Posix нет аналогов Interlocked*. Понимаю, что дань независимости от конкретных возможностей процессоров, но все равно жаль...
На практике, под POSIX пишут на gcc, или компиляторе, совместимым с gcc (Intel). Поэтому IMO безопасно полагаться, что присутствуют ф-ции libstdc++ __gnu_cxx::__exchange_and_add и __gnu_cxx::__atomic_add.
Здравствуйте, eao197, Вы писали:
E>Добрый день всем.
Я думаю так —
что во всех вышеприводимых примерах в потоках использовались
функции практически ничего не делающие поэтому естественно компилятор имел полное право
забить переменную в регистр и читать ее только оттуда (без применения violatile)а втором потоке допустим
писал значение в память поэтому происходило зацикливание процесса т.е. в данном конкретном
случае имеет смысл указывать violatile (чтобы переменная каждый раз сливалась читалась из памяти) но я не вижу здесь смысла использовать обьекты
синхронизации поскольку запись или чтение переменной если и не происходят за один такт
то по крайней мере я не думаю что переключение контекста потока произойдет во время выполнения операциию.
Я использую синхронизацию в основном где может идти чтение запись данных которые могут быть непрочитаны \недозаписаны
между переключением контекста.
Но в конкретных приложениях выполняются какие либо операции (я имею в виду не просто i++и в ходе их выполнения
для освобождения регистра его все равно приходиться сливать в память хотя это видимо не 100% гарантия
поэтому думаю что имеет смысл пользовать violatile хотя бы для очистки совести.....
до прочтения этой ветки я не испльзовал violatile и не сталкивался с проблемами его не использования но теперь думаю буду
Всегда хочется быть лучше
Здравствуйте, mukos, Вы писали:
M>Я думаю так - M>до прочтения этой ветки я не испльзовал violatile и не сталкивался с проблемами его не использования но теперь думаю буду
А я вот, во время проектирования приложений (в том числе и кроссплатформенных), всегда, следуя правилам хорошего тона, синхронизировал конкурентные данные с помощью соответствующих примитивов синхронизации (сперва самописных, реализованных через Crirical Section на винде и через pthreads на линуксе, потом — запользовал boost::recursive_mutex), но никогда volatile не пользовал, ибо мне не совсем было ясно на фига это делать, а теперь, после прочтения всей ветки, гарантированно никогда volatile не заюзаю, ибо понял, что юзать квалификатор volatile реально незачем!
P.S.: ... да, и кстати, как там на счёт статьи? ... чтобы коллег просвещать не 25-страничным тредом на форуме, а лаконично изложенной грамотной статьёй с примерами сорцов! Кто нибудь из уважаемых <b>Alex Alexandrov</b>, <b>MaximE</b> или Александр Терехов, взялся за это благое дело?
"Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.