2012-04-28 21 views
18

ILSpy mostra che String.IsNullOrEmpty è implementato in termini di String.Length. Ma allora perché è String.IsNullOrEmpty(s) più veloce di s.Length == 0?Perché String.IsNullOrEmpty è più veloce di String.Length?

Per esempio, è 5% più veloce in questo benchmark:..

var stopwatches = Enumerable.Range(0, 4).Select(_ => new Stopwatch()).ToArray(); 
var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,STU,V,W,X,Y,Z,".Split(','); 
var testers = new Func<string, bool>[] { s => s == String.Empty, s => s.Length == 0, s => String.IsNullOrEmpty(s), s => s == "" }; 
int count = 0; 
for (int i = 0; i < 10000; ++i) { 
    stopwatches[i % 4].Start(); 
    for (int j = 0; j < 1000; ++j) 
     count += strings.Count(testers[i % 4]); 
    stopwatches[i % 4].Stop(); 
} 

(altri benchmark mostrano risultati simili Questo minimizzata l'effetto di cruft in esecuzione sul mio computer anche, per inciso, i test a confronto a stringhe vuote è uscito lo stesso a circa il 13% più lento di IsNullOrEmpty.)

Inoltre, perché è solo più veloce IsNullOrEmpty su x86, considerando che, il 64 String.Length è circa il 9% più veloce?

Aggiornamento: Dettagli di configurazione test: .NET 4.0 in esecuzione su Windows 7 a 64 bit, processore Intel Core i5, progetto console compilato con "Codice ottimizzato" abilitato. Tuttavia, è stata abilitata anche l'opzione "Sopprimi l'ottimizzazione JIT sul carico del modulo" (vedere risposta e commenti accettati).

Con ottimizzazione completamente abilitata, Length è circa il 14% più velocemente rispetto IsNullOrEmpty con il delegato e costo computazionale rimosso, come in questo test:

var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,,STU,V,,W,,X,,,Y,,Z,".Split(','); 
int count = 0; 
for (uint i = 0; i < 100000000; ++i) 
    count += strings[i % 32].Length == 0 ? 1 : 0; // Replace Length test with String.IsNullOrEmpty 
+7

Senza conoscere la situazione esatta in cui ci si trova, sono abbastanza fiducioso che nella maggior parte delle situazioni ci sono altre ottimizzazioni per agonizzare anziché il modo più rapido per verificare se una stringa non contiene dati. – Andrew

+0

@minitech Perché ci sono 4 tester e li sta cronometrando in modo indipendente. –

+0

Come 'Empty' e' == 'confronta con' Length' e 'IsNullOrEmpty'? –

risposta

20

È perché è stato eseguito il benchmark all'interno di Visual Studio che impedisce al compilatore JIT di ottimizzare il codice. Senza ottimizzazioni, questo codice viene prodotto per String.IsNullOrEmpty

00000000 push  ebp 
00000001 mov   ebp,esp 
00000003 sub   esp,8 
00000006 mov   dword ptr [ebp-8],ecx 
00000009 cmp   dword ptr ds:[00153144h],0 
00000010 je   00000017 
00000012 call  64D85BDF 
00000017 mov   ecx,dword ptr [ebp-8] 
0000001a call  63EF7C0C 
0000001f mov   dword ptr [ebp-4],eax 
00000022 movzx  eax,byte ptr [ebp-4] 
00000026 mov   esp,ebp 
00000028 pop   ebp 
00000029 ret 

e ora confrontarlo con il codice prodotto per Lunghezza == 0

00000000 push ebp 
00000001 mov ebp,esp 
00000003 sub esp,8 
00000006 mov dword ptr [ebp-8],ecx 
00000009 cmp dword ptr ds:[001E3144h],0 
00000010 je  00000017 
00000012 call 64C95BDF 
00000017 mov ecx,dword ptr [ebp-8] 
0000001a cmp dword ptr [ecx],ecx 
0000001c call 64EAA65B 
00000021 mov dword ptr [ebp-4],eax 
00000024 cmp dword ptr [ebp-4],0 
00000028 sete al 
0000002b movzx eax,al 
0000002e mov esp,ebp 
00000030 pop ebp 
00000031 ret 

Si può vedere, che codificano per Lunghezza == 0 fa tutto ciò che fa il codice per String.IsNullOrEmpty, ma in aggiunta prova qualcosa di simile a convertire folle valore booleano (restituito dal confronto lunghezza) aga in to boolean e questo lo rende più lento di String.IsNullOrEmpty.

Se si compila il programma con le ottimizzazioni abilitate (modalità di rilascio) e si esegue il file .exe direttamente da Windows, il codice generato dal compilatore JIT è molto meglio. Per stringa .IsNullOrEmpty è:

001f0650 push ebp 
001f0651 mov  ebp,esp 
001f0653 test ecx,ecx 
001f0655 je  001f0663 
001f0657 cmp  dword ptr [ecx+4],0 
001f065b sete al 
001f065e movzx eax,al 
001f0661 jmp  001f0668 
001f0663 mov  eax,1 
001f0668 and  eax,0FFh 
001f066d pop  ebp 
001f066e ret 

e per Lunghezza == 0:

001406f0 cmp  dword ptr [ecx+4],0 
001406f4 sete al 
001406f7 movzx eax,al 
001406fa ret 

Con questo codice, risultato sono come previsto, vale a dire Lunghezza == 0 è leggermente più veloce di String . IsNullOrEmpty.

Vale anche la pena ricordare che l'utilizzo di Linq, espressioni lambda e modulo di calcolo nel benchmark non è una buona idea, poiché queste operazioni sono lente (relativamente al confronto tra stringhe) e rendono impreciso il risultato del benchmark.

+0

Ottengo gli stessi risultati se eseguo i test dall'interno Ho eseguito i test all'interno o all'esterno di Visual Studio. In entrambi i casi, sto costruendo in modalità di rilascio su .NET Framework 4 e l'impostazione "Codice ottimizzato" è attiva nel file di progetto (impostazione predefinita). In Visual Studio, vedo il codice assembly non ottimizzato che hai pubblicato.Come si visualizza il codice assembly generato durante l'esecuzione all'esterno di Visual Studio? –

+0

È strano. Ho testato questo benchmark su 3 computer diversi con sistemi operativi diversi (server Windows 2008 x64, Windows XP x86) con diverse CPU e ho sempre capito che Length == 0 è più veloce. Inoltre ho disattivato la generazione di file .PDB in Visual Studio per questo progetto, ma probabilmente non è un problema. Hai provato un altro computer? Ho allegato al processo in esecuzione con [WinDbg] (http://archive.msdn.microsoft.com/debugtoolswindows) per vedere il codice assembly ottimizzato. –

+0

Ho ripreso gli esperimenti di inside vs. outside VS2010 e non sono riuscito a riprodurre ciò che ho riportato nel mio precedente commento. Ora vedo lo stesso come te, che 'Length' è un po 'più veloce quando fuori VS2010. Ho anche ricordato questa impostazione: Strumenti> Opzioni> Debug> Generale> Elimina l'ottimizzazione JIT sul carico del modulo. Mi ero dimenticato di spegnerlo. Quando l'ho fatto, ottengo gli stessi risultati dentro e fuori VS2010, con 'Length' essere più veloce. Inoltre, attivando l'impostazione di ottimizzazione JIT, posso riprodurre tutti i 4 elenchi di codici assembly in VS2010. –

-4

può essere causato dai tipi delle variabili coinvolte. * Vuoto sembra usare un booleano, una lunghezza e un int (credo).

Pace!

  • edit
+3

-1 per indovinare. E indovinando in modo errato. Mentre sei sicuramente incoraggiato a condividere le tue conoscenze in questo forum, indovinare a una risposta aggiunge rumore indesiderato al nostro discorso. –

1

Si test è somethere sbagliato. IsNullOrEmpty non può essere più veloce per definizione, poiché esegue un'operazione di confronto nullo aggiuntiva e quindi verifica la lunghezza.

Quindi la risposta può essere: è più veloce a causa del test. Tuttavia, anche il tuo codice mostra che IsNullOrEmpty è costantemente più lento sulla mia macchina in entrambe le modalità x86 e x64.

+0

Credo che IsNullOrEmpty possa essere eseguito più rapidamente nel caso di una stringa nulla, poiché il controllo della lunghezza non viene eseguito. Anche se dubito che si possa osservare un apprezzamento delle prestazioni apprezzabile, se la stringa è spesso considerata nulla, questo controllo potrebbe avere più senso. – overslacked

+1

Credo che non sia valido parlare di caso di stringhe 'null' dal momento che' .Length' non è applicabile in questo caso :) –

+0

Touché! . . . . – overslacked

4

Il benchmark non misura String.IsNullOrEmpty vs String.Length, ma piuttosto come vengono generate diverse espressioni lambda alle funzioni. Cioè non è molto sorprendente che il delegato che contiene solo una chiamata a funzione singola (IsNullOrEmpty) sia più veloce di uno con la funzione call e comparison (Length == 0).

Per ottenere il confronto di tutte le chiamate - codice di scrittura che le chiama direttamente senza delegati.

MODIFICA: le mie misurazioni approssimative mostrano che la versione delegata con IsNullOrEmpty è leggermente più veloce rispetto al resto, mentre le chiamate dirette allo stesso confronto sono nell'ordine inverso (e circa due volte più veloce a causa di un numero significativamente inferiore di codice aggiuntivo) sulla mia macchina . Risultati suscettibili di diffidare tra le macchine, la modalità x86/x64, nonché tra le versioni del runtime. Per scopi pratici, prenderei in considerazione tutti e 4 i modi sono quasi gli stessi se è necessario utilizzarli nelle query LINQ.

Nel complesso dubito che ci sia una differenza misurabile nel programma reale scelto per scelta tra questi metodi, quindi scegli quello che è più leggibile e usalo. Generalmente preferisco IsNullOrEmpty poiché offre meno possibilità di ottenere == /! = Errato in una condizione.

La rimozione della manipolazione di stringhe del tutto dal codice critico di tempo porterà probabilmente molto hifif che il picking tra queste scelte, anche l'eliminazione di LINQ per il codice critico è un'opzione. Come sempre, assicurati di misurare la velocità complessiva del programma nello scenario reale.

1

Credo che il test non è corretta:

Questo test dimostra che string.IsNullOrEmpty è sempre più lento di s.Length==0 perché esegue un ulteriore controllo nullo:

var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,STU,V,W,X,Y,Z,".Split(','); 
var testers = new Func<string, bool>[] { 
    s => s == String.Empty, 
    s => s.Length == 0, 
    s => String.IsNullOrEmpty(s), 
    s => s == "" , 
}; 
int n = testers.Length; 
var stopwatches = Enumerable.Range(0, testers.Length).Select(_ => new Stopwatch()).ToArray(); 
int count = 0; 
for(int i = 0; i < n; ++i) { // iterate testers one by one 
    Stopwatch sw = stopwatches[i]; 
    var tester = testers[i]; 
    sw.Start(); 
    for(int j = 0; j < 10000000; ++j) // increase this count for better precision 
     count += strings.Count(tester); 
    sw.Stop(); 
} 
for(int i = 0; i < testers.Length; i++) 
    Console.WriteLine(stopwatches[i].ElapsedMilliseconds); 

Risultati:

6573 
5328 
5488 
6419 

È possibile utilizzare s.Length==0 quando si è certi che i dati di destinazione non contengano stringhe null. In altri casi, ti suggerisco di utilizzare lo String.IsNullOrEmpty.

+0

Quando strutturo il test in questo modo, ottengo gli stessi risultati, ma una deviazione standard più elevata tra i test. Penso che sia perché è più facile per altri processi o codice OS influenzare un singolo tester ingiustamente. In media, trovo ancora "IsNullOrEmpty" più veloce su x86. Sono in esecuzione su un Core i5 a 64 bit. Trovate costantemente 'Length' è più veloce? –

0

Penso che sia impossibile IsNullOrEmpty essere più veloce perché come tutto il resto ha detto che fa anche un assegno per null. Ma più veloce o meno la differenza sarà così piccola, che questo dà un vantaggio sull'uso di IsNullOrEmpty proprio a causa di questo controllo null aggiuntivo che rende il tuo codice più sicuro.

-2

In CLR via CSharp capitolo 10 "Proprietà" Jeff Richter scrive:

Un metodo di proprietà può richiedere molto tempo per l'esecuzione; l'accesso al campo viene sempre completato immediatamente. Un motivo comune per utilizzare le proprietà è eseguire la sincronizzazione dei thread, che può interrompere il thread per sempre e, pertanto, non è necessario utilizzare una proprietà se è richiesta la sincronizzazione dei thread. In quella situazione, è preferito un metodo. Inoltre, se è possibile accedere alla classe in remoto (ad esempio, la classe è derivata da System.MarshalByRefObject), la chiamata del metodo property sarà molto lenta e, pertanto, un metodo viene preferito a una proprietà. A mio parere, le classi derivate da MarshalByRefObject non dovrebbero mai utilizzare le proprietà.

Quindi, se vediamo String.Length è di proprietà e String.IsNullOrEmpty è un metodo che può eseguire più velocemente di quanto la proprietà String.Length.

+1

Una proprietà esiste solo come metadata. Quando si "ottiene" la proprietà, viene chiamato un metodo regolare denominato 'get_PropertyName', e quando si" imposta "la proprietà viene chiamato un metodo regolare denominato" nome_proprietà_set ". Dal punto di vista della JIT e del tempo di esecuzione, non vi è alcuna differenza tra una proprietà e un metodo. –

Problemi correlati