6

Sto frequentando un corso universitario sui compilatori e abbiamo appena finito di parlare della raccolta dei rifiuti e dei modi per liberare memoria. Tuttavia, nelle lezioni di classe e nel nostro libro di testo, sono stato indotto a credere che il conteggio dei riferimenti non fosse un ottimo modo per gestire la memoria.In che modo Objective-C fa riferimento al conteggio in modo efficiente?

Il ragionamento è stato che il conteggio dei riferimenti è molto costoso perché il programma deve inserire numerose istruzioni aggiuntive per incrementare e decrementare il conteggio dei riferimenti. Inoltre, ogni volta che cambia il conteggio dei riferimenti, il programma deve verificare se è uguale a zero e, in tal caso, recuperare la memoria.

Il mio libro di testo, anche ha la frase: ". In generale, i problemi con il conteggio dei riferimenti siano superiori i suoi vantaggi ed è raramente utilizzato per la gestione automatica dello storage in ambienti di linguaggio di programmazione

Le mie domande sono: sono queste preoccupazioni legitamate ? Obiettivo-c evitarli in qualche modo? Se sì, come?

+2

"numerose istruzioni aggiuntive" - ​​numero == uno; 'INC [esp + 12]'. Inoltre, Objective-C utilizza un assembly sintonizzato a mano ed è anche un linguaggio compilato => veloce. –

+2

ha bisogno di un prefisso LOCK su quell'INC e non tiene conto della tabella laterale di objc e supporto degli strumenti –

+2

Non sarei d'accordo, il conteggio dei riferimenti è un modo molto efficace per gestire le complessità della gestione manuale della memoria. Non è adatto a tutte le situazioni, ma le prestazioni e il controllo rendono superfluo qualsiasi garbage collection. –

risposta

5

Il conteggio dei riferimenti ha un sovraccarico significativo, è vero. Tuttavia, la soluzione "classico libro di testo" dei garbage collector di tracciamento non è priva di aspetti negativi. nondeterminismo, ma la pausa e il throughput sono una preoccupazione significativa pure

Alla fine, comunque, ObjC non ha davvero una scelta. Un collezionista di copia allo stato dell'arte richiede determinate proprietà del linguaggio (nessun puntatore grezzo ad esempio) che ObjC non ha. Di conseguenza, provare ad applicare la soluzione da manuale a ObjC finisce per richiedere un collector parzialmente conservatore, non copiante, che in pratica è all'incirca della stessa velocità del conteggio ma senza il suo comportamento deterministico.

(modifica) I miei sentimenti personali sono che il throughput è una secondaria, o addirittura terziario, preoccupazione e che il dibattito veramente importante si riduce a un comportamento deterministico vs raccolta ciclo e heap compattazione copiando. Tutte e tre sono proprietà così preziose che sarei costretto a sceglierne una.

+0

Ma il significativo, "significativo overhead" prezzo è pagato al volo, come avviene deallocazione (al contrario del costo banale di decrementare il ref-count) quindi anche se il tuo codice rilascia un intero oggetto grafico come il risultato di un singolo ref -Conto decremento, non solo è una conseguenza del modo in cui il tuo codice è stato scritto (il tuo codice ha deciso di decrementare quel ref-count), ma si diffonde anche tale costo per tutta la durata della tua app, senza pause apprezzabili dall'utente. Ci devono essere dei motivi per cui Apple sta eliminando progressivamente il suo stato dell'arte generazionale Garbage Collector per ObjC su OS X ... – verec

+1

libauto (GC di Apple) è il collezionista parzialmente conservatore e non copiante che ho menzionato. Ha problemi con le false radici, la frammentazione dell'heap e le prestazioni così così (anche se molto meglio dei tipici garbage collector di C). –

+0

È possibile, ovviamente, utilizzare il GC (in stile Java) senza copiare e mettere in pausa. –

0

Nel complesso, i problemi con riferimento conteggio siano superiori suoi vantaggi ed è raramente utilizzati per la gestione di memorizzazione automatica in ambienti linguaggio di programmazione

La parola difficile è automatic

riferimento conteggio manuale che è il tradizionale modo Obj-C di fare le cose, evita i problemi delegandoli al programmatore. Il programmatore deve conoscere il conteggio dei riferimenti e aggiungere manualmente le chiamate retain e release. Se lui/lei crea un ciclo di riferimento, lui/lei è responsabile per risolverlo.

Il moderno conteggio dei riferimenti automatico fa molte cose per il programmatore, ma non è ancora una gestione della memoria trasparente. Il programmatore deve ancora conoscere il conteggio dei riferimenti, deve ancora risolvere i cicli di riferimento.

Ciò che è veramente difficile è creare un framework che gestisca la gestione della memoria mediante il conteggio dei riferimenti in modo trasparente, ovvero senza che il programmatore ne sia al corrente. Ecco perché non viene utilizzato per la gestione automatica della memoria.

La perdita di prestazioni causata da istruzioni aggiuntive non è molto grande e in genere non è importante.

+0

Sì, il conteggio dei riferimenti "automatico" Objective-C è fondamentalmente solo un conteggio dei riferimenti manuale con l'elemento umano sostituito dall'Analizzatore (e con molte restrizioni imposte al programmatore per rendere possibile ciò). Pensaci: il 95% (almeno) del conteggio dei riferimenti "manuale" in Objective-C è "automatico": le proprietà del puntatore vengono sempre mantenute e tu le rilasci sempre in "dealloc". Ecc. Mentre il nuovo supporto ARC è impressionante nella sua raffinatezza, non è così difficile da un problema concettualmente. –

1

Il consenso sulla RC vs tracing in ricerca informatica è stata, per lungo tempo, che ha tracciato il throughput della CPU superiore, nonostante più volte (massimo) di pausa. (Per esempio.vedi here, here e here.) Solo molto recentemente, nel 2013, è stato pubblicato un documento (ultimo collegamento sotto questi tre) che presenta un sistema basato su RC che offre prestazioni uguali o un po 'migliori rispetto al GC di tracciamento migliore, per quanto riguarda Throughput della CPU. Inutile dire che non ha ancora implementazioni "reali".

Ecco un piccolo punto di riferimento ho appena fatto sul mio iMac con 3.1 i5 GHz, in iOS 7.1 a 64 bit simulatore:

long tenmillion = 10000000; 
NSTimeInterval t; 

t = [NSDate timeIntervalSinceReferenceDate]; 
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:tenmillion]; 
for (long i = 0; i < tenmillion; ++i) 
    [arr addObject:[NSObject new]]; 
NSLog(@"%f seconds: Allocating ten million objects and putting them in an array.", [NSDate timeIntervalSinceReferenceDate] - t); 

t = [NSDate timeIntervalSinceReferenceDate]; 
for (NSObject *obj in arr) 
    [self doNothingWith:obj]; // Can't be optimized out because it's a method call. 
NSLog(@"%f seconds: Calling a method on an object ten million times.", [NSDate timeIntervalSinceReferenceDate] - t); 

t = [NSDate timeIntervalSinceReferenceDate]; 
NSObject *o; 
for (NSObject *obj in arr) 
    o = obj; 
NSLog(@"%f seconds: Setting a pointer ten million times.", [NSDate timeIntervalSinceReferenceDate] - t); 

Con ARC disabilitato (-fno-objc-arc), questo ha pronunciato la seguente:

2.029345 seconds: Allocating ten million objects and putting them in an array. 
0.047976 seconds: Calling a method on an object ten million times. 
0.006162 seconds: Setting a pointer ten million times. 

Con ARC abilitato, che diventa:

1.794860 seconds: Allocating ten million objects and putting them in an array. 
0.067440 seconds: Calling a method on an object ten million times. 
0.788266 seconds: Setting a pointer ten million times. 

A quanto pare l'allocazione obje i metodi di chiamata e di chiamata sono diventati in qualche modo più economici. L'assegnazione a un puntatore di oggetto è diventata più costosa per ordini di grandezza, sebbene non si dimentichi che non ho chiamato -retain nell'esempio non ARC e si noti che è possibile utilizzare __unsafe_unretained se si dispone di un hotspot che assegna puntatori di oggetti come pazzi. Ciononostante, se si vuole "dimenticare" la gestione della memoria e lasciare che ARC inserisca le chiamate di mantenimento/rilascio dove vuole, si sprecheranno, nel caso generale, molti cicli CPU, ripetutamente e in tutti i percorsi di codice che impostano i puntatori. D'altra parte un GC tracciante lascia il tuo codice da solo, e solo a calci in momenti selezionati (di solito quando si assegna qualcosa), facendo la sua cosa in un colpo solo. (Naturalmente i dettagli sono un molto più complicato in verità, dato GC generazionale, GC incrementale, GC concorrente, ecc)

Quindi sì, dal momento RC di Objective-C utilizza atomica mantenere/release, è piuttosto costoso ma Objective-C ha anche molte più inefficienze di quelle imposte dal Refcounting. (Ad esempio, la natura completamente dinamica/riflessiva dei metodi, che può essere "inondata" in qualsiasi momento dal run-time, impedisce al compilatore di eseguire molte ottimizzazioni incrociate che richiederebbero l'analisi del flusso di dati e così via. Un objc_msgSend () è sempre una chiamata a una black box "collegata dinamicamente" dalla vista dell'analizzatore statico, per così dire.) Tutto sommato, l'Objective-C come linguaggio non è esattamente il più efficiente o il meglio ottimizzato in circolazione; la gente lo chiama "sicurezza di tipo C con la velocità fulminante di Smalltalk" per una ragione. ;-)

Durante la scrittura di Objective-C, quello generalmente solo strumenti intorno ben implementati librerie di Apple, che utilizzano sicuramente C e C++ e montaggio o qualsiasi altra cosa per i loro hotspot. Il tuo codice raramente ha bisogno di essere efficiente. Quando c'è un punto caldo, puoi renderlo molto efficiente scendendo verso i costrutti di basso livello come il puro codice in stile C all'interno di un singolo metodo Objective-C, ma raramente ne hai mai bisogno. Ecco perché Objective-C può sostenere il costo dell'ARC nel caso generale. Non sono ancora convinto che tracciare GC abbia problemi di inerenti a in ambienti con limitazioni di memoria e pensare che si possa usare un linguaggio di alto livello per strumentare anche le librerie, ma a quanto pare RC si trova meglio con Apple/iOS. Si deve considerare l'intero quadro che hanno costruito finora e tutte le loro librerie legacy quando si chiedono perché non sono andati con un GC di tracciamento; per esempio ho sentito che RC è piuttosto profondamente integrato in CoreFoundation.