Overhead di paradigmi fail-fast

1

Fail-fast sembra un modo giusto poiché semplifica il rilevamento dei bug. Ma è un danno per le prestazioni a causa del controllo multiplo della stessa cosa a diversi livelli del sistema.

Semplice esempio. C'è una funzione che i parametri di input non devono essere nulli. E c'è una funzione che lo avvolge, che attende anche gli stessi parametri anche non nulli. Dopo alcune attività, function-wrapper passa i parametri di input al primo. In modo che gli stessi elementi vengano controllati due volte: all'inizio della funzione wrapper e all'interno della funzione wrapping.

Quindi vorrei sapere quanto sia diffuso questo stile. Dovrei scrivere codice fail-fast o controllare tutto solo una volta?

    
posta SerG 29.01.2014 - 14:07
fonte

5 risposte

10

Ti manca un punto vitale: non è né uno né uno scenario.

Devi solo controllare i parametri "non attendibili". In generale, questo significa i confini della tua interfaccia pubblica. Se hai una catena di funzioni pubbliche che chiamano altre funzioni pubbliche e così via, sì, dovrai controllare l'input più volte. Forse hai anche bisogno di rivisitare il tuo design per astrarre e incapsulare correttamente cose che forse non hanno bisogno di essere pubbliche.

Un altro punto in cui ciò si verifica sta lavorando con componenti che possono fallire (come database o connessioni di rete). Se una connessione di rete fallisce, non si limita a passare null al backup dello stack di chiamate, lo si identifica in anticipo e si genera un'eccezione o si interrompe in altro modo. Non è necessario controllare il valore di ritorno fino allo stack.

    
risposta data 29.01.2014 - 15:20
fonte
5

Il controllo anticipato delle precondizioni sarà rapido o veloce rispetto a facendo gli stessi controlli nel mezzo del tuo calcolo . La parte in corsivo è dove il tuo ragionamento va fuori strada. Fail-fast / early checking non significa che controlli più cose o controlli più spesso, significa che esegui gli stessi checks prima nel calcolo (preferibilmente prima di iniziare a fare un "vero lavoro" in una funzione).

Per prendere l'esempio di due funzioni ( Foo e Bar ) dove entrambi richiedono che i loro parametri siano non nulli e Foo invochi Bar passando alcuni dei suoi parametri.

Con il controllo iniziale, entrambe le funzioni verificano se i loro parametri soddisfano il presupposto di non essere nulli.
Senza un controllo preliminare, entrambe le funzioni devono ancora assicurarsi che i loro parametri non siano nulli, ma il controllo viene ritardato fino al primo utilizzo del parametro e, nel caso peggiore, deve essere ripetuto ad ogni utilizzo successivo (a seconda della struttura del codice).

Se, senza controllo iniziale, Bar non verifica che i parametri non siano nulli (credendo che il controllo sia già stato fatto in Foo ), allora lo stesso trust può essere usato per escludere il controllo nel caso di controllo iniziale.

    
risposta data 29.01.2014 - 14:51
fonte
2
  1. Utilizza riferimenti e strumenti di analisi statica per evitare un sovraccarico del codice di produzione con controllo banale.

  2. Gestisci sempre condizioni impreviste reali (al contrario di immaginarie). La gestione dovrebbe arrestare build di sviluppo / debug, ma dovrebbe registrare il messaggio e gestire il problema nei build di produzione.

  3. Comprimere la richiesta di arresto anomalo / log in una macro breve. Supponiamo che "soft_err ()" sia più che sufficiente: non c'è alcun punto nei messaggi elaborati: non accadrebbe abbastanza spesso da giustificare la digitazione, il nome del file e il numero di riga saranno sufficienti per eseguire il debug dei problemi dal campo e per i build di sviluppo lì saranno core-dump.

  4. Nota: poiché la gestione di condizioni impreviste non si verifica spesso, è accettabile che sia relativamente costosa: ad esempio, la funzione di back-end per registrare o arrestarsi invece di fare affidamento su macro, rende le funzioni di back-end installate in collegare il tempo per adattare il codice a vari ambienti ecc.

I vantaggi combinati superano di gran lunga i costi di esecuzione del controllo.

    
risposta data 29.01.2014 - 15:12
fonte
1

@DieterLucking ha assolutamente ragione, ma volevo aggiungere a ciò che ha detto. Quella regola non si applica solo al C ++ ma a quasi tutte le lingue, comprese quelle gestite.

L'idea di fail veloce è che non esagerare e fornire tutti i tipi di controllo e gestione delle eccezioni per "provare" a continuare a funzionare quando ci sono condizioni di errore impreviste perché hai l'impressione che il mio prodotto sia 24/7 deve correre. Essere in grado di eseguire 24 ore su 24, 7 giorni su 7, è un grande requisito e tutti possiamo sforzarci di farlo, ma la realtà è che il prodotto ha bisogno di eseguire correttamente 24/7. Ma il software ha un bug occasionale, alcuni sono recuperabili ma molti non lo sono. In questi casi, si potrebbe anche andare in crash, perché un processo del server che va in crash, passa in modalità offline per 20 secondi e viene riavviato è ancora un'alternativa migliore di un processo del server che rimane in esecuzione per ore ma gestisce le richieste in entrata in modo errato per ore fino al riavvio di un utente esso.

Quindi cosa significa per te come sviluppatore? Se ci sono circostanze impreviste, non controllarle (non le anticiperai mai nemmeno se ci provi) e lasci semplicemente che il tuo codice si blocchi. Quando ciò accade, sarai in grado di catturare un crash dump e determinare la posizione esatta del crash e spesso la causa principale.

E prima che qualcuno lo legga, si innervosisce e inizia a commentare come difendo la scrittura di codice che va in crash e in che modo mi rende uno sviluppatore terribile, ecco una piccola storia.

Un ingegnere viene da me per chiedere aiuto. Dice che ha passato un'intera settimana a lavorare su un problema con il cliente e che non ha idee. Possono duplicare il problema seguendo una serie esatta di passaggi, che richiede l'apertura della finestra di dialogo A, facendo clic su un gruppo di pulsanti, quindi chiudendo la finestra di dialogo e andando a una parte completamente diversa dell'applicazione. Se esegui esattamente questi passaggi, l'app si arresta in modo anomalo ma non riesce a capire perché.

Quindi insieme daremo un'occhiata al crash dump e vediamo che l'heap dei processi è stato corrotto, ma non ci sono indicazioni su dove o come ciò sia accaduto. Dopo un bel po 'di scavo abbiamo trovato il codice nel dialogo A che assomigliava a questo:

try {
    ... do some work
}
catch( ... ) {
    ... not even a log statement here ...
}

Quindi il codice nella finestra di dialogo A stava andando alla fine e stava facendo cose davvero brutte. Anziché arresto anomalo precoce con una traccia dello stack che mostra il problema esatto, lo sviluppatore ha deciso che gli arresti anomali sono negativi e l'app deve rimanere in esecuzione. Così, invece, l'app si bloccherebbe da 15 minuti a 4 ore più tardi in un luogo completamente casuale quando stavate facendo un'azione completamente innocua. Quel sviluppatore non ha aiutato nessuno con il suo codice protetto.

    
risposta data 29.01.2014 - 17:24
fonte
0

In C / C ++ è anche comune non applicare alcun controllo (se non si dispone di codice di debug). Avere una funzione come memcpy e far sì che il controllo degli argomenti implichi un serio sovraccarico generale. Quindi le condizioni preliminari vengono applicate all'utilizzo della funzione (attribuendo tutte le responsabilità al programmatore). Il migliore 'fail fast' è un errore al momento della compilazione. Con C ++ potrebbe essere una funzione prendere un riferimento e nessun puntatore (ma non impedirà a un programmatore di dereferenziare un puntatore e passare quello).

    
risposta data 29.01.2014 - 14:55
fonte

Leggi altre domande sui tag