Re[4]: volatile у переменной класса
От: MaximE Великобритания  
Дата: 10.01.05 17:06
Оценка:
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 Yegorushkin
Posted via RSDN NNTP Server 1.9
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.