2010-09-07 19 views

risposta

12

Non definito. Si richiede esplicitamente C#, ma questo è qualcosa che dipende dall'architettura del processore, cioè il compilatore di runtime CLR.

+1

Suppongo che questo sia vero per quasi tutti i linguaggi di programmazione. – Andy

+0

... e ciò che è meglio è determinato da ciò che è più leggibile nel contesto in cui è usato. –

44

Nessuno dei due; entrambi dovrebbero compilare la stessa cosa se uno è più veloce o migliore.

Ancora più importante, la maggior parte dei programmatori troverà probabilmente più leggibile > 0 e la leggibilità è più importante delle micro ottimizzazioni secondarie come questa.

+7

+1 per sottolineare l'importanza della leggibilità – Matt

+7

Non so riguardo alla tua leggibilità; Ho letto> = 1 come "1 o più", e questo è un modo più naturale per definire una condizione che "più di zero". Tuttavia, "più di cinque" ha perfettamente senso nella conversazione. Penso che la leggibilità dipenda dal contesto, dalla presenza e dalla formulazione di qualsiasi documento o guida dell'utente che gli sviluppatori guarderebbero in parallelo al codice. Dipende anche molto dal tipo di matematica coinvolta; come ha affermato Jesse Millikan, i due confronti non sarebbero equivalenti nel mondo a virgola mobile. – KeithS

+0

@KiethS: Ho letto l'intento di '> 0' come" intero positivo diverso da zero ". Leggere '> = 1' mi fa pensare a come gli interi non abbiano le cifre giuste del decimale per decidere che. E sì, le espressioni non sono le stesse; sebbene non abbia mai fatto programmi in virgola mobile estesi prima. Ci sono casi in cui '> = 1' sarà migliore? Sicuro. Ma dato il modo in cui la domanda è stata scritta, ho assunto "intero positivo non zero". –

7

La differenza di prestazioni sarà trascurabile tra i due (se ne esiste anche uno). Sto lavorando per dimostrare esattamente quello che potrebbe essere (dipenderà dalla piattaforma in quanto qualsiasi altro verrebbe probabilmente al codice emesso ed eseguito da JIT).

Tenete a mente, tuttavia, che le prestazioni in termini di micro-ottimizzazione estreme sono molto probabilmente ingiustificate.

La scelta migliore sarà quella che sarà sempre più leggibile e trasmetterà le tue intenzioni al meglio nel tuo codice.

1

Per rispondere a quello più veloce, non ne sono sicuro, ma penso che siano equivalenti. E per rispondere in meglio, penso che dipenda dal contesto.

0

Non si noterà alcuna differenza a meno che possibilmente in un ciclo molto stretto che è critico per le prestazioni nella propria applicazione. Quindi è necessario profilare il codice in ogni caso per decidere quale sia il migliore.

Utilizzare quello che ha più senso nella vostra applicazione.

26

Quello che è migliore è quello che esprime più chiaramente il tuo intento.

Se si esegue il test per vedere se un intero è nel range [1, 6] allora si dovrebbe scrivere come:

if (x >= 1 && x <= 6) { ... } 

Scrivendo questo avrebbe funzionato, ma non così ovviamente soddisfare le specifiche :

if (x > 0 && x < 7) { ... } 

Suppongo anche che si stia parlando di tipi interi qui. Se hai a che fare con numeri decimali o virgola mobile, allora non sono equivalenti.


meno che non hai profilato il codice e trovato che questo è il collo di bottiglia, non si deve preoccupare di micro-ottimizzazioni. Anche così può essere interessante esaminare il codice generato dal compilatore C# in ciascun caso per vedere se sono compilati o meno con lo stesso IL. Questo può essere fatto usando .NET Reflector.

if (x >= 1) 
{ 
    Console.WriteLine("True!"); 
} 

Risultati in:

L_000b: ldloc.0   // Load the value of x 
L_000c: ldc.i4.1   // Load the constant 1 
L_000d: blt.s L_0019  // Branch if less than 
L_000f: ldstr "True!" 
L_0014: call void [mscorlib]System.Console::WriteLine(string) 

considerando quanto segue:

if (x > 0) 
{ 
    Console.WriteLine("True!"); 
} 

risultati nei seguenti IL:

L_000b: ldloc.0   // Load the value of x 
L_000c: ldc.i4.0   // Load the constant 0 
L_000d: ble.s L_0019  // Branch if less than or equal 
L_000f: ldstr "True!" 
L_0014: call void [mscorlib]System.Console::WriteLine(string) 

In entrambi i casi il compilatore ha invertito il confronto. Il test "maggiore o uguale a" è stato compilato con un'istruzione "minore di" e il test "maggiore di" è stato compilato su "minore o uguale a". In generale, il compilatore è libero di apportare tali modifiche e l'esecuzione di una versione diversa del compilatore potrebbe produrre un bytecode diverso (ma equivalente).

Dato che non vengono compilati con lo stesso IL, il modo migliore per vedere quale è il più veloce è eseguire effettivamente il codice in un ciclo e vedere quanto tempo impiega ciascuna versione per l'esecuzione. Ho provato a farlo ma non ho visto alcuna differenza di prestazioni misurabili tra i due modi di scrivere il codice.

+0

Sono d'accordo! È ciò che ci si aspetta dalla dichiarazione ...> 0 e> = 1 fa davvero 2 cose diverse (e talvolta uguali) ... o le vuoi tutte o parte di essa, che nessuna di esse! –

+0

Mi piace che tu abbia mostrato l'IL, specialmente quando cambia il confronto nel bytecode. – Nick

0

Quindi di solito quando si confronta qualcosa con> 0 o> = 1, sto provando a vedere se una matrice/collezione contiene elementi. In questo caso, anziché utilizzare .Count > 0, prova a utilizzare il metodo di supporto Enumerable.Any() in System.Linq che dovrebbe essere molto più veloce.

In caso contrario, non so :)

+0

Perché 'Any()' dovrebbe essere più veloce? Penserei che sarebbe più lento, dal momento che 'Count' è solo un accessorio di proprietà ma' Any() 'deve enumerare il primo termine della sequenza. – jnylen

+0

@jnylen: dipende dal conteggio che stai utilizzando. 'Enumerable.Count()' è molto peggio di 'Enumerable.Any()'. Se il tuo contenitore ha una proprietà 'Count' (ad esempio' List') allora sì, dovresti usarlo. –

+2

Penso. Qualsiasi() restituisce .Count> 0 se la raccolta supporta quel membro. Ed è sempre più veloce di .Count() quando la raccolta non lo supporta. Non sono sicuro di cosa compaia IL, quindi potrebbe non essere di grande aiuto ... –

2

Non v'è alcuna differenza, perché la CPU non internamente una sottrazione dei due numeri e ispeziona il risultato e troppo pieno. Non ci sono passaggi aggiuntivi coinvolti in nessuna delle due istruzioni.

Quando si tratta di codice dipende da cosa si sta tentando di documentare. > = 1 significa che 1 è il numero più basso possibile. > 0 significa che 0 non è permesso. C'è una piccola differenza semantica che i professionisti noteranno. Sceglieranno l'operatore giusto per documentare il loro intento.

se si pensa che> = n e> = n + 1 sono gli stessi che si tratti di un errore:> = int.MaxValue e> (int.MaxValue + 1) sono diverse ^^

3

Sono d'accordo con il altre risposte che le micro-ottimizzazioni non dovrebbero essere prese in considerazione di solito. Tuttavia può essere interessante vedere quale delle due versioni ha IL/apparently_faster più piccolo.

Quindi:

using System; 

namespace IL_Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int i = 3; 
      if (i > 0) 
      { 
       Console.Write("i is greater than zero"); 
      } 
     } 
    } 
} 

si traduce in:

(DEBUG)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i, 
     [1] bool CS$4$0000) 
    L_0000: nop 
    L_0001: ldc.i4.3 
    L_0002: stloc.0 
    L_0003: ldloc.0 
    L_0004: ldc.i4.0 
    L_0005: cgt 
    L_0007: ldc.i4.0 
    L_0008: ceq 
    L_000a: stloc.1 
    L_000b: ldloc.1 
    L_000c: brtrue.s L_001b 
    L_000e: nop 
    L_000f: ldstr "i is greater than zero" 
    L_0014: call void [mscorlib]System.Console::Write(string) 
    L_0019: nop 
    L_001a: nop 
    L_001b: ret 
} 

(STAMPA)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i) 
    L_0000: ldc.i4.3 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: ldc.i4.0 
    L_0004: ble.s L_0010 
    L_0006: ldstr "i is greater than zero" 
    L_000b: call void [mscorlib]System.Console::Write(string) 
    L_0010: ret 
} 

mentre

using System; 

namespace IL_Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int i = 3; 
      if (i >= 1) 
      { 
       Console.Write("i is greater than zero"); 
      } 
     } 
    } 
} 

in

(DEBUG)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i, 
     [1] bool CS$4$0000) 
    L_0000: nop 
    L_0001: ldc.i4.3 
    L_0002: stloc.0 
    L_0003: ldloc.0 
    L_0004: ldc.i4.1 
    L_0005: clt 
    L_0007: stloc.1 
    L_0008: ldloc.1 
    L_0009: brtrue.s L_0018 
    L_000b: nop 
    L_000c: ldstr "i is greater than zero" 
    L_0011: call void [mscorlib]System.Console::Write(string) 
    L_0016: nop 
    L_0017: nop 
    L_0018: ret 
} 

(STAMPA)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i) 
    L_0000: ldc.i4.3 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: ldc.i4.1 
    L_0004: blt.s L_0010 
    L_0006: ldstr "i is greater than zero" 
    L_000b: call void [mscorlib]System.Console::Write(string) 
    L_0010: ret 
} 

Per quanto posso vedere la i> = 1 è leggermente più veloce rispetto i> 0 IN DEBUG MODE

In modalità di rilascio al l la differenza è all'offset 0004 a BLE vs a BLT. Suppongo che queste due operazioni di IL si traducano in opsioni nativi che consumano CPU.

+0

Sono quelli scambiati o mi manca qualcosa? –

+2

Dici che uno è leggermente più veloce - perché non hai pubblicato i risultati del tuo benchmark? Su quale macchina hai provato? Ottimizzazioni su? L'ottimizzazione è fatta dal JIT, non dal compilatore C#, quindi guardare l'IL generato non dice molto sulle prestazioni. – Niki

+2

@Caspar: il primo è in esecuzione: 'if (i> 0) == false', quindi restituisce (non console.write). Il secondo sta facendo: 'if (i <1) == true', quindi restituisce. L'IL prodotto dal test di Andrei sembra essere molto più prolisso di quello che Mark Byers ha trovato. Punto fatto da altri: eseguirlo, confrontarlo, chiedere è VERAMENTE più lento? Il codice qui può essere compilato con DEBUG, o con un'altra versione del compilatore .. non lo sappiamo. Sì, le JIT possono ottimizzare di più o meno. Benchmark .. – maxwellb

4

Naturalmente, dipende dall'architettura della CPU su cui verrà eseguito il programma. Su x86 le istruzioni jge e jg, che sono rilevanti qui, prendono lo stesso numero di cicli IIRC. Nel caso specifico di test per> 0, se si utilizzano numeri interi non firmati potrebbe essere più veloce utilizzare l'istruzione test anziché cmp, poiché per gli interi senza segno> 0 è equivalente a! = 0 Altre architetture possono essere diverse. Il punto è che questo è così di basso livello che, anche nei rari casi in cui vale la pena ottimizzare, non esiste un modo indipendente dall'hardware per ottimizzarlo.

Modifica: Ho dimenticato di menzionare: qualsiasi compilatore o VM degno di nota dovrebbe essere in grado di capire che il test> = 1 equivale a testare> 0 ed eseguire una ottimizzazione così banale se fa anche la differenza nel linguaggio assembly livello.

0

Se ci sarebbe una differenza tra i due, allora direi che questa sarebbe una micro-ottimizzazione, che non dovrebbe influire sulle prestazioni generali dell'applicazione.

Inoltre, quando si sta veramente cercando di capire se deve usare> 0 o> = 1, allora direi che il costo per capire quale è più veloce, non supera il beneficio (minimo) delle prestazioni.

Pertanto, direi anche che dovresti usare l'opzione che più esprime l'intenzione.