2009-10-29 10 views
5

Ho un programma scritto in C# dove ci sono molti confronti tra interi e stringhe.E 'più efficiente confrontare ints e ints o stringhe e stringhe

Quindi, per motivi di prestazioni, vorrei solo sapere quale è più efficiente?

Se abbiamo:

int a = 5; 
string b = "5"; 

if(a == int.Parse(b)) { } 

O

if(a.ToString() == b) { } 
+4

Risposta di magazzino per queste domande: hai provato a profilare effettivamente i dati? –

+0

Im a noob:/Come faccio a profilare questo? –

+0

Esegui entrambe le varianti in un ciclo di alcune migliaia o milioni di iterazioni e misura il tempo necessario. – Joey

risposta

4

Alcuni commenti hanno menzionato l'esecuzione di uno strumento di profilatura per dimostrare quale ha prestazioni migliori.

Questo è un problema, ma il modo più semplice per verificare l'esecuzione di istruzioni specifiche è metterli in un ciclo e utilizzare la classe Cronometro.

Jeff Atwood ha chiesto di rendere questo tipo di tempistica ancora più semplice in this question. In quella domanda e risposta troverai anche alcuni esempi di codice e dettagli di sfondo.

Heres un esempio molto semplice di lavoro:

System.Diagnostics.Stopwatch sw=new System.Diagnostics.Stopwatch(); 


    int a = 5; 
    string b = "5"; 

    sw.Start(); 

    for (int i=0;i<1000000;i++) 
    { 
     if(a == int.Parse(b)) 
     { 

     } 
    } 

    sw.Stop(); 

    Console.WriteLine("a == int.Parse(b) milliseconds: " + sw.ElapsedMilliseconds); 

    sw.Reset(); 

    sw.Start(); 

    for (int i=0;i<1000000;i++) 
    { 
     if(a.ToString() == b) 
     { 

     }  
    }  

    sw.Stop(); 

    Console.WriteLine("a.ToString() == b milliseconds: " + sw.ElapsedMilliseconds); 

Sul mio computer essa stampa:

a == int.Parse (b) millisecondi: 521

a.ToString() = = b millisecondi: 697

Quindi in questo semplice scenario int.Parse() è leggermente più veloce, ma non abbastanza per preoccuparsi veramente.

+0

è la classe Debug Stopwatch? soz im un po 'di un principiante quando si tratta di C# :) –

+1

È la classe System.Diagnostics.Stopwatch. Dovresti essere in grado di digitare solo: cronometro sw = nuovo cronometro(); e iniziare a usarlo (è in System.dll). – Ash

+0

+1, questo non è il caso dell'utilizzo di un profiler "reale". – SoftMemes

1

Maggiore è il numero andrò per il primo metodo. a. se b non è un numero, fallirà prima di provare a confrontare. b. la stringa viene confrontata in base alla lunghezza e al numero contemporaneamente.

0

L'analisi di una stringa su Int32 richiede più prestazioni ed è più sensibile agli errori. Dovrai assicurarti che Int32.Parse abbia successo in primo luogo. Inoltre puoi usare un'alternativa per '=='. Usa .Equals(), questo è più facile da leggere e capire.

if(b.Equals(a)) 
{ 

} 
+3

1.Iquals ("1") si compilano, ma restituiscono false. Questo non è ciò che vuole. – SoftMemes

0

Inoltre ho letto somwhere (MSDN) utilizzando il seguente è più veloce di == per i confronti di stringhe

StringA.ToUpperInvariant() == StringB.ToUpperInvariant() 
+0

Qual è il vantaggio dell'uso della maiuscola quando si confrontano i numeri? – Kobi

+0

Lo stesso se si trattasse di stringhe? –

+0

http://msdn.microsoft.com/en-us/library/ms973919.aspx –

0

Ci sono molti modi di rappresentare lo stesso numero come stringa ...

7

Io in realtà profilato questo usando alcuni esempi e cicli temporizzati. Risulta Parse vince per i piccoli interi e ToString vince per quelli grandi. Questa differenza è così piccola che non dovrebbe essere una preoccupazione, tuttavia, come altri hanno già detto, è probabile che tu possa fare una scelta migliore pensando ai casi in cui la stringa non rappresenta affatto un intero.

Edit: Per chi fosse interessato, ecco la fonte, veloce 'n' sporca:

using System; 
using System.Diagnostics; 

namespace CompareTest 
{ 
    static class Program 
    { 
     static void Main(string[] args) 
     { 
      int iterations = 10000000; 
      int a = 5; 
      string b = "5"; 

      Stopwatch toStringStopwatch = new Stopwatch(); 
      toStringStopwatch.Start(); 

      for (int i = 0; i < iterations; i++) { 
       bool dummyState = a.ToString() == b; 
      } 

      toStringStopwatch.Stop(); 

      Stopwatch parseStopwatch = new Stopwatch(); 
      parseStopwatch.Start(); 

      for (int i = 0; i < iterations; i++) { 
       bool dummyState = a == int.Parse(b); 
      } 

      parseStopwatch.Stop(); 

      Console.WriteLine("ToString(): {0}", toStringStopwatch.Elapsed); 
      Console.WriteLine("Parse(): {0}", parseStopwatch.Elapsed); 
      Console.ReadLine(); 
     } 
    } 
} 
4

La vostra scelta è tra il seguente
Codice A

int a = 5; 
string b = "5"; 
//Assuming these two values are input received by the application at runtime 
int bInt; 
if (int.TryParse(b, NumberStyles.None, CultureInfo.InvariantCulture, out bInt) 
    && a.Equals(bInt)) 
{ 

} 

e

Codice B

int a = 5; 
string b = "5"; 
//Assuming these two values are input received by the application at runtime 
if (string.Compare(b, a.ToString(), StringComparison.Ordinal) != -1) 
{ 

} 

Ho testato questo con il cronometro (come indicato nella risposta selezionata) e abbiamo trovato un Codice di essere molto più veloce. Ma il Codice B è più leggibile!

Codice A batte if(a == int.Parse(b))

1

dubito sia chiamata avrà un impatto davvero significativo l'applicazione a meno che davvero sta creando qualcosa su una grande scala.

Entrambe le tecniche sono la creazione di una nuova stringa, tuttavia int.ToString() deve eseguire molto meno compiti di int.Parse().

int.ToString() viene eseguita internamente al CLR (comnumber). int.Parse() viene eseguito all'interno di BCL source utilizzando Number.ParseInt32() ->Number.StringToNumber() ->Number.ParseNumber().

ParseNumber esegue un numero enorme di controlli, quindi da un dito nell'aria indovinate che potreste immaginare int.ToString() è più veloce. Come altri hanno già menzionato, un buon test delle prestazioni con la classe StopWatch sarà un modo migliore per scoprirlo. Dovresti provare questo con il formato numerico che ti aspetti: decimale, esadecimale.

È possibile confrontare il C++ che il CLR sta usando per ToString() here: Cercare

  • NumberToString (che viene utilizzato per ToString() e altri formati), che viene utilizzato in FCIMPL3, chiamato da int. ToString() come una chiamata extern.
  • Int32ToDecStr viene utilizzato per il formatter "D".

Il C#

var x = 5.ToString("D"); 
var y = 5.ToString(); 

potrei sbagliarmi su FCIMPL3, per favore correggetemi se sono.

3

Internamente, ToString e Parse effettuare le seguenti operazioni:

Parse

value = 0 
for each char in string 
    value = value * 10 + valueof(char) // i.e. '0' -> 0, '7' -> 7 

ToString

string="" 
while value > 0 
    string.insert_at_front value % 10 // so that 0 -> '0' and 6 -> '6' 
    value /= 10 

// on IA32, the % and/can be done at the same time but requires 
// a 64bit source for 32bit values 

Il ToString deve essere più lenta rispetto Parse poiché divisione è generalmente più lento di moltiplicazione. Tuttavia, quanto sopra non tiene conto di eventuali overhead che le funzioni Parse e ToString potrebbero eseguire durante la conversione (cioè generare eccezioni, allocare memoria), il che significa che non è così netta che sarà più ottimale.

Dalle altre risposte sembra che la differenza sia comunque marginale, quindi basta usare quello che ha più senso per voi.

2

Hai già ricevuto qualche buona risposta, ma lascia che aggiunga un paio di piccoli punti.

  1. Uno dei rischi noti con micro-benchmark è che un piccolo numero di ripetizioni può finire rumore di misura (ad esempio, i tempi può essere inclinato di un messaggio in arrivo o IM), ma un gran numero di le ripetizioni possono finire per misurare le prestazioni del tuo garbage collector (ad esempio se il tuo codice crea costantemente e scartando le stringhe).

  2. Quando mi trovo in una posizione scomoda nel codice, a volte è utile chiedere a me stesso, "Quali sono le ipotesi o scelte mi ha messo in questa situazione? Cosa avrei potuto fare in modo diverso?" Per esempio (solo indovinare), quando hai scritto "... dove ci sono un sacco di confronti tra di int * [sic] * e archi", non che implica che si può essere utilizzando più volte gli stessi valori (ad esempio, confrontando i nuovi valori rispetto ai valori precedenti)? In tal caso, è possibile convertire ciascuna stringa in int e memorizzare nella cache il valore convertito per il successivo riutilizzo, invece di doverlo riconvertire successivamente?

2

Il mio caso che mi ha portato qui è stato quello di verificare se "5" == 5 in uno switch caso e perché io sarò sempre ricevere i numeri da 0 a 9 ho scoperto che il modo più veloce è: (int)b[0] == 53 Quindi sto prendendo il primo carattere della stringa "5" (b [0]) e lo getto al valore ACSII che è 53 e dopo questo confronto. Ecco i risultati: a == int.Parse(b) milliseconds: 194 a.ToString() == b milliseconds: 142 a == (int)(b[0]) milliseconds: 8 Anche se è molto raro che la differenza su un enorme array sia ovvia;

EDIT: come richiesto da Dirk Horsten. Sbagliato come sono. Ho detto nel mio post che uso che in un caso interruttore in modo userò in tutti i miei casi, i valori ASCII in modo che sarà simile a quanto segue: switch((int)b[0]) { case 48: Console.WriteLine("0"); break; case 49: Console.WriteLine("1"); break; case 50: Console.WriteLine("2"); break; case 51: Console.WriteLine("3"); break; case 52: Console.WriteLine("4"); break; case 53: Console.WriteLine("5"); break; case 54: Console.WriteLine("6"); break; case 55: Console.WriteLine("7"); break; case 56: Console.WriteLine("8"); break; case 57: Console.WriteLine("9"); break; } E per una buona causa ordine Ecco i risultati, come mi hai chiesto: a == int.Parse(b) milliseconds: 184 a.ToString() == b milliseconds: 135 a + 48 ==(int)b[0] milliseconds: 8 Come si può vedere, non c'è una grande differenza aggiungendo solo un'aggiunta.

+0

la tua risposta è brillante :) ma sbagliato :(La cosa corretta da fare è 'a + 48 == (int) (b [0])', perché '(int) (" 0 "[0]) == 48 'Per favore, esegui il tuo benchmark con questo codice e comunicaci i risultati. –