2015-01-08 8 views
6

La documentazione Intel ATÈ x86 CMPXCHG atomico?

http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf

dice

"Questa istruzione può essere utilizzato con un prefisso LOCK per permettere l'istruzione da eseguire atomicamente."

La mia domanda è

  1. Può cmpxchg operare con indirizzo di memoria? Dal documento sembra non esserlo, ma qualcuno può confermare che funziona solo con VALUE effettivi nei registri, non nell'indirizzo di memoria?

  2. Se CMPXCHG non è atomico e un livello di linguaggio di alto livello CAS deve essere implementato tramite LOCK CMPXCHG (con prefisso LOCK), qual è lo scopo di introdurre tale istruzione?

    prefisso
+0

Ovviamente è possibile utilizzare un indirizzo di memoria, questo è il punto. Il primo operando è di tipo r/m, quindi eccoti. E come potresti prefisso l'istruzione con 'lock' se non esistesse? – harold

+0

@harold Non capisco cosa non esista. Si prefissa con LOCK se si desidera che l'istruzione sia atomica. Quindi CMPXCHG, senza prefisso LOCK, è atomico o no? –

+0

No, ma nella tua domanda 2 ti sembra di chiedere perché "cmpxchg senza blocco" esista, che è un po 'strano, dato che la combinazione non può esistere senza le parti - se questo non è ciò che intendi, puoi chiarire? – harold

risposta

9

Si confondono i blocchi di alto livello con la funzione CPU di basso livello che è stata denominata LOCK.

I blocchi di alto livello che gli algoritmi bloccati cercano di evitare possono proteggere frammenti di codice arbitrari la cui esecuzione può richiedere tempo arbitrario e, quindi, questi blocchi dovranno mettere i thread in stato di attesa fino a quando il blocco è disponibile che è costoso operazione, ad es implica il mantenimento di una coda di thread in attesa.

Questa è una cosa completamente diversa dalla funzione di prefisso CPU LOCK che protegge una singola istruzione e quindi potrebbe contenere altri thread per la durata di quella singola istruzione. Poiché questo è implementato dalla CPU stessa, non richiede sforzi software aggiuntivi.

Quindi la sfida dello sviluppo di algoritmi lock-free non è la rimozione della sincronizzazione completamente, si riduce a ridurre la sezione critica del codice a una singola operazione atomica che verrà fornita dalla CPU stessa.

+0

Quindi è corretto dire che CMPXCHG mantiene ancora il blocco che è diverso dal blocco del livello di programma (cioè blocco JVM) –

+1

@Rohit Sachan: potresti dire che tiene il blocco BUS, ma dal momento che questo vale per ogni accesso alla memoria, l'unica differenza è che è tenuto per due accessi di memoria fatti da una singola istruzione e, cosa più importante, è solo confuso quando si parla di "programmazione senza blocco". In altre parole, dovresti sempre preoccuparti se la discussione riguarda l'architettura hardware o software ... – Holger

+2

Penso che l'OP chieda in parte "qual è il punto di' cmpxchg' senza 'lock'?". Vedi [la mia risposta] (https://stackoverflow.com/questions/27837731/is-x86-cmpxchg-atomic/44273130#44273130): Intel l'ha progettata in questo modo perché è utile su un sistema uniprocessore. –

2

la serratura è per bloccare l'accesso alla memoria per il comando corrente, in modo che altri comandi che sono in cantiere CPU possono accedere alla memoria a questo punto. Utilizzando il prefisso LOCK, l'esecuzione del comando non verrà interrotta da un altro comando nella pipeline della CPU a causa dell'accesso alla memoria di altri comandi che vengono eseguiti contemporaneamente. Il manuale INTEL dice:

Il prefisso serratura può essere anteposto solo per quanto segue in structions e solo a quelle forme di istruzioni in cui l'operando destinazione è un operando di memoria: ADD, ADC, E, BTC , BTR, BTS, CMPXCHG, CMPXCH8B, CMPXCHG16B, DEC, INC, NEG, NON, O, SBB, SUB, XOR, XADD e XCHG. Se il prefisso LOCK viene utilizzato con una di queste istruzioni e l'operando di origine è un operando di memoria, è possibile che venga generata un'eccezione opcode non definita (#UD).

6

sembra che parte quello che stai realmente chiedendo è:

Perché non è il prefisso di lock implicito per cmpxchg con un operando di memoria, like it is for xchg?

La semplice risposta (che altri hanno dato) è semplicemente che Intel l'ha progettata in questo modo. Ma questo porta alla domanda:

Perché Intel l'ha fatto? Esiste un caso d'uso per cmpxchg senza lock?

In un sistema a CPU singola, cmpxchgè atomico rispetto altri fili, o qualsiasi altro codice in esecuzione sullo stesso nucleo CPU. (Ma non per gli osservatori "di sistema" come un dispositivo I/O mappato in memoria, o un dispositivo che fa letture DMA di memoria normale, quindi lock cmpxchg era rilevante anche su progetti CPU uniprocessore).

Gli interruttori di contesto possono verificarsi solo su interrupt e gli interrupt si verificano prima o dopo un'istruzione, non nel mezzo. Qualsiasi codice in esecuzione sulla stessa CPU vedrà lo cmpxchg come completamente eseguito o non del tutto.


Ad esempio, il kernel di Linux è normalmente compilato con il supporto SMP, in modo che utilizza lock cmpxchg per CAS atomica. Tuttavia, quando viene avviato su un sistema a processore singolo, verrà applicato il prefisso lock a nop ovunque il codice sia stato allineato, poiché nopcmpxchg viene eseguito molto più rapidamente di lock cmpxchg. Per maggiori informazioni, vedi questo LWN article about Linux's "SMP alternatives" system. Può anche applicare patch ai prefissi lock prima di collegare a caldo una seconda CPU.

Leggi di più riguardo atomicità delle singole istruzioni su sistemi monoprocessore in this answer, e in @supercat's answer + comments su Can num++ essere atomica per int num. Vedere my answer there per molti dettagli su come l'atomicità funziona davvero/è implementata per le istruzioni di lettura-modifica-scrittura come lock cmpxchg.


(Lo stesso ragionamento vale per cmpxchg8b/cmpxchg16b, e xadd, che di solito utilizzato solo per synchonization/OPS atomici, non fare-single threaded conduzione codice più veloce. Ovviamente memoria destinazione add [mem], reg è all'esterno utile il caso lock add [mem], reg.)

+0

"Ma quando viene avviato su un sistema a processore singolo, patcherà il prefisso di blocco a un nop ovunque il codice sia stato allineato, poiché nop cmpxchg è molto più veloce di lock cmpxchg." Immagino che tu voglia compilare in un sistema a processore singolo? Poiché non sono a conoscenza del sistema operativo, è possibile applicare patch alle istruzioni compilate in fase di esecuzione. –

+0

@AlexSuo: No, il sistema di Linux [SMP alternative] (https://lwn.net/Articles/164121/) davvero applica l'immagine del kernel sui sistemi UP. (E BTW, se si trattasse solo di una cosa in fase di compilazione, dipenderà dal fatto che stiate costruendo * per * un sistema UP, non * su * un sistema UP. Penso che se si ometta 'CONFIG_SMP', qualche blocco/la roba di sincronizzazione può essere lasciata fuori completamente invece di essere aggiunta in NOP al momento dell'avvio, ma di questi tempi probabilmente non tanto, specialmente con lo standard 'CONFIG_PREEMPT' che consente di anticipare il codice del kernel.) –