2015-11-19 21 views
6

Ho esaminato un codice di colleghi e gli ho detto di riordinare i confronti booleani nel seguente predicato Linq Any per motivi di prestazioni. Quindi, datoOrdine di valutazione C#

public class JobResult 
{ 
    public JobResult(); 

    public string Id{ get; set; } 
    public StatusEnum Status{ get; set; } 
    public string JobType{ get; set; } 
} 

e

IList<JobResult> jobsList = _jobRepository.FetchJobs() 

ho suggerito modificando la seguente:

//Exit if there is already a job of type "PurgeData" running 
if (jobsList.Any(job => job.Status == JobStatus.Running //1 
        && job.Id != currentJobId   //2 
        && job.JobType == "PurgeData")) //3 
    return false; 

per diventare

//Exit if there is already a job of type "PurgeData" running 
if (jobsList.Any(job => job.JobType == "PurgeData"  //3 
        && job.Status == JobStatus.Running //1 
        && job.Id != currentJobId))    //2 
    return false; 

Il mio ragionamento era che la maggior parte dei posti di lavoro in jobsList falliscono esimo e test per JobType, solo alcuni non supereranno il test per Running e solo uno fallirà il test per Id. Se una partita fallisce, non vi è alcun punto che valuti gli altri e, a causa dei punti di sequenza, ciò non si verificherà.

La mia domanda in tre parti è: è vero, è verosimilmente vero e c'è una spiegazione migliore che posso dare al mio collega perché il riordino è una buona idea?

+3

Tenere presente che un confronto 'int' o' enum' implica una singola istruzione macchina, mentre un confronto tra stringhe è molto più complicato. –

+1

Puoi provarlo spostando ogni clausola su una funzione e all'interno di quella funzione viene visualizzato un log o 'Console.WriteLine' per examlpe. – DavidG

+0

Ho messo il confronto delle stringhe per ultimo. Pensare a qualcosa di più difficile sarebbe una perdita di tempo. Il confronto tra 2 ints una o due volte non sarà il collo di bottiglia. – SimpleVar

risposta

7

Il mio ragionamento era che la maggior parte dei lavori in jobsList fallisce il test per JobType, solo alcuni falliscono il test per Running e solo uno fallirà il test per Id. Se una partita fallisce, non vi è alcun punto che valuti gli altri e, a causa dei punti di sequenza, ciò non si verificherà. È vero?

È vero che il secondo e il terzo predicato non verranno valutati quando il primo è falso? Sì. Il tuo ragionamento è corretto lì.

È vero che evitare la valutazione del secondo e terzo predicato quando il primo è falso è una chiara vittoria in termini di prestazioni? Non necessariamente, per due motivi.

Innanzitutto, non tutti i confronti sono ugualmente costosi. Confrontare le stringhe "PurgeData" e "PurgeDatz" richiede il confronto di otto caratteri prima del salvataggio; confrontare i numeri interi è più economico. Potrebbe essere nel caso medio meno costoso evitare il confronto tra stringhe anche se è più probabile che sia falso.

In secondo luogo, ricordare, evitando codice in esecuzione elimina il costo di tale codice, ma si è dovuto scrivere codice per verificare se l'altro codice deve essere evitato. Un test costa poco ma non è gratis! Ci sono situazioni in cui evitare codice è in realtà più costoso della semplice esecuzione.

Vedi il mio recente articolo sul tema:

http://ericlippert.com/2015/11/02/when-would-you-use-on-a-bool/

c'è una spiegazione migliore che posso dare al mio collega del motivo per cui riordino è una buona idea?

Sì. È possibile impostare una metrica di prestazioni e un obiettivo di prestazione incentrato sul cliente realistico, importante, è possibile dimostrare empiricamente che il codice in un modo non riesce a raggiungere l'obiettivo misurato in base alla metrica e si può dimostrare empiricamente che il codice soddisfa il proprio obiettivo quando lo scrivi in ​​un altro modo

Se non lo fai, ciò che stai descrivendo è una differenza che non puoi misurare e non interessa a nessuno; il tuo collega avrebbe perfettamente ragione a dirti di smettere di perdere tempo a cambiare codice funzionante per fare la differenza non puoi misurare a cui nessuno importa.

+0

Vorrei che più persone capissero che cercare di ottimizzare il codice senza misurare/profilare/raggiungere l'obiettivo è un esercizio infruttuoso. Se avessi un soldo per ogni volta che qualcuno ha suggerito questo tipo di modifica del codice, posso ritirarmi facilmente. – SolutionYogi

+0

Punti salienti @Eric ma non ho scritto alcun codice per testare nulla. Se sapessi che un'ottimizzazione era verosimilmente vera nel caso generale, allora avrei potuto strutturare il mio codice in quel modo sempre. Ho perso la spesa per il confronto delle stringhe, ma è per questo che sono qui ... per essere educato sul modo generalmente corretto di fare le cose. Ora posso applicare questo ragionamento in futuro e so che potrei non salvare alcun ciclo della CPU, ma non ne sto sprecando neanche uno. – cl0h

4

Il mio ragionamento era che la maggior parte dei posti di lavoro nel jobsList falliscono il test per jobtype, solo pochi falliranno il test per l'esecuzione e solo uno falliranno il test per l'identificazione. Se una partita fallisce, non vi è alcun punto che valuti gli altri e, a causa dei punti di sequenza, ciò non si verificherà.

Sì, le || e && operatori short-circut in C#. Significa che non continueranno a valutare altre espressioni se la condizione è già stata soddisfatta. Dato che JobType restituisce false, gli altri predicati non verranno calcolati. Tieni presente che il tuo codice potrebbe avere un overhead aggiuntivo che supera il riordino di tali predicati, come il confronto di uno int vs uno string (dove quest'ultimo probabilmente sarà più costoso).

questo non rientrano nella categoria di micro-ottimizzazioni, e diverso da quello che spiega che cosa corto circuito è ad un tuo collega, vi suggerisco di testare in realtà questo codice per vedere se fornisce alcun beneficio delle prestazioni a tutti.

+0

Il cortocircuito è il concetto a cui stavo pensando. Grazie – cl0h

+1

Penso che tu abbia colto il concetto di cortocircuito contrapposto alla spesa dei confronti ragionevolmente bene e attirato l'attenzione sul fatto che forse mi stavo concentrando sulla cosa sbagliata. – cl0h

4

Personalmente non mi dispiacerebbe troppo. Il sovraccarico dell'utilizzo di LINQ è molto più del confronto stesso. E se il primo predicato viene valutato come falso, tutti gli altri vengono ignorati, quindi testare il meno probabile prima è una buona idea.

Continuando sulla valutazione, le enumerazioni sono numeri interi, quindi confrontare un enum costa meno che valutare una stringa nel suo complesso. Anche la seconda valutazione è un intero, quindi meno che il confronto di una stringa. Ho pensato prima la lunghezza confrontata con lo string, quindi anche questo sarebbe un confronto tra interi, il confronto di ogni personaggio costa di più. Quindi se la stringa è uguale a dove viene confrontata, la valutazione della stringa è più pesante degli interi. Altrimenti non importa troppo.

+0

Alcune ottimizzazioni utili qui. Suggerirò di cambiare JobType da una stringa a un enum. Sembra che ciò fornirebbe un maggiore incremento delle prestazioni rispetto al riordino. – cl0h

+0

Infatti. Se possibile, sarebbe un'opzione. –

0

È molto facile perdere ore ottimizzando le cose che non sono lente.

Se si deve chiedere quale metodo è più veloce, non si può dire la differenza (cioè entrambi sono abbastanza veloci).

Le query Linq a volte vengono convertite in SQL, quindi la velocità può dipendere dall'implementazione interna del database, che è fuori dal vostro controllo.

Problemi correlati