Meriti della semantica copy-on-write

10

Mi chiedo quali sono i possibili vantaggi che ha il copy-on-write? Naturalmente, non mi aspetto opinioni personali, ma scenari pratici del mondo reale in cui può essere tecnicamente e praticamente utile in modo tangibile. E per tangibile intendo qualcosa di più che salvarti la digitazione di un carattere & .

Per chiarire, questa domanda è nel contesto dei tipi di dati, in cui l'assegnazione o la costruzione di copia crea una copia implicita superficiale, ma le modifiche apportate creano una copia implicita e applica le modifiche ad esso anziché l'oggetto originale.

La ragione per cui la sto chiedendo è che non trovo alcun merito di avere COW come comportamento implicito predefinito. Io uso Qt, che ha implementato COW per molti dei tipi di dati, praticamente tutti che hanno una memoria allocata dinamicamente sottostante. Ma in che modo può davvero giovare all'utente?

Un esempio:

QString s("some text");
QString s1 = s; // now both s and s1 internally use the same resource

qDebug() << s1; // const operation, nothing changes
s1[o] = z; // s1 "detaches" from s, allocates new storage and modifies first character
           // s is still "some text"

Cosa vinciamo usando COW in questo esempio?

Se tutto ciò che intendiamo fare è usare le operazioni const, s1 è ridondante, potrebbe anche usare s .

Se intendiamo modificare il valore, allora COW ritarda la copia della risorsa solo fino alla prima operazione non const, al costo (anche se minimo) di incrementare il conteggio ref per la condivisione implicita e la disconnessione dalla memoria condivisa. Sembra che tutto il sovraccarico di COW sia inutile.

Non è molto diverso nel contesto del passaggio dei parametri - se non si intende modificare il valore, passare come riferimento const, se si desidera modificare, si effettua una copia implicita se non si vuoi modificare l'oggetto originale o passare per riferimento se desideri modificarlo. Ancora una volta COW sembra un sovraccarico inutile che non raggiunge nulla e aggiunge solo una limitazione che non è possibile modificare il valore originale anche se lo si desidera, poiché qualsiasi modifica si scollegherà dall'oggetto originale.

Quindi, a seconda che tu sappia di COW o di esserne ignaro, potrebbe comportare un codice con intenzioni oscure e overhead inutili, o un comportamento completamente confuso che non corrisponde alle aspettative e ti lascia grattando la testa.

Per me sembra che ci siano soluzioni più efficienti e più leggibili se si vuole evitare una copia profonda non necessaria o se si intende crearne una. Allora, dov'è il vantaggio pratico di COW? Presumo che ci deve essere qualche vantaggio in quanto utilizzato in un framework così popolare e potente.

Inoltre, da quello che ho letto, COW è ora esplicitamente proibito nella libreria standard C ++. Non so se il contro che vedo in esso abbia qualcosa a che fare con esso, ma in ogni caso, ci deve essere una ragione per questo.

    
posta dtech 26.11.2015 - 18:41
fonte

2 risposte

14

Copia su scrittura viene utilizzata in situazioni in cui molto spesso si crea una copia dell'oggetto e non lo si modifica. In quelle situazioni, si ripaga da solo.

Come hai detto, puoi passare un oggetto const e in molti casi è sufficiente. Tuttavia, const garantisce solo che il chiamante non possa mutarlo (a meno che non sia const_cast , ovviamente). Non gestisce i casi di multithreading e non gestisce i casi in cui vi sono callback (che potrebbero mutare l'oggetto originale). Passare un oggetto COW in base al valore pone la sfida di gestire questi dettagli sullo sviluppatore dell'API piuttosto che sull'utente API.

Le nuove regole per C + 11 vietano COW per std::string in particolare. Gli iteratori su una stringa devono essere invalidati se il buffer di backup è staccato. Se l'iteratore è stato implementato come char* (al contrario di string* e un indice), questi iteratori non sono più validi. La comunità C ++ doveva decidere quanto spesso gli iteratori potevano essere invalidati e la decisione era che operator[] non dovrebbe essere uno di quei casi. operator[] su std::string restituisce un char& , che può essere modificato. Pertanto, operator[] avrebbe bisogno di staccare la stringa, invalidando gli iteratori. Questo è stato considerato un cattivo scambio e, a differenza di funzioni come end() e cend() , non c'è modo di chiedere la versione const di operator[] short di const che getta la stringa. ( related ).

COW è ancora vivo e ben al di fuori dell'STL. In particolare, ho trovato molto utile nei casi in cui è irragionevole per un utente delle mie API aspettarsi che ci sia qualche oggetto pesante dietro quello che sembra essere un oggetto molto leggero. Potrei desiderare di usare COW in background per assicurarmi che non debbano mai preoccuparsi di tali dettagli di implementazione.

    
risposta data 26.11.2015 - 20:12
fonte
5

Per le stringhe e simili sembra che potrebbe pessimizzare casi d'uso più comuni del solito, dal momento che il caso comune per le stringhe è spesso stringhe di piccole dimensioni, e il sovraccarico di COW tende a superare di gran lunga il costo della semplice copia della stringa piccola . Una piccola ottimizzazione del buffer ha molto più senso per me al fine di evitare l'allocazione dell'heap in questi casi invece delle copie di stringa.

Se hai un oggetto più pesante, tuttavia, come un androide, e volevi copiarlo e sostituire solo il suo braccio cibernetico, COW sembra abbastanza ragionevole come un modo per mantenere una sintassi mutabile evitando la necessità di copiare in profondità l'intero Android solo per dare alla copia un braccio unico. Rendendolo semplicemente immutabile come una struttura dati persistente a quel punto potrebbe essere superiore, ma una "COW parziale" applicata alle singole parti Android sembra ragionevole per questi casi.

In tal caso le due copie dell'androide condividono / eseguono lo stesso busto, gambe, piedi, testa, collo, spalle, bacino, ecc. L'unico dato che sarebbe diverso tra loro e non condiviso è il braccio che è stato reso unico per il secondo androide a sovrascrivere il suo braccio.

    
risposta data 11.12.2015 - 04:06
fonte

Leggi altre domande sui tag