Здравствуйте, sysenter, Вы писали:
S>Если не выровнены, будет дополнительная операция чтения перед которой может вклиниться другой поток + падение производительности на чтении не выровненных данных.
Пентиум читает/пишет целыми кеш-лайнами. Которые могут быть и 8 байт, и 16, и 32 и даже 64, в зависимости от модели процессора.
Как данные выровнены внутри кеш-лайна, процессору все равно. Если данные пересекают границу кеш-лайна, насколько я понимаю, аппаратура не гарантирует атомарного обращения, чего бы не было написано в программе.
Здравствуйте, 23W, Вы писали:
23W>1. простая операция чтения из переменной (1, 2, 4 байта) — атомарна.
да 23W>2. простая операция записи в такую же переменную — атомарна.
да 23W>3. сложные операторы и комбинации операций 1. и 2. — не атомарны и требуют вызова InterlockedXXX.
в многопоточных приложениях — да 23W>Причем операции 1 и 2 атомарны для 2 и 4 байтовых переменных если они выровнены (кстатий, вопрос — а если нет, что тогда?). Для однобайтных переменных операции 1 и 2 всегда атомарны.
да 23W>Атомарность операций 1 и 2 гарантируется архитектурой, а чтобы эта гарантия не была испорчена оптимизирующим компиляторам — нужна директива volatile переде переменными (и только для этого).
атомарность гарантируется архитектурой процессора, и компилятор и слово volatile здесь не причем.
Volatile только подавляет оптимизацию работы с переменной в компиляторе. Т.е., везде, где у тебя выполняются какие-то действия с такой переменной, компилятор будет честно вставлять операции чтения/записи в оперативную память, даже если у него под рукой будет регистр с значением этой переменной. У MSVC есть дополнительная плюшка — действие с volatile переменной (чтение или запись) является барьером для других операций такого же типа (чтение или запись). Т.е., компилятор не поменяет их местами.
23W>P.S.: как я уже спросил, — что делать если пемеренные 2 и 4х байтные и при этом не выровнены по адресу памяти (т.е. разделены границей кеш-линии).
Я не знаю.
Обычно переменные выровнены так, как надо.
Чтобы получить невыровеннные данные надо специально "целится себе в ногу".
Здравствуйте, sysenter, Вы писали:
S>>volatile нужен, чтобы компилятор не применял оптимизацию и только. Оптимизация подразумевает в том числе и перестановку ассемблерных инструкций.
S>+ проц не будет хранить значение volatile переменной в кеше, а только в памяти.
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, 23W, Вы писали:
23W>>Атомарность операций 1 и 2 гарантируется архитектурой, а чтобы эта гарантия не была испорчена оптимизирующим компиляторам — нужна директива volatile переде переменными (и только для этого). 23W>>Правильно ?
Pzz>Отцы, вы забываете про то, что процессор может изменить порядок фактического обращения к памяти — по сравнению с ассемблерным кодом, а даже не сишным. При этом если "смотреть" с одного ядра, то для x86-х процессоров гарантируется, что вы этого не заметите. А вот если смотреть на ту же память с соседнего ядра, то можете и заметить.
Pzz>В общем, если не хотите приключений на собственную ж..., лучше предохраняться использовать предназначенные для этого атомарные операции и/или примитивы синхронизации.
Скажите, как такое поведение процессора может сказаться на атомарности операции записи или чтения выровренных данных. Ведь два процессора одновременно писать в одну ячейку памяти не могут, только последовательно (шина то — одна).
Здравствуйте, 23W, Вы писали:
.S>>+ проц не будет хранить значение volatile переменной в кеше, а только в памяти. 23W>а вот это уже явно — неправда.
Строго говоря, значение volatile переменной не будет храниться в регистре это факт.
Но, у ядер современных процов есть не разделяемые кеши и если хранить такую переменную в кеше, которая изменяется незаметно, то ядро просто этого не заметит. Логично предположить, что ядро каждый раз её из памяти читает.
Здравствуйте, sysenter, Вы писали:
S>Строго говоря, значение volatile переменной не будет храниться в регистре это факт. S>Но, у ядер современных процов есть не разделяемые кеши и если хранить такую переменную в кеше, которая изменяется незаметно, то ядро просто этого не заметит. Логично предположить, что ядро каждый раз её из памяти читает.
У тебя неверное представление об архитектуре интеловских процессоров.
Все, на самом деле, не так.
Здравствуйте, Pzz, Вы писали:
Pzz>Пентиум читает/пишет целыми кеш-лайнами. Которые могут быть и 8 байт, и 16, и 32 и даже 64, в зависимости от модели процессора.
В кеш проц может блоками прочитать из памяти т.к. принцип сосредоточенности во времени никто не отменял и именно так и происходит.
Мы же говорим про атомарность операций над простыми данными размером — 2 или 4 байта. Операции над ними тот же инкремент происходят в регистре, если данные не выровнены в регистр поместить данные за одно обращение нельзя.
Или нет? Отчего тогда падение производительности на операциях с не выровненными данными?
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, 23W, Вы писали:
23W>>Атомарность операций 1 и 2 гарантируется архитектурой, а чтобы эта гарантия не была испорчена оптимизирующим компиляторам — нужна директива volatile переде переменными (и только для этого). 23W>>Правильно ?
Pzz>Отцы, вы забываете про то, что процессор может изменить порядок фактического обращения к памяти — по сравнению с ассемблерным кодом, а даже не сишным. При этом если "смотреть" с одного ядра, то для x86-х процессоров гарантируется, что вы этого не заметите. А вот если смотреть на ту же память с соседнего ядра, то можете и заметить.
Да, но это уже другой вопрос Для его решения существуют барьеры, volatile тут ни при чём
Pzz>В общем, если не хотите приключений на собственную ж..., лучше предохраняться использовать предназначенные для этого атомарные операции и/или примитивы синхронизации.
Здравствуйте, rus blood, Вы писали:
RB>Volatile только подавляет оптимизацию работы с переменной в компиляторе. Т.е., везде, где у тебя выполняются какие-то действия с такой переменной, компилятор будет честно вставлять операции чтения/записи в оперативную память, даже если у него под рукой будет регистр с значением этой переменной. У MSVC есть дополнительная плюшка — действие с volatile переменной (чтение или запись) является барьером для других операций такого же типа (чтение или запись). Т.е., компилятор не поменяет их местами.
Любой вменяемый компилятор обладает этой "плюшкой".
И кстати, в отношении любых нелокальных переменных (а не только volatile) любой вызов не-inline функции обладает тем же эффектом: компилятор же не может знать, имеет эта функция доступ к этим переменным, или не имеет, и поэтому предполагает худшее. Благодаря этому данные, защищенные mutex'ом, не обязательно делать volatile: вызовы функций, захватывающих/освобождающих mutex заодно заставят компилятор упорядочить обращения к данным.
Здравствуйте, Jolly Roger, Вы писали:
JR>Здравствуйте, Pzz, Вы писали:
Pzz>>Здравствуйте, 23W, Вы писали:
23W>>>Атомарность операций 1 и 2 гарантируется архитектурой, а чтобы эта гарантия не была испорчена оптимизирующим компиляторам — нужна директива volatile переде переменными (и только для этого). 23W>>>Правильно ?
Pzz>>Отцы, вы забываете про то, что процессор может изменить порядок фактического обращения к памяти — по сравнению с ассемблерным кодом, а даже не сишным. При этом если "смотреть" с одного ядра, то для x86-х процессоров гарантируется, что вы этого не заметите. А вот если смотреть на ту же память с соседнего ядра, то можете и заметить.
JR>Да, но это уже другой вопрос Для его решения существуют барьеры, volatile тут ни при чём
Да нет же, это все тот же вопрос! Как изменение порядка фактического обращения к памяти может разрушить атомарность простых операций чтения и записи для выровненных данных ? Это важно!
И если проц. эту атомарность разрушает — как именно, конкретно с этим бороться! Какие функции API, какие приемы ?
Pzz>>В общем, если не хотите приключений на собственную ж..., лучше предохраняться использовать предназначенные для этого атомарные операции и/или примитивы синхронизации. JR>Так проще, но не всегда лучше.
Дайте лучшее решение.
Здравствуйте, 23W, Вы писали:
23W>Суммируя то что сказали Jolly Roger и rus blood. 23W>Я понял что: 23W>1. простая операция чтения из переменной (1, 2, 4 байта) — атомарна. 23W>2. простая операция записи в такую же переменную — атомарна. 23W>3. сложные операторы и комбинации операций 1. и 2. — не атомарны и требуют вызова InterlockedXXX.
В общем и целом — да.
23W>Причем операции 1 и 2 атомарны для 2 и 4 байтовых переменных если они выровнены (кстатий, вопрос — а если нет, что тогда?).
Тогда для её записи или чтения процессору может потребоваться два обращения к шине.
23W>Атомарность операций 1 и 2 гарантируется архитектурой, а чтобы эта гарантия не была испорчена оптимизирующим компиляторам — нужна директива volatile переде переменными (и только для этого). 23W>Правильно ?
Не совсем. Компилятор может "испортить" не гарантию атомарности, она никуда не денется. Компилятор может просто сохранить значение переменной в регистре и использовать его(или вообще выкинуть обращение к данной переменной), и тогда изменения этой переменной другим потоком будут не видны данному потоку.
23W>P.S.: как я уже спросил, — что делать если пемеренные 2 и 4х байтные и при этом не выровнены по адресу памяти (т.е. разделены границей кеш-линии).
Кэш управляется процессором, он "прозрачен" для программы, поэтому в данном случае лучше вообще не брать во внимание наличие кэшей.
PS Вообще на RSDN на эту тему было очень много обсуждений , лучше воспользуйтесь поиском и внимательно прочитайте, это будет полезно.
Здравствуйте, 23W, Вы писали:
Pzz>>В общем, если не хотите приключений на собственную ж..., лучше предохраняться использовать предназначенные для этого атомарные операции и/или примитивы синхронизации. 23W>Скажите, как такое поведение процессора может сказаться на атомарности операции записи или чтения выровренных данных. Ведь два процессора одновременно писать в одну ячейку памяти не могут, только последовательно (шина то — одна).
В одном потоке вы пишете: flag = true; i = 5. Другой поток, на другом процессоре, может увидеть на мгновение, что i уже 5, а flag все еще false. Атомарности операций это не противоречит, но вот порядок исполнения — другой. А если то же самое делать через InterlockedXXX(), или под мьютексом, то порядок будет правильный, такой же, как в тексте.
Здравствуйте, sysenter, Вы писали:
S>Строго говоря, значение volatile переменной не будет храниться в регистре это факт. S>Но, у ядер современных процов есть не разделяемые кеши и если хранить такую переменную в кеше, которая изменяется незаметно, то ядро просто этого не заметит. Логично предположить, что ядро каждый раз её из памяти читает.
Пентиум умеет синхронизировать кеши между процессорами/ядрами так, что программисту это незаметно. Это называется "когерентный кэш". И да, они между собой переговариваются, чтобы знать, когда синхронизировать кэш
Здравствуйте, rus blood, Вы писали:
RB>Volatile только подавляет оптимизацию работы с переменной в компиляторе. Т.е., везде, где у тебя выполняются какие-то действия с такой переменной, компилятор будет честно вставлять операции чтения/записи в оперативную память, даже если у него под рукой будет регистр с значением этой переменной.
Подожди, вот я написал, что проц не хранит volatile переменную в неразделяемом кеше, а ты мне написал что я не разбираюсь в архитектуре процов intel. Теперь сам пишешь, что операции чтения/записи с volatile переменными происходят в оперативной памяти. Так где же правда?
Здравствуйте, sysenter, Вы писали:
S>Если DMA изменит значение volatile переменной, а она у одного ядра закешированна в не разделяемом кеше L1, как это разруливается?
Вам нужно понять одну простую вещь.
Процессор, ядра, кеш, и прочее железо "понятия не имеют" о слове volatile.
На участках памяти, которые выделяются под переменные, слово volatile никак не влияет.
Здравствуйте, rus blood, Вы писали:
RB>Вам нужно понять одну простую вещь. RB>Процессор, ядра, кеш, и прочее железо "понятия не имеют" о слове volatile. RB>На участках памяти, которые выделяются под переменные, слово volatile никак не влияет.