Perché ho bisogno di std :: get_temporary_buffer?


84

Per quale scopo dovrei usare std::get_temporary_buffer? Standard dice quanto segue:

Ottiene un puntatore alla memoria sufficiente per memorizzare fino an oggetti T adiacenti.

Ho pensato che il buffer verrà allocato sullo stack, ma non è vero. Secondo lo standard C ++ questo buffer non è in realtà temporaneo. Quali vantaggi ha questa funzione rispetto alla funzione globale ::operator new, che non costruisce nemmeno gli oggetti. Ho ragione che le seguenti affermazioni sono equivalenti?

int* x;
x = std::get_temporary_buffer<int>( 10 ).first;
x = static_cast<int*>( ::operator new( 10*sizeof(int) ) );

Questa funzione esiste solo per la sintassi dello zucchero? Perché c'è temporarynel suo nome?


Un caso d'uso è stato suggerito nel Dr. Dobb's Journal, 1 luglio 1996 per l'implementazione di algoritmi:

Se non è possibile allocare alcun buffer, o se è più piccolo di quanto richiesto, l'algoritmo funziona ancora correttamente, si limita a rallentare.


2
Cordiali saluti, std::get_temporary_buffersarà deprecato in C ++ 17.
Deqing

1
@Deqing Sì. Verrà rimosso anche in C ++ 20 e per una buona ragione (come quelle menzionate di seguito). Quindi muoviti spettatore ..
Nikos

Risposte:


44

Stroustrup dice in "The C ++ Programming Language" ( §19.4.4 , SE):

L'idea è che un sistema possa mantenere un numero di buffer di dimensioni fisse pronti per l'allocazione rapida in modo che la richiesta di spazio per n oggetti possa produrre spazio per più di n . Tuttavia, può anche produrre meno, quindi un modo di utilizzare get_temporary_buffer()è chiedere con ottimismo molto e quindi utilizzare ciò che risulta essere disponibile.
[...] Poiché get_temporary_buffer()è di basso livello e può essere ottimizzato per la gestione dei buffer temporanei, non dovrebbe essere utilizzato come alternativa a new o allocator :: allocate () per ottenere una memoria a lungo termine.

Inizia anche l'introduzione alle due funzioni con:

Gli algoritmi richiedono spesso uno spazio temporaneo per funzionare in modo accettabile.

... ma non sembra fornire una definizione di temporaneo oa lungo termine da nessuna parte.

Un aneddoto in "Dalla matematica alla programmazione generica" afferma che Stepanov ha fornito un'implementazione di segnaposto fasulla nel design STL originale, tuttavia:

Con sua sorpresa, ha scoperto anni dopo che tutti i principali fornitori che forniscono implementazioni STL stanno ancora utilizzando questa terribile implementazione [...]


12
Sembra che l'implementazione di VC ++ sia semplicemente un ciclo che chiama operator newcon argomenti successivamente più piccoli fino a quando l'allocazione ha successo. Nessuna ottimizzazione speciale lì.
jalf

9
Lo stesso con g ++ 4.5 - sembra che fosse ben intenzionato ma ignorato dai fornitori.
Georg Fritzsche

4
Sembra che avrebbero dovuto crazy_allocator
racchiudere

Ma restiamo seri: forse uno sarebbe felice di allocare molto spazio di archiviazione. Quello che ora possiamo fare è chiedere al sistema di ottenere quell'enorme quantità di spazio di archiviazione, utilizzando get_temporary_buffer. Tuttavia, se riceviamo meno dell'importo richiesto (cosa sarebbe un vero peccato) continuiamo a provare a fare il nostro lavoro con lo spazio di archiviazione che abbiamo. Potrebbe essere meglio che rilevare le bad_alloceccezioni causate dal tentativo di allocare più memoria di quella disponibile. Tuttavia, la reale utilità rimane e cade con una buona implementazione.
0xbadf00d

@jalf È deprecato in C ++ 17 :) (secondo cppreference.com ).
4LegsDrivenCat

17

Il ragazzo della libreria standard di Microsoft dice quanto segue ( qui ):

  • Potresti forse spiegare quando utilizzare "get_temporary_buffer"

Ha uno scopo molto specializzato. Nota che non genera eccezioni, come new (nothrow), ma non costruisce nemmeno oggetti, a differenza di new (nothrow).

Viene utilizzato internamente dall'STL in algoritmi come stable_partition (). Questo accade quando ci sono parole magiche come N3126 25.3.13 [alg.partitions] / 11: stable_partition () ha complessità "Al massimo (last - first) * log (last - first) swap, ma solo numero lineare di swap se esiste è sufficiente memoria extra. " Quando compaiono le parole magiche "se c'è abbastanza memoria extra", l'STL usa get_temporary_buffer () per tentare di acquisire spazio di lavoro. Se è possibile, può implementare l'algoritmo in modo più efficiente. Se non può, perché il sistema sta funzionando pericolosamente vicino all'esaurimento della memoria (o gli intervalli coinvolti sono enormi), l'algoritmo può ricorrere a una tecnica più lenta.

Il 99,9% degli utenti STL non avrà mai bisogno di sapere di get_temporary_buffer ().


9

Lo standard dice che alloca lo spazio di archiviazione per un massimo di n elementi. In altre parole, il tuo esempio potrebbe restituire un buffer abbastanza grande solo per 5 oggetti.

Tuttavia, sembra piuttosto difficile immaginare un buon caso d'uso per questo. Forse se stai lavorando su una piattaforma molto limitata dalla memoria, è un modo conveniente per ottenere "quanta più memoria possibile".

Ma su una piattaforma così limitata, immagino che aggireresti il ​​più possibile l'allocatore di memoria e utilizzeresti un pool di memoria o qualcosa su cui hai il pieno controllo.


4

Per quale scopo dovrei usare std::get_temporary_buffer?

La funzione è deprecata in C ++ 17, quindi la risposta corretta ora è "inutile, non usarla".


2
ptrdiff_t            request = 12
pair<int*,ptrdiff_t> p       = get_temporary_buffer<int>(request);
int*                 base    = p.first;
ptrdiff_t            respond = p.sencond;
assert( is_valid( base, base + respond ) );

rispondere può essere inferiore alla richiesta .

size_t require = 12;
int*   base    = static_cast<int*>( ::operator new( require*sizeof(int) ) );
assert( is_valid( base, base + require ) );

la dimensione effettiva della base deve essere maggiore o uguale a richiedere .


2

Forse (solo una supposizione) ha qualcosa a che fare con la frammentazione della memoria. Se continui ad allocare e deallocare pesantemente la memoria temporale, ma ogni volta che lo fai assegni una certa memoria a lungo termine dopo aver allocato il temp ma prima di deallocarlo, potresti finire con un mucchio frammentato (immagino).

Quindi il get_temporary_buffer potrebbe essere inteso come un pezzo di memoria più grande del necessario che viene allocato una volta (forse ci sono molti blocchi pronti per accettare più richieste), e ogni volta che hai bisogno di memoria ne prendi solo uno dei pezzi. Quindi la memoria non viene frammentata.


1
Pensiero molto interessante. Anche se sembra essere attualmente implementato come lascia-solo-fare-qualcosa-che-funziona dalla maggior parte delle implementazioni, questo potrebbe benissimo essere supportato più strettamente e integrato con il resto delle routine di gestione della memoria. Voto per noi aspettandoci effettivamente che questo sia adatto per questo, e anche i commenti di Bjarnes sembrano suggerirlo.
gustaf r

Ho cercato ora ciò che Bjarne dice al riguardo e dice che è progettato per un'allocazione rapida senza inizializzazione. Quindi sarebbe come un operatore void * solo dell'allocatore (non inizializzatore) new (size_t size), ma è più veloce da allocare, poiché è pre-allocato.
Daniel Munoz
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.