Ik heb een stukje C++11 code gemaakt wat item insert in een lock-free circulair buffer. Thread 1 insert in het buffer, thread 2 leest en delete uit het buffer. Thread 2 voert in deze case geen writes uit, alleen plain-ol' reads.
Bij het inserten wordt gebruik gemaakt van de volgende variabelen:
Het inserten gebeurt als volgt:
De volatile is van belang omdat ik een bepaalde optimalisatie in een andere thread wil voorkomen.
Over deze code heb ik 2 vragen:
• Is het gegarandeerd dat, op een x86 architectuur, 'de andere thread' de wijziging aan array[] te zien krijgt vóór of gelijktijdig aan de wijziging aan end? Dat wil zeggen, de compiler/CPU/cache system mag de writes aan de geheugenlocaties van end en array[] niet omdraaien.
• houdt deze assumptie ook stand wanneer wij het hebben over een non-x86 architectuur, zoals POWER?
• bonusvraag: Houdt deze assumptie nog steeds stand wanneer de InterlockedIncrement() word verwisseld met een plain ++?
Volgens mij is het antwoord op vraag 1 'ja, want x86 reordert geen memory writes, en het memory model van C++11 staat geen write reordering toe tenzij op een std::atomic<> met std::memory_order_relaxed.'.
Het antwoord op vraag is 2 is volgens mij nee. Omdat ARM/POWER wel reads reordert en er geen impliciete acquire op standaard loads zit. Om dit op te lossen is een release op beide memory writes en een acquire op beide memory reads nodig.
Het antwoord op de bonusvraag is volgens mij 'ja onder x86, nee onder andere architecturen.'. Vanwege precies dezelfde redenen als eerst.
Bij het inserten wordt gebruik gemaakt van de volgende variabelen:
C++:
1
2
3
| // shared variabelen: volatile unsigned end; // 4-byte aligned Foo* array[ 256 ]; // 8-byte aligned |
Het inserten gebeurt als volgt:
C++:
1
2
3
4
5
| void insert( Foo* ptr ) { array[ end % 256 ] = ptr; InterlockedIncrement( &end ); // neem aan dat hiervan een non-x86 equivalent bestaat. }; |
De volatile is van belang omdat ik een bepaalde optimalisatie in een andere thread wil voorkomen.
Over deze code heb ik 2 vragen:
• Is het gegarandeerd dat, op een x86 architectuur, 'de andere thread' de wijziging aan array[] te zien krijgt vóór of gelijktijdig aan de wijziging aan end? Dat wil zeggen, de compiler/CPU/cache system mag de writes aan de geheugenlocaties van end en array[] niet omdraaien.
• houdt deze assumptie ook stand wanneer wij het hebben over een non-x86 architectuur, zoals POWER?
• bonusvraag: Houdt deze assumptie nog steeds stand wanneer de InterlockedIncrement() word verwisseld met een plain ++?
Volgens mij is het antwoord op vraag 1 'ja, want x86 reordert geen memory writes, en het memory model van C++11 staat geen write reordering toe tenzij op een std::atomic<> met std::memory_order_relaxed.'.
Het antwoord op vraag is 2 is volgens mij nee. Omdat ARM/POWER wel reads reordert en er geen impliciete acquire op standaard loads zit. Om dit op te lossen is een release op beide memory writes en een acquire op beide memory reads nodig.
Het antwoord op de bonusvraag is volgens mij 'ja onder x86, nee onder andere architecturen.'. Vanwege precies dezelfde redenen als eerst.
offtopic:
Overigens is het absoluut zuigend dat de support voor std::atomic en std::memory order super slecht is in veel compilers. Daarom kan je met c++11 atomics op dit moment nog geen code schrijven die daadwerkelijk op elke architectuur optimaal werkt met de meest populaire compilers. In theorie is het allemaal wel mogelijk.
Overigens is het absoluut zuigend dat de support voor std::atomic en std::memory order super slecht is in veel compilers. Daarom kan je met c++11 atomics op dit moment nog geen code schrijven die daadwerkelijk op elke architectuur optimaal werkt met de meest populaire compilers. In theorie is het allemaal wel mogelijk.