Perché i puntatori intelligenti conteggio di riferimento sono così popolari?

51

Come posso vedere, i puntatori intelligenti sono ampiamente utilizzati in molti progetti C ++ reali.

Anche se alcuni tipi di puntatori intelligenti sono ovviamente utili per supportare RAII e trasferimenti di proprietà, c'è anche una tendenza ad usare i puntatori condivisi di default , come modo di "garbage collection" , in modo che il programmatore non debba pensare molto all'assegnazione.

Perché i puntatori condivisi sono più popolari dell'integrazione di un vero garbage collector come Boehm GC ? (O siete d'accordo sul fatto che sono più popolari dei GC attuali?)

Conosco due vantaggi dei GC convenzionali rispetto al conteggio dei riferimenti:

  • Gli algoritmi GC convenzionali non presentano alcun problema con cicli di riferimento .
  • Il conteggio dei riferimenti è generalmente più lento di un GC corretto.

Quali sono i motivi dell'utilizzo dei puntatori intelligenti con conteggio di riferimento?

    
posta Miklós Homolya 14.08.2013 - 04:28
fonte

5 risposte

57

Alcuni vantaggi del conteggio dei riferimenti rispetto alla garbage collection:

  1. Overhead basso. I raccoglitori di immondizie possono essere piuttosto invadenti (ad es. Rendere il programma bloccato a orari imprevedibili mentre un ciclo di raccolta dei dati inutili si elabora) e abbastanza intensi in termini di memoria (ad esempio l'impronta di memoria del processo aumenta inutilmente a molti megabyte prima che la raccolta di immondizia possa finalmente iniziare)

  2. Comportamento più prevedibile. Con il conteggio dei riferimenti, hai la garanzia che il tuo oggetto sarà liberato nel momento in cui l'ultimo riferimento ad esso sparirà. D'altro canto, con la garbage collection, il tuo oggetto verrà liberato "a volte", quando il sistema farà il giro. Per la RAM, di solito questo non è un grosso problema su desktop o server leggermente caricati, ma per altre risorse (ad es. Handle di file) è spesso necessario che siano chiusi al più presto per evitare potenziali conflitti in seguito.

  3. Più semplice. Il conteggio dei riferimenti può essere spiegato in pochi minuti e implementato in un'ora o due. I netturbini, in particolare quelli con prestazioni decenti, sono estremamente complessi e non sono in molti a capirli.

  4. standard. C ++ include il conteggio dei riferimenti (tramite shared_ptr) e gli amici nell'STL, il che significa che la maggior parte dei programmatori C ++ ha familiarità con esso e la maggior parte del codice C ++ funzionerà con esso. Tuttavia non esiste un garbage collector standard di C ++, il che significa che devi sceglierne uno e spero che funzioni bene per il tuo caso d'uso, e se non lo fa, è il tuo problema da risolvere, non quello della lingua.

Per quanto riguarda i presunti aspetti negativi del conteggio dei riferimenti - il non rilevamento dei cicli è un problema, ma uno che non ho mai incontrato personalmente negli ultimi dieci anni di utilizzo del conteggio dei riferimenti. La maggior parte delle strutture dati sono naturalmente acicliche e, se si incontra una situazione in cui sono necessari riferimenti ciclici (ad esempio, il puntatore genitore in un nodo ad albero), è sufficiente utilizzare un pointer debole o un puntatore C per la "direzione indietro". Finché sei a conoscenza del potenziale problema quando stai progettando le tue strutture dati, è un non-problema.

Per quanto riguarda le prestazioni, non ho mai avuto problemi con le prestazioni del conteggio dei riferimenti. Ho avuto problemi con le prestazioni della garbage collection, in particolare i blocchi casuali che GC può sostenere, a cui l'unica soluzione ("non allocare oggetti") potrebbe essere riformulata come "non utilizzare GC" .

    
risposta data 14.08.2013 - 04:48
fonte
26

Per ottenere prestazioni ottimali da un GC, il GC deve essere in grado di spostare gli oggetti nella memoria. In un linguaggio come C ++ in cui è possibile interagire direttamente con le posizioni di memoria, questo è praticamente impossibile. (Microsoft C ++ / CLR non conta perché introduce una nuova sintassi per i puntatori gestiti da GC e quindi è effettivamente una lingua diversa.)

Il Boehm GC, mentre un'idea elegante, è in realtà il peggiore di entrambi i mondi: hai bisogno di un malloc () che è più lento di un buon GC, e quindi perdi il comportamento deterministico di allocazione / deallocazione senza il corrispondente incremento delle prestazioni di un GC generazionale. Inoltre è per necessità conservativo, quindi non raccoglierà necessariamente tutti i tuoi rifiuti in ogni caso.

Un buon GC ben congegnato può essere una grande cosa. Ma in un linguaggio come C ++, i guadagni sono minimi e spesso i costi non ne valgono la pena.

Sarà interessante vedere, tuttavia, come C ++ 11 diventa più popolare, se lambda e semantica di acquisizione iniziano a guidare la comunità C ++ verso lo stesso tipo di allocazione e problemi di durata dell'oggetto che hanno causato alla comunità Lisp di inventare GC in primo luogo.

Vedi anche la mia risposta a una domanda correlata su StackOverflow .

    
risposta data 14.08.2013 - 09:04
fonte
4

As I can see, smart pointers are used extensively in many real-world C++ projects.

Vero ma, oggettivamente, la maggior parte del codice è ora scritta in lingue moderne con i garbage collector di tracciamento.

Though some kind of smart pointers are obviously beneficial to support RAII and ownership transfers, there is also a trend of using shared pointers by default, as a way of "garbage collection", so that the programmer do not have to think about allocation that much.

Questa è una cattiva idea perché devi ancora preoccuparti dei cicli.

Why are shared pointers more popular than integrating a proper garbage collector like Boehm GC? (Or do you agree at all, that they are more popular than actual GCs?)

Oh wow, ci sono così tante cose che non vanno nel tuo modo di pensare:

  1. Il GC di Boehm non è un GC "corretto" in alcun senso della parola. È veramente terribile. È conservativo, quindi perde ed è inefficiente di progettazione. Vedi: link

  2. I puntatori condivisi, oggettivamente, non sono neanche lontanamente tanto popolari quanto GC perché la stragrande maggioranza degli sviluppatori utilizza ora le lingue di GC e non ha bisogno di puntatori condivisi. Basta guardare Java e Javascript nel mercato del lavoro rispetto a C ++.

  3. Sembra che tu stia limitando la considerazione a C ++ perché, presumo, pensi che GC sia un problema tangenziale. Non è (il modo solo per ottenere un GC decente è di progettare la lingua e la VM dall'inizio), quindi stai introducendo il bias di selezione. Le persone che vogliono davvero una corretta raccolta dei rifiuti non si attaccano al C ++.

What are the reasons for using reference-counting smart pointers?

Sei limitato al C ++ ma desideri avere una gestione automatica della memoria.

    
risposta data 26.01.2016 - 22:10
fonte
3

In MacOS X e iOS, e con gli sviluppatori che usano Objective-C o Swift, il conteggio dei riferimenti è popolare perché viene gestito automaticamente e l'uso della raccolta dei dati obsoleti è notevolmente diminuito poiché Apple non lo supporta più (mi è stato detto che le app che utilizzano la garbage collection si interromperanno nella prossima versione di MacOS X e la raccolta di dati inutili non è mai stata implementata in iOS). In realtà dubito seriamente che ci fosse sempre molto software che utilizzava la garbage collection quando era disponibile.

La ragione per sbarazzarsi della garbage collection: non ha mai funzionato in modo affidabile in un ambiente in stile C in cui i puntatori potevano "scappare" in aree non accessibili dal garbage collector. Apple crede fermamente e crede che il conteggio dei riferimenti sia più veloce. (Puoi fare affermazioni sulla velocità relativa, ma nessuno è stato in grado di convincere Apple). E alla fine, nessuno ha usato la raccolta dei rifiuti.

La prima cosa che tutti gli sviluppatori MacOS X o iOS apprendono è come gestire i cicli di riferimento, quindi non è un problema per uno sviluppatore reale.

    
risposta data 27.01.2016 - 01:53
fonte
2

Il più grande svantaggio della raccolta di dati inutili in C ++ è che è praticamente impossibile avere ragione:

  • In C ++, i puntatori non vivono nella loro comunità murata, sono mescolati con altri dati. Di conseguenza, non è possibile distinguere un puntatore da altri dati che hanno solo un pattern di bit che può essere interpretato come un puntatore valido.

    Conseguenza: qualsiasi garbage collector di C ++ perde oggetti che devono essere raccolti.

  • In C ++, puoi eseguire l'aritmetica del puntatore per ricavare i puntatori. Pertanto, se non trovi un puntatore all'inizio di un blocco, ciò non significa che quel blocco non possa essere referenziato.

    Conseguenza: qualsiasi garbage collector di C ++ deve prendere in considerazione queste rettifiche, trattando qualsiasi sequenza di bit che accade puntare ovunque all'interno di un blocco, incluso subito dopo la sua fine, come un puntatore valido che fa riferimento al blocco.

    Nota: nessun garbage collector C ++ può gestire codice con trucchi come questi:

    int* array = new int[7];
    array--;    //undefined behavior, but people may be tempted anyway...
    for(int i = 1; i <= 7; i++) array[i] = i;
    

    È vero, questo richiama un comportamento indefinito. Ma un codice esistente è più intelligente di quello che fa per lui, e può far scattare una deallocazione preliminare da parte di un garbage collector.

risposta data 26.01.2016 - 22:34
fonte

Leggi altre domande sui tag