abclinuxu.cz AbcLinuxu.cz itbiz.cz ITBiz.cz HDmag.cz HDmag.cz abcprace.cz AbcPráce.cz
Inzerujte na AbcPráce.cz od 950 Kč
Rozšířené hledání
×
    dnes 02:11 | Nová verze

    Linuxová distribuce Endless OS (Wikipedie) byla vydána ve verzi 6.0.0. Přehled novinek i s náhledy v příspěvku na blogu, poznámkách k vydání a také na YouTube.

    Ladislav Hagara | Komentářů: 0
    včera 15:44 | Nová verze

    Byl vydán Mozilla Firefox 126.0. Přehled novinek v poznámkách k vydání, poznámkách k vydání pro firmy a na stránce věnované vývojářům. Vylepšena byla funkce "Zkopírovat odkaz bez sledovacích prvků". Přidána byla podpora zstd (Zstandard). Řešeny jsou rovněž bezpečnostní chyby. Nový Firefox 126 je již k dispozici také na Flathubu a Snapcraftu.

    Ladislav Hagara | Komentářů: 0
    včera 15:22 | Nová verze

    Grafana (Wikipedie), tj. open source nástroj pro vizualizaci různých metrik a s ní související dotazování, upozorňování a lepší porozumění, byla vydána ve verzi 11.0. Přehled novinek v aktualizované dokumentaci.

    Ladislav Hagara | Komentářů: 0
    včera 14:55 | Nová verze

    Byla vydána nová verze 24.0 linuxové distribuce Manjaro (Wikipedie). Její kódové jméno je Wynsdey. Ke stažení je v edicích GNOME, KDE PLASMA a XFCE.

    Ladislav Hagara | Komentářů: 2
    včera 13:00 | Nová verze

    Byla představena oficiální rozšiřující deska Raspberry Pi M.2 HAT+ pro připojování M.2 periferii jako jsou NVMe disky a AI akcelerátory k Raspberry Pi 5. Cena je 12 dolarů.

    Ladislav Hagara | Komentářů: 2
    včera 12:44 | Pozvánky

    V Praze o víkendu proběhla bastlířská událost roku - výstava Maker Fair v Praze. I strahovští bastlíři nelenili a bastly ostatních prozkoumali. Přijďte si proto i vy na Virtuální Bastlírnu popovídat, co Vás nejvíce zaujalo a jaké projekty jste si přinesli! Samozřejmě, nejen českou bastlířskou scénou je člověk živ - takže co se stalo ve světě a o čem mohou strahováci něco říct? Smutnou zprávou může být to, že provozovatel Sigfoxu jde do

    … více »
    bkralik | Komentářů: 0
    včera 12:33 | Humor

    Kam asi vede IllllIllIIl.llIlI.lI? Zkracovač URL llIlI.lI.

    Ladislav Hagara | Komentářů: 1
    13.5. 22:00 | IT novinky

    Společnost OpenAI představila svůj nejnovější AI model GPT-4o (o jako omni, tj. vše). Nově také "vidí" a "slyší". Videoukázky na 𝕏 nebo YouTube.

    Ladislav Hagara | Komentářů: 0
    13.5. 15:44 | Zajímavý článek

    Ondřej Filip publikoval reportáž z ceremonie podpisu kořenové zóny DNS. Zhlédnout lze také jeho nedávnou přednášku Jak se podepisuje kořenová zóna Internetu v rámci cyklu Fyzikální čtvrtky FEL ČVUT.

    Ladislav Hagara | Komentářů: 0
    13.5. 14:22 | IT novinky

    Společnost BenQ uvádí na trh novou řadu monitorů RD určenou pro programátory. První z nich je RD240Q.

    Ladislav Hagara | Komentářů: 20
    Podle hypotézy Mrtvý Internet mj. tvoří většinu online interakcí boti.
     (73%)
     (5%)
     (10%)
     (11%)
    Celkem 260 hlasů
     Komentářů: 16, poslední včera 11:05
    Rozcestník

    Dotaz: Má být podmínková proměnná volatile?

    18.6.2009 03:31 Andrej | skóre: 51 | blog: Republic of Mordor
    Má být podmínková proměnná volatile?
    Přečteno: 1979×

    Jinými slovy, mám-li nějaké klasické použití podmínkové proměnné,

    pthread_mutex_lock( &queueMutex );
    while ( itemsAvailable == 0 ) {
        pthread_cond_wait( &queueCondVar, &queueMutex );
    }
    pthread_mutex_unlock( &queueMutex );
    

    má být proměnná itemsAvailable deklarovaná jako volatile?

    Argument pro: Přistupuje se k ní z několika vláken. (Sice ji v daný okamžik čte vždy jen jedno vlákno, ale to není podstatné.)

    Argument proti: Nehrozí, že by kompilátor optimalizoval přístup k proměnné, pokud není lokální a zároveň cyklus obsahuje volání nějaké funkce. To je přesně tento případ.

    Zatím jsem pokaždé v těchto situacích volatile použil, ale tuhle jsem se pohádal s kolegou, který tvrdí, že to je naprosto zbytečné. Co si o tom myslíte vy?

    Odpovědi

    18.6.2009 08:08 Milan
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Naprosto zbytecne a jeste bych rekl ze to brani kompilatoru delat optimalizace pristupu k te promenne.

    18.6.2009 08:43 cronin | skóre: 49
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Neprogramujem sice v C, ale v Jave. Myslim ale, ze nizsie uvedene sa vztahuje na obe prostredia rovnako.

    Ak je instancna premenna itemsAvailable modifikovana a citana potencialne z roznych vlakien, musia byt pre spravne fungovanie zabezpecene dve veci: 1) musi byt zabezpecena naslednost akcii a 2) musia byt zmeny sposobene zapisujucim vlaknom viditelne v citajucom vlakne.

    Ak su zmeny atomicke, napr.
    itemsAvailable = 0;
    
    staci na zabezpecenie oboch oznacenie premennje ako volatile. Ak su ale zmeny neatomicke, napr.
    itemsAvailable++;
    
    alebo
    itemsAvailable = !itemsAvailable;
    
    vtedy musi prist k slovu "tazkotonazna" synchronizacia. Vyssie uvedeny kod synchronizaciu obsahuje; teda aspon pri citani premennej itemsAvailable. Dolezite je, aby vsetky zapisy do tejto premennej boli vykonane pod tym istym zamkom. Ak je tomu tak, premenna volatilna byt nemusi, lebo obe vyssie uvedene podmienky zabezpeci synnchronizacia.

    Na zaver by som uz len rad dodal, ze akekolvek argumenty typu "bez volatile to bude rychlejsie" alebo "volatile zabrani optimalizaciam" su nezmyselne. Synchronizacia, zamykanie, volatilnost ci akykolvek iny mechanizmus zabezpecenia spravnosti komunikacie asynchronnych udalosti jednoducho nesmie byt obetovany kvoli vykonnosti. Je k nicomu mat rychly a nespravny program. Ak sa ukaze, ze synchronizacia sposobuje superenie o zamok a brani skalovatelnosti, je potrebne zmenit dizaj na skalovatelnejsi, ale nie jednoducho odstranit synchronizaciu, ktora zabezpecuje spravnost programu.
    18.6.2009 08:58 cronin | skóre: 49
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Este k tomu "beztrestnemu" odstranovaniu synchronizacie. Par mesiacov dozadu sa v jadernych novinach preberalo jedno volanie, tusim to bolo nejake open() a tusim bolo implementovane ovladacmi, pricom drviva vacsina implementacii ho implementovala ako prazdnu funkciu. Preto sa natiskala myslienka proste ho odstranit. V zapati sa zistilo, ze odstranene nemoze byt, lebo je synchronizovane. Takze hoci volanie nic nerobi per-se, sluzi ako synchronizacna bariera: pocka na ziskanie synchronizacneho zamku a v zapati ho uvolni. Ak by sa toto volanie odstranilo, odstranila by sa synchronizacna bariera a kod by mohol pokracovat skor, ako by mal, t.j. mohol by vidiet udajove struktury v nekonzistentnom stave a inicializacia ovladaca by nemusela prebehnut spravne.

    Rovnake je to s volatilnostou premennej: bud tam kvoli spravnosti fungovania programu byt musi, alebo nemusi. Argumenty o optimalizacii su irelevantne.
    18.6.2009 09:38 Milan
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Samozrejme ze musime zachovat synchronizaci. O tom zadna. Jde jenom o to ze jestli je ta promenna intenzivneji pouzivana uvnitr synchronizace tak to ze by byla volatile by mohlo zpusobit horsi optimalizaci (uvnitr synchronizacniho bloku).

    18.6.2009 10:27 cronin | skóre: 49
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Lenze bez znalosti VSETKYCH miest kde sa dana premenna nastavuje alebo cita bolo tvrdenie, ze volatilnost je - citujem: "uplne zbytocna" - prinajmensom odvazne. Vidime tu sice kod, ktory synchronizuje citanie, lenze nevidime kod, ktory synchronizuje zapis. Ak je aj KAZDY zapis vykonany pod tym istym zamkom, je skutocne volatilnost zbytocna.
    18.6.2009 10:52 Milan
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    No, popravde jsem pokladal to ze veskery pristup k dane zdilene promenne je synchronizovan za samozrejmost...

    18.6.2009 11:03 cronin | skóre: 49
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Praveze nemusia byt. Ak je zapis do premennej atomicky, nemusi byt osetreny explicitnym lockovanim; vtedy prave staci volatilnost premennej, co je efektivnejsie ako explicitna synchronizacia.

    Kazdy z dostupnych sposobov synchronizacie ma svoje opodstatnenie a su situacie kedy vyhovuje viac, kedy menej a kedy nemoze byt pouzity vobec.
    18.6.2009 10:57 Michy
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Nedá se spoléhat ani na to, že inicializace


    itemsAvailable = 0;

    je za všech okolností atomická operace. Např.:


    int64_t itemsAvailable;
    itemsAvailable = 0;

    bude na 32-bitovém systému provedena přinejmenším ve dvou instrukcích a proto se nelze na atomicitu inicializace vždy spoléhat.

    18.6.2009 11:05 cronin | skóre: 49
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    V pripade volatilnej premennej bude zapis atomicky a k preruseniu medzi tymito zapisujucimi instrukciami nedojde; prave to je jedna z veci, ktore zabezpecuje volatilnost premennej, ako som vysvetloval nizsie.
    18.6.2009 11:35 Michy
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Nerad bych se mýlil, ale přinejmenším na x86 architektuře instrukční prefix LOCK zamyká pouze jednu instrukci a pokud i následující instrukce má rovněž prefix LOCK, neznamená to, že mezi těmito instrukcemi nemůže nastat přerušení. K zajištění nepřerušitelnosti souvislého úseku kódu slouží privilegovaná instrukce pro maskování přerušení a privilegovaná je z toho důvodu, že odstavení přerušení je potencionálně nebezpečná operace. Proto pochybuju, že neprivilegovaným prefixem LOCK by šlo dosáhnout stejného efektu, to by nedávalo moc velký smysl.
    18.6.2009 12:09 vrosecky | skóre: 5
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Záleží na hardwarové platformě. Pokud platforma nepodporuje atomický zápis 64b proměnných do paměti a realizuje ho dvěmi po sobě jdoucími 32b zápisy, tak přiřazení není atomické bez ohledu na to, zda je 64b proměnná deklarována jako volatile či ne.

    18.6.2009 12:35 Ladicek | skóre: 28 | blog: variace | Havlíčkův brod
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Předřečník psal, že je Javista, v čemž by mohl být problém: v Javě (od verze 5) skutečně platí, že zápis do volatile longu je atomický. C++ bude mít vícevláknový memory model až v C++1x.
    Ještě na tom nejsem tak špatně, abych četl Viewegha.
    18.6.2009 15:06 vrosecky | skóre: 5
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Ano, v Javě  od verze 5 platí, že zápis do proměnné volatile long musí být atomický, v C či C++ tomu tak být nemusí.

    18.6.2009 10:23 Michy
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Podle mě jedině deklarování proměnné itemsAvailable jako volatile může spolehlivě zabránit optimalizátoru, aby takovou proměnnou neuložil do registru. Dokonce mám ten pocit, že přesně kvůli tomuhle je v C/C++ zavedeno klíčové slovo volatile. Programátor tím upozorňuje překladač, že hodnota této proměnné se může změnit mezi dvěma operacemi čtení a tudíž ji není možné uchovávat v registrech.
    18.6.2009 10:24 chochi | skóre: 29 | Praha
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Ja bych na to odpovedel asi takhle: rozhodne pouzit.

    Volatile rika kompilatoru, ze promena muze byt modifikovana nejakym "neznamym" zpusobem (HW, jine vlakno, ...) - jedina vec kterou to zpusobi v tomhle pripade bude, ze se promena bude cist vzdy z pameti (nebude se optimalizovat ulozenim do registru).

    S argumentem pro souhlasim. Argument proti muze davat smysl, ale spolehat se na to, ze treba na ten kod nepouzije nekdo jinej kompilator (ktery to bude optimalizovat) je nesmyslne (popripade to pouzijete nekde jinde, kde se to bude optimalizovat).

    Rekl bych ze se to bude chovat takhle:

    1.) s volatile - mate jistotu ze to vzdy fungovat - promena se bude cist vzdy z pameti

    2.) bez volatile - bud to kompilator nezoptimalizuja a vysledny kod bude stejny jako v 1.), nebo to kompilator zomptimalizuje a nebude to fungovat

     

    18.6.2009 10:47 cronin | skóre: 49
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Odporucam velmi pozorne si prestudovat trochu dlhsi clanok What every programmer should know about memory.
    Ja bych na to odpovedel asi takhle: rozhodne pouzit.
    Nie je celkom pravda. Naco vynucovat zbytocny flush cache (pozri nizsie) po zapise, ak je to uz osetrene lockovanim?
    Volatile rika kompilatoru, ze promena muze byt modifikovana nejakym "neznamym" zpusobem (HW, jine vlakno, ...) - jedina vec kterou to zpusobi v tomhle pripade bude, ze se promena bude cist vzdy z pameti (nebude se optimalizovat ulozenim do registru).
    Nie je celkom pravda. V konecnom dosledku hodnota premennej musi skoncit v registri tak ci tak. Volatilnost sposobi, ze pri zapise premennej (presnejsie pred nim) sa invaliduje jej kopia v cache pamati vsetkych procesorov a (po zapise) sa cache flushne do hlavnej pamate. Zaroven sa tato akcia vykona atomicky, a to i v pripade, ak na zapis celej hodnoty je potrebnych viacero cyklov (niektore udajove typy sa zapisuju "nadvakrat"). Ak bude chciet iny procesor pristupovat k premennej, musi si natiahnut jej aktualizovanu hodnotu z hlavnej pamate.
    1.) s volatile - mate jistotu ze to vzdy fungovat - promena se bude cist vzdy z pameti
    Nie je celkom pravda. Bude to fungovat v pripade atomickeho zapisu. V pripade modifikacie typu itemsAvailable++; volatilnost nestaci, lebo, modifikacia nie je atomicka, ale sklada sa z troch krokov: 1) nacitania starej hodnoty, 2) inkrementacie a 3) zapisania novej hodnoty. Ak dojde k preruseniu vlakna medzi tymito krokmi a viacero vlakien sa pokusi o to iste, bez synchronizacie to povedie k chybnym vysledkom. Volatilnost premennej staci iba ak nova hodnota priradena do premennej je nezavisla na predchadzajucej hodnote. V opacnom pripade musi byt synchronizovany cely blok vykonavajuci citanie starej a vypocet a priradenie novej hodnoty.

    18.6.2009 11:10 Michy
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Obávám se, že o nutnosti synchronizace tady nikdo nepochyboval a vyprázdnění cache je přesně to, co potřebujeme. Pokud by totiž takový program běžel na vícejádrovém/procesorovém stroji a každá cache by uchovávala různé hodnoty, tak to rovněž znamená nefunkční program.
    18.6.2009 11:19 cronin | skóre: 49
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Invalidaciu cache pamati ostatnych procesorov a flusch vlastne cache robi tak volitalnost premennej, ako aj explictina synchronizacia. Pouzitie oboch mechanizmov sucastne je zbytocne. V pripade operacii zapisu premennej, kde jej nova hodnota nezalezi na starej, staci volatilnost. V pripade operacii zapisu premennej, kde sa nova hodnota vypocita zo starej, volatilnost nestaci a je potrebne zamykanie.
    18.6.2009 11:32 vrosecky | skóre: 5
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Pokud se nová hodnota počítá ze starší, dá se v jistých případech zamykání vyhnout použitím instrukce CAS (en.wikipedia.org/wiki/Compare-and-swap).

    18.6.2009 11:49 Michy
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Explicitní synchronizace vláken/procesů (např. pthread_mutex_lock) rozhodně nemá a ani nemůže mít vliv na synchronizovanou proměnnou, protože vůbec netuší, kterou proměnnou nebo proměnné daný synchronizační objekt (mutex) vlastně synchronizuje. Volání pthread_mutex_lock(&mutex) tak nanejvýš vyprázdní, či jinak synchronizuje, cache přidělenou objektu mutex, ale víc udělat nedokáže, nemá k tomu žádné informace.
    18.6.2009 12:08 Michy
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Nevím, jestli tady nedošlo k pomýlení, ale volatile v C/C++ neříká překladači nic o tom, jak se k proměnné chovat při zápisu. Pouze sděluje, že mezi dvěma čteními může dojít ke změně a tudíž nelze hodnotu takové proměnné uchovávat v registrech procesoru, ale vždy je nutné ji načíst znovu z paměti. Atomické zamykání instrukcí a podobné techniky lze realizovat pouze na úrovni assembleru, jinak je vynutit v C/C++ nejde.
    18.6.2009 12:35 chochi | skóre: 29 | Praha
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Presne tak. Promena typu "volatile" nerika vubec nic o vyprazdneni cache. Je rozhodne nutna pro specifikaci, ze k promene pristupuje vice vlaken - tudiz nelze si uchovavat "mezikroky" treba v registrech.

    Pr;:

    $ cat a.c

    int main(){
        volatile int i = 0;
        i++;
        return i;
    }

    $ gcc -S -O3 a.c && cat a.s

        .file    "a.c"
        .text
        .p2align 4,,15
    .globl main
        .type    main, @function
    main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl    -4(%ecx)
        pushl    %ebp
        movl    %esp, %ebp
        pushl    %ecx
        subl    $16, %esp
        movl    $0, -8(%ebp)
        movl    -8(%ebp), %eax
        addl    $1, %eax
        movl    %eax, -8(%ebp)
        movl    -8(%ebp), %eax
        addl    $16, %esp
        popl    %ecx
        popl    %ebp
        leal    -4(%ecx), %esp
        ret
        .size    main, .-main
        .ident    "GCC: (GNU) 4.1.2 20070626 (Red Hat 4.1.2-14)"
        .section    .note.GNU-stack,"",@progbits

    $ cat a2.c

    int main(){
        int i = 0;
        i++;
        return i;
    }

    $ gcc -S -O3 a2.c && cat a2.s

        .file    "a2.c"
        .text
        .p2align 4,,15
    .globl main
        .type    main, @function
    main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl    -4(%ecx)
        movl    $1, %eax
        pushl    %ebp
        movl    %esp, %ebp
        pushl    %ecx
        popl    %ecx
        popl    %ebp
        leal    -4(%ecx), %esp
        ret
        .size    main, .-main
        .ident    "GCC: (GNU) 4.1.2 20070626 (Red Hat 4.1.2-14)"
        .section    .note.GNU-stack,"",@progbits

    Tady je pekne videt, ze "volatile" ma vliv jen na ulozeni mezi-vysledku (dokonce pokud neni promena deklarovana jako volatile, tak nemusi byt v pameti ani alokovana a je platna jen pro aktualni vlakno). Zadna prace s cache.

    18.6.2009 11:44 Atom321 | skóre: 20
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Bude-li se proměnná číst nebo zapisovat ve více cyklech, atomické to nebude.
    18.6.2009 13:41 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    To se nam tu urodilo nesmyslu. V prvni rade bych zcela ignoroval prispevky kolegy cronina, protoze ty se mozna tykaji javy (ale tu neznam, takze nedokazu posoudit, jestli to co pise je pravda alespon v ni), ale urcite ne c/c++.

    Ve vasem pripade je volatile zbytecne, protoze promennou itemsAvailable mate chranenou zamkem queueMutex a tedy k ni muze v jednom okamziku pristupovat pouze jedno vlakno. Zaroven fce pthread_mutex_* funguji jako pametove bariery, nemuze se stat, ze by vam pres ne "pretekaly" cteni ci zapisy.

    Klicove slovo volatile je pro synchronizaci mezi vlakny nepouzitelne, protoze jim sice ukecate prekladac, ale procesor muze zapisy do pameti prerovnavat z vlastniho rozmaru.

    18.6.2009 14:31 Michy
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Ve wikipedii je docela srozumitelně popsáno vše ohledně volatile proměnných v C/C++.

    Klíčové slovo volatile rozhodně zbytečné v uvedeném příkladě není, mutex pouze zabraňuje současnému přístupu z více vláken najednou, ale nedokáže zabránit překladači, aby vygeneroval kód, kdy si před vstupem do cyklu uloží obsah proměnné itemsAvailable do registru a následně už testuje pouze obsah tohoto registru, aniž by si pokaždé znovu načetl aktuální hodnotu itemsAvailable.

    Jak synchronizace mutexem, tak klíčové slovo volatile jsou nezbytně nutné!!!

    18.6.2009 14:51 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Mylite se, mutex, respektive volani tech tri funkci, presne tomuto zabrani, volatile je tam naprosto zbytecne.

    18.6.2009 15:56 Atom321 | skóre: 20
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Naopak, mýlíte se vy a on má pravdu. Mutex a volatile dělají naprosto odlišné věci. Přečtěte si manuál.
    18.6.2009 14:57 Michy
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Možná na vysvětlenou ještě malá poznámka, volání funkce pthread_cond_wait(&cond, &mutex) uvolní mutex a čeká, až nějaké jiné vlákno zavolá pthread_cond_signal(&cond), čímž se cond probudí z waitu a poté mutex opět zamkne. Pokud by tam nebylo ono pthread_cond_wait(&cond, &mutex), potom by samozřejmě k žádné změně proměnné itemsAvailable dojít nemohlo (za předpokladu, že ostatní vlákna zodpovědně synchronizují přístup k itemsAvailable přes stejný mutex) a jednalo by se o nekonečnou smyčku.

    Doufám, že se nám to konečně podařilo společným úsilím rozmotat.

    18.6.2009 14:55 cronin | skóre: 49
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Objektivne uznavam, ze moja extrapolacia vyznamu klucoveho slova volatile z Javy na C bola nespravna. Medizcasom som si na zaklade tejto diskusie nastudoval detaily o C - skutocne tam volatile negarantuje - narozdiel od Javy, kde su garancie tohto klucoveho slova dost silne a zahrnaju aj sematiku happen before - takmer nic.

    Stale vsak pochybujem o tomto:
    Ve vasem pripade je volatile zbytecne, protoze promennou itemsAvailable mate chranenou zamkem queueMutex a tedy k ni muze v jednom okamziku pristupovat pouze jedno vlakno. Zaroven fce pthread_mutex_* funguji jako pametove bariery, nemuze se stat, ze by vam pres ne "pretekaly" cteni ci zapisy.
    Zmieneny mutex tak ako ho vidime zabezpeci, ze ten konkretny blok kodu bude vykonavany iba jednym vlaknom. Nijako to ale nezabrani tomu, aby ine bloky kodu pracujuce s inkriminovanou premennou bezali pararelne, ci uz preto, ze nie su synchronizovane vobec, alebo preto, ze su synchronizovane inym zamkom. Alebo sa opat mylim a zapis do premennej nemusi byt synchronizovany pomocou toho isteho mutexu ako citanie?

    18.6.2009 15:01 Michal Kubeček | skóre: 72 | Luštěnice
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Nijako to ale nezabrani tomu, aby ine bloky kodu pracujuce s inkriminovanou premennou bezali pararelne, ci uz preto, ze nie su synchronizovane vobec, alebo preto, ze su synchronizovane inym zamkom.

    To by ale byla chyba programu (stejně jako když mutex nepoužijete vůbec) a proti ní by vám volatile stejně nijak nepomohlo.

    18.6.2009 15:10 cronin | skóre: 49
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Nejako prestavam chapat, naco je vobec v C to volatile dobre. Podla toho, o com ma tu presviedcate, je synchronizacia mutexami nutna a postacujuca podmienka pre spravne fungovanie. Kedy mi teda volatile pomoze?

    Michy avatar 18.6.2009 15:19 Michy | skóre: 11 | Praha
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Přinejmenším tohle.
    18.6.2009 15:29 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    ???

    Cemu a jak tam volatile pomuze?

    18.6.2009 15:32 chochi | skóre: 29 | Praha
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Vyznam to ma jenom zabranit "nechtene" optimalizaci - vzdy pri dotazu na promenou se podiva do pameti (a ne treba do pracovniho registru kde je ulozena hodnota z posledniho precteni - ona se totiz mohla mezitim prepsat nekym "jinym")

    Jsou dve udalosti kde by se to melo pouzit:

    1.) Vicevlaknova aplikace a pristup ke globalni promene - hodnotu moze prepsat jine vlakno

    2.) Pri komunikaci s HW zarizeni, jez je mapovano nekam do pameti (na presnou znamou adresu) - vzdy je nutne cist pametove misto, jelikoz HW to muze kdykoli zmenit

    Snad se mi to konecne podarilo rozumne vysvetlit :-) - pri vypnute optimalizaci to smysl nema, jelikoz se nepouzivaji registry pro uchovavani mezivysledku.

    18.6.2009 15:40 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    No a prave u bodu 1) je volatile zbytecne, od toho mate zamky.

    18.6.2009 15:59 Atom321 | skóre: 20
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Ne. Zámky předcházejí kolizím, zatímco volatile zajistí, že se změna projeví i v ostatních threadech. Pokud tam volatile nebude, s optimalizacemi se to přeloží na jedno porovnání a nekonečný cyklus.
    18.6.2009 16:08 Andrej Herceg | skóre: 43
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Pokiaľ viem, tak podľa POSIX normy by sa mal prekladač postarať o to, že premenné, ktoré sú v obkolesené lock/unlock, sa môžu meniť aj niekde inde.
    18.6.2009 16:19 Atom321 | skóre: 20
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Ne, POSIX standardizuje funkce, které implementují zámky. Tyto funkce jsou součástí příslušné knihovny, případně volání operačního systému. S překladačem nemají vůbec nic společného.
    18.6.2009 16:03 chochi | skóre: 29 | Praha
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Zamky vam v tomhle vubec nepomuzou - zamky vam zaruci na jednom miste budete provade jen jednu operaci, ane treba ze zaroven bude cist a zoroven zapisovat (coz muze vyprodukovat uplne jina data nez ktera byla predtim, nebo potom).

    Tohle jen rika prekladaci, ze "ta hodnota se muze zmenit".

    Teoreticka situace: promenou itemsAvailable nebudete deklarovat jako volatile - chytry prekladac si vsimne, ze v tom while cyklu se nikde nemeni (a zjisti, ze ani volani pthread_cond_wait ji zmenit nemuze) a tak si rekne ze hodnotu ulozi do registru (aby neprovadel drahe opearace cteni pameti) - takze muzou nastat dve situace - pred cylkem bude itemsAvailable == 0, pak vznikne nekonecny cyklus (protoze se stale bude porovnavat 0 == 0 bez ohledu na to zda itemsAvailable nekdo zmenil ci ne, protoze mame jeji "kopii" v registru), nebo itemsAvailable != 0 a cylkus vubec neprobehne.

    Michy avatar 18.6.2009 16:00 Michy | skóre: 11 | Praha
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Pro názornost:

    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    volatile int x = 0;
    
    // 1. vlákno
    pthread_mutex_lock(&mutex);         // zamkne mutex
    while (x == 0)                      // při každém průchodu čtu novou hodnotu x (mohlo ji modifikovat jiné vlákno)
    {
      pthread_cond_wait(&cond, &mutex); // odemkne mutex (umožní tak modifikaci x), čeká na pthread_cond_signal, zamkne mutex
    }
    pthread_mutex_unlock(&mutex);       // odemkne mutex
    
    // 2. vlákno
    pthread_mutex_lock(&mutex);         // zamkne mutex
    x = 1
    pthread_cond_signal(&cond);         // způsobí probuzení pthread_cond_wait v 1. vlákně
    pthread_mutex_unlock(&mutex);       // odemkne mutex a umožní tak 1. vláknu si ho opět přivlastnit ve funkci pthread_cond_wait
    
    

    Pokud by proměnná x nebyla volatile, měl by překladač plné právo předpokádat, že hodnota x se nemůže sama od sebe změnit a může ji tedy beztrestně uložit do registru. Napadlo mě jen, že díky tomu, že v cyklu je ještě obsaženo volání funkce pthread_cond_wait o níž neví, zda nemodifikuje obsah x, tak se překladač nemůže spoléhat, že x zůstane zachované a žádnou optimalizaci ohledně x si nemůže dovolit. Avšak v případě, že pthread_cond_wait je inline funkce a nevolá žádné další non-inline funkce, mohl by kompilátor vědět, zda se hodnota x mění, či ne a naznačenou optimalizaci by mohl provést. Přenositelný kód by neměl dělat žádné předpoklady o funkci pthread_cond_wait (inline/non-inline), a proto je užití volatile jistě opodstatněné, i když s velkou pravděpodobností jeho absence nezpůsobí žádné problémy.

    18.6.2009 16:31 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Přesně takhle jsem uvažoval taky a zatím jsem volatile vždy používal. Nejspíš tím nemůžu nic zkazit, protože jakákoliv případná optimalizace přístupu k té proměnné by v každém případě byla nekorektní. Takže v podstatě o žádnou důležitou (a proveditelnou) optimalizaci nepřicházím. Dokonce se mi občas zdá, že volatile může i trochu zlepšit přehlednost a čitelnost toho kódu, protože jasně říká bacha, tímhle kouskem dat se něco synchronizuje.

    Napadlo mě jen, že díky tomu, že v cyklu je ještě obsaženo volání funkce pthread_cond_wait o níž neví, zda nemodifikuje obsah x, tak se překladač nemůže spoléhat, že x zůstane zachované a žádnou optimalizaci ohledně x si nemůže dovolit.

    Tak s tímto souvisí hlavní důvod, proč se pořád přikláním k názoru, že by tam volatile mělo být. Otázka totiž je, který překladač si tu optimalizaci nedovolí. Jistě, současný překladač GCC to skutečně neumí a když je v cyklu volání funkce, všechny nelokální proměnné vždy čte z paměti. Nicméně nepřipadá mi správné se na takové chování i do budoucna spoléhat.

    Člověk si musí položit otázky: Co když přijde úžasny-překladač-z-budoucnosti (tm), který bude umět analýzu aliasů i přes volání funkcí? A co když někoho někdy napadne použít můj kód v něčem, co se linkuje staticky? Tam by možná agresivnější analýzu aliasů zvládl i některý dnešní překladač, který není jen výplodem mé fantazie...

    Michy avatar 18.6.2009 16:55 Michy | skóre: 11 | Praha
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Tady si dovolím nesouhlasit. Sebegeniálnější překladač zná jen hlavičku funkcí a nemá vůbec k dispozici tělo funkce (pochopitelně s výjimkou inline funkcí), které by mohl použít k optimalizacím. V případě knihovních funkcí zpravidla ani zdrojový kód nemáme k dispozici. Linkování je až následná operace po kompilaci a během linkování již k žádným optimalizacím dojít nemůže, pouze se propojují odkazy na proměnné a funkce, měnit kód se už v této fázi nedá.
    18.6.2009 19:27 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

     

    Linkování je až následná operace po kompilaci a během linkování již k žádným optimalizacím dojít nemůže, pouze se propojují odkazy na proměnné a funkce, měnit kód se už v této fázi nedá.

    Tohle ani dnes není tak úplně pravda. Navíc je otázka, jako to bude v budoucnu.

    Kdybych měl celou diskusi vtěsnat do jedné otázky: Garantuje mi samotný standard C++ (nebo C), že se nikdy nestane, aby neaktuální verze (non-volatile) proměnné zůstávala v registru i přes volání funkcí?

    Je úplně jedno, že nějaký konkrétní překladač zrovna dnes takové věci nedělá. To je chabá útěcha. Předpoklady na chování toho kódu musí mít oporu v nějakém standardu.

     

    18.6.2009 19:47 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Garantuje mi samotný standard C++ (nebo C), že se nikdy nestane, aby neaktuální verze (non-volatile) proměnné zůstávala v registru i přes volání funkcí?

    Ano. Objekt musi byt v pameti na jednom miste, jestlize prekladac provadi takove optimalizace, diky kterym je de facto na dvou ruznych mistech (v registru a v RAMce), musi se postarat o to, aby to navenek nebylo poznat.

    18.6.2009 19:57 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    A jaké jsou tedy podmínky pro to, aby překladač mohl dělat optimalizace? Podle mne by pak překladač mohl dělat optimalizace jedině pro lokální proměnné od místa jejich deklarace po první volání jakékoli funkce.
    18.6.2009 21:13 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Pokud jeji adresu nedate do globalni promenne, nepredate ji nejake funkci, nebo s ni neudelate nejakou jinou skopicinu, tak ji zbytek programu nemuze videt.

    18.6.2009 18:04 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Frknu to sem k vam, ale tyka se to i ostatnich volatilistu.

    Pratele, mne se zda, ze nejen ze neznate standard, ale ani se na chvili nezamyslite nad tim, kde bychom s vasimi predstavami dovedenymi do dusledku skoncili. Prekladac muze optimalizovat kod, ale obecne jen do te miry, aby nezmenil pozorovatelne chovani programu. Standard predpoklada, ze vsechny objekty lezi kdesi v pameti, jedne pro cely program, a prekladac muze optimalizovat pristup k nim tim, ze si je natahne do registru procesoru, nicmene toto muze udelat pouze tehdy, pokud je presvedceny, ze je nikdo mezitim v pameti neprepsal. Vezmneme si trivialni priklad:

    void clobber();
    
    bool test(int* a)
    {
    	*a = 3;
    	clobber();
    
    	if( *a == 3 )
    		return true;
    	else
    		return false;
    }

    Po zavolani fce clobber() musi prekladac znovu nacist hodnotu *a z pameti, nemuze vedet, jestli se mezitim nezmenila, protoze nezna definici clobber() a nevi, co presne dela. Takze zaverecny if nemuze odoptimalizovat.

    Tohle plati obecne pro vsechny funkce a pro pthread_mutex_lock, pthread_mutex_unlock a pthread_cond_wait obzvlast, nebot za jejich "behu" muze dojit ke zmene v pameti uz z definice. Nejaky hypoteticky prekladac budoucnosti by teoreticky tuto optimalizaci mohl provest, ale jen pokud by zanalyzoval cely program a dosel k zaveru, ze zadne jine vlakno a ani nikdo jiny nebude v prubehu vykonavani fce pthread_cond_wait() menit hodnotu itemsAvailable. Dokud si timto nebude jist, tak vzdy bude muset znovu natahnout hodnotu z pameti, jinak se nechova podle standardu.

    Abych to shrnul a asi po treti zopakoval sam sabe. Klicove slovo volatile je zcela zbytecne, protoze to oc vam jde, totiz natahnuti hodnoty promene z pameti po pripadnem probuzeni, zaruci samo volani funkci kolem mutexu. Deklaraci volatile akorat svuj program zpomalujete (potencialne docela hodne, zalezi jak casto s takovou promennou pracujete), protoze prekladac nutite nacitat/zapisovat promennou z/do pameti vzdy, i kdyz nehrozi, ze by ji nekdo jiny zmenil.

    18.6.2009 19:00 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    * Vezmeme si
    18.6.2009 19:39 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Takže vlastně překladač nemůže použít registry procesoru nikdy, protože nikdy nemůže vědět, že mu proměnnou v paměti nezmění jiné vlákno? Spíš bych řekl, že překladač může použít optimalizaci pokud ví, že to samé vlákno proměnnou v paměti nezmění. Pochybuju o tom, že ve standardu je něco o tom, že překladač musí předpokládat, že kterékoli volání funkce může změnit hodnotu proměnné v paměti – daleko pravděpodobnější se mi jeví, že může dělat analýzu inline funkcí a zjistit, že volání funkce nemůže hodnotu proměnné změnit a může ji tak optimalizovat. Leda by ty funkce měly nějaký speciální příznak, že zaručují paměťovou bariéru, pak by se jím překladač asi měl řídit. Ale spoléhat se na to, že zrovna teď ty funkce překladač neprokoukne a tudíž nemůže přístup k proměnné optimalizovat, to je špatný přístup. To se vám taky klidně jednoho dne může stát, že se jednoho dne chování těch funkcí nebo překladače změní. Programátor by nikdy neměl spoléhat na vedlejší efekty kódu, ale pokud něco potřebuje, měl by to v kódu explicitně uvést (i když třeba sousední kód zrovna dnes dělá to samé jaksi bokem).
    18.6.2009 20:06 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Pochybuju o tom, že ve standardu je něco o tom, že překladač musí předpokládat, že kterékoli volání funkce může změnit hodnotu proměnné v paměti – daleko pravděpodobnější se mi jeví, že může dělat analýzu inline funkcí a zjistit, že volání funkce nemůže hodnotu proměnné změnit a může ji tak optimalizovat.
    Obzvlášť v uvedeném příkladu, kdy funkci clobber() není hodnota lokální proměnné a nijak předávána, takže se dá docela dobře předpokládat, že tam se a nemění.

    A i kdyby - jak už psali jiní, obecně je pitomost spoléhat se na chování překladače, protože to se může s jinou verzí změnit.
    Quando omni flunkus moritati
    18.6.2009 20:14 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Předpokládám, že jste chtěl napsat adresa lokální proměnné. Jenže jak čtu tu diskusi, měl by překladač předpokládat i to, že někdo adresu té proměnné "uhodne" a její hodnotu změní.
    18.6.2009 20:22 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    To je právě takové dilema.

    Agresivní přístup: Na zásobník mi nemá kdo co sahat bez dovolení! Když mi tam sáhne, dostane do nosu a je to jeho problém. Pokud nikomu nepředám pointer na svou lokální proměnnou, neexistuje žádný korektní způsob, jak by mohl proměnnou změnit. Tedy mohu optimalizovat.

    Ultrakonzervativní přístup: Kdykoliv předám řízení někomu jínému (tj. zavolám funkci), musím počítat s tím, že volaná funkce může přepsat kterékoliv místo v paměti. Neřeším, zda je takový zápis korektní nebo ne. Každou proměnnou (včetně lokální) musím po volání funkce znovu načíst.

    Otázka je, co o tom říká standard a jak přesně to překladače řeší. Já bych tipoval, že pravda jen někde uprostřed, nicméně podle některých příspěvků by měl standard vynucovat spíš ten konzervativní přístup...

    18.6.2009 21:04 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Nikolivek. Stale plati pravidla o viditelnosti, takze pokud mam lokalni promennou, kterou nikde neinzeruju (treba ze bych nejake funkci predal odkaz na ni), podle standardu se k ni ostatni casti programu nemaji sanci dostat a proto si napr. prekladac muze dovolit ten luxus predpokladat, ze takova promenna se mu nebude menit pod rukama.

    18.6.2009 21:55 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    V takovém případě si ale protiřečíš, protože toto tvé tvrzení se vylučuje s:
    Po zavolani fce clobber() musi prekladac znovu nacist hodnotu *a z pameti, nemuze vedet, jestli se mezitim nezmenila, protoze nezna definici clobber() a nevi, co presne dela. Takze zaverecny if nemuze odoptimalizovat.
    Quando omni flunkus moritati
    19.6.2009 10:04 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    A jak je to potom s předáváním parametrů funkcím v registrech? Nevím, zda zrovna C může tuhle optimalizaci dělat automaticky, nebo to vždy musí programátor povolit – ale pokud by kompilátor mohl do registrů dávat jen ty hodnoty, které nejsou viditelné odjinud, nemohl by předávat parametry v registrech nikdy, protože nikdy neví, co volaný s adresou té proměnné udělá. Nebo je ve standardu C nadefinováno i to, že volající nesmí adresu předávaného parametru funkce poskytnout nikam jinam, než volané funkci?
    21.6.2009 20:08 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Vsechny parametry vcetne odkazu jsou predavane hodnotou a stavaji se lokalnimi promennymi volane funkce, volajici funkce ani nikdo jiny se k nim po predani kontroly nad behem programu nemuze dostat. Samozrejme volana funkce se muze o sve promenne podelit z vlastni vule, ale pokud nekomu dalsimu preda odkaz na parametr, ktery byl predan v registru, tak prekladac musi vygenerovat kod, ktery udela misto v pameti (nejspis na zasobniku), obsah regitru tam nakopiruje a pote preda ukazatel. To uz ale neni starost volajici funkce.

    21.6.2009 21:10 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    To ale platí jedině pokud je volajícím taky kód v C. Jenže volajícím může být cokoli, na co se žádný standard jazyka C nevztahuje.
    21.6.2009 22:57 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Pokud tohle volajici porusi, tak je to jeho chyba a s tim cecko tezko neco udela. To byste taky mohl klast C za vinu, ze nebude fungovat, kdyz mu budete paremetry funkci sypat na zasobnik v opacnem poradi, nez ocekava.

    18.6.2009 21:53 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Předpokládám, že jste chtěl napsat adresa lokální proměnné.
    To předpokládáš špatně, chtěl jsem napsat lokální proměnná. To že je to náhodou pointer, je totiž úplně jedno. (Když clobber() nezná pointer, nemůže se rejpat ani v hodnotě)
    Quando omni flunkus moritati
    18.6.2009 22:19 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    A proc by clobber() ten pointer nemohl znat? Tady mate clobber() implementovan tak, ze se k promenne *a dostane a zmeni ji, takze test vrati false.

    19.6.2009 11:39 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Tady mate clobber() implementovan tak, ze se k promenne *a dostane a zmeni ji, takze test vrati false.
    Kdežto tady, což je příspěvek, ke kterému jsem se vyjadřoval, ne.
    Quando omni flunkus moritati
    21.6.2009 19:15 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Kdybyste cetl pozorne, tak byste si vsiml, ze v tom prispevku vubec neni clobber() definovana. Takze mi neni jasne, na zaklade ceho jste jen tak od boku vystrelil, ze se v hodnote rejpat nemuze. A ze to mozne je, dokazuje druhy kus kodu.

    21.6.2009 22:40 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Takze mi neni jasne, na zaklade ceho jste jen tak od boku vystrelil, ze se v hodnote rejpat nemuze.
    Na základě tohohle:
    Obzvlášť v uvedeném příkladu, kdy funkci clobber() není hodnota lokální proměnné a nijak předávána, takže se dá docela dobře předpokládat, že tam se a nemění.
    Tím samozřejmě neříkám, že nemůže. Ale zavání to nějakou prasárnou.
    Quando omni flunkus moritati
    19.6.2009 09:54 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Pokud clobber() má okopírovanou hodnotu lokální proměnné, může si s tou hodnotou dělat co chce, ale původní proměnnou to nezmění. Takže do volání funkce se nesmí předat adresa té proměnné, její hodnota se tam okopírovat klidně může.
    19.6.2009 11:42 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Pokud clobber() má okopírovanou hodnotu lokální proměnné, může si s tou hodnotou dělat co chce, ale původní proměnnou to nezmění.

    To sice ne, ale vzhledem k tomu, že ta hodnota je shodou okolností pointer, tak funkce sice nezmění tu původní lokální proměnnou, ale data v ní (potenciálně viditelná jinde) už jo.
    Quando omni flunkus moritati
    18.6.2009 20:23 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Vsimnete si, ze promenna *a neni lokalni, lokalni je jen odkaz na ni, predany jako parametr.

    Pokud ten kod nahore ulozime jako a.cc, nasledujici kus jako b.cc a obvyklym zpusobem je zkompilujeme, jakou myslite, ze program vrati hodnotu? Bude zaviset na nastavenych optimalizacich?

    bool test(int* a);
    
    int a;
    
    void clobber()
    {
            a = 10;
    }
    
    int main(int argc, char** argv)
    {
            return test(&a);
    }
    18.6.2009 20:55 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Spíš bych řekl, že překladač může použít optimalizaci pokud ví, že to samé vlákno proměnnou v paměti nezmění.

    Ano, a protoze c/c++ standard se o vlaknech vubec nezminuje, prekladac se chova vzdy tak, jako by bezelo jen jedno vlakno.

    Znovu opakuji, pro standard existuje jenom jedna pamet do ktere sahaji vsichni, takze pokud nejaka funkce zmeni spolecnou promenou, ostatni tu zmenu nemuzou neprosvihnou. Prace s registry je problem prekladace a on musi zajistit, ze tomu tak bude i s optimalizovanym kodem. Proto napr. v gcc muzete funkci deklarovat s atributem const (mysleno takove to __attributte__ ((const))), cimz prekladaci date najevo, ze funkce zavisi pouze na svych parametrech, necte ani nemeni zadne globalni promenne, tudiz behem jejiho volani neni nutne soupat s "ohrozenymi" promennymi z registru do pameti a zase zpatky.

    21.6.2009 19:18 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    * nemuzou prosvihnout.
    18.6.2009 20:08 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Pratele, mne se zda, ze nejen ze neznate standard, ...

    V mém případě je to naprostá pravda. Tu šílenou bichli jsem skutečně nečetl.

    Po zavolani fce clobber() musi prekladac znovu nacist hodnotu *a z pameti, nemuze vedet, jestli se mezitim nezmenila, protoze nezna definici clobber() a nevi, co presne dela. Takze zaverecny if nemuze odoptimalizovat.

    Vsuktu? Platí tohle i v případě, že knihovnu s funkcí clobber() linkuji staticky? Co když funkce clobber() neobsahuje vůbec žádný mov se zápisem do paměti (na Intelu)? Co když neobsahuje žádný store (na nějakém RISCu)? Klidně může existovat funkce, která vůbec nikam do paměti nepíše. (Kromě manipulace se zásobníkem, ale na tu opravdu nikdo jiný paralelně nesahá.) Opravdu si něčeho takového kompilátor nesmí všímat? Ani v budoucnu?

    Nejaky hypoteticky prekladac budoucnosti by teoreticky tuto optimalizaci mohl provest, ale jen pokud by zanalyzoval cely program a dosel k zaveru, ze zadne jine vlakno a ani nikdo jiny nebude v prubehu vykonavani fce pthread_cond_wait() menit hodnotu itemsAvailable.

    Žádný překladač se určitě nepustí do tohoto algoritmicky neřešitelného problému. ;-) Nicméně pořád mi připadá v zásadě správné tu proměnnou označit jako volatile...

    Klicove slovo volatile je zcela zbytecne, protoze to oc vam jde, totiz natahnuti hodnoty promene z pameti po pripadnem probuzeni, zaruci samo volani funkci kolem mutexu.

    Možná. Například klíčové slovo const před deklarací pointerů je taktéž na spoustě míst (na první pohled) zbytečné, nicméně chrání programátora před budoucími ošklivými chybami. Říká „pozor, s těmito daty si nemůžeš hrát, ta ještě bude někdo potřebovat“. A volatile zase říká „pozor, s těmihle daty si občas paralelně hraje někdo jiný“. Že kompilátor možný problém postřehne a že nebude optimalizovat přístup do paměti přes volání funkcí, není z pohledu logiky celé věci až tak relevantní.

    Deklaraci volatile akorat svuj program zpomalujete (potencialne docela hodne, zalezi jak casto s takovou promennou pracujete), protoze prekladac nutite nacitat/zapisovat promennou z/do pameti vzdy, i kdyz nehrozi, ze by ji nekdo jiny zmenil.

    Zpomaluji? Pokud se ta proměnná čte i zapisuje vždy pouze pod (jedním a tím samým) mutexem a pokud mezi každými dvěma přísupy k té proměnné leží alespoň jedno volání funkce (třeba pthread_cond_wait(), když nic jiného), měla by být „rychlost“ programu s volatile i bez volatile srovnatelná. Nebo jsem něco přehlédl?

    Velmi striktně vzato, jediné místo, kde je volatile skutečně bezpodmínečně potřeba, je spinlock. (Ten samozřejmě v userspace nemá co dělat, leda snad v implicitní podobě, kterou zajišťuje adaptivní mutex.) Nicméně stále mám (možná mylný) dojem, že použití volatile u podmínkové proměnné je tak nějak „logicky správné“. Navíc (jak už jsem poznamenal) skutečně nevidím žádné nevýhody stran efektivity. (Ale můžu se mýlit, což se mi stává často.)

    18.6.2009 21:36 Ladicek | skóre: 28 | blog: variace | Havlíčkův brod
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Já teda nejsem Céčkař, ale pod vlivem téhle diskuse jsem dneska chvíli trápil Google a v zásadě se věc má tak, že Céčkové volatile nemá z pohledu thread-safety žádný efekt. Asi nejlíp to vysvětluje Arch Robinson, architekt intelích Threading Building Blocks: Volatile: Almost Useless for Multi-Threaded Programming (včetně diskuse).
    Ještě na tom nejsem tak špatně, abych četl Viewegha.
    18.6.2009 22:04 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Já teda nejsem Céčkař, ale pod vlivem téhle diskuse jsem dneska chvíli trápil Google a v zásadě se věc má tak, že Céčkové volatile nemá z pohledu thread-safety žádný efekt.
    Kdyby sis diskuzi pročetl pořádně, zjistil bys, že to skoro nikdo netvrdí.
    Quando omni flunkus moritati
    18.6.2009 22:23 Ladicek | skóre: 28 | blog: variace | Havlíčkův brod
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Zvláštní, to by ta diskuse asi nebyla tak dlouhá. Někdo tu tvrdí, že volatile je v předmětném případě dobré, někdo, že je dokonce nezbytné, volatilisti by si tu s nevolatilistama nejradši vyrvali vlasy :-), no nechme toho.
    Ještě na tom nejsem tak špatně, abych četl Viewegha.
    19.6.2009 11:52 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    To jo, jenže to dobré a nezbytné se netýká thread-safety, ale optimalizací.
    Quando omni flunkus moritati
    19.6.2009 12:22 Ladicek | skóre: 28 | blog: variace | Havlíčkův brod
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Nikoliv, spíš možného vlivu optimalizací na thread-safety. Klíčová informace je, že překladač ani procesor nesmí optimalizovat přes bariéru, čímž je thread-safety zajištěna.
    Ještě na tom nejsem tak špatně, abych četl Viewegha.
    18.6.2009 22:09 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    To to vazne vysvetluju tak blbe?

    Muzete si to predstavit tak, ze na zacatku je vsude takove male volatile, ktere optimalizator postupne vyskrtava. Kdyz si vezmete treba takovyhle kousek kodu:

    a = 1;
    a *= 5;
    a = a - 3;

    a prelozite ho zcela bez optimalizaci, vyplivne vam gcc toto:

    movl    $1, -8(%ebp)
    movl    -8(%ebp), %edx
    movl    %edx, %eax
    sall    $2, %eax
    addl    %edx, %eax
    movl    %eax, -8(%ebp)
    subl    $3, -8(%ebp)

    Jak vidite, bud pracuje primo s pameti, nebo nahraje promennou do registru, provede operaci a zase ji do pameti ulozi.

    Pokud budete linkovat staticky, nebo nejakym jinym zpusobem umoznite prekladaci dostat se k definici fce clobber(), tak jestlize bude schopen vydedukovat, ze clobber() nemuze *a zadnym zpusobem zmenit, pak tu optimalizaci muze provest. Ale az pote, co se o tom ujisti, nikdy ne drive.

    Pouzitim volatile tyto optimalizace blokujete, pokud a z prikladu na zacatku deklarujete jako volatile, tak prekladec ten vypocet vzdy preklopi do toho stejneho assembleru, a to i kdyz pristup k a chranite mutexem a nikdo jiny vam ho nebude menit pod rukama. Nahrani a do registru po zamknuti mutexu a jeho zapsani do pameti pred uvolnenim mate zaruceno tak jako tak, takze opravdu jenom zpomalujete program, k nicemu jinemu to dobre neni.

    18.6.2009 18:37 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Avšak v případě, že pthread_cond_wait je inline funkce a nevolá žádné další non-inline funkce, mohl by kompilátor vědět, zda se hodnota x mění, či ne a naznačenou optimalizaci by mohl provést.

    Nemohl. I pokud by thread_cond_wait byla inline, musi byt nadefinovana tak, aby prekladac vedel, ze se v pameti muze cokoliv zmenit.

    Přenositelný kód by neměl dělat žádné předpoklady o funkci pthread_cond_wait (inline/non-inline), a proto je užití volatile jistě opodstatněné, i když s velkou pravděpodobností jeho absence nezpůsobí žádné problémy.

    Jenze vychozi stav je, ze volani funkce muze zmenit cokoliv a sahat kamkoliv, takze pokud o ni nedelam vubec zadne predpoklady, musim pred jejim zavolanim vsechny zmeny ulozit do promennych v pameti a po navratu je zase nacist.

    Michy avatar 18.6.2009 17:35 Michy | skóre: 11 | Praha
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Ještě mě napadlo jedno řešení a sice podmínkovou proměnnou nedeklarovat jako volatile, ale použít přetypování pouze v místě, kde bezpečně víme, že žádnou optimalizaci nechceme připustit.

    int x = 0;
    while (*(const volatile int*)(&x) == 0)           // C zápis
    while (const_cast< const volatile int& >(x) == 0) // C++ zápis
    

    Tak můžeme sdělit překladači, že právě jen v podmínce cyklu se má zdržet jakýchkoliv optimalizací x a všude jinde nechť přístup k x optimalizuje dle libosti.

    18.6.2009 17:52 Atom321 | skóre: 20
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Takový přístup k programování vede na nečitelný kód s těžko odhalitelnými chybami.

    Mnohem lepší je prostě napsat volatile do deklarace a pustit to z hlavy. Až bude celý program fungovat, proženete ho profilerem a zjistíte výkonově kritické místa. Ty pak můžete ručně optimalizovat.
    18.6.2009 20:11 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    No, mně se zdá, že proměnná, která smí mít někde volatile a jinde zase non-volatile přístup, zkrátka zavání chybou v návrhu...

    18.6.2009 22:06 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Proč?

    Když někde vím, že se mi proměnná pod rukama změnit nemůže (např. běží v zamčeném kusu kódu a nikdo do ní nehrabe), pak nemá smysl ji v celém tom kusu kódu považovat za volatile.
    Quando omni flunkus moritati
    19.6.2009 00:45 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Tak tohle je průšvih.

    Děkuji všem za komentáře a zejména za odkazy na zajímavé články. Nicméně právě teď jsem udělal praktický pokus s ošklivými spinlocky. Výsledek mě vyděsil. Posuďte sami. Většina toho, co tu zatím bylo řečeno, zcela zjevně není pravda.

    Tady je zdrojový kód:

    #include <unistd.h>
    #include <limits.h>
    #include <stdio.h>
    #include <pthread.h>
    #include <time.h>
    
    static const struct timespec snooze = { 5, 0 };
    
    static int useless;
    static int flag = 1;
    static volatile int FLAG = 1;
    
    static void foo( void ) {}
    static void FOO( int * something ) { useless = *something; }
    
    static void * t1( void * param ) { while ( flag ); puts( "T1 finished." ); return NULL; }
    static void * t2( void * param ) { while ( flag ) foo(); puts( "T2 finished." ); return NULL; }
    static void * t3( void * param ) { while ( flag ) FOO( &flag ); puts( "T3 finished." ); return NULL; }
    static void * t4( void * param ) { while ( FLAG ); puts( "T4 finished." ); return NULL; }
    static void * t5( void * param ) { while ( FLAG ) foo(); puts( "T5 finished." ); return NULL; }
    
    void * ( * const func[ 5 ] )( void * ) = { t1, t2, t3, t4, t5 };
    
    int main( void ) {
            int i;
            pthread_attr_t attr;
            pthread_t threads[ 5 ];
    
            pthread_attr_init( &attr );
            pthread_attr_setstacksize( &attr, PTHREAD_STACK_MIN );
    
            puts( "Spawning threads." );
            for ( i = 0; i < 5; ++i ) pthread_create( &threads[ i ], &attr, func[ i ], NULL );
            nanosleep( &snooze, NULL );
    
            puts( "Setting the death flag." );
            FLAG = flag = 0;
            nanosleep( &snooze, NULL );
    
            puts( "That's the end." );
            return 0;
    }
    

    Ano, je to moc ošklivý kód. Ano, nevolám nikde pthread_join(). (To jsou reakce na předpokládané FAQ.) A tady jsou výsledky, ze kterých mrazí v zádech.

    [andrej@argos pokusy]$ gcc -O0 -pthread spin.c -o spin
    [andrej@argos pokusy]$ ./spin
    Spawning threads.
    Setting the death flag.
    T4 finished.
    T2 finished.
    T1 finished.
    T5 finished.
    T3 finished.
    That's the end.

    Tak tady je vše podle předpokladů. Při -O0 se proměnná čte vždy znovu z paměti.

    [andrej@argos pokusy]$ gcc -O1 -pthread spin.c -o spin
    [andrej@argos pokusy]$ ./spin
    Spawning threads.
    Setting the death flag.
    T5 finished.
    T4 finished.
    That's the end.
    

    Tady se čtou pouze volatile proměnné. Druhé a třetí vlákno proměnnou flag nepřečtou znovu, přestože se pointer na ni předává ven a přestože se v cyklu volá funkce! Při -O2 a -O3 se to chová stejně. Co když teď zdrojový kód trochu pozněníme...?

    --- spin.c      2009-06-18 23:46:52.000000000 +0200
    +++ spin2.c     2009-06-18 23:51:55.000000000 +0200
    @@ -11,7 +11,7 @@
     static volatile int FLAG = 1;
    
     static void foo( void ) {}
    -static void FOO( int * something ) { useless = *something; }
    +static void FOO( int * something ) { ++( *something ); }
    
     static void * t1( void * param ) { while ( flag ); puts( "T1 finished." ); return NULL; }
     static void * t2( void * param ) { while ( flag ) foo(); puts( "T2 finished." ); return NULL; }

    Teď jde opravdu do tuhého a nastává zmatek, jaký jsem ještě neviděl.

    [andrej@argos pokusy]$ gcc -O0 -pthread spin2.c -o spin
    [andrej@argos pokusy]$ ./spin
    Spawning threads.
    Setting the death flag.
    T4 finished.
    T5 finished.
    T2 finished.
    T1 finished.
    That's the end.

    Naprosto špatně. Vlákno T3 by mělo skončit první, ale neskončí vůbec. Proměnnou v cyklu nečte i přesto, že je vypnutá optimalizace a že ji předává jiné funkci, která ji pozmění! Dobře, zkusme optimalizaci zapnout.

    [andrej@argos pokusy]$ gcc -O1 -pthread spin2.c -o spin
    [andrej@argos pokusy]$ ./spin
    Spawning threads.
    T3 finished.
    Setting the death flag.
    T4 finished.
    T5 finished.
    That's the end.

    Ano, takhle to má podle předpokladů (ne)fungovat... O stupeň vyšší optimalizace se chová stejně. Ale u -O3 číhá ošklivé překvapení:

    [andrej@argos pokusy]$ gcc -O3 -pthread spin2.c -o spin
    [andrej@argos pokusy]$ ./spin
    Spawning threads.
    T3 finished.
    T1 finished.
    Setting the death flag.
    T4 finished.
    T5 finished.
    That's the end.

    Tohle už je opravdu těžko vysvětlitelné. Teď znovu vyzkouším první zdrojový kód, jen s jiným kompilátorem:

    [andrej@argos pokusy]$ icc -O0 -pthread spin.c -o spin
    [andrej@argos pokusy]$ ./spin
    Spawning threads.
    Setting the death flag.
    T4 finished.
    T5 finished.
    T3 finished.
    T2 finished.
    T1 finished.
    That's the end.

    Tady žádné překvapení nečíhá.

    [andrej@argos pokusy]$ icc -O1 -pthread spin.c -o spin
    [andrej@argos pokusy]$ ./spin
    Spawning threads.
    T1 finished.
    T2 finished.
    T3 finished.
    Setting the death flag.
    T5 finished.
    T4 finished.
    That's the end.

    Ale toto je prosím pěkně neuvěřitelné. Kompilátor přesunul přiřazení globální proměnné přes několik volání funkcí! Netvrdil tu někdo před chvílí, že to není možné? Jedině volatile proměnná byla přiřazena (a přečtena) ve správnou dobu. Vyšší stupně optimalizace se chovají stejně.

    Nyní znovu vyzkouším pozměněný zdroják s kompilátorem Intel.

    [andrej@argos pokusy]$ icc -O0 -pthread spin2.c -o spin
    [andrej@argos pokusy]$ ./spin
    Spawning threads.
    Setting the death flag.
    T2 finished.
    T1 finished.
    T4 finished.
    T5 finished.
    T3 finished.
    That's the end.

    Toto je další odlišnost od GCC. Tentokrát skončila všechna vlákna. Nicméně vyšší úrovně optimalizace přinesou další překvapení:

    [andrej@argos pokusy]$ icc -O1 -pthread spin2.c -o spin
    [andrej@argos pokusy]$ ./spin
    Spawning threads.
    T2 finished.
    T1 finished.
    Setting the death flag.
    T5 finished.
    T4 finished.
    That's the end.

    Další podivný výsledek. Jedno z vláken neskončilo a tento stav se nepodobá žádnému z předchozích.

    [andrej@argos pokusy]$ icc -O2 -pthread spin2.c -o spin
    [andrej@argos pokusy]$ ./spin
    Spawning threads.
    T1 finished.
    T2 finished.
    T3 finished.
    Setting the death flag.
    T4 finished.
    T5 finished.
    That's the end.

    A zvýšení stupně optimalizace to zase dá do pořádku, přestože jde o jiný kompilátor a jiný stupeň optimalizace. Třetí stupeň se chová stejně.

    Jaké je ponaučení z tohoto pokusu?

    1. Kompilátor umí optimalizovat přístupy do paměti přes volání jakýchkoliv (i knihovních!) funkcí.
    2. Přes volání statických funkcí optimalizuje kompilátor skoro vždy a nelze tomu zabránít.

    3. Schopnost gcc zjistit, zda je volané funkci předáván pointer na danou proměnnou a zda ji tato volaná funkce může nebo nemůže změnit, zjevně závisí na zvolené úrovni optimalizace. (!!!) Výsledky z GCC po aplikaci patche to jasně ukazují. Tohle se vůbec netýká vláken a mohl by to být bug v kompilátoru.

    A jeden fakt na závěr: Vlákna čtoucí volatile proměnnou neselhala ani jednou. Ostatní selhala téměř vždy, přestože podle většiny odpovědí v této diskusi by selhat neměla!

    Jestliže kompilátor dokáže přesouvat přiřazení globální proměnné přes několik systémových volání, přes vytváření vláken a dokonce i přes nanosleep(), jak má potom člověk věřit, že je nebude přesouvat přes synchronizační primitiva?

    Když může přiřazení do globální proměnné přeskočit nanosleep(), proč by nemohlo přeskočit pthread_cond_wait() nebo pthread_cond_signal()? Jak je možné, že vůbec nějaká synchronizace funguje? Možná mi budete spílat, ale pro mě je závěr z tohoto pokusu jednoznačný: volatile vždy a všude!

    Tak teď jsem z toho jelen.

    19.6.2009 01:21 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Errata a několik postřehů

    Je zjevné, že kompilátor Intel někdy přesouvá přiřazení globální proměnné směrem nahoru i přes několik volání funkcí.

    Nicméně GCC jsem hodně křivdil a byla to moje chyba. V tom pozměněném zdrojáku je inkrementace té proměnné. Tedy jde o naprosto normální race condition, která může dopadnout pokaždé jinak a samozřejmě závisí na zvolené optimalizaci. Tedy za humbuk kolem vlákna T3 se moc omlouvám. Tak to dopadá, když člověk dělá zoufalé pokusy pozdě v noci.

    Je možné, ne-li pravděpodobné, že GCC přiřazení do proměnné flag buď přesune směrem dolů, nebo zcela vynechá, protože flag už funkce main() po přiřazení nikde nečte. FLAG je volatile, tedy se vždy správně přiřadí.

    Podivné chování vláken při některých optimalizacích se ovšem nedá vysvětlit jinak než tím, že kompilátor přesouvá přiřazení globální proměnné přes několik volání funkcí. Fakt je tohle korektní? Kde mám vzít jistotu, že to přiřazení nepřesune přes pthread_mutex_lock() a pthread_mutex_unlock()???

    Některé výsledky mého pokusu možná přece jen budou mít racionální vysvětlení a už mě to tolik neděsí. :-D Nicméně pořád jsem z toho dost (nepříjemně) překvapený. Svět prostě nefunguje tak, jak jsem si dlouhou dobu představoval. Že jsem zatím při programování na žádnou z těchto ošklivých situací nenarazil, to je prostě obrovská šťastná náhoda. Jinak si to neumím vysvětlit.

    Například provedu-li tohle,

    pthread_mutex_lock( &queueMutex );
    ++itemsAvailable;
    pthread_cond_signal( &queueCond );
    pthread_mutex_unlock( &queueMutex );
    

    kde vezmu jistotu, že kompilátor neprovede inkrementaci někdy před pthread_mutex_lock() nebo po pthread_mutex_unlock()? Obojí by mělo fatální následky. Čím přesně je zaručeno, že se to nestane? Jinými slovy, čím přesně se ty dvě zmíněné funkce liší od nanosleep(), pthread_ceate(), puts() a dalších, které kompilátor klidně zpřehází a přesune přes ně přiřazení?

    19.6.2009 01:22 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.

    Proletl jsem jenom zacatek a nikde nevidim, ze byste pri manipulaci s flag/FLAG promennymi pouzival zamky. Vyrobil jste priklad na nedefinovane chovani a pak vam z toho celkem pochopitelne muze lezt naprosto cokoliv. Resenim neni zahodit zamykani a ponechat volatile, nybrz presny opak: zahodit volatile a ponechat zamky.

    Co se tyce prvni kompilace, ktera podle vas dava "neocekavane" vysledky (tzn. celkem druha), deje se pravdepodobne to, ze prekladac vidi definice foo()/FOO() a spocita si, ze promenna flag se nemuze zmenit, proto jeji opakovane nacitani v 1-3 vlaknu odoptimalizuje. Prekladac o dalsich vlaknech nic netusi, proto pristup k flag musite obalit zamkem. Ale jak rikam, to co jste napsal je ukazkove nedefinovane chovani, takze i kdyby vam to vyplivlo kompletni dilo pana JiKa, bylo by to zcela v poradku.

    Zbytek jsem neresil.

    19.6.2009 02:19 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.

    Pokud kompilátor zjevně přesouvá přístup k flag před nanosleep() i před puts(), kde je jistota, že ho nepřesune před pthread_mutex_lock()? Čím je volání té zamykací funkce tak speciální? Tohle pořád ještě nechápu.

    19.6.2009 10:00 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.
    Čím je volání té zamykací funkce tak speciální? Tohle pořád ještě nechápu.
    Ta funkce musí vytvořit paměťovou bariéru, aby nemohl instrukce přeskládat procesor. A o tom by zřejmě měl vědět i kompilátor, a také by neměl přehazovat instrukce přes volání této funkce. Jak je to zařízené prakticky u C/C++ nevím, každopádně synchronizační funkce nemohou být pro kompilátor funkce jako kterékoli jiné, musí s nimi zacházet speciálním způsobem.
    19.6.2009 12:55 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.

    Jak se tomu postupně snažím porozumět, mám dojem, že je to naopak. Kompilátor nemá seznam funkcí, ke kterým se musí chovat speciálně. Má seznam s opačným významem.

    Na tom seznamu jsou funkce, které zaručeně nikdy nemohou mít pozorovatelný vedlejší efekt. Mezi tyto „bezpečné“ funkce patří například putchar(), puts() a jim podobné. Všechny ostatní funkce jsou zpočátku implicitně „nebezpečné“. U těch, jejichž kód má kompilátor bezprostředně k dispozici v době překladu, se pokusí určit, zda je lze považovat za bezpečné. Ty, jejichž kód není k dispozici nebo u nichž analýza aliasů nedává stoprocentní záruky bezpečnosti, jsou i nadále považovány za nebezpečné.

    Tedy zjednodušeně řečeno, pthread_mutex_lock() a moje_funkce_z_dynamické_knihovny() jsou z pohledu kompilátoru obě stejně nebezpečné a k oběma se musí přistupovat v rukavičkách, zejména pokud jde o paměťové operace kolem jejich volání.

    Pokud lze pthread_mutex_lock() inlinovat, není to žádný problém. Obsahuje totiž kus asm volatile, který zcela zjevně dělá něco s pamětí. Tedy kompilátor může inlinovat, ale rozhodně si nedovolí příslušnou paměťovou bariéru zpřeházet s ostatními instrukcemi. A už vůbec si netroufne vynechat okolní čtení z paměti.

    Nejjednodušším případem je funkce static void myFunction( void ) { puts( "Stupid kidding" ); }. Tu lze kompletně zanalyzovat, označit za bezpečnou a paměťové operace kolem jejího volání přehazovat či vynechávat jakkoliv dle libovůle.

    Ještě bych rád uvedl jeden zajímavý případ: Co když standardní výstup vede na vstup jiného programu, který má s tím naším sdílenou paměť? Co když ten druhý program v reakci na zprávu zaslanou pomocí puts() zapíše něco do sdílené paměti? Co když zapisuje právě na místo, kde leží řídící proměnná cyklu volajícího puts()? Odpověď je jednoduchá: Výsledek něčeho takového je nedefinovaný, stejně jako jakýkoliv jiný nesynchronizovaný přístup ke sdíleným datům. O tom jsem se koneckonců (i když ne tak krkolomně) sám přesvědčil v mém pokusu níže.

    Výše uvedený případ je podle mě (jen tak mimochodem) jedním z vzácných případů, kdy by se místo (náročné meziprocesové) synchronizace hodilo řídící proměnnou prostě označit (z pohledu obou procesů) jako volatile. Tím se samozřejmě nedosáhne ani synchronizace přístupů k proměnné ani atomicity změn. Pouze se zajistí viditelnost těch změn.

    To všechno ale nic nemění na faktu, že tam, kde už používám (mutex | barrier | rwlock | mutex + condvar) nepotřebuji volatile vůbec.

    Ale pořád nevím, jestli je tohle všechno pravda a jestli to už konečně chápu správně.

    19.6.2009 13:04 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.
    To všechno ale nic nemění na faktu, že tam, kde už používám (mutex | barrier | rwlock | mutex + condvar) nepotřebuji volatile vůbec.
    I kdyby tomu tak v současnosti bylo, nemůžeš se spolehnout, že to tak bude vždycky.
    Quando omni flunkus moritati
    19.6.2009 13:45 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.

    Dokud bude v pthread_mutex_lock() několik speciálních instrukcí v asm volatile, můžu se na to spolehnout, ať už se inlinuje nebo ne. A těch pár instrukcí tam bude vždycky.

    19.6.2009 14:40 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.
    A jsi si jistý, že novější verze překladače si neřekne "OK, tyhle speciální instrukce nemají s tamtou proměnnou nic společného, takže můžu vesele optimalizovat" ?
    Quando omni flunkus moritati
    19.6.2009 16:22 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.
    To si žádná verze překladače říct nemůže. Protože ty instrukce říkají mimo jiné „co se dělo před touto instrukcí a co se bude tím po ní nelze pomíchat“. Primárně je to zpráva pro procesor, ale překladač se tím samozřejmě musí řídit také.
    19.6.2009 16:45 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.
    Protože ty instrukce říkají mimo jiné „co se dělo před touto instrukcí a co se bude tím po ní nelze pomíchat“.
    Což nemusí vždy být relevantní. Příklad:
    1. zápis proměnné (hodnota původně v registru)
    2. co, se dělo před touto instrukcí, to se nesmí pomíchat s tím, co se dělo po ní
    3. čtení stejné proměnné
    Není-li proměnná volatile, není potřeba ji číst z paměti, když její hodnota stále bude v tom registru. Nic se nepomíchalo, ale program je stejně náchylný k chybě, když se hodnota změní z jiného vlákna.
    Quando omni flunkus moritati
    19.6.2009 17:05 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.
    Ty instrukce dále říkají, že procesor musí synchronizovat hodnotu té proměnné s hlavní pamětí (a nemůže použít svou lokální cache). I tím se musí řídit překladač.
    19.6.2009 17:13 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.
    Z povinnosti synchronizovat hodnotu s hlavní pamětí IMO nevyplývá, že hodnoty uložené v registrech nyní neplatí.
    Quando omni flunkus moritati
    19.6.2009 17:29 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.
    Nevyplývá. Ale také z toho nevyplývá, že ty hodnoty platí. A kompilátor (i procesor) musí očekávat ten horší případ. Ta instrukce říká „zaručeně platná je teď pouze hodnota v hlavní paměti, jakékoliv keše je nutné obnovit“ – a hodnota uložená v registru je právě takovou keší.
    20.6.2009 06:43 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.

    Je zajímavé si přečíst, jak se u kusů assembleru označují registry, které ten assembler mění. Dá se taky naprosto explicitně říct: Tento kus assembleru mění paměť, bacha na něj! To samozřejmě ještě nezaručuje, že příslušný assembler nebude vynechán. Nicméně pokud má blok assembleru v clobber listu "memory" a zároveň je označen jako volatile, není žádná šance, že by ho kompilátor mohl vynechat nebo zpřeházet. (Nehledě na to, že ke speciálním instrukcím v něm obsaženým by se tak či tak měl chovat slušně.)

    Povídání o clobber listu je tady. A tady je něco o asm volatile.

    Je jisté, že každé synchronizační primitivum musí takový blok assembleru použít. Pak je ovšem úplně šumák, jestli je to primitivum implementováno jako makro, jako statická funkce, jako funkce z dynamické knihovny nebo jako ošklivý assemblerový hack. Žádná proměnná nemusí být volatile, pokud se k ní přistupuje vždy pouze pod ochranou příslušného primitiva.

    19.6.2009 16:34 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.
    Jak se tomu postupně snažím porozumět, mám dojem, že je to naopak. Kompilátor nemá seznam funkcí, ke kterým se musí chovat speciálně. Má seznam s opačným významem.
    Jasně, zapomněl jsem, že kompilátor v době překladu nezná „instrukční obsah“ všech volaných funkcí.

    Ještě by mne zajímalo, jak je to s tím vícevláknovým přístupem. Zda kompilátor musí předpokládat i ovlivnění z jiného vlákna (a může tedy optimalizovat jen lokální proměnné, jejichž adresu prokazatelně nikdo nezná, a např. předpokládá, že volající dodržuje standard jazyka C, i když to nemusí být Céčkový kód), nebo zda se o vlákna nezajímá, a očekává, že pokud může být proměnná ovlivněna z jiného vlákna, musí programátor explicitně použít bariéry.
    19.6.2009 02:32 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.

    Ještě drobná poznámka: Ani náznakem jsem nemluvil o tom, že bych snad chtěl zahodit zamykání. Pouze mi není jasné, jak může to zamykání korektně fungovat bez volatile.

    Například tenhle kus kódu, který jsem už uváděl výše:

    pthread_mutex_lock( &queueMutex );
    ++itemsAvailable;
    pthread_cond_signal( &queueCond );
    pthread_mutex_unlock( &queueMutex );
    

    K čemu je mi zamykání, pokud itemsAvailable není volatile? Bez volatile kompilátor klidně může přesunout inkrementaci před pthread_mutex_lock() nebo za pthread_mutex_unlock(), jak se mu to hodí.

    Já vím, tvrdíte, že podle standardu to udělat nemůže. Nicméně v mých pokusech naprosto zjevně přesunul přiřazení přes volání funkce nanosleep(). Tudíž jsou dvě možnosti: Buď je možné naprosto cokoliv, nebo jsou volání funkcí ovládajících mutex něčím odlišná a jakýmsi záhadným způsobem přinutí kompilátor chovat se jinak.

    19.6.2009 02:55 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.

    Přece jen ještě do třetice: Chápu-li to správně, platí tohle:

    Pokud přiřazuji do globální proměnné flag bez zamykání, nikdo mi negarantuje, kdy přesně (a v jakém pořadí) uvidím tyto změny v ostatních vláknech.

    OK, jestli je tohle pravda, zní to rozumně. Nicméně například mutexy nejsou součástí jazyka ani kompilátoru. Jedná se jen o obyčejná volání funkcí. Jak mi tedy mutex (použitý bez volatile) může správnou „viditelnost“ zaručit? Co když ji prostě zaručit nedokáže? Má snad kompilátor někde schovaný seznam funkcí, jejichž volání musí být memory fence? Podle mě žádný takový seznam nemá.

    19.6.2009 03:30 zha
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.
    Kompilátor umí optimalizovat přístupy do paměti přes volání jakýchkoliv (i knihovních!) funkcí.
    V těch cyklech není volání žádné funkce, kterou by neznal.

    Přes volání statických funkcí optimalizuje kompilátor skoro vždy a nelze tomu zabránít.
    Pokud přesně ví, co dělají, tak proč by nemohl.

    Schopnost gcc zjistit, zda je volané funkci předáván pointer na danou proměnnou a zda ji tato volaná funkce může nebo nemůže změnit...
    Toto už jsi zjistil sám. Test se změnou hodnoty je špatně.

    Vlákna čtoucí volatile proměnnou neselhala ani jednou. Ostatní selhala téměř vždy...
    O volatile nikdo nepochybuje, ostatní jsi napsal/vyhodnotil špatně.

    Jestliže kompilátor dokáže přesouvat přiřazení globální proměnné přes několik systémových volání...
    Pořadí v jakém se ty hlášky vypisují a pořadí v jakém je vydíš, jsou dvě různé věci. Pochybuji o tom, že se něco přesunulo.
    19.6.2009 05:27 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Tak tohle je průšvih.

    Pozor, můj vlastní příspěvek, na který reaguji, už není aktuální. Zde jsem své chybné závěry z tohoto pokusu uvedl na pravou míru. Omlouvám se za tento omyl.

    19.6.2009 05:26 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše A přece jen je to ještě trochu jinak!

    Tak, celou noc jsem pročítal assembler, který vznikal z různých pokusných programů. Už začínám tušit, jak moc jsem se mýlil a jak špatně jsem pochopil výsledky mého výše uvedeného pokusu. Vše dále popsané jsem pro jistotu zkoušel pouze s -O3.

    Nic se nepřesunulo přes nanosleep(). Zdání něčeho takového vzniklo prostě takto:

    1. Kompilátor zjistil, že v celém programu jen main() přiřazuje do proměnné flag a přiřazuje tam konstantu.
    2. Proto z proměnné flag udělal konstantu s (poslední) hodnotou 0.
    3. Proto právě ta vlákna, která končila podezřele brzy, žádný spinlock v sobě neměla. Optimalizace ho zrušila. ;-)

    Další dvě důležité poznámky:

    1. Stačilo do kterékoliv další funkce přidat přiřazení do flag a najednou všechny funkce poctivě flag četly. Sice pouze jednou, ale přece. (V důsledku toho pak vlákna neskončila vůbec, zůstala ve spinlocku.)
    2. Stačilo do cyklu přidat volání nějaké nedefinované funkce a proměnná flag se pak už četla znovu v každém cyklu. Zajímavé je, že například puts() a putchar() čtení v cyklu nezpůsobily, zatímco pthread_yield(), pthread_mutex_lock() nebo something_undefined() ano. Kompilátor tedy skutečně má nějaký interní seznam „bezpečných“ funkcí a všechny ostatní považuje implicitně za „nebezpečné“.

    Teď už asi konečně chápu, proč ta synchronizační primitiva fungují i bez volatile. Může se k nim přistupovat pouze dvěma způsoby:

    • Pomocí funkcí, které jsou z dynamicky linkované knihovny. Takové funkce kompilátor neinlinuje a dokonce se zdá, že jejich volání je v assembleru vždy přesně na požadovaném místě. Svůj hloupý omyl s pořadím nanosleep() jsem si už ujasnil a jinou záměnu pořadí jsem zatím nepozoroval.
    • Pomocí funkcí, které lze inlinovat. Takové funkce ovšem vždy obsahují nějaký vhodný blok asm volatile s náležitými synchronizačními instrukcemi. Tudíž ani v tomto případě není důvod obávat se změny pořadí.

    Můj celkový závěr (po důkladné revizi a úspěšném pochopení výsledků mého pokusu) tedy zní: Sinuhet měl pravdu a já jsem se mýlil. Skutečně není třeba používat volatile současně se synchronizačními primitivy.

    Děkuji všem za věcnou a zajímavou diskusi a zejména Sinuhetovi za objasnění celé záležitosti. Pro mě z toho plyne poučení, že jsem si měl napřed přečíst assembler a teprve potom dělat závěry ohledně výstupu mého pokusného programu.

    19.6.2009 11:54 Atom321 | skóre: 20
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!
    Až totéž přeložíte s jinou implementací pthread, která bude inlinovat pthread_mutex_lock(), budete se šeredně divit.
    19.6.2009 12:19 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!

    Klidně se může inlinovat pthread_mutex_lock(). Kde je problém? Ten pthread_mutex_lock() v sobě někde obsahuje kus kódu typu asm volatile, jak už jsem psal. A tento kód zajistí nejen paměťovou bariéru, ale i slušné chování kompilátoru, který pak důležité instrukce nemůže libovolně zpřeházet mezi ostatními.

    Takže čemu bych se pak měl divit? Pointa celé věci je v tom, že to synchronizační primitivum už v sobě obsahuje nějaký typ paměťové bariéry. (A ta se bude spolu s ním samozřejmě taktéž inlinovat, když už se (v nějaké hypotetické implementaci) inlinuje.) Součástí implementace té bariéry může (a nemusí) být nějaká proměnná s volatile přístupem. Nicméně uživatele toho synchronizačního primitiva to vůbec nemusí zajímat. On sám volatile nepotřebuje.

    Zajímavé případy použití (a zneužití) volatile jsou vysvětlené ve skvělém blogpostu, na který odkazuje kolega zde v diskusi.

    Dobře, třeba máte pravdu, třeba se už zase mýlím. (Nebylo by to dnes poprvé.) Ale mohl byste tedy uvést konkrétní příklad kompilátoru splňujícího normu C[++] a operačního systému splňujícího normu POSIX, jejichž kombinace povede k nefunkční synchronizaci, pokud nepoužiji volatile? Jedině pokud taková kombinace existuje, uvěřím, že je použití volatile (například kvůli přenositelnosti) nutné.

    Zatím to ovšem stále vidím tak, že mé původní přesvědčení bylo chybné a že podmínkovou proměnnou není třeba kombinovat s volatile.

    19.6.2009 14:35 Atom321 | skóre: 20
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!
    Synchronizační primitiva nemají s volatile proměnnou vůbec nic společného. Nikde není definováno, že by tomu tak mělo být. Máte snad v manuálu k pthread_mutex_lock() a pthread_cond_wait() napsáno, že za všech okolností musí dělat paměťovou bariéru? To že to v současné implementaci (a možná ve všech současných implementacích) funguje je prostě a jednoduše náhoda. Nikde nemáte zaručeno, že se to chování nemůže změnit.
    20.6.2009 00:19 Ivan
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!

    Presne tak. pthread_mutex_lock je inline fce ktera evidentne nemodifikuje obsah volatile promenne, ani nemuze, protoze netusi kde a jak je volatile promenna ulozena. Proto kompilator optimalizovat cteni z pameti.

    Takovehle "optimalizace", ktere funguji v 99.99% pripadu me uz staly desitky hodin zivota. Kdyby na miste pthread_cond_wait bylo volano makro preprocessoru, tak nemuzete o tom zdrojaku rict vubec nic.

     

    20.6.2009 00:57 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!
    Presne tak. pthread_mutex_lock je inline fce ktera evidentne nemodifikuje obsah volatile promenne, ani nemuze, protoze netusi kde a jak je volatile promenna ulozena. Proto kompilator optimalizovat cteni z pameti.

    To je úplně jedno, co modifikuje. Ta funkce obsahuje asm volatile se synchronizační instrukcí. Jakmile kompilátor takovou instrukci uvidí, nikdy si nedovolí ji nějak přeuspořádat. Taková instrukce se prostě vždy považuje za nebezpečnou a nikdy se nevynechá. Je přitom úplně jedno, jestli se celá ta funkce inlinuje nebo ne.

    Takovehle "optimalizace", ktere funguji v 99.99% pripadu me uz staly desitky hodin zivota. Kdyby na miste pthread_cond_wait bylo volano makro preprocessoru, tak nemuzete o tom zdrojaku rict vubec nic.

    To je prosím pěkně holý nesmysl. Je třeba si uvědomit, že překladač žádná makra nezná. Překladač prostě zkoumá až to, co leze z preprocesoru, a je mu zoufale fuk, jestli to vzniklo z maker nebo ne. Ta „funkce“ klidně může být makro. Pokud to bude správné a funkční makro, které dělá přesně to, co má, bude rozhodně obsahovat asm volatile a v něm vhodnou synchronizační instrukci. Je to opět tentýž případ: Proměnnou, ke které přistupuji vždy pod mutexem, není třeba označovat jako volatile.

    19.6.2009 12:42 mich | skóre: 16
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!
    Taky jsem dnes v noci pročítal assembler co z těch vašich příkladů vylezl, jsem rád, že když už jste mučil mě, tak sebe taky:-)
    1. Kompilátor zjistil, že v celém programu jen main() přiřazuje do proměnné flag a přiřazuje tam konstantu.
    2. Proto z proměnné flag udělal konstantu s (poslední) hodnotou 0.
    3. Proto právě ta vlákna, která končila podezřele brzy, žádný spinlock v sobě neměla. Optimalizace ho zrušila. ;-)
    Právě stahuju vaše oblíbené icc, protože gcc mi rozhodně nic takového neprovádí. Pokud vláknem, které skončilo dříve myslíte t3 ve spin2.c, tak podle mě je to proto, že kompilátor znal fci FOO (a v té flag časem dojde do 0) a ne proto, že by main časem přiřadil do flag nulu. Doteď mi to přišlo rozmně vysvětlitelné, tak mi v tom nedělejte zmatek ;-).
    je to teď v módě, na žive o tom furt píšou
    19.6.2009 13:27 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!

     

    Právě stahuju vaše oblíbené icc, protože gcc mi rozhodně nic takového neprovádí.

    To souhlasí. Tuto optimalizaci jsem viděl pouze v ICC, nikoliv v GCC.

    Pokud vláknem, které skončilo dříve myslíte t3 ve spin2.c, tak podle mě je to proto, že kompilátor znal fci FOO (a v té flag časem dojde do 0) a ne proto, že by main časem přiřadil do flag nulu.

    Vlákno t3() je můj hloupý omyl. Ten test je špatně navržený a je tam race condition. V případě gcc a -O0 čirou náhodou t3() zvítězí nad main() a jak tak usilovně inkrementuje flag, nepostřehne, že tam main() přiřadí nulu. (K přiřazení nuly dojde mezi testem a inkrementací.) Tedy uvázne.

    V případě -O1 t3() vůbec nebude flag inkrementovat. Z kompilátoru v tomto případě vypadne taková podivná slátanina, která tam automaticky předpokládá nulu. Pokud je vaše domněnka správná, počítá kompilátor s přetečením při inkrementaci. To je zvláštní. Počítal by s tím i u 64-bitového čísla? To by tedy byla pořádná optimalizace! Ale nejspíš to tak bude...

    Doteď mi to přišlo rozmně vysvětlitelné, tak mi v tom nedělejte zmatek ;-).

    Mně to doteď taky přišlo vysvětlitelné, ale bylo to pouze tím, že jsem to chápal nesprávně. :-D Zmatek, kterému teď čelím, se mi zdá být přece jen o něco blíž pravdě.

     

    19.6.2009 13:03 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!
    Kompilátor zjistil, že v celém programu jen main() přiřazuje do proměnné flag a přiřazuje tam konstantu. Proto z proměnné flag udělal konstantu s (poslední) hodnotou 0.
    Vzhledem k tomu, že program startuje s touto proměnnou přednastavenou na 1 a neví, kdy jsou volány funkce tX, pak tuto optimalizaci lze považovat za poměrně nekorektní.
    Quando omni flunkus moritati
    19.6.2009 13:39 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!

    Možná. Ale assembler to říká jasně. Žádný while cyklus tam u -O3 prostě není, žádný spinlock. Pouze se rovnou zavolá puts(). Striktně vzato, celý ten zdroják je hodně nekorektní, takže se není čemu divit, když dává nekorektní výstup.

    Chování kompilátoru je přece jen aspoň zčásti pochopitelné. Z jeho pohledu jediné, co kdy poběží, je main(). Tedy vše, co main() přiřadí do globálních proměnných, musí nutně být konečné. Ostatní funkce, které tam jsou, se nikde nevolají a nikde do globálních proměnných nezapisují. Kompilátor by za normálních okolností takové funkce zcela vypustil.

    Jenže... Pointery na ty funkce se používají k inicializaci nějakého pole, které se pak čte. Tudíž funkce nelze jen tak vyškrtnout. Dobře tedy, kompilátor je tam nacpe. Nicméně optimalizuje je takovým způsobem, jako by před jejich voláním proběhl kompletně main(). Vzhledem k tomu, že ty funkce jsou všechny statické a nelze je po linkování volat odjinud, jde o (téměř) pochopitelný předpoklad. Není ani správný, ani rozumný, ale je prostě pochopitelný. Kompilátor nebere v potaz, že z těch funkcí někdo uprostřed main() udělá vlákna. To prostě není jeho starost.

    19.6.2009 15:22 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!
    Striktně vzato, celý ten zdroják je hodně nekorektní, takže se není čemu divit, když dává nekorektní výstup.
    Ne. Tady překladač prostě naprosto jasně optimalizoval až moc. Řekněme, že by pthread_create nebyla knihovní funkce, ale moje vlastní funkce, která tři parametry zahodí a jeden vezme jako ukazatel na funkci, tu spustí a počká až doběhne. To všechno v hlavním vlákně programu.

    V takovém případě mám jasné právo očekávat, že ve flag/FLAG bude jednička, protože funkci volám před tím přiřazením. Optimalizace flag = vždy 0 tedy není korektní.
    Ostatní funkce, které tam jsou, se nikde nevolají a nikde do globálních proměnných nezapisují. Kompilátor by za normálních okolností takové funkce zcela vypustil.
    Na to se nemůžeš spolehnout. Minimálně jedna verze GCC to nedělá.
    Nicméně optimalizuje je takovým způsobem, jako by před jejich voláním proběhl kompletně main(). Vzhledem k tomu, že ty funkce jsou všechny statické a nelze je po linkování volat odjinud, jde o (téměř) pochopitelný předpoklad. Není ani správný, ani rozumný, ale je prostě pochopitelný.
    Ne není. Jestliže dokončení main() znamená ukončení programu, pak optimalizovat, jako by před jejich voláním kompletně proběhl main(), je blbost. (A mám pocit, že tohle chování sis domyslel, přestože překladač ve skutečnosti dělá něco jiného.)
    Quando omni flunkus moritati
    19.6.2009 21:15 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!
    (A mám pocit, že tohle chování sis domyslel, přestože překladač ve skutečnosti dělá něco jiného.)

    Píšu, co jsem viděl v assembleru. Může to být bug v kompilátoru, ale tohle si vůbec netroufám posoudit.

    21.6.2009 19:47 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!

    Neni to bug, vas program obsahuje nedefinovane chovani, takze zadny vystup nemuze byt spatne. Kompilator je komplexni hromada kodu, a kdyz mu podrazite nohy porusenim nekterych predpokladu, tak se muzete dockat necekanych vysledku.

    19.6.2009 16:12 zha
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!
    Nic se nepřesunulo přes nanosleep(). Zdání něčeho takového vzniklo prostě takto:
    1. Kompilátor zjistil, že v celém programu jen main() přiřazuje do proměnné flag a přiřazuje tam konstantu.
    2. Proto z proměnné flag udělal konstantu s (poslední) hodnotou 0.
    3. Proto právě ta vlákna, která končila podezřele brzy, žádný spinlock v sobě neměla. Optimalizace ho zrušila.
    Především, opakuji, pořadí těch textů je výsledek toho, jak se jednotlivá vlákna poprala při jejich vypisování. O řazení/časování kódu kolem to neříká nic.
    19.6.2009 16:19 zha
    Rozbalit Rozbalit vše Re: A přece jen je to ještě trochu jinak!
    PLUS, pokud jde o ty příčiny, tak stejně jako kompilátor zjistil to přiřazení, tak taky asi zjistil referenci těch funkcí ještě před tím a určite tu inicializaci úplně na začátku ;) Ty spiny se vyhodily, protože nic nedělají.
    19.6.2009 13:18 Jose
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Úplně jednoduše : pokud k proměnné přistupuje více vláken musí být tato volatile, pokud navíc nad ní jsou prováděny neatomické operace musí být tyto kryty zámky.
    19.6.2009 13:42 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Můžete prosím uvést nějaký příklad kódu, kde synchronizační primitiva selžou kvůli nepřítomnosti volatile?

    19.6.2009 13:57 Jose
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Není mi úplně jasné otázka, volatile a zámek jsou úplně odlišné věci. Volatile řídí způsob chování překladače, zámek způsob přerušování kódu. Zámek samosřejmě neselže kvůli nepřítomnosti volatile.
    19.6.2009 14:21 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

     

    Zámek samosřejmě neselže kvůli nepřítomnosti volatile.

    Pokud tedy přistupuje k proměnné více vláken a vždy se tak děje pouze pod zámkem, tato proměnná nemusí být volatile. (Selháním zámku rozumím (například) existenci nekonzistentního stavu proměnné v okamžiku, kdy něko zámek získá.)

     

    19.6.2009 14:39 Jose
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    první funkční varianta :

    volatile int a; int i;

    int firstfibre () { a = 0; for (i = 0; i < xx; i++) { if (a == 1) foo (a); } }

    int secondfibre () { a = 1; }

    druhá nefunkční varianta :

    int a;

    int firstfibre () { LOOCK a = 0; for (i = 0; i < xx; i++) { if (a == 1) foo (a); } UNLOCK }

    int secondfibre () { a = 1; }

    Jde o to, že pokud není volatile tak překladač může optimalizovat na tento tvar : int firstfibre () {LOCK a = 0; i = xx; UNLOCK }
    19.6.2009 14:48 Atom321 | skóre: 20
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Pochopte už konečně, že volatile a zámky jsou naprosto nesouvisející věci. Funguje vám to prostě proto, že volání zamykacích funkcí vedou na externí knihovnu, o které kompilátor nic neví. Neví, jestli volaná funkce na tu proměnnou sahá. Proto obsah proměnné uloží do paměti tak, aby na ni sahat mohla a po návratu ji zase přečte. Shodou okolností je to implemetováno tak, že se změna projeví i ve druhém vláknu. Ale nikde není psáno, že to tak musí být ve všech implementacích a na všech architekturách. Tudíž musíte předpokládat, že tomu tak není.

    21.6.2009 21:30 l4m4
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Pochopte konečně, že tu jde o problém řízení přístupu k proměnné.

    Synchronizační primitiva a paměťové bariéry v nich obsažené umožňují tento problém vyřešit.

    To je celé.

    Je úplně jedno, zda kromě toho existuje nějaké volatile a co dělá.
    21.6.2009 22:30 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Psano to je.
    19.6.2009 15:27 trekker.dk | skóre: 72
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    Proměnná musí být volatile, protože to jediné skutečně zaručuje, že překladač neudělá nechtěné optimalizace. Jakékoliv jiné "řešení" se ti může ošklivě vymstít, protože může z ničeho nic přestat fungovat.
    Quando omni flunkus moritati
    19.6.2009 18:37 Ivan
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Samozrejme ze promenna MUSI byt volatile. Zamek nema zadny vliv na to kde a jak je promenna ulozena. Zamek akorat omezuje dobu kdy k ni muzete pristupovat. Po byste pouzil knihovnu boost a jeji atomicky decrement tak by tam ten zamek ani byt nemusel. volatile tam musi byt vzdy. To ze vam to funguje je jen shoda okolnosti. C++ kompilator nema jak zjistit ze se promenna itemsAvailable behem cyklu nemeni. Kdyby to vedel, tak by klidne tu podminku vyloucil a pro test "itemsAvailable==0" by nevygeneroval zadny kod.

    19.6.2009 21:26 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Ke třem reakcím výše:

    1. Vývojáři kompilátoru Intel a jeho knihoven jsou jiného názoru. Tedy je otázka, kdo má pravdu.
    2. Ještě jsem tu neviděl ani jeden příklad přeložitelného Cčkového zdrojáku, který přistupuje k nějaké proměnné vždy pod mutexem a zároveň selže kvůli nepřítomosti volatile. Nějaké pseudokódy jsou sice zajímavé, ale rád bych viděl zdroják, který takovou chybu jednoznačně prokáže. Dokud ten zdroják neuvidím (a jsem si už téměř jist, že ho neuvidím), budu věřit Sinuhetovi a jeho interpretaci celé věci.
    3. Funkčnost synchronizačních primitiv i bez volatile podle mě není žádná náhoda a žádný dočasný stav. Když se zamykací funkce inlinují, obsahují v sobě instrukce, které si nikdy žádný překladač (ani dnes, ani v budoucnu) nedovolí zpřeházet s okolními paměťovými operacemi. No a pokud se neinlinují, je to ještě jednodušší. Pak jde prostě o neznámé knihovní funkce, které nelze „odoptimalizovat“ nebo zavolat někdy jindy.
    19.6.2009 23:36 Jose
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    http://cs.wikipedia.org/wiki/Volatile

    Mimochodem, on ten zdroják který selže můžete také vidět až po mnoha příjemně strávených nocích které věnujete záhadně nechodícímu programu. Ta debata mi připadá jako kdybych argumentoval že přeci klidně skočím z okna jelikož nikdo z těch co mi to rozmlouvají zatím z okna reálně neskočil tudíž nemají o skákání z oken nejmenší potuchy.

    Jaký mají vývojáři kompilátoru od intelu názor mně zas tak nebere, intel není rozhodně jediný procesor a jejich překladač netvoří ani náhodou závaznou specifikaci jak se překladače mají chovat, dtto platí pro GCC. Právě kvůli tomu existuje od samého počátku specifikace volatile. Specifikace pro C/C++ umožňuje optimalizace přesunutím nebo vynecháním nadbytečného kódu, tím je to jasné.

    Mimochodem, pojem neznámá knihovní funkce zas není až tak jistý, jestli mně paměť neklame existují i překladače které mají přehled co se v knihovnách děje a klidně zoptimalizují i takovéto případy.
    20.6.2009 00:48 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

     

     

    Mimochodem, pojem neznámá knihovní funkce zas není až tak jistý, jestli mně paměť neklame existují i překladače které mají přehled co se v knihovnách děje a klidně zoptimalizují i takovéto případy.

    Ano. Příkladem takových překladačů jsou GCC a ICC. To ale pořád ještě neznamená, že podmínková proměnná musí být volatile.

     

    20.6.2009 01:13 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Ještě mám tak trochu off-topic poznámku. Není to náhodou v té Wikipedii trochu nepřesně?

    Podle mě jsou v podstatě tři kombinace:

    • Ukazatel na nestálou proměnnou: volatile int * pointer
      Tohle říká, že data, na která pointer ukazuje, se mohou kdykoliv změnit.
    • Nestálý ukazatel na proměnnou: int * volatile pointer
      Tohle říká, že samotný pointer se může kdykoliv změnit.
    • Nestálý ukazatel na nestálo proměnnou: volatile int * volatile pointer
      Tohle říká, že samotný pointer i proměnná, na kterou ukazuje, mohou doznat nečekaných změn.

    Z pohledu kompilátoru jsou druhá a třetí možnost v podstatě přesně totéž, protože když se může pointer nečekaně změnit, musí se tak či tak při každé jeho dereferenci znovu načíst z paměti nejen pointer samotný, ale i data, na která ukazuje. Jestli jsou referencovaná data označena jako volatile nebo ne, to už není příliš podstatné.

    20.6.2009 02:23 Michal Kubeček | skóre: 72 | Luštěnice
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?
    IMHO je nesmysl brát si Wikipedii, do které si může kdokoli napsat, co ho napadne, jako rozhodčího mezi dvěma hojně rozšířenými protichůdnými názory.
    21.6.2009 22:42 Sinuhet | skóre: 31
    Rozbalit Rozbalit vše Re: Má být podmínková proměnná volatile?

    Pokud by se pointer nezmenil (to lze zkontrolovat porovnanim s predchozi hodnotou), tak by se ve druhem pripade data znovu nacitat nemusela. Takze rozdil tam asi je, ale je to hodne za vlasy pritazeny priklad...

    20.6.2009 07:31 Andrej | skóre: 51 | blog: Republic of Mordor
    Rozbalit Rozbalit vše Další překvapivý příklad

    Ještě tu mám dva zdrojáky, ve kterých funkce volaná v odděleném vlákně mění lokální proměnnou jiného vlákna, na které závisí další běh programu.

    Napřed bez synchronizačních primitiv:

    #include <unistd.h>
    #include <stdio.h>
    #include <pthread.h>
    
    static void * setzero( void * param ) {
            *( (int *) param ) = 0;
            return NULL;
    }
    
    int main( void ) {
            int local = 1;
            pthread_t thread;
    
            pthread_create( &thread, NULL, setzero, &local );
            while ( local );
            pthread_join( thread, NULL );
            puts( "The thread has finished." );
            return 0;
    }

    Tohle dopadne zruba takto:

      -O0 -O1+
    GCC skončí zamrzne
    ICC skončí skončí

    Chování GCC je zcela podle předpokladů. Proměnná není není chráněna primitivem a zároveň není volatile, tudíž při zapnutí optimalizace původní záměr samozřejmě selže. Proč ale kód z ICC nezamrzne? Je to absurdní, ale ICC celý while cyklus odstraní!

    Naskýtá se otázka: Že by snad ICC analyzoval vytváření vláken a zjistil, co se může s tou proměnnou stát? Ne, to se neděje. Je snadné to dokázat: Když ve funkci setzero() přiřadíme místo nuly například dvojku, while cyklus bude odstraněn taky. A to je naprosto špatně. Je to bug v kompilátoru, nebo jen v mé hlavě? V assembleru je přesně toto:

            pushl     %eax                                          #14.42
            pushl     $setzero                                      #14.42
            pushl     $0                                            #14.42
            pushl     %edx                                          #14.42
            call      pthread_create                                #14.2
                                    # LOE ebx esi edi
    ..B1.2:                         # Preds ..B1.7
            pushl     $0                                            #16.24
            pushl     32(%esp)                                      #16.24
            call      pthread_join                                  #16.2

    Teď druhý kousek, tentokrát se synchronizačními primitivy.

    #include <unistd.h>
    #include <stdio.h>
    #include <pthread.h>
    
    static pthread_mutex_t mutex;
    static pthread_cond_t cond;
    
    static void * setzero( void * param ) {
            *( (int *) param ) = 0;
            pthread_cond_signal( &cond );
            return NULL;
    }
    
    int main( void ) {
            int local = 1;
            pthread_t thread;
    
            pthread_mutex_init( &mutex, NULL );
            pthread_cond_init( &cond, NULL );
            pthread_create( &thread, NULL, setzero, &local );
    
            pthread_mutex_lock( &mutex );
            while ( local ) pthread_cond_wait( &cond, &mutex );
            pthread_mutex_unlock( &mutex );
    
            pthread_join( thread, NULL );
            puts( "The thread has finished." );
            pthread_cond_destroy( &cond );
            pthread_mutex_destroy( &mutex );
            return 0;
    }
    

    Funkce setzero() vůbec nezamyká mutex, když signalizuje změnu podmínkové proměnné. Nicméně podle manuálové stránky je něco takového povoleno. Vše funguje zcela korektně pro všechny úrovně optimalizace u GCC i ICC. I z pohledu assembleru je vše v pořádku, lokální promměná v cyklu v main() se skutečně vždy načte znovu.

    Tady je fragment assembleru z kompilátoru Intel při -O3, který odpovídá té smyčce v main():

    ..B1.5:                         # Preds ..B1.20 ..B1.19
            movl      4(%esp), %eax                                 #22.10
            testl     %eax, %eax                                    #22.10
            je        ..B1.10       # Prob 10%                      #22.10
                                    # LOE ebx esi edi
    ..B1.7:                         # Preds ..B1.5
            pushl     $mutex.0                                      #22.44
            pushl     $cond.0                                       #22.44
            call      pthread_cond_wait                             #22.18
                                    # LOE ebx esi edi
    ..B1.20:                        # Preds ..B1.7
            addl      $8, %esp                                      #22.18
            jmp       ..B1.5        # Prob 100%                     #22.18
                                    # LOE ebx esi edi
    

    Dokonce ani lokální proměnná tedy nemusí být volatile, když se používá jako podmínková. V případě inlinování pthread_cond_wait() by to dopadlo stejně, protože tato funkce musí za všech okolností obsahovat speciální instrukce a tudíž i blok asm volatile s kouzelným slovem "memory" v seznamu změněných registrů.

    Začínám být čím dál pevněji přesvědčen, že podmínková proměnná nikdy nemusí být volatile a že toto chování překladačů se ani v budoucnu nemůže změnit.

    20.6.2009 11:03 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Další překvapivý příklad
    Ten první případ, kde podezříváte ICC z chyby), podle mne potvrzuje to, co jsem psal jinde – kompilátor nezajímají nějaká vlákna nebo souběžný přístup do té doby, než použijete příslušné speciální instrukce. Takže zjistí, že v daném vláknu se proměnná local nemůže před použitím v cyklu změnit, a proto může cyklus odstranit. V tom kódu stejně nemáte zaručeno, jestli kód v novém vlákně proběhne ještě před začátkem cyklu, nebo až v jeho průběhu – a nemůžete to ani nijak ovlivnit, výsledek závisí na náhodě. ICC vám z těch různých „správných“ výsledků běhu programu vybere jeden a ostatní neumožní, ale to není špatně, protože i ten jeden výsledek je správný vzhledem k tomu, co je napsáno ve zdrojáku. Pokud tedy hodnota může být změněna z jiného vlákna, musíte ji chránit zámkem. Pak překladač pozná, že kód toho vlákna není jediný, kdo tu hodnotu může změnit, a nemůže provést příslušnou optimalizaci.
    20.6.2009 13:15 Ivan
    Rozbalit Rozbalit vše Re: Další překvapivý příklad

    A jak to kompilator pozna? Jak pozna ktera promenna patri ke kteremu zamku? Jak pozna, ze fce pthread_mutex neco zamyka? Co kdyz zavolam QMutex->lock() ? Pokud autor prispevku chce neco optimalizovat pro "rychlost", tak je nejlepsi tam vubec zadny zamky nedavat. Flag definovat jako volatile a pouzit atomicky decrement z knihovny boost.

     

     

    20.6.2009 13:31 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Další překvapivý příklad
    Kompilátor nepotřebuje vědět, která proměnná patří ke kterému zámku. Jemu stačí, že se použije zámek (resp. příslušná instrukce procesoru), a to pro něj představuje bariéru, přes kterou nesmí „přetáhnout“ žádné předpoklady. Prostě co platilo před bariérou, po ní už platit nemusí a veškeré předpoklady je nutné ověřovat znova. Respektive ještě to funguje opačně – ne, že kompilátor ví, která funkce používá příslušnou instrukci, ale předpokládá to u všech, a vedle toho si vytváří seznam bezpečných volání, u kterých má jistotu, že onu instrukci nepoužívají.
    Pokud autor prispevku chce neco optimalizovat pro "rychlost", tak je nejlepsi tam vubec zadny zamky nedavat. Flag definovat jako volatile a pouzit atomicky decrement z knihovny boost.
    To není zrovna moc dobrá optimalizace pro rychlost. Optimalizace pro rychlost znamená používat synchronizaci co nejméně – zámky vám rozsekají kód na bloky kódu mezi zámky, volatile jej rozseká na bloky kódu mezi užitím příslušné proměnné, při častém používání té proměnné tedy až na jednotlivé instrukce. Což bude samozřejmě pomalejší.
    20.6.2009 11:10 Filip Jirsák | skóre: 68 | blog: Fa & Bi
    Rozbalit Rozbalit vše Re: Další překvapivý příklad
    Ještě k tomu volatilevolatile by ta proměnná měla být, pokud se opravdu mění mimo program, tj. je to třeba HW mapovaný do paměti. Pokud jde „jen“ o souběžný přístup vláken jednoho programu, stačí zámky – to pak umožní překladači optimalizovat úseky kódu uvnitř zámku, případně by nějaký budoucí překladač mohl optimalizovat i kód s ohledem na vlákna (zjistí, že kód je sice chráněn zámkem, ale ani souběžné vlákno nemůže hodnotu změnit, a tudíž může opět provést optimalizaci).

    Založit nové vláknoNahoru

    Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

    ISSN 1214-1267   www.czech-server.cz
    © 1999-2015 Nitemedia s. r. o. Všechna práva vyhrazena.