Hur skiljer sig memory_order_seq_cst och memory_order_acq_rel?

Hur skiljer sig memory_order_seq_cst och memory_order_acq_rel?

http://en.cppreference.com/w/cpp/atomic/memory_order har ett bra exempel längst ner som bara fungerar med memory_order_seq_cst . I huvudsak memory_order_acq_rel ger läs- och skrivordningar i förhållande till atomvariabeln, medan memory_order_seq_cst ger läs- och skrivordning globalt. Det vill säga de sekventiellt konsekventa operationerna är synliga i samma ordning i alla trådar.

Exemplet kokar ner till detta:

bool x= false;
bool y= false;
int z= 0;

a() { x= true; }
b() { y= true; }
c() { while (!x); if (y) z++; }
d() { while (!y); if (x) z++; }

// kick off a, b, c, d, join all threads
assert(z!=0);

Operationer på z skyddas av två atomvariabler, inte en, så du kan inte använda förvärv-frigörande semantik för att genomdriva den z är alltid inkrementerad.


På ISA som x86 där atomics mappar till barriärer och den faktiska maskinmodellen inkluderar en butiksbuffert:

  • seq_cst butiker kräver att butiksbufferten töms så att den här trådens senare läsningar fördröjs tills efter att butiken är globalt synlig.
  • acq_rel gör inte spola lagringsbufferten. Normala x86-laddningar och butiker har i huvudsak acq och rel semantik. (seq_cst plus en lagringsbuffert med lagringsvidarebefordran.)

    Men x86 atomic RMW-operationer flyttas alltid upp till seq_cst eftersom x86 asm lock prefix är en fullständig minnesbarriär. Andra ISA kan göra avslappnade eller acq_rel RMWs i asm.

https://preshing.com/20120515/memory-reordering-caught-in-the-act är ett lärorikt exempel på skillnaden mellan en seq_cst-butik och en vanlig versionsbutik. (Det är faktiskt mov + mfence kontra vanlig mov i x86 asm. I praktiken xchg är ett mer effektivt sätt att lagra en seq_cst på de flesta x86-processorer, men GCC använder mov +mfence )

Kul fakta:AArch64:s STLR release-store-instruktion är faktiskt en sekventiell -släpp. I hårdvara har den laster/butiker med relaxed eller seq_cst, samt en full-barriär-instruktion.

I teorin kräver STLR bara att lagra bufferten före nästa LDAR , inte före andra operationer. d.v.s. före nästa seq_cst-laddning. Jag vet inte om äkta AArch64 HW implementerar det på detta sätt eller om det bara dränerar butiksbufferten innan den utför en STLR. (I vilket fall som helst måste alla tidigare butiker förbinda sig före STLR, men inte nödvändigtvis före senare vanliga laddningar.)

Så att stärka rel eller acq_rel till seq_cst genom att använda LDAR / STLR behöver inte vara dyrt.

Vissa andra ISA (som PowerPC) har fler val av barriärer och kan förstärka upp till mo_rel eller mo_acq_rel billigare än mo_seq_cst , men deras seq_cst kan inte vara så billig som AArch64; seq-cst-butiker behöver en fullständig barriär.


Försök att bygga Dekkers eller Petersons algoritmer med bara förvärva/släppa semantik.

Det kommer inte att fungera eftersom förvärva/släppa semantik inte tillhandahåller [StoreLoad]-stängsel.

I fallet med Dekkers algoritm:

flag[self]=1 <-- STORE
while(true){
    if(flag[other]==0) { <--- LOAD
        break;
    }
    flag[self]=0;
    while(turn==other);
    flag[self]=1        
}

Utan [StoreLoad]-stängsel kunde butiken hoppa framför lasten och då skulle algoritmen gå sönder. 2 trådar samtidigt skulle se att det andra låset är ledigt, sätter sitt eget lås och fortsätter. Och nu har du 2 trådar inom det kritiska avsnittet.