emusic wrote:
>>> The volatile keyword is a type qualifier used to declare that an object can be modified in the program by something other than statements, such as the operating system, the hardware, or a concurrently executing thread.
>>> [/q]
>
> ME>Это не так. Кусок не из стандарта, а наверное, из MSDN. Почитайте про sequence points.
>
> Sequence points — это вообще из другой оперы. Sequence point — место, где заканчиваются побочные эффекты, и не более того. Причем относится это исключительно к имеющему место "потоку выполнения", и к параллельным потокам отношения не имеет.
Именно так. Стандарт вообще не упоминает никаких потоков кроме единственного control flow и еще упоминают обработчики сигналов, для которых собственно и был придуман volatile.
И это проблема стандарта, над которой уже работают умы.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1680.pdf
> А вот при наличии volatile при каждом обращении к такому объекту формируется sequence point.
Именно так.
> Прохождение sequence point отнюдь не гарантирует, что состояния всех объектов в этой точке будут непременно отражены в памяти — это гарантируется только для volatile-объектов.
Я этого и не утверждал, как раз я утверждаю обратное.
Все что я утверждал, что volatile бесполезен, потому, что он не сделает не потокобезопасный код потокобезопасным. Потокобезопасность достигается использованием соответствующих примитивов синхронизации. Использование
любого примитива синхронизации в POSIX и win32 является вызовом ф-ции. Вызов ф-ции — sequence point. Если у тебя уже есть sequence points вокруг защищаемых данных, зачем тогда volatile?
> ME>Вообще, квалификатор volatile у всех Interlocked* функций абсолютно бесполезен, и даже вреден, так как сбивает людей с толку. Так как это ф-ции, то они представляют для компилятора sequence points.
>
> Функции используются только для изменения volatile-переменных. Чтение их значений может происходить в параллельных потоках без каких-либо функций.
Это так только по случайному стечению обстоятельств на IA32. volatile имеет влияние лишь на код, генерируемый компилятором. На переупорядочивание инструкций процессором он не имеет никакого влияния. Именно по этой причине на всех современных RISC процессорах, а также на IA64, тебе придется использовать барьеры памяти чтобы прочитать актуальное значение.
> ME> После sequence points, компилятор обязан перечитывать значения переменных из памяти, неважно, volatile ли, const ли, или это переменная без cv-квалификатора
>
> С чего бы он вдруг стал обязан? Компилятор обязан лишь обеспечить последовательное выполнение заданных действий в рамках одного потока исполнения. Для взаимодействия с "внешними" процессами и существует volatile.
С тем, что состояние программы после sequence point is indeterminate.
> Простейший пример:
>
> > void f (int &a, int &b) {
> if (!a) {
> b = a;
> }
> }
>
>
> При включенной оптимизации многие компиляторы (например, VC++ 6.0) сгенерируют только один опрос переменной a — в проверке условия. Для присваивания будет сгенерирован явный нуль.
Здесь именно простейший пример: нет вызовов ф-ций, компилятор видит весь контекст и плевал на sequence points. Добавим вызов ф-ции.
#include <cstdio>
void f1(int &a, int &b)
{
if (!a)
{
b = a;
}
}
void f2(int volatile &a, int &b)
{
if (!a)
{
b = a;
}
}
void f3(int &a, int &b)
{
if (!a)
{
printf("0x%08x\n", &a);
b = a;
}
}
int main(int ac, char** av)
{
int b;
f1(ac, b);
f2(ac, b);
f3(ac, b);
return b;
}
MS VC 7.1
Command Lines
Creating temporary file "c:\windows\temp\Vad43A.bs" with contents
[
/c /Ox /Og /Ob2 /Oi /Ot /Oy /G6 /GF /FD /EHsc /ML /Zc:wchar_t /Zc:forScope /GR /Zi /nologo /W3 /Wp64 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fo"Release/" /Fd"Release/vc70.pdb" /Gd /TP
.\main.cpp
]
Creating command line "cl.exe @c:\windows\temp\Vad43A.bs"
Creating temporary file "c:\windows\temp\Vad43C.bs" with contents
[
/LTCG /OUT:"Release/exp.exe" /INCREMENTAL:NO /DEBUG /PDB:"Release/exp.pdb" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /TLBID:1 /MACHINE:IX86 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib
Release\main.obj
]
Creating command line "xilink.exe @c:\windows\temp\Vad43C.bs"
--- d:\src\exp\main.cpp --------------------------------------------------------
int main(int ac, char** av)
{
int b;
f1(ac, b);
00401070 8B 4C 24 04 mov ecx,dword ptr [esp+4] ; сохранили ac в ecx
00401074 85 C9 test ecx,ecx
00401076 75 04 jne main+0Ch (40107Ch)
00401078 33 C0 xor eax,eax
0040107A EB 04 jmp main+10h (401080h)
0040107C 8B 44 24 04 mov eax,dword ptr [esp+4] ; oops, перечетали ac, видимо такая оптимизация
f2(ac, b);
00401080 8B 54 24 04 mov edx,dword ptr [esp+4] ; еще раз сохранили ac в ecx
00401084 85 D2 test edx,edx
00401086 75 04 jne main+1Ch (40108Ch)
00401088 8B 44 24 04 mov eax,dword ptr [esp+4] ; перечитали ac, так и должно быть, volatile
f3(ac, b);
0040108C 85 C9 test ecx,ecx ; используем сохраненное ac в ecx
0040108E 75 16 jne main+36h (4010A6h)
00401090 8D 44 24 04 lea eax,[esp+4]
00401094 50 push eax
00401095 68 FC 60 40 00 push offset string "0x%08x\n" (4060FCh)
0040109A E8 08 00 00 00 call printf (4010A7h)
return b;
0040109F 8B 44 24 0C mov eax,dword ptr [esp+0Ch] ; перечитали ac, так и должно быть, function call
004010A3 83 C4 08 add esp,8
}
004010A6 C3 ret
В этом примере компилятор с максимальной оптимизацией перечитывал переменную из памяти, даже когда ее можно было прочитать из регистра.
> Если к a добавить volatile — будет сгенерировано повторное чтение из памяти. И совершенно правильно.
С этим никто не спорит.
Мой point: volatile бесполезен (и даже вреден) для multithreading, барьеры нужны по-любому.
--
Maxim YegorushkinPosted via RSDN NNTP Server 1.9