[C++] volatile correct (veilig) in dit geval?

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Bob
  • Registratie: Mei 2005
  • Laatst online: 11:26
Mijn situatie lijkt me vrij veel voorkomend, maar in de wirwar van info over volatile in C++ en het al dan niet (meer niet dan wel) gebruiken ervan om te syncen kan ik toch geen duidelijk antwoord vinden. Kort samengevat:

Een thread produceert gegevens, op een bepaald ogenblik zijn deze gegevens klaar. Hierna veranderen de gegevens niet meer, en mogen ze door verschillende threads gelezen worden.
Dus:
* gegevens onbestaand of work in progress: geen read access
* gegevens af: lezen maar, geen synchronisatie nodig, er verandert toch niets meer

Het eenvoudigste lijkt me dus om een volatile variable flag te voorzien die wordt aangepast als de gegevens klaar zijn. In code (in de praktijk members, geen lokale vars):

C++:
1
2
3
4
5
6
7
volatile bool flag = false;
std::vector<std::string> data;
data.reserve(1000);
for(int i = 0; i != 1000; ++i){
  data.push_back("boeiend");
}
flag = true;


Is dit correct? Ik zou het natuurlijk kunnen testen, maar ik wil eerder weten of het altijd gaat werken, maw of het volgens de standaard en qua idee juist is.

Acties:
  • 0 Henk 'm!

  • Bob
  • Registratie: Mei 2005
  • Laatst online: 11:26
Ok, na een laatste opzoekpoging blijkt het dus echt een stom plan om dit met volatile op te lossen:
http://software.intel.com...lti-threaded-programming/

De compiler mag nog steeds naar hartelust reorderen, en m'n flag kan dus vrolijk al geset zijn voor de data af is ...

edit:
Op deze manier kan het wel, maar het is compiler specifiek en x86 specifiek (writes worden door de x86 CPU ten opzichte van elkaar sowieso niet verplaatst)

C++:
1
2
3
// schrijf data
_ReadWriteBarrier(); // Visual C++ specifiek, compiler zal over deze barrier niet reorderen
// set flag

[ Voor 31% gewijzigd door Bob op 24-02-2010 11:59 . Reden: toch mogelijk ]


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

gebruik een synchronisatie primitive (mutex).

boost heeft een redelijk platform-onafhankelijke implementatie als je dat nodig hebt, voor windows kan je anders gewoon CreateMutex uit de Win32 API pakken (zie MSDN voor meer info)

-niks-


Acties:
  • 0 Henk 'm!

  • Bob
  • Registratie: Mei 2005
  • Laatst online: 11:26
Boost heb ik nog niet aan de praat gekregen in VS 2010. Met een mutex kan ik het idd zo fiksen dat ik 100 procent zeker ben dat het zal werken, maar ik heb het gevoel dat het overkill is om het hier met een mutex te doen.
De enige garantie die ik nodig heb is dat er pas naar de flag geschreven wordt wanneer de data klaar is. Eens dat gebeurd is zal er veel data gelezen worden en zal er nooit meer naar geschreven worden, beetje zonde om dan elke keer te gaan locken.

Acties:
  • 0 Henk 'm!

  • NC83
  • Registratie: Juni 2007
  • Laatst online: 21-08 21:44
Als je niet over een proccess grens heen moet in Win is een CriticalSection beter omdat deze niet kernel mode in duikt.

ex-FE Programmer: CMR:DiRT2,DiRT 3, DiRT Showdown, GRID 2, Mad Max


Acties:
  • 0 Henk 'm!

  • Bob
  • Registratie: Mei 2005
  • Laatst online: 11:26
Ik ben het nu aan het testen met enkel de flag, al dan niet als volatile gedeclareerd. In Visual C++ heb je extra garanties wanneer je volatile gebruikt die ook reordering voorkomen, en hier voor mijn probleem dus zouden moeten volstaan.

Het ziet er alleszins naar uit dat het werkt, zelfs zonder volatile. Waarschijnlijk herordent hij de write flag niet eens :)
Maar ik denk dat ik toch eens ga kijken naar degelijke threading libs, het zal niet altijd zo simpel blijven als het nu is.

[ Voor 1% gewijzigd door Bob op 24-02-2010 14:34 . Reden: woorden gereordered ;) ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je originele code is idd veilig onder MSVC++ op x86 :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Bob
  • Registratie: Mei 2005
  • Laatst online: 11:26
Ik heb er ondertussen wat verder mee gespeeld, en zie nu in de assembly deels waarom het volatile declareren geen verschil maakt, om het duidelijk te maken een voorbeeld:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
// in header:
volatile int BasePolygon::mmAvailableResolution;

// in .cpp
bool BasePolygon::allNeighboursReady(unsigned int requiredResolution) const{
    for(auto it = mmNineNeighbours.begin(); it != mmNineNeighbours.end(); ++it){
        if((*it)->getAvailableResolution() < mmAvailableResolution){
            return false;
        }
    }
    return true;
}


Hierin is het belangrijk dat de waarde (*it)->getAvailableResolution() zo up to date mogelijk is en vooral correct is. Die waarde kan door een andere thread elk moment veranderd worden, maar het is een int dus no worries. Op de meeste platformen toch niet (atomic write). :)
Enfin, de compiler doet hier dan wat debiel, (*it)->getAvailableResolution() geeft in beide gevallen, volatile of niet, een up to date waarde terug aangezien hij telkens dereferencet bij het vergelijken:

non volatile, esi+84 is wat we nodig hebben, een niet register waarde van it->getAvailable..:
GAS:
1
2
mov esi, DWORD PTR [eax]
cmp DWORD PTR [esi+84], edx


volatile, hier twee keer +84, een keer voor de eigen mmAvailable... (onzinnig) en een keer wat we nodig hebben:
GAS:
1
2
3
4
mov edx, DWORD PTR [eax]
mov edx, DWORD PTR [edx+84]
mov esi, DWORD PTR [ecx+84]
cmp edx, esi


waarbij mmAvailableResolution afhankelijk van al dan niet volatile te zijn cached in een register zit of telkens weer met de this pointer wordt opgehaald. Wat debiel is, want net die waarde, van het huidige object, is gegarandeerd niet veranderd door een externe thread ...

Conclusie: de veel teruggevonden opmerking dat volatile meestal een stom plan is, is ook hier weer op zijn plaats :) De overhead is gelukkig miniem, ik kan het alleszins niet meten en het gros van het werk wordt natuurlijk hier niet gedaan.

Ik heb het vooral voor mezelf uitgezocht, maar misschien leert nog iemand anders er iets bij.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Volatile is géén stom plan, tenzij je expliciet barriers gebruikt. Het feit dat het in je voorbeeld niet uitmaakt wil niet zeggen dat dat ook zo blijft na andere compiler settings of in iets andere situaties.

Maar meestal ben je niet eens geïnteresseerd in een altijd up to date waarde, waar je in geïnteresseerd bent is of de visible state consistent is. Zoals in dat loopje bijvoorbeeld, is het ook wel voldoende om 1 keer de waarde van mmAvailableResolution uit het geheugen te halen (en die evt. op te slaan in een local). Wil je echter een bepaalde state publishen (zoals een object die je invult en waarna je een volatile pointer ernaar laat wijzen), dan is het van belang dat de write naar de pointer gegarandeerd na de writes naar het object zelf gebeuren. Volatile (of expliciete barriers) zijn dan van levensbelang omdat de compiler anders de writes kan reorderen.

[ Voor 61% gewijzigd door .oisyn op 18-03-2010 14:51 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Bob
  • Registratie: Mei 2005
  • Laatst online: 11:26
Voor wat gebruik jij het dan? In VS is het misschien nog nuttig, met die custom interpretatie van volatile, maar het idee dat je code vast hangt aan een compiler lijkt me soms onhandig.

ook edit: inderdaad, maar je voorbeeld heeft het over reordering en da's dus VSC++ only stuff. Ik ga de komende jaren sowieso ook GNU gebruiken en dan weet ik weeral niet meer hoe het daar nu zit met volatile. Liever iets generiek dan.

[ Voor 39% gewijzigd door Bob op 18-03-2010 14:58 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

GNU hecht volgens mij dezelfde semantische betekenis aan volatile als MSVC++. Intel C++ doet dat ook, en dan heb je de grotere compilers wel zo'n beetje gehad :). Overigens: ook in de volgende C++ standaard die binnenkort uitkomt zal volatile die betekenis hebben. Hmm ik dacht dat ik dat ergens gelezen had, maar dat is niet zo. C++0x heeft wel expliciete barrier functies, en support voor atomic operations.

[ Voor 47% gewijzigd door .oisyn op 18-03-2010 15:09 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1