Re[4]: volatile у переменной класса
От: achp  
Дата: 12.01.05 11:51
Оценка:
Здравствуйте, emusic, Вы писали:

E>А вот при наличии volatile при каждом обращении к такому объекту формируется sequence point.


Откуда это следует? В стандарте написано, что обращение к volatile-объектам встроенных типов является частью обозримого поведения (observable behaviour) программы, а следовательно не может быть устранено средствами оптимизации. Но это не имеет никакого отношения к точкам следования.
Я кончил, джентльмены, мне остается только поблагодарить вас за внимание.
Re[4]: volatile у переменной класса
От: achp  
Дата: 12.01.05 12:00
Оценка:
Здравствуйте, emusic, Вы писали:

E>Надо отдать должное реализации этих функций: если секция не захвачена другим потоком — объекты синхронизации не задействуются, все выливается только в вызовы InterlockedXXX.


Собственно, в этом их raison d'etre. Иначе мьютексов бы хватало.
Я кончил, джентльмены, мне остается только поблагодарить вас за внимание.
Re[5]: volatile у переменной класса
От: emusic Франция https://software.muzychenko.net/ru
Дата: 12.01.05 12:06
Оценка:
Здравствуйте, achp, Вы писали:

E>>А вот при наличии volatile при каждом обращении к такому объекту формируется sequence point.


A>Откуда это следует?


Тут я спутал — обращение к volatile-объекту стандарт относит к явлениям побочных эффектов.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Re[8]: читаем про volatile
От: Аноним  
Дата: 12.01.05 12:18
Оценка:
ME>За эту статью Александреску был публично выпорон

Как-то колбасит от этого слова. Выпорон или выпорот?
Re[10]: volatile у переменной класса
От: MaximE Великобритания  
Дата: 14.01.05 18:36
Оценка:
Здравствуйте, emusic, Вы писали:

[]

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


Я скомпилировал следующий код на VC71 и Intel 8 с максимальными настройками оптимизации, с LTCG/IPO и без. Во всех четырех случаях при чтении переменная не кэшировалась, неважно, volatile ли была переменная или нет.

Оптимизатор, более мощный чем у VC71, мне не известен. Пример прост, и, скорее всего, более легкий пример для оптимизатора сложно придумать.

Хотелось бы увидеть multithreaded код, где volatile реально чем-то помогает.

// main.cpp

#include <stdio.h>
#include "windows.h"

extern void foo();

long a;
long b;
long volatile c;

void f()
{
    while(!a)
        Sleep(20);
    printf("%s is done\n", __FUNCTION__);
}

void g()
{
    while(!b)
        Sleep(20);
    printf("%s is done\n", __FUNCTION__);
}

void h()
{
    while(!c)
        Sleep(20);
    printf("%s is done\n", __FUNCTION__);
}

int main()
{
    foo();

    f();
    g();
    h();
}


// other.cpp

#include "windows.h"

extern long a;
extern long b;
extern long volatile c;


DWORD WINAPI thread(void*)
{
    Sleep(200);
    InterlockedIncrement(&a);
    Sleep(200);
    ++b;
    Sleep(200);
    ++c;
    return 0;
}

void foo()
{
    CreateThread(0, 0, thread, 0, 0, 0);
}


/c  /Ox /Og /Ob2 /Oi /Ot /Oy /G7 /GF /FD /ML /Zi /nologo /W4 /Wp64 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FAs /Fa"Release/" /Fo"Release/" /Fd"Release/vc70.pdb" /Gd /TP
/OUT:"Release/volatile.exe" /INCREMENTAL:NO /DEBUG /PDB:"Release/volatile.pdb" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /OPT:NOWIN98 /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 

--- d:\src\volatile\main.cpp ---------------------------------------------------

int main()
{
004010C0  push        esi  
    foo();
004010C1  call        foo (4011A0h) 

    f();
004010C6  cmp         dword ptr [a (4096C8h)],0 
004010CD  mov         esi,dword ptr [__imp__Sleep@4 (407000h)] 
004010D3  jne         main+22h (4010E2h) 
004010D5  push        14h  
004010D7  call        esi  
004010D9  cmp         dword ptr [a (4096C8h)],0 
004010E0  je          main+15h (4010D5h) 
004010E2  push        offset string "f" (407108h) 
004010E7  push        offset string "%s is done\n" (4070FCh) 
004010EC  call        printf (4011B6h) 
004010F1  add         esp,8 
    g();
004010F4  cmp         dword ptr [b (4096C0h)],0 
004010FB  jne         main+4Dh (40110Dh) 
004010FD  lea         ecx,[ecx] 
00401100  push        14h  
00401102  call        esi  
00401104  cmp         dword ptr [b (4096C0h)],0 
0040110B  je          main+40h (401100h) 
0040110D  push        offset string "g" (40710Ch) 
00401112  push        offset string "%s is done\n" (4070FCh) 
00401117  call        printf (4011B6h) 
0040111C  add         esp,8 
    h();
0040111F  cmp         dword ptr [c (4096C4h)],0 
00401126  jne         main+75h (401135h) 
00401128  push        14h  
0040112A  call        esi  
0040112C  cmp         dword ptr [c (4096C4h)],0 
00401133  je          main+68h (401128h) 
00401135  push        offset string "h" (407110h) 
0040113A  push        offset string "%s is done\n" (4070FCh) 
0040113F  call        printf (4011B6h) 
00401144  add         esp,8 
}
00401147  xor         eax,eax 
00401149  pop         esi  
0040114A  ret              
--- No source file -------------------------------------------------------------
0040114B  int         3    
0040114C  int         3    
0040114D  int         3    
0040114E  int         3    
0040114F  int         3    
--- d:\src\volatile\other.cpp --------------------------------------------------
#include "windows.h"

extern long a;
extern long b;
extern long volatile c;


DWORD WINAPI thread(void*)
{
00401150  push        esi  
    Sleep(200);
00401151  mov         esi,dword ptr [__imp__Sleep@4 (407000h)] 
00401157  push        0C8h 
0040115C  call        esi  
    InterlockedIncrement(&a);
0040115E  push        offset a (4096C8h) 
00401163  call        dword ptr [__imp__InterlockedIncrement@4 (407004h)] 
    Sleep(200);
00401169  push        0C8h 
0040116E  call        esi  
    ++b;
00401170  add         dword ptr [b (4096C0h)],1 
    Sleep(200);
00401177  push        0C8h 
0040117C  call        esi  
    ++c;
0040117E  mov         eax,dword ptr [c (4096C4h)] 
00401183  add         eax,1 
00401186  mov         dword ptr [c (4096C4h)],eax 
    return 0;
0040118B  xor         eax,eax 
0040118D  pop         esi  
}
0040118E  ret         4


/c  /Ox /Og /Ob2 /Oi /Ot /Oy /G7 /GF /FD /ML /Zi /nologo /W4 /Wp64 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FAs /Fa"Release/" /Fo"Release/" /Fd"Release/vc70.pdb" /Gd /TP
/LTCG /OUT:"Release/volatile.exe" /INCREMENTAL:NO /DEBUG /PDB:"Release/volatile.pdb" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /OPT:NOWIN98 /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 

--- d:\src\volatile\main.cpp ---------------------------------------------------

int main()
{
004010C0  push        esi  
    foo();
004010C1  call        foo (4011A0h) 

    f();
004010C6  cmp         dword ptr [a (4096C8h)],0 
004010CD  mov         esi,dword ptr [__imp__Sleep@4 (407000h)] 
004010D3  jne         main+22h (4010E2h) 
004010D5  push        14h  
004010D7  call        esi  
004010D9  cmp         dword ptr [a (4096C8h)],0 
004010E0  je          main+15h (4010D5h) 
004010E2  push        offset string "f" (407108h) 
004010E7  push        offset string "%s is done\n" (4070FCh) 
004010EC  call        printf (4011B6h) 
004010F1  add         esp,8 
    g();
004010F4  cmp         dword ptr [b (4096C0h)],0 
004010FB  jne         main+4Dh (40110Dh) 
004010FD  lea         ecx,[ecx] 
00401100  push        14h  
00401102  call        esi  
00401104  cmp         dword ptr [b (4096C0h)],0 
0040110B  je          main+40h (401100h) 
0040110D  push        offset string "g" (40710Ch) 
00401112  push        offset string "%s is done\n" (4070FCh) 
00401117  call        printf (4011B6h) 
0040111C  add         esp,8 
    h();
0040111F  cmp         dword ptr [c (4096C4h)],0 
00401126  jne         main+75h (401135h) 
00401128  push        14h  
0040112A  call        esi  
0040112C  cmp         dword ptr [c (4096C4h)],0 
00401133  je          main+68h (401128h) 
00401135  push        offset string "h" (407110h) 
0040113A  push        offset string "%s is done\n" (4070FCh) 
0040113F  call        printf (4011B6h) 
00401144  add         esp,8 
}
00401147  xor         eax,eax 
00401149  pop         esi  
0040114A  ret              
--- No source file -------------------------------------------------------------
0040114B  int         3    
0040114C  int         3    
0040114D  int         3    
0040114E  int         3    
0040114F  int         3    
--- d:\src\volatile\other.cpp --------------------------------------------------
#include "windows.h"

extern long a;
extern long b;
extern long volatile c;


DWORD WINAPI thread(void*)
{
00401150  push        esi  
    Sleep(200);
00401151  mov         esi,dword ptr [__imp__Sleep@4 (407000h)] 
00401157  push        0C8h 
0040115C  call        esi  
    InterlockedIncrement(&a);
0040115E  push        offset a (4096C8h) 
00401163  call        dword ptr [__imp__InterlockedIncrement@4 (407004h)] 
    Sleep(200);
00401169  push        0C8h 
0040116E  call        esi  
    ++b;
00401170  add         dword ptr [b (4096C0h)],1 
    Sleep(200);
00401177  push        0C8h 
0040117C  call        esi  
    ++c;
0040117E  mov         eax,dword ptr [c (4096C4h)] 
00401183  add         eax,1 
00401186  mov         dword ptr [c (4096C4h)],eax 
    return 0;
0040118B  xor         eax,eax 
0040118D  pop         esi  
}
0040118E  ret         4


/c /Qvc7.1 /Qlocation,link,"C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7/bin"  /Ox /Og /Ob2 /Oi /Ot /Oy /G7 /GF /FD /ML /Zi /nologo /W4 /Wp64 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FAs /Fa"Release/" /Fo"Release/" /Fd"Release/vc70.pdb" /Gd /TP
/OUT:"Release/volatile.exe" /INCREMENTAL:NO /DEBUG /PDB:"Release/volatile.pdb" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /OPT:NOWIN98 /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 

--- .\main.cpp -----------------------------------------------------------------

int main()
{
00401000  push        ebx  
00401001  mov         ebx,esp 
00401003  and         esp,0FFFFFFF0h 
00401006  call        ___intel_proc_init (4060A4h) 
    foo();
0040100B  call        foo (401164h) 

    f();
00401010  mov         eax,dword ptr [a (4096C8h)] 
00401015  test        eax,eax 
00401017  jne         main+2Ah (40102Ah) 
00401019  push        14h  
0040101B  call        dword ptr [__imp__Sleep@4 (407000h)] 
00401021  mov         eax,dword ptr [a (4096C8h)] 
00401026  test        eax,eax 
00401028  je          main+19h (401019h) 
0040102A  push        offset KERNEL32_NULL_THUNK_DATA+28h (407104h) 
0040102F  push        offset string "%s is done\n" (407114h) 
00401034  call        printf (401178h) 
00401039  add         esp,8 
    g();
0040103C  mov         eax,dword ptr [b (4096C4h)] 
00401041  test        eax,eax 
00401043  jne         main+56h (401056h) 
00401045  push        14h  
00401047  call        dword ptr [__imp__Sleep@4 (407000h)] 
0040104D  mov         eax,dword ptr [b (4096C4h)] 
00401052  test        eax,eax 
00401054  je          main+45h (401045h) 
00401056  push        offset KERNEL32_NULL_THUNK_DATA+24h (407100h) 
0040105B  push        offset string "%s is done\n" (407114h) 
00401060  call        printf (401178h) 
00401065  add         esp,8 
    h();
00401068  mov         eax,dword ptr [c (4096C0h)] 
0040106D  test        eax,eax 
0040106F  jne         main+82h (401082h) 
00401071  push        14h  
00401073  call        dword ptr [__imp__Sleep@4 (407000h)] 
00401079  mov         eax,dword ptr [c (4096C0h)] 
0040107E  test        eax,eax 
00401080  je          main+71h (401071h) 
00401082  push        offset KERNEL32_NULL_THUNK_DATA+20h (4070FCh) 
00401087  push        offset string "%s is done\n" (407114h) 
0040108C  call        printf (401178h) 
00401091  add         esp,8 
}
00401094  xor         eax,eax 
00401096  mov         esp,ebx 
00401098  pop         ebx  
00401099  ret              
#include <stdio.h>
#include "windows.h"

extern void foo();

long a;
long b;
long volatile c;

void f()
{
    while(!a)
0040109A  mov         eax,dword ptr [a (4096C8h)] 
0040109F  test        eax,eax 
004010A1  jne         f+1Ah (4010B4h) 
        Sleep(20);
004010A3  push        14h  
004010A5  call        dword ptr [__imp__Sleep@4 (407000h)] 
#include <stdio.h>
#include "windows.h"

extern void foo();

long a;
long b;
long volatile c;

void f()
{
    while(!a)
004010AB  mov         eax,dword ptr [a (4096C8h)] 
004010B0  test        eax,eax 
004010B2  je          f+9 (4010A3h) 
    printf("%s is done\n", __FUNCTION__);
004010B4  push        offset KERNEL32_NULL_THUNK_DATA+2Ch (407108h) 
004010B9  push        offset string "%s is done\n" (407114h) 
004010BE  call        printf (401178h) 
}
004010C3  add         esp,8 
004010C6  ret              
004010C7  nop              

void g()
{
    while(!b)
004010C8  mov         eax,dword ptr [b (4096C4h)] 
004010CD  test        eax,eax 
004010CF  jne         g+1Ah (4010E2h) 
        Sleep(20);
004010D1  push        14h  
004010D3  call        dword ptr [__imp__Sleep@4 (407000h)] 

void g()
{
    while(!b)
004010D9  mov         eax,dword ptr [b (4096C4h)] 
004010DE  test        eax,eax 
004010E0  je          g+9 (4010D1h) 
    printf("%s is done\n", __FUNCTION__);
004010E2  push        offset KERNEL32_NULL_THUNK_DATA+30h (40710Ch) 
004010E7  push        offset string "%s is done\n" (407114h) 
004010EC  call        printf (401178h) 
}
004010F1  add         esp,8 
004010F4  ret              
004010F5  nop              

void h()
{
    while(!c)
004010F6  mov         eax,dword ptr [c (4096C0h)] 
004010FB  test        eax,eax 
004010FD  jne         h+1Ah (401110h) 
        Sleep(20);
004010FF  push        14h  
00401101  call        dword ptr [__imp__Sleep@4 (407000h)] 

void h()
{
    while(!c)
00401107  mov         eax,dword ptr [c (4096C0h)] 
0040110C  test        eax,eax 
0040110E  je          h+9 (4010FFh) 
    printf("%s is done\n", __FUNCTION__);
00401110  push        offset KERNEL32_NULL_THUNK_DATA+34h (407110h) 
00401115  push        offset string "%s is done\n" (407114h) 
0040111A  call        printf (401178h) 
}
0040111F  add         esp,8 
00401122  ret              
00401123  nop              
--- .\other.cpp ----------------------------------------------------------------
#include "windows.h"

extern long a;
extern long b;
extern long volatile c;


DWORD WINAPI thread(void*)
{
    Sleep(200);
00401124  push        0C8h 
00401129  call        dword ptr [__imp__Sleep@4 (407000h)] 
    InterlockedIncrement(&a);
0040112F  push        offset a (4096C8h) 
00401134  call        dword ptr [__imp__InterlockedIncrement@4 (407004h)] 
    Sleep(200);
0040113A  push        0C8h 
0040113F  call        dword ptr [__imp__Sleep@4 (407000h)] 
    ++b;
00401145  add         dword ptr [b (4096C4h)],1 
    Sleep(200);
0040114C  push        0C8h 
00401151  call        dword ptr [__imp__Sleep@4 (407000h)] 
    ++c;
00401157  add         dword ptr [c (4096C0h)],1 
    return 0;
0040115E  xor         eax,eax 
00401160  ret         4    
00401163  nop              
}

void foo()
{
    CreateThread(0, 0, thread, 0, 0, 0);
00401164  xor         eax,eax 
00401166  push        eax  
00401167  push        eax  
00401168  push        eax  
00401169  push        offset thread (401124h) 
0040116E  push        eax  
0040116F  push        eax  
00401170  call        dword ptr [__imp__CreateThread@24 (407008h)] 
}
00401176  ret              
00401177  nop


/c /Qvc7.1 /Qlocation,link,"C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7/bin"  /GL /Ox /Og /Ob2 /Oi /Ot /Oy /G7 /GF /FD /ML /Zi /nologo /W4 /Wp64 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FAs /Fa"Release/" /Fo"Release/" /Fd"Release/vc70.pdb" /Gd /TP
/LTCG /OUT:"Release/volatile.exe" /INCREMENTAL:NO /DEBUG /PDB:"Release/volatile.pdb" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /OPT:NOWIN98 /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 

--- .\main.cpp -----------------------------------------------------------------
#include <stdio.h>
#include "windows.h"

extern void foo();

long a;
long b;
long volatile c;

void f()
{
    while(!a)
        Sleep(20);
    printf("%s is done\n", __FUNCTION__);
}

void g()
{
    while(!b)
        Sleep(20);
    printf("%s is done\n", __FUNCTION__);
}

void h()
{
    while(!c)
        Sleep(20);
    printf("%s is done\n", __FUNCTION__);
}

int main()
{
00401000  push        ebx  
00401001  mov         ebx,esp 
00401003  and         esp,0FFFFFFF0h 
00401006  call        ___intel_proc_init (406014h) 
    foo();
0040100B  call        foo (40109Ah) 

    f();
00401010  mov         eax,dword ptr [___decimal_point_length+174h (4096C0h)] 
00401015  test        eax,eax 
00401017  jne         main+2Ah (40102Ah) 
00401019  push        14h  
0040101B  call        dword ptr [__imp__Sleep@4 (407000h)] 
00401021  mov         eax,dword ptr [___decimal_point_length+174h (4096C0h)] 
00401026  test        eax,eax 
00401028  je          main+19h (401019h) 
0040102A  push        offset KERNEL32_NULL_THUNK_DATA+28h (407104h) 
0040102F  push        offset KERNEL32_NULL_THUNK_DATA+2Ch (407108h) 
00401034  call        printf (4010F4h) 
00401039  add         esp,8 
    g();
0040103C  mov         eax,dword ptr [___decimal_point_length+178h (4096C4h)] 
00401041  test        eax,eax 
00401043  jne         main+56h (401056h) 
00401045  push        14h  
00401047  call        dword ptr [__imp__Sleep@4 (407000h)] 
0040104D  mov         eax,dword ptr [___decimal_point_length+178h (4096C4h)] 
00401052  test        eax,eax 
00401054  je          main+45h (401045h) 
00401056  push        offset KERNEL32_NULL_THUNK_DATA+24h (407100h) 
0040105B  push        offset KERNEL32_NULL_THUNK_DATA+2Ch (407108h) 
00401060  call        printf (4010F4h) 
00401065  add         esp,8 
    h();
00401068  mov         eax,dword ptr [___decimal_point_length+17Ch (4096C8h)] 
0040106D  test        eax,eax 
0040106F  jne         main+82h (401082h) 
00401071  push        14h  
00401073  call        dword ptr [__imp__Sleep@4 (407000h)] 
00401079  mov         eax,dword ptr [___decimal_point_length+17Ch (4096C8h)] 
0040107E  test        eax,eax 
00401080  je          main+71h (401071h) 
00401082  push        offset KERNEL32_NULL_THUNK_DATA+20h (4070FCh) 
00401087  push        offset KERNEL32_NULL_THUNK_DATA+2Ch (407108h) 
0040108C  call        printf (4010F4h) 
00401091  add         esp,8 
}
00401094  xor         eax,eax 
00401096  mov         esp,ebx 
00401098  pop         ebx  
00401099  ret              
--- .\other.cpp ----------------------------------------------------------------
}

void foo()
{
0040109A  sub         esp,0Ch 
    CreateThread(0, 0, thread, 0, 0, 0);
0040109D  xor         eax,eax 
0040109F  push        eax  
004010A0  push        eax  
004010A1  push        eax  
004010A2  push        offset thread (4010B4h) 
004010A7  push        eax  
004010A8  push        eax  
004010A9  call        dword ptr [__imp__CreateThread@24 (407004h)] 
}
004010AF  add         esp,0Ch 
004010B2  ret              
004010B3  nop              
#include "windows.h"

extern long a;
extern long b;
extern long volatile c;


DWORD WINAPI thread(void*)
{
    Sleep(200);
004010B4  push        0C8h 
004010B9  call        dword ptr [__imp__Sleep@4 (407000h)] 
    InterlockedIncrement(&a);
004010BF  push        offset ___decimal_point_length+174h (4096C0h) 
004010C4  call        dword ptr [__imp__InterlockedIncrement@4 (407008h)] 
    Sleep(200);
004010CA  push        0C8h 
004010CF  call        dword ptr [__imp__Sleep@4 (407000h)] 
    ++b;
004010D5  add         dword ptr [___decimal_point_length+178h (4096C4h)],1 
    Sleep(200);
004010DC  push        0C8h 
004010E1  call        dword ptr [__imp__Sleep@4 (407000h)] 
    ++c;
004010E7  add         dword ptr [___decimal_point_length+17Ch (4096C8h)],1 
    return 0;
004010EE  xor         eax,eax 
004010F0  ret         4    
004010F3  nop
Re[11]: volatile у переменной класса
От: achp  
Дата: 14.01.05 19:30
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Я скомпилировал следующий код на VC71 и Intel 8 с максимальными настройками оптимизации, с LTCG/IPO и без. Во всех четырех случаях при чтении переменная не кэшировалась, неважно, volatile ли была переменная или нет.


Возможно, дело в вызовах Sleep; компилятор не имеет возможности проверить, не изменяет ли Sleep переменные, поэтому генерирует код из предположения, что переменная там может измениться.
Я кончил, джентльмены, мне остается только поблагодарить вас за внимание.
Re[11]: volatile у переменной класса
От: Шахтер Интернет  
Дата: 15.01.05 03:18
Оценка: 18 (1)
Здравствуйте, MaximE, Вы писали:

ME>Здравствуйте, emusic, Вы писали:


ME>[]


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


ME>Я скомпилировал следующий код на VC71 и Intel 8 с максимальными настройками оптимизации, с LTCG/IPO и без. Во всех четырех случаях при чтении переменная не кэшировалась, неважно, volatile ли была переменная или нет.


Ты не совсем прав в данном случае. Нет никаких гарантий, что подобное поведение сохраниться в будующем. В этом примере компилятор не смог оценить побочные эффекты от вызова Sleep, поэтому и перечитал переменную из памяти. А вот теперь уберем Sleep. И получим звездец. И в будующем ситуация может стать аналогичной и с вызовом Sleep. Дело в том, что существует предложение о введении спецификатора для функций без побочных эффектов (pure). Если это будет реализовано и функция Sleep будет помечена как pure, то и её вызов не будет рассматриваться компилятором как могущий изменить переменную a, что приведёт к нежелательной оптимизации.

volatile int sleep_var;

void sleep(int n) // Побочные эффекты от этой функции компилятор может оценить. См. ниже, к чему это приводит.
 {
  sleep_var=n;
 
  for(; sleep_var ;sleep_var--);
 }

long a;
volatile long b;

void f()
{
    while(!a)
        sleep(20);
}

void g()
{
    while(!b)
        sleep(20);
}


_TEXT    SEGMENT
?f@@YAXXZ PROC NEAR                    ; f, COMDAT

; 21   :     while(!a)

    mov    eax, DWORD PTR ?a@@3JA            ; a
    test    eax, eax
    jne    SHORT $L285
    mov    eax, 20                    ; 00000014H
    npad    2
$L284:

; 22   :         sleep(20);

    mov    DWORD PTR ?sleep_var@@3HC, eax        ; sleep_var
    mov    ecx, DWORD PTR ?sleep_var@@3HC        ; sleep_var
    test    ecx, ecx
    je    SHORT $L284
    npad    1
$L312:
    mov    ecx, DWORD PTR ?sleep_var@@3HC        ; sleep_var
    dec    ecx
    mov    DWORD PTR ?sleep_var@@3HC, ecx        ; sleep_var
    jne    SHORT $L312

; 21   :     while(!a)

    jmp    SHORT $L284
$L285:

; 23   : }

    ret    0
?f@@YAXXZ ENDP                        ; f
_TEXT    ENDS
PUBLIC    ?g@@YAXXZ                    ; g
; Function compile flags: /Ogtpy
;    COMDAT ?g@@YAXXZ
_TEXT    SEGMENT
?g@@YAXXZ PROC NEAR                    ; g, COMDAT

; 27   :     while(!b)

    mov    eax, DWORD PTR ?b@@3JC            ; b
    test    eax, eax
    jne    SHORT $L290
    mov    eax, 20                    ; 00000014H
    npad    2
$L289:

; 28   :         sleep(20);

    mov    DWORD PTR ?sleep_var@@3HC, eax        ; sleep_var
    mov    ecx, DWORD PTR ?sleep_var@@3HC        ; sleep_var
    test    ecx, ecx
    je    SHORT $L326
    npad    1
$L324:
    mov    ecx, DWORD PTR ?sleep_var@@3HC        ; sleep_var
    dec    ecx
    mov    DWORD PTR ?sleep_var@@3HC, ecx        ; sleep_var
    jne    SHORT $L324
$L326:
    mov    ecx, DWORD PTR ?b@@3JC            ; b
    test    ecx, ecx
    je    SHORT $L289
$L290:

; 29   : }

    ret    0
?g@@YAXXZ ENDP                        ; g
_TEXT    ENDS
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[12]: volatile у переменной класса
От: MaximE Великобритания  
Дата: 15.01.05 12:27
Оценка: -1
Шахтер wrote:

> ME>Я скомпилировал следующий код на VC71 и Intel 8 с максимальными настройками оптимизации, с LTCG/IPO и без. Во всех четырех случаях при чтении переменная не кэшировалась, неважно, volatile ли была переменная или нет.

>
> Ты не совсем прав в данном случае. Нет никаких гарантий, что подобное поведение сохраниться в будующем. В этом примере компилятор не смог оценить побочные эффекты от вызова Sleep, поэтому и перечитал переменную из памяти. А вот теперь уберем Sleep. И получим звездец.

Ты хочешь сказать, что добавим volatile — и все ok? Действительно, это может заставить компилятор прочитать значение из памяти, а может и нет, так как семантика volatile — implementation defined.

Чтение из памяти на текущих процессорах Intel гарантировано прочитает актуальное значение, даже если память была изменена в кэше другого процессора, т.к. Intel для текущих процессоров применяет cache snooping.

When operating in an MP system, IA-32 processors (beginning with the Intel486 processor) have the ability to snoop other processor’s accesses to system memory and to their internal caches. They use this snooping ability to keep their internal caches consistent both with system memory and with the caches in other processors on the bus. For example, in the Pentium and P6 family processors, if through snooping one processor detects that another processor intends to write to a memory location that it currently has cached in shared state, the snooping processor will invalidate its cache line forcing it to perform a cache line fill the next time it accesses the same memory location.


> И в будующем ситуация может стать аналогичной и с вызовом Sleep. Дело в том, что существует предложение о введении спецификатора для функций без побочных эффектов (pure). Если это будет реализовано и функция Sleep будет помечена как pure, то и её вызов не будет рассматриваться компилятором как могущий изменить переменную a, что приведёт к нежелательной оптимизации.


В будущем для Intel и в настоящем для многих других процессоров, volatile точно не поможет, так как на Intel он опирается на две processor dependent фичи: на memory ordering и когерентность кешей (cache snooping выше).

7.2.1. Memory Ordering in the Pentium® and Intel486™ Processors

The Pentium and Intel486 processors follow the processor-ordered memory model; however, they operate as strongly-ordered processors under most circumstances. Reads and writes always appear in programmed order at the system bus—except for the following situation where processor ordering is exhibited. Read misses are permitted to go ahead of buffered writes on the system bus when all the buffered writes are cache hits and, therefore, are not directed to the same address being accessed by the read miss.

In the case of I/O operations, both reads and writes always appear in programmed order.

Software intended to operate correctly in processor-ordered processors (such as the Pentium 4, Intel Xeon, and P6 family processors) should not depend on the relatively strong ordering of the Pentium or Intel486 processors. Instead, it should insure that accesses to shared variables that are intended to control concurrent execution among processors are explicitly required to obey program ordering through the use of appropriate locking or serializing operations (see Section 7.2.4., “Strengthening or Weakening the Memory Ordering Model”).

...

7.2.4. Strengthening or Weakening the Memory Ordering Model
...
It is recommended that software written to run on Pentium 4, Intel Xeon, and P6 family processors assume the processor-ordering model or a weaker memory-ordering model. The Pentium 4,
Intel Xeon, and P6 family processors do not implement a strong memory-ordering model, except when using the UC memory type. Despite the fact that Pentium 4, Intel Xeon, and P6 family processors support processor ordering, Intel does not guarantee that future processors will support this model. To make software portable to future processors, it is recommended that operating systems provide critical region and resource control constructs and API’s (application
program interfaces) based on I/O, locking, and/or serializing instructions be used to synchronize access to shared areas of memory in multiple-processor systems. Also, software should not depend on processor ordering in situations where the system hardware does not support this memory-ordering model.




Так что volatile не является ни необходимым, ни достаточным для multithreading. Если малтитредовой проге необходим volatile чтобы корректно работать, такая программа непортабельна.

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9
Re[13]: volatile у переменной класса
От: Шахтер Интернет  
Дата: 15.01.05 13:16
Оценка: 3 (1) +2 :)
Здравствуйте, MaximE, Вы писали:

ME>Шахтер wrote:


>> ME>Я скомпилировал следующий код на VC71 и Intel 8 с максимальными настройками оптимизации, с LTCG/IPO и без. Во всех четырех случаях при чтении переменная не кэшировалась, неважно, volatile ли была переменная или нет.

>>
>> Ты не совсем прав в данном случае. Нет никаких гарантий, что подобное поведение сохраниться в будующем. В этом примере компилятор не смог оценить побочные эффекты от вызова Sleep, поэтому и перечитал переменную из памяти. А вот теперь уберем Sleep. И получим звездец.

ME>Ты хочешь сказать, что добавим volatile — и все ok?


Да. Именно для этого это ключевое слово и предназнчается.

ME>Чтение из памяти на текущих процессорах Intel гарантировано прочитает актуальное значение, даже если память была изменена в кэше другого процессора, т.к. Intel для текущих процессоров применяет cache snooping. ...


А какое это отношение имеет к обсуждаемому вопросу? volatile -- это средство ограничения оптимизации кода. Пример того, как оно работает, был приведён выше. К тому, что происходит в процессоре, это не имеет никакого отношения.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[11]: volatile у переменной класса
От: emusic Франция https://software.muzychenko.net/ru
Дата: 15.01.05 13:28
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Я скомпилировал следующий код на VC71 и Intel 8 с максимальными настройками оптимизации, с LTCG/IPO и без.


В этих примерах используются глобальные переменные, и глобальные же функции. Откуда оптимизатору знать, что функция Sleep не изменяет этих переменных?

Если переделать все переменные и функции на статические, и добавить ключ запрета aliasing'а — оптимизатор вполне может начать кэшировать значения. Хотя в VC++ 7.0 оптимизатор это делать ленится.

ME>Хотелось бы увидеть multithreaded код, где volatile реально чем-то помогает.


Я уже приводил такой код. Приведу в более убедительном виде:

#include <windows.h>

CRITICAL_SECTION CS;

int f (int *a, int *b) {
  EnterCriticalSection (&CS);
  if (!*a) {
    LeaveCriticalSection (&CS);
    Sleep (100);
    EnterCriticalSection (&CS);
    *b = *a;
  }
  LeaveCriticalSection (&CS);
  return *b;
}


Если скомпилить его VC++ 6/7 с ключом /Oa — в присваивании внутри условия используется константа 0. Разумеется, код притянут за уши, но он наглядно показывает, что оптимизатор может кэшировать значение объекта, если у того нет volatile. aliasing'а в данном примере нет — нигде в программе другого доступа к объектам *a и *b быть не обязано. Без параллелизма данный код будет работать всегда. С параллелизмом — сам догадайся

Большинство примеров кода без volatile работает в многопоточной среде только за счет того, что компилятор до последнего подозревает о наличии этого самого aliasing'а — что любая из вызываемых функций, код которой нельзя проанализировать сейчас же, потенциально может изменить переменную через указатель на нее. Именно поэтому после вызова функций переменные перечитываются, а отнюдь не из-за sequience points.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Re[12]: volatile у переменной класса
От: Шахтер Интернет  
Дата: 15.01.05 13:39
Оценка:
Здравствуйте, emusic, Вы писали:

E>Здравствуйте, MaximE, Вы писали:


ME>>Я скомпилировал следующий код на VC71 и Intel 8 с максимальными настройками оптимизации, с LTCG/IPO и без.


E>В этих примерах используются глобальные переменные, и глобальные же функции. Откуда оптимизатору знать, что функция Sleep не изменяет этих переменных?


E>Если переделать все переменные и функции на статические, и добавить ключ запрета aliasing'а — оптимизатор вполне может начать кэшировать значения. Хотя в VC++ 7.0 оптимизатор это делать ленится.


ME>>Хотелось бы увидеть multithreaded код, где volatile реально чем-то помогает.


E>Я уже приводил такой код. Приведу в более убедительном виде:


E>
E>#include <windows.h>

E>CRITICAL_SECTION CS;

E>int f (int *a, int *b) {
E>  EnterCriticalSection (&CS);
E>  if (!*a) {
E>    LeaveCriticalSection (&CS);
E>    Sleep (100);
E>    EnterCriticalSection (&CS);
E>    *b = *a;
E>  }
E>  LeaveCriticalSection (&CS);
E>  return *b;
E>}
E>


E>Если скомпилить его VC++ 6/7 с ключом /Oa — в присваивании внутри условия используется константа 0. Разумеется, код притянут за уши, но он наглядно показывает, что оптимизатор может кэшировать значение объекта, если у того нет volatile. aliasing'а в данном примере нет — нигде в программе другого доступа к объектам *a и *b быть не обязано. Без параллелизма данный код будет работать всегда. С параллелизмом — сам догадайся


E>Большинство примеров кода без volatile работает в многопоточной среде только за счет того, что компилятор до последнего подозревает о наличии этого самого aliasing'а — что любая из вызываемых функций, код которой нельзя проанализировать сейчас же, потенциально может изменить переменную через указатель на нее. Именно поэтому после вызова функций переменные перечитываются, а отнюдь не из-за sequience points.


Погоди. Тут что-то не так. a или b вполне могут указывать внутрь критической секции. В этом случае оптимизация незаконна.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[14]: volatile у переменной класса
От: MaximE Великобритания  
Дата: 15.01.05 14:24
Оценка:
Шахтер wrote:

> ME>Чтение из памяти на текущих процессорах Intel гарантировано прочитает актуальное значение, даже если память была изменена в кэше другого процессора, т.к. Intel для текущих процессоров применяет cache snooping. ...

>
> А какое это отношение имеет к обсуждаемому вопросу? volatile -- это средство ограничения оптимизации кода. Пример того, как оно работает, был приведён выше. К тому, что происходит в
процессоре, это не имеет никакого отношения.

Тема была о volatile и multithreading.

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9
Re[12]: volatile у переменной класса
От: MaximE Великобритания  
Дата: 15.01.05 14:30
Оценка:
emusic wrote:

[]

> Если скомпилить его VC++ 6/7 с ключом /Oa — в присваивании внутри условия используется константа 0. Разумеется, код притянут за уши, но он наглядно показывает, что оптимизатор может кэшировать значение объекта, если у того нет volatile. aliasing'а в данном примере нет — нигде в программе другого доступа к объектам *a и *b быть не обязано. Без параллелизма данный код будет работать всегда. С параллелизмом — сам догадайся


Я про это и пишу:

Так что volatile не является ни необходимым, ни достаточным для multithreading. Если малтитредовой проге необходим volatile чтобы корректно работать, такая программа непортабельна.


> Большинство примеров кода без volatile работает в многопоточной среде только за счет того, что компилятор до последнего подозревает о наличии этого самого aliasing'а — что любая из вызываемых функций, код которой нельзя проанализировать сейчас же, потенциально может изменить переменную через указатель на нее. Именно поэтому после вызова функций переменные перечитываются, а отнюдь не из-за sequience points.


Да, с этим я согласен, про sequience points, похоже, я херню написал.

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9
Re[2]: volatile у переменной класса
От: Tom Россия http://www.RSDN.ru
Дата: 15.01.05 16:04
Оценка: +1 -1
Здравствуйте, MaximE, Вы писали:

ME>Vet wrote:


>> В объекте класса есть переменная, которая используется двумя потоками.

>> Один из потоков иногда меняет ее значение.
>> Есть ли необходимость в этом случае делать переменную как volatile.
>> И зависит ли ответ от того, сздан ли объект класса на стеке или в динамической памяти.
>>
>> Вопрос вызван тем, что у Рихтера прочитал, что если глобальная переменная используется
>> разными потоками, то она обязана быть volatile. Вот меня и рабирают сомнения нужно ли
>> тоже самое делать для переменных(членов) класса.

ME>Рихтер ошибался.


ME>volatile не имеет никакого отношения к multithreading, поэтому его применение в этой ситуации не только бесполезно, но может быть и вредно, так как с volatile компилятор не сможет соптимизировтать доступ к этой переменной. Но если один из потоков изменяет переменную, синхронизация при помощи мютексов обязательна.


ME>--

ME>Maxim Yegorushkin

Абсолютно неверный и опасный совет. Как раз volatile тут необходим, как в прочем и желательна защита обьекта при помощи примитивов синхронизации. volatile нужен дабы избежать лишней оптимизации (компилятор может моложить переменную в регистр, и защищая её или нет, но другой поток об том, что переменная изменилась — не узнает никогда, или несвоевременно)
Народная мудрось
всем все никому ничего(с).
Re[13]: volatile у переменной класса
От: emusic Франция https://software.muzychenko.net/ru
Дата: 15.01.05 16:20
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Погоди. Тут что-то не так. a или b вполне могут указывать внутрь критической секции. В этом случае оптимизация незаконна.


Мне сколько раз нужно повторить слово aliasing, чтобы оно было, наконец, воспринято? Указание ключа /Oa сообщает компилятору, что в программе подобные совмещения исключены.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Re[13]: volatile у переменной класса
От: emusic Франция https://software.muzychenko.net/ru
Дата: 15.01.05 16:20
Оценка: +1
Здравствуйте, MaximE, Вы писали:

ME>Я про это и пишу:

ME>

ME>Так что volatile не является ни необходимым, ни достаточным для multithreading. Если малтитредовой проге необходим volatile чтобы корректно работать, такая программа непортабельна.


Обосновать это свое утверждение чем-либо, кроме собственной смутной интуиции, можешь? Я тебе привел пример программы, которая без volatile корректно работать не будет. Найди в этом примере ошибку, либо приведи доказательство того, что любая многопоточная программа обязана корректно работать без volatile. Пока бОльшую часть приведенных тебе аргументов ты тихо опускаешь, повторяя в ответ одни и те же измышления.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Re[3]: volatile у переменной класса
От: MaximE Великобритания  
Дата: 15.01.05 23:57
Оценка:
Tom wrote:

[]

> Абсолютно неверный и опасный совет. Как раз volatile тут необходим, как в прочем и желательна защита обьекта при помощи примитивов синхронизации. volatile нужен дабы избежать лишней оптимизации (компилятор может моложить переменную в регистр, и защищая её или нет, но другой поток об том, что переменная изменилась — не узнает никогда, или несвоевременно)


Почитай топик от начала до конца.

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9
Re[14]: volatile у переменной класса
От: MaximE Великобритания  
Дата: 16.01.05 00:01
Оценка: 1 (1)
emusic wrote:

[]

> Обосновать это свое утверждение чем-либо, кроме собственной смутной интуиции, можешь? Я тебе привел пример программы, которая без volatile корректно работать не будет. Найди в этом примере ошибку, либо приведи доказательство того, что любая многопоточная программа обязана корректно работать без volatile. Пока бОльшую часть приведенных тебе аргументов ты тихо опускаешь, повторяя в ответ одни и те же измышления.


Весь мир давно заездил эту тему до дыр, только до rsdn прогрессивная мысль никак не доберется (.

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9
Re[14]: volatile у переменной класса
От: MaximE Великобритания  
Дата: 16.01.05 00:08
Оценка:
emusic wrote:

> Ш>Погоди. Тут что-то не так. a или b вполне могут указывать внутрь критической секции. В этом случае оптимизация незаконна.

>
> Мне сколько раз нужно повторить слово aliasing, чтобы оно было, наконец, воспринято? Указание ключа /Oa сообщает компилятору, что в программе подобные совмещения исключены.

... но на самом деле они у тебя в проге есть и ты полагаешься на результат этого альясинга. Ты сообщил компилятору неверную информацию — all bets are off.

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9
Re[15]: volatile у переменной класса
От: c-smile Канада http://terrainformatica.com
Дата: 16.01.05 00:37
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Весь мир давно заездил эту тему до дыр, только до rsdn прогрессивная мысль никак не доберется (.


Макс, это уже не смешно.
Или давай технические спеки или ссылки на докуметнацию компиляторов.
Короче что-нибудь, но не ссылки на threads в дискуссиях ведущихся анонимными авторами.

"Весь мир давно заездил эту тему" ты имеешь ввиду себя и пару тройку "просвященных"?
Примерно 25% этого мира посещяет RSDN на котором мы видим "луч света в темном царстве" в твоем лице.

Чего ты уперся? volatile is a C++ keyword. Period.

http://www.embedded.com/story/OEG20010615S0107
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.