Come rispettare la regola "allocare in chiamante" quando la dimensione è calcolata nel callee?

2

Diciamo che abbiamo un tipo opaco handle_t gestito tramite una semplice interfaccia con funzioni come:

handle_t *handle_init(void);
int handle_do(handle_t *);
void handle_free(handle_t *);

Il problema è che la dimensione non può essere sempre determinata nel passo di inizializzazione e deve essere calcolata in handle_do . Una soluzione che ho visto è chiamare handle_do due volte, la prima delle quali restituisce la dimensione necessaria per l'allocazione, ma ciò non influisce sulle prestazioni?

È una cattiva pratica allocare o riallocare la memoria in handle_do ? Ha importanza anche quando si usano i tipi di handle (dato che le routine init / free sono state chiarite)?

    
posta Nick 10.09.2014 - 04:46
fonte

3 risposte

2

La tua domanda è un po 'poco chiara. Non c'è alcuna "dimensione" nel tuo esempio di codice, immagino tu stia parlando delle dimensioni di alcuni array interni del tipo "handle_t". Suppongo inoltre nel momento in cui viene chiamato handle_init , non è chiaro quante volte verrà chiamato handle_do , o quali parametri aggiuntivi a handle_do saranno passati, con un impatto diverso sulla memoria necessaria.

In realtà, da un punto di vista dell'utente della tua API, non dovrebbe importare come e dove viene eseguita l'allocazione. Se "handle_t" è un tipo opaco, il modulo dovrebbe astrarre la strategia di allocazione della memoria interna dall'utente del modulo. Se e dove si assegnano, realloc e liberano le strutture di dati interne dovrebbero essere tenuti nascosti dall'esterno. In nessun caso l'utente del modulo deve chiamare il handle_do due volte da solo (in una "modalità di calcolo" e una "modalità di allocazione") se l'unica ragione della doppia chiamata è il calcolo della memoria. Sarebbe indecifrabile e quindi soggetto a errori.

Quindi cosa fare internamente, all'interno del tuo modulo? Questo dipende Se ti ho capito bene, stai pensando di fare qualche realloc in handle_do , o alternativamente di fare alcuni dei passaggi di calcolo della dimensione della memoria due volte, una volta solo per scoprire quanta memoria hai intenzione di allocare, e una volta per l'effettiva allocazione. Entrambe possono essere strategie praticabili, ed entrambi hanno alcuni inconvenienti. Molte riallocazioni possono avere un impatto negativo sulle prestazioni, ma anche eseguire due volte calcoli intensivi e il secondo può rendere il codice più complicato, quindi c'è sempre un compromesso.

Come compromesso, ti suggerisco di verificare se è possibile implementare il tuo tipo seguendo le linee del seguente esempio: l'implementazione std::vector in C ++.

Un std::vector ha una dimensione e una capacità, dove la dimensione è sempre inferiore o uguale alla capacità. Quando un'operazione vettoriale aumenta la dimensione oltre la capacità, la capacità viene automaticamente aumentata (di un fattore moltiplicativo, non solo di 1!). Ciò porterà a una riallocazione interna, ma per la maggior parte dei casi del mondo reale non troppe riallocazioni. L'utente di un vettore può anche chiedere più capacità alla "inizializzazione", in modo da ridurre il numero di riallocazioni necessarie in un secondo momento.

Quindi nel tuo caso: controlla se handle_init può fare un po 'di allocazione in anticipo, e se handle_do nota che la memoria preassegnata è troppo piccola, verrà automaticamente riallineata (con un margine extra se ti aspetti ulteriori operazioni che potrebbero bisogno di più memoria interna).

    
risposta data 10.09.2014 - 08:33
fonte
0

One solution I have seen is calling handle_do two times, the first of which returns the size needed to allocate, but doesn't that affect the performance?

Chiedere prestazioni come questa è la domanda sbagliata da porre. Prendi in considerazione l'implementazione, la definizione dei requisiti di rendimento, la misurazione delle prestazioni e, se si tratta effettivamente di un problema, puoi prendere in considerazione l'implementazione di una soluzione alternativa.

Ho visto la "chiamata API due volte, una volta per i parametri per la seconda chiamata" solo nell'API di Windows. In particolare, la scelta di chiamare le API due volte in questo modo è dannosa per i client (nell'API di Windows, ottenendo una dannata stringa di errore, sembra di scrivere mezzo romanzo di codice). Rompe anche SRP .

Idealmente, dovresti ottimizzare il tuo design per il contrario (rendere il codice client più facile da scrivere, a spese dell'implementazione dell'API, se necessario).

Is it a bad practice to allocate or reallocate memory in handle_do?

No.

Does it even matter when using handle types (since the init/free routines are made clear)?

No, non è così. Devi solo ricordarti di testarlo per verificare combinazioni di API non valide (ad esempio, aggiungere test come chiamare handle_init, handle_free, handle_do , in questo ordine).

The problem is that the size cannot be always determined in the initialization step and it has to be calculated in handle_do.

La dimensione è visibile nel codice cliente? Questa dimensione è importante nel codice cliente? Se la risposta ad entrambe le domande è "no", la dimensione è solo un dettaglio di implementazione (cioè non dovrebbe essere rilevante nel codice client). Se la risposta è "sì" ad entrambe le domande, dovresti probabilmente aggiungere un'API per calcolare questa dimensione come operazione separata.

    
risposta data 10.09.2014 - 14:15
fonte
0

Tutti i tipi hanno le loro dimensioni determinate in fase di compilazione. Per handle_t modificare la sua dimensione in runtime, i dati devono essere al di fuori del sistema di tipi. std :: vector è un modo per spostare i dati al di fuori del sistema di tipi, dal momento che mantiene un'area di memoria separata per array allocati su heap. All'array non è associato alcun tipo, solo gli elementi hanno un tipo. sizeof(std::vector<int>) non contiene il contenuto dell'array.

Considerate queste restrizioni, è necessario memorizzare un puntatore sul blocco di memoria di dimensioni variabili che deve essere riallocato quando le dimensioni cambiano.

    
risposta data 09.10.2014 - 02:40
fonte

Leggi altre domande sui tag