2010-03-31 27 views
14

¿Hay una manera de mejorar esto:¿Cuál es la manera más rápida de contar nuevas líneas en una cadena .NET grande?

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; 
} 

Estoy particularmente preocupado por el descriptor de acceso Punto de la cadena. No estoy seguro de si es solo una aritmética de puntero como C/C++.

+0

dónde viene la cadena viene? Supongo que ya que están preocupados por perf es una cadena grande? Si esta cadena grande proviene de un archivo o una llamada a un servicio web, entonces la pregunta debe ser "la forma más rápida de contar las nuevas líneas en una secuencia". La razón por la cual convertir todo en una cadena será costoso. – Simon

+2

Por cierto, esto podría ser realmente un tonto de abril, no puedo decir más :-) –

+0

Me preocupa la perforación porque # 1, es potencialmente grande (32k) y # 2, está siendo utilizada en un método OnPaint() en un control de Windows Forms. Por lo tanto, se llama * a menudo *. – Cheeso

Respuesta

20

He probado estas implementaciones

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; 
} 

Aquí están mis resultados de los tiempos de 100000 iteraciones en una cadena de ~ 25k. Más bajo es más rápido.

   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, para mí, la implementación enumerador fue el más rápido para mí, por un grado significativo - 20% más rápido que la siguiente aplicación más cercana. Los resultados fueron repetibles, independientemente del orden en que se ejecutaron los métodos. También utilicé una fase de calentamiento para asegurar que los efectos transitorios (jit, etc.) fueron eliminados.

Ésta era una liberación de construcción (/ Optimizar +)

+0

Buena evaluación comparativa – jefissu

4

Esta es probablemente la opción más eficiente - el elemento de acceso está optimizado internamente y puede tratarlo como si realizara un punteo aritmético.

0

puede convertir la cadena en una matriz char con "ToCharArray();" pero no creo que mejore el rendimiento ... podrías intentar usar un código inseguro (puntero) en lugar de hacerlo, pero eso tiene sus desventajas.

+0

Correcto, podría hacer eso. No he intentado eso. Es una cadena grande, y se llamará repetidamente, por lo que me inclino a evitar crear nuevas matrices solo para contarla. – Cheeso

+0

¿Esto se llamará en la misma cadena más de una vez? Podría tener un dictonary con una referencia débil como clave y un int como resultado. De esta manera puede almacenar en caché el resultado. – Peter

6

Estoy bastante seguro de que esto no será mucho más lento que la conversión de la cadena a bytes y la comprobación de esos, si no más rápido. La clase String debe estar altamente optimizada.

Si se trata de una cadena grande, tal vez una ejecución paralelapor varios hilos hará las cosas más rápido :-)

+0

La ejecución paralela solo aceleraría las cosas en una máquina multinúcleo/multiprocesador, pero puede ser una opción para cadenas * realmente * grandes. – LBushkin

+0

Claro, y no tiene sentido crear más hilos que la cantidad total de núcleos. Si esta cadena contiene muchos saltos de línea, puede que se trate de un archivo de texto muy grande o algo así, por lo que el OP realmente puede usar esta opción ... –

0

Que sea un método de instancia, si lo vas a usar en un bucle.

+0

¿Cómo es esto importante? –

+0

como cuando se utiliza systemIO, archivo de llamadas.Copia y nuevo FileInfo ... .Copy(). Es de O'Reilly C# Cookbook –

2

Bueno, String implementa IEnumerable<char>, por lo que definitivamente iba a tratar:

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

Tan agradable como esto se ve, el método original es 30 veces más rápido :)

no he renunciado a la sin embargo, IEnumerable, por lo que también he intentado:

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

que parece más rápido que el método original.

Cuestiones relacionadas