2010-03-31 14 views
14

C'è un modo per migliorare questo:Qual è il modo più veloce per contare le nuove righe in una stringa .NET di grandi dimensioni?

private static int CountNewlines(string s) 
{ 
    int len = s.Length; 
    int c = 0; 
    for (int i=0; i < len; i++) 
    { 
     if (s[i] == '\n') c++; 
    } 
    return c; 
} 

Sono particolarmente preoccupato per la funzione di accesso voce sulla corda. Non sono sicuro se si tratta solo di aritmetica puntatore come C/C++.

+0

Da dove viene la stringa? Suppongo che dal momento che si parla di perf è una grande stringa? Se questa stringa di grandi dimensioni proviene da un file o da una chiamata al servizio web, la domanda deve essere "il modo più rapido per contare le nuove righe in un flusso". Il motivo per cui si converte l'intera stringa in una stringa sarà costoso. – Simon

+2

BTW, questo potrebbe davvero essere un pesce d'aprile, non posso dirlo più :-) –

+0

Sono preoccupato per il fatto che il primo è potenzialmente grande (32k) e # 2, è usato in un metodo OnPaint() in un controllo Windows Form. Quindi è chiamato * spesso *. – Cheeso

risposta

20

ho provato queste implementazioni

private static int Count1(string s) 
{ 
    int len = s.Length; 
    int c = 0; 
    for (int i=0; i < len; i++) 
    { 
     if (s[i] == '\n') c++; 
    } 
    return c+1; 
} 

private static int Count2(string s) 
{ 
    int count = -1; 
    int index = -1; 

    do 
    { 
     count++; 
     index = s.IndexOf('\n', index + 1); 
    } 
    while (index != -1); 

    return count+1; 
} 

private static int Count3(string s) 
{ 
    return s.Count(c => c == '\n') + 1; 
} 


private static int Count4(string s) 
{ 
    int n = 0; 
    foreach(var c in s) 
    { 
     if (c == '\n') n++; 
    } 
    return n+1; 
} 

private static int Count5(string s) 
{ 
    var a = s.ToCharArray(); 
    int c = 0; 
    for (int i=0; i < a.Length; i++) 
    { 
     if (a[i]=='\n') c++; 
    } 
    return c+1; 
} 

Qui ci sono i miei risultati di temporizzazione per 100000 iterazioni su una serie di ~ 25k. Più basso è più veloce.

   Time Factor 
Count1 4.8581503  1.4 
Count2 4.1406059  1.2 
Count3 45.3614124 13.4 
Count4 3.3896130  1.0 
Count5 5.9304543  1.7 

Sorprendentemente, per me, l'attuazione Enumeratore stato il più veloce per me, da un grado significativo - 20% più veloce rispetto a quello successivo più vicino implementazione. I risultati sono stati ripetibili, indipendentemente dall'ordine in cui sono stati eseguiti i metodi. Ho anche usato una fase di riscaldamento per assicurare effetti transitori (jit, ecc.).

Questo era per una versione di build (/ optimize +)

+0

Bel benchmarking – jefissu

4

Questa è probabilmente l'opzione più efficiente - l'elemento accessor è ottimizzato internamente e lo si può trattare come se eseguisse il puntatore aritmetico.

0

è possibile convertire la stringa in un array di caratteri con "ToCharArray();" ma non credo che migliorerà le prestazioni .. potresti provare a usare il codice non sicuro (puntatore) invece di ma che ha i suoi svantaggi.

+0

Giusto, potrei farlo. Non l'ho provato È una stringa grande, e sarà chiamata ripetutamente, quindi sono propenso a evitare di creare nuovi array solo per contarli. – Cheeso

+0

Sarà chiamato sulla stessa stringa più di una volta? Si potrebbe avere un dictonary con à weakreference come chiave e un int come risultato. In questo modo è possibile memorizzare nella cache il risultato. – Peter

6

Sono abbastanza sicuro che non sarà molto più lento della conversione della stringa in byte e del controllo di quelli, se non più veloce. La classe String dovrebbe essere altamente ottimizzata.

Se questo è un grande stringa, forse un esecuzione parallela da più thread renderà le cose più veloce :-)

+0

L'esecuzione parallela accelera le cose solo su una macchina multicore/multiprocessore, ma può essere un'opzione per * veramente * grandi stringhe. – LBushkin

+0

Certo, e non ha senso creare più thread rispetto alla quantità totale di core. Se questa stringa contiene molte interruzioni di riga, potrebbe essere solo un file di testo molto grande o qualcosa del genere, quindi l'OP potrebbe effettivamente utilizzare questa opzione ... –

0

Ne fanno un metodo di istanza, se lo userete in un ciclo.

+0

Come funziona? –

+0

come quando si utilizza systemIO, chiamando file.Copy e new FileInfo .. .Copy(). E 'da O'Reilly C# Cookbook –

2

Beh, String implementa IEnumerable<char>, quindi mi piacerebbe provare:

s.Count(c => c == '\n') 

bello come questo sembra, il metodo originale è 30x più veloce :)

non ho rinunciato alla IEnumerable ancora, così ho anche provato:

int n = 0; 
foreach(var c in s) 
{ 
    if (c == '\n') n++; 
} 
return n; 

che sembra veloce come il metodo originale.

Problemi correlati