2013-06-06 16 views
18

Nel suo Blog Herb Sutter scriveIl decremento atomico è più costoso dell'incremento?

[...] perché incrementare il conteggio dei riferimenti puntatore intelligente di solito può essere ottimizzato per essere lo stesso di un incremento ordinario in un ottimizzato shared_ptr implementazione - solo un incremento ordinaria istruzione, e nessuna recinzione, nel codice generato.

Tuttavia, il decremento deve essere un decremento atomico o equivalente, che genera istruzioni speciali memoria del processore che sono più costosi in stessi, e che per di più indurre memoria restrizioni recinzione sull'ottimizzazione del codice circostante.

Il testo è di circa l'attuazione di shared_ptr e io non sono sicuro se la sua osservazione si applica solo su questo o è generalmente il caso. Dalla sua formulazione ritengo che sia generalmente.

Ma quando ci penso, posso solo pensare a "decremento più costoso" quando segue immediatamente un if(counter==0) - che probabilmente è il caso di shared_ptr.

Perciò mi chiedo se il atomica operazione ++counter è (di solito) sempre più veloce di quanto--counter, o solo perché viene utilizzato if(--counter==0)... con shared_ptr?

+1

* "Dalla sua formulazione ritengo che sia in generale" * - Tuttavia, raccolgo il contrario. –

+0

Puoi anche dare un'occhiata alla barriera WriteRead che è richiesta per il --counter == 0, e che generalmente è la più costosa, in quanto è l'unico tipo di barriera che richiede che dopo aver scritto i dati deve essere riletto garantendo la sincronizzazione tra i core. Per counter ++ basta scrivere senza requisiti immediati per ottenere uno stato sincronizzato di questo valore o altri dati. –

risposta

11

Credo che si riferisca al fatto che l'incremento può essere "nascosto", dove il "decremento e controllo" deve essere eseguito come un'unica operazione.

Io non sono a conoscenza di qualsiasi architettura in cui --counter (o counter--, asssuming stiamo parlando di tipi di dati semplici come int, char, ecc) è più lento di ++counter o counter++.

+0

Cosa intendi con "nascosto"? – curiousguy

+0

È esattamente l'opposto di quello che sta dicendo, però. Sta dicendo esplicitamente "incremento ordinario". Non si tratta di non controllare il valore in seguito o qualsiasi cosa del genere. Si tratta di utilizzare un incremento ordinario (leggere la cache, modificare, scrivere la cache) che perdono i riferimenti in presenza di concorrenza. Sono quindi fortemente propenso a dire che ciò che sta dicendo è semplicemente sbagliato. Sutter può dire anche cose sbagliate, perché no. – Damon

16

Ne discute più dettagliatamente da qualche parte, penso nella sua presentazione atomic<> weapons. Fondamentalmente si tratta di dove sono necessarie le recinzioni di memoria nel caso d'uso shared_ptr, non di alcuna proprietà intrinseca di incrementi atomici vs decrementi.

Il motivo è che non ti interessa davvero l'esatto ordine degli incrementi con un puntatore intelligente conteggiato di riferimento fintanto che non ne perdi nessuno, ma con i decrementi è fondamentale che siano presenti barriere di memoria in modo che sul decremento finale che attiva l'eliminazione non si ha alcuna possibilità di accesso alla memoria precedente da un altro thread all'oggetto di proprietà del puntatore intelligente che viene riordinato dopo che la memoria è stata liberata.

+0

Quel discorso di Herb è super-duper! Grazie! +1 – towi

+0

La "parte da non perdere nessuna" è comunque la parte importante. Quello, e l'accadimento, prima della garanzia rispetto al decremento (ma che è gestito dal decremento atomico). Sta dicendo esplicitamente "incremento normale", che salterà alcuni incrementi in uno scenario concorrente. Forse lui deve dire qualcosa di diverso, ma è quello che ha detto comunque. – Damon

8

Il problema di cui parla Sutter è il fatto che l'incremento del conteggio dei riferimenti non richiede alcuna azione di follow-up per la correttezza. Stai prendendo un conteggio di riferimento diverso da zero a un altro conteggio diverso da zero, quindi non è richiesta alcuna ulteriore azione. Tuttavia, il decremento richiede un'azione di follow-up per la correttezza. Il decremento prende un conteggio di riferimento diverso da zero per un conteggio di riferimento diverso da zero o zero e se il decremento del conteggio dei riferimenti è zero, è necessario eseguire un'azione, in particolare deallocare l'oggetto di riferimento.Questa dinamica decrementa e attua richiede una maggiore coerenza, sia a livello di recinzione (quindi la deallocazione non viene riordinata con qualche altra lettura/scrittura su un altro core riordinato dalla memoria della CPU/logica di gestione della cache) e al livello del compilatore (in modo che il compilatore non riordini le operazioni attorno al decremento che potrebbe causare ri-ordinate letture/scritture intorno alla possibile deallocazione).

Quindi, per lo scenario descritto da Sutter, la differenza di costo tra incremento e decremento non è nelle operazioni fondamentali stesse, è nei vincoli di coerenza imposti sull'utilizzo effettivo del decremento (in particolare, agendo sul decremento stesso) che non si applicano all'incremento.