Mantieni ottimizzazioni locali, rendili ovvi, documentale bene e semplifica il confronto tra le versioni ottimizzate e la versione non ottimizzata, sia in termini di codice sorgente che di prestazioni in fase di esecuzione.
Risposta completa
Se tali ottimizzazioni sono così importanti per il tuo prodotto, allora devi sapere non solo perché le ottimizzazioni sono state utili in precedenza, ma anche fornire informazioni sufficienti per aiutare gli sviluppatori a sapere se saranno utili in il futuro.
Idealmente, è necessario archiviare i test delle prestazioni nel processo di compilazione, in modo da scoprire quando le nuove tecnologie invalidano le vecchie ottimizzazioni.
Ricorda:
The First Rule of Program Optimisation: Don't do it.
The Second Rule of Program Optimisation (for experts only!): Don't do it yet."
— Michael A. Jackson
Per sapere se ora è il momento che richiede benchmark e test.
Come accennato, il problema più grande con il codice altamente ottimizzato è che è difficile da mantenere così, per quanto possibile, è necessario mantenere le parti ottimizzate separate dalle parti non ottimizzate. Sia che si faccia questo attraverso il collegamento in fase di compilazione, le chiamate di funzioni virtuali di runtime o qualcosa in mezzo non dovrebbero avere importanza. Ciò che dovrebbe importare è che quando esegui i test, vuoi essere in grado di testare tutti delle versioni che ti interessano al momento.
Sarei propenso a costruire un sistema in modo tale che la versione base non ottimizzata del codice di produzione possa sempre essere utilizzata per comprendere l' intento del codice, quindi costruire diversi moduli ottimizzati insieme a questo contenente la versione o le versioni ottimizzate, che documentano esplicitamente ovunque la versione ottimizzata differisca dalla linea di base. Quando esegui i test (unità e integrazione), esegui la versione non ottimizzata e su tutti i moduli ottimizzati correnti.
Esempio
Ad esempio, supponiamo di avere una funzione Trasformazione di Fourier veloce . Forse hai un'implementazione di base algoritmica in fft.c
e prove in fft_tests.c
.
Poi arriva il Pentium e decidi di implementare la versione a virgola fissa in fft_mmx.c
usando istruzioni MMX . Successivamente arriva il pentium 3 e decidi di aggiungere una versione che utilizza Streaming SIMD Extensions in fft_sse.c
.
Ora vuoi aggiungere CUDA , quindi aggiungi fft_cuda.c
, ma lo trovi con il set di dati di prova che Ho usato per anni, la versione CUDA è più lenta della versione SSE! Fai qualche analisi e alla fine aggiungi un set di dati che è 100 volte più grande e ottieni la velocità che ti aspetti, ma ora sai che il tempo di installazione per usare la versione CUDA è significativo e che con piccoli set di dati dovresti usare un algoritmo senza il costo di impostazione.
In ognuno di questi casi si sta implementando lo stesso algoritmo, tutti dovrebbero comportarsi allo stesso modo, ma verranno eseguiti con diverse efficienze e velocità su architetture diverse (se verranno eseguite affatto). Dal punto di vista del codice, tuttavia, è possibile confrontare qualsiasi coppia di file sorgente per scoprire perché la stessa interfaccia è implementata in modi diversi e, in genere, il modo più semplice sarà fare riferimento alla versione originale non ottimizzata.
Lo stesso vale per un'implementazione OOP in cui una classe base che implementa l'algoritmo non ottimizzato e le classi derivate implementano diverse ottimizzazioni.
L'importante è mantenere le stesse cose che sono uguali , in modo che le differenze siano ovvie .