ciao, sono lo spoiler di questo post! |
#include <stdio.h> #include <time.h> #include <stdlib.h> #include <vector> #define MYSIZE 1000000 // variabile dummy per evitare lo svuotamento totale delle funzioni usando GCC -O2 int avoid_optimization; // prototipi locali void testVLA(int size); void testMallocVLA(int size); void testStackFLA(int dum); void testHeapFLA(int dum); void testAllocaVLA(int size); void testVectorVLA(int size); void testNewVLA(int size); void runTest(int iterations, void (*funcptr)(int), int size, const char *name); // funzione main() int main(int argc, char* argv[]) { // test argomenti if (argc != 2) { // errore: conteggio argomenti errato printf("%s: wrong arguments counts\n", argv[0]); printf("usage: %s vla iterations [e.g.: %s 10000]\n", argv[0], argv[0]); return EXIT_FAILURE; } // estrae iterazioni int iterations = atoi(argv[1]); // esegue test runTest(iterations, &testVLA, MYSIZE, "testVLA"); runTest(iterations, &testMallocVLA, MYSIZE, "testMallocVLA"); runTest(iterations, &testStackFLA, 0, "testStackFLA"); runTest(iterations, &testHeapFLA, 0, "testHeapFLA"); runTest(iterations, &testAllocaVLA, MYSIZE, "testAllocaVLA"); runTest(iterations, &testVectorVLA, MYSIZE, "testVectorVLA"); runTest(iterations, &testNewVLA, MYSIZE, "testNewVLA"); // esce return EXIT_SUCCESS; } // funzione testAllocaVLA() void testAllocaVLA( int size) // size per alloca() { int *allocavla = (int*)alloca(size * sizeof(int)); // loop di test for (int i = 0; i < size; i++) allocavla[i] = i; // istruzione per evitare lo svuotamento totale della funzione usando GCC -O2 avoid_optimization = allocavla[size / 2]; } // funzione testNewVLA() void testNewVLA( int size) // size per new { int *newvla = new int[size]; // loop di test for (int i = 0; i < size; i++) newvla[i] = i; // istruzione per evitare lo svuotamento totale della funzione usando GCC -O2 avoid_optimization = newvla[size / 2]; delete[] newvla; }Come potete vedere, le due funzioni aggiunte sono perfettamente allineate stilisticamente con le altre che avevo già proposto e sono, come sempre, iper-commentate, così non devo neanche dilungarmi in spiegazioni. E i risultati del test? Vediamoli!
aldo@ao-linux-nb:~/blogtest$ g++ vlacpp.cpp -o vlacpp aldo@ao-linux-nb:~/blogtest$ ./vlacpp 2000 testVLA - Tempo trascorso: 4.318492 secondi testMallocVLA - Tempo trascorso: 3.676805 secondi testStackFLA - Tempo trascorso: 4.339859 secondi testHeapFLA - Tempo trascorso: 4.340040 secondi testAllocaVLA - Tempo trascorso: 3.678644 secondi testVectorVLA - Tempo trascorso: 10.934088 secondi testNewVLA - Tempo trascorso: 3.679624 secondi aldo@ao-linux-nb:~/blogtest$ g++ -O2 vlacpp.cpp -o vlacpp aldo@ao-linux-nb:~/blogtest$ ./vlacpp 2000 testVLA - Tempo trascorso: 0.746956 secondi testMallocVLA - Tempo trascorso: 0.697261 secondi testStackFLA - Tempo trascorso: 0.696310 secondi testHeapFLA - Tempo trascorso: 0.700047 secondi testAllocaVLA - Tempo trascorso: 0.691677 secondi testVectorVLA - Tempo trascorso: 1.384563 secondi testNewVLA - Tempo trascorso: 0.695037 secondiAllora, cosa si può dire? I risultati dei test dei post precedenti li abbiamo già ampliamene commentati, quindi ora possiamo solo aggiungere che: alloca() è molto veloce, visto che è, in pratica, una malloc() nello stack (e, usandola in maniera appropriata, potrebbe/dovrebbe essere la più veloce del gruppo). E la new? Beh, si comporta (come previsto) benissimo, anche perché, quasi sempre, la new usa internamente la malloc().
Va bene, la alloca() è veloce, ma lo è (solo un po' meno) anche un VLA, e questo non lo ha salvato dal essere eletto come cattivo del film. Quindi dovremo fare di nuovo una lista di pro e contro, e vedere quale parte è più pesante. Vediamo prima i pro:
- la alloca() è molto veloce, già che usa lo stack invece del heap.
- la alloca() è facile da usare, è una malloc() senza free(). La variabile allocata ha uno scope a livello di funzione, quindi rimane valida fino a quando la funzione ritorna al chiamante, esattamente come una qualsiasi variabile automatica locale (anche un VLA funziona più o meno così, ma il suo scope è a livello di blocco, non di funzione, e questo è, probabilmente, un punto a favore dei VLAs).
- per il motivo visto al punto 2 la alloca() non lascia in giro residui di memoria in caso di errori gravi nella attività di una funzione (e con malloc() + free() non è altrettanto facile realizzare questo). Se poi siete soliti a usare cosucce come longjmp() i vantaggi in questo senso sono grandissimi.
- a causa della sua implementazione interna (senza entrare in dettagli profondi) la alloca() non causa frammentazione della memoria.
- la gestione degli errori è problematica, perché non c'è maniera di sapere se alloca() ha allocato bene o ha provocato un stack overflow (in questo caso provoca effetti simili a quelli di un errore per ricorsione infinita)... uh, questo è esattamente lo stesso problema dei VLAs.
- la alloca() non è molto portatile, visto che non è una funzione standard e il suo funzionamento/presenza dipende molto dal compilatore in uso.
- la alloca() è error prone (parte 1): bisogna usarla con attenzione, visto che induce, tipicamente, a errori come usare la variabile allocata quando oramai non è più valida (passarla con un return o inserirla dentro una struttura dati esterna alla funzione, per esempio)... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
- la alloca() è error prone (parte 2): ci sono problemi ancora più sottili da considerare nell'uso, ad esempio può risultare MOLTO pericoloso mettere una alloca() dentro un loop o in una funzione ricorsiva (povero stack!) o in una funzione inline (che usa lo stack in una maniera che si scontra un po' con la maniera di usare lo stack della alloca())... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
- la alloca() è error prone (parte 3): la alloca() usa lo stack, che è normalmente limitato rispetto allo heap (specialmente negli ambienti embedded che sono molto frequentati dai programmatori C...). Quindi esaurire lo stack e provocare uno stack overflow è facile (e difficile da controllare, vedi il punto 1)... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
Ciao e al prossimo post!
Nessun commento:
Posta un commento