2011-10-28 29 views
6

Esta simple pieza de código C# que pretende encontrar bloques de script en HTML tarda 0,5 segundos en ejecutarse en una cadena de caracteres de 74K con solo 9 bloques de script. Este es un binario de descarga sin amortiguar en una CPU i7 de 2.8Ghz. Realicé varias ejecuciones a través de este código para asegurarme de que el rendimiento no se vea obstaculizado por JIT. No lo es.string.IndexOf performance

Este es VS2010 .NET 4.0 Client Profile. x64

¿Por qué es esto tan lento?

   int[] _exclStart = new int[100]; 
       int[] _exclStop = new int[100]; 
       int _excl = 0; 
       for (int f = input.IndexOf("<script", 0); f != -1;) 
       { 
        _exclStart[_excl] = f; 
        f = input.IndexOf("</script", f + 8); 
        if (f == -1) 
        { 
         _exclStop[_excl] = input.Length; 
         break; 
        } 
        _exclStop[_excl] = f; 
        f = input.IndexOf("<script", f + 8); 
        ++_excl; 
       } 
+0

intente utilizar la expresión regular que se compila –

+0

Tengo que adivinar que * la entrada * es en realidad una propiedad que es muy costosa de recuperar. Hacer una copia. –

+0

de entrada es una cadena que ya está allí. Tiene más de 74,000 caracteres. No hay una penalización de perf por retirarlo. – Ghostrider

Respuesta

16

I utiliza la fuente en esta página como un ejemplo, que entonces duplicado el contenido 8 veces, resultando en una página algunos 334.312 bytes de longitud. Usar StringComparision.Ordinal produce una diferencia de rendimiento masiva.

string newInput = string.Format("{0}{0}{0}{0}{0}{0}{0}{0}", input.Trim().ToLower()); 
//string newInput = input.Trim().ToLower(); 

System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); 
sw.Start(); 
int[] _exclStart = new int[100]; 
int[] _exclStop = new int[100]; 
int _excl = 0; 
for (int f = newInput.IndexOf("<script", 0, StringComparison.Ordinal); f != -1;) 
{ 
    _exclStart[_excl] = f; 
    f = newInput.IndexOf("</script", f + 8, StringComparison.Ordinal); 
    if (f == -1) 
    { 
     _exclStop[_excl] = newInput.Length; 
     break; 
    } 
    _exclStop[_excl] = f; 
    f = newInput.IndexOf("<script", f + 8, StringComparison.Ordinal); 
    ++_excl; 
} 
sw.Stop(); 
Console.WriteLine(sw.Elapsed.TotalMilliseconds); 

correr 5 veces produce casi el mismo resultado para cada uno (los tiempos de bucle no cambió significativamente por lo que para este código simple que casi no hay tiempo para JIT compilarlo)

de salida usando el original código (en milisegundos):

10.2786 
11.4671 
11.1066 
10.6537 
10.0723 

salida utilizando el código de seguridad en su lugar (en milisegundos):

0.3055 
0.2953 
0.2972 
0.3112 
0.3347 

en cuenta que los resultados de mis pruebas son alrededor de 0,010 segundos (código original) y 0,0003 segundos (para el código ordinal). Lo que significa que tienes algo más equivocado aparte de este código directamente.

Si, como dice, utilizar StringComparison.Ordinal no significa nada para su rendimiento, significa que utiliza temporizadores incorrectos para medir el tiempo o tiene una gran sobrecarga al leer su valor input, como leerlo de nuevo de una secuencia que de lo contrario no te das cuenta.

Probado en Windows 7 x64 ejecutándose en un 3GHz i5 utilizando .NET 4 Client Profile.

Sugerencias:

  1. uso StringComparison.Ordinal
  2. Asegúrese de que está utilizando System.Diagnostics.Stopwatch de rendimiento en tiempo
  3. declara una variable local para el input en lugar de utilizar los valores externos a la función (por ejemplo: string newInput = input.Trim().ToLower();)

Nuevamente, recalco, obtengo 50 veces más rápido velocidad para datos de prueba aparentemente más de 4 veces más grandes utilizando exactamente el mismo código que usted proporciona. Lo que significa que mi prueba se está ejecutando unas 200 veces más rápido que la tuya, que no es algo que nadie esperaría dado que ambos estamos ejecutando el mismo entorno y solo i5 (me) versus i7 (usted).

+0

El OP dijo que su código se ejecuta en 0.5 segundos. Sus tiempos parecen en el mismo orden ... –

+0

mis tiempos devueltos son 'TotalMilliseconds' no' TotalSeconds' son significativamente más rápidos. Lo que significa que el problema de rendimiento de OP no está directamente en esta sección de código. – Seph

+0

Perdí eso. Estás en lo correcto. –

3

Recomendaría usar RegEx para esto, ofrece una mejora significativa en el rendimiento porque las expresiones se compilan solo una vez. Mientras que IndexOf es esencialmente un bucle que se ejecuta por carácter, lo que probablemente significa que tienes 3 "bucles" dentro de tu bucle principal, por supuesto, IndexOf no será tan lento como un bucle regular, pero aún cuando el tamaño de entrada aumente el tiempo aumenta Regex tiene funciones incorporadas que devolverán el número y las posiciones de las ocurrencias de cada patrón que defina.

Editar: esto podría arrojar algo más de luz sobre el rendimiento de IndexOf IndexOf Perf

+0

He intentado usar RegEx compilado también - el rendimiento es idéntico. También intenté agregar RegexOptions.CultureInvariant a las opciones: cero efecto – Ghostrider

3

La sobrecarga IndexOf que está utilizando es sensible a la cultura, lo que afectará el rendimiento. En lugar de ello, utilizar:

input.IndexOf("<script", 0, StringComparison.Ordinal); 
+0

usando StringComparison.Ordinal no tiene ningún efecto mensurable en el rendimiento de este código. – Ghostrider

1

no discuto el código aquí, que probablemente coul ser escrito con expresiones regulares y así sucesivamente ... pero para mí es lento porque el IndexOf() *dentro * la for siempre repite el análisis de la cadena desde el principio (siempre comienza desde el índice 0) intente escanear desde la última ocurrencia encontrada en su lugar.

2

acabo de probar el rendimiento IndexOf con .NET 4.0 en Windows 7

public void Test() 
{ 
    var input = "Hello world, I'm ekk. This is test string"; 

    TestStringIndexOfPerformance(input, StringComparison.CurrentCulture); 
    TestStringIndexOfPerformance(input, StringComparison.InvariantCulture); 
    TestStringIndexOfPerformance(input, StringComparison.Ordinal); 

    Console.ReadLine(); 
} 

private static void TestStringIndexOfPerformance(string input, StringComparison stringComparison) 
{ 
    var count = 0; 
    var startTime = DateTime.UtcNow; 
    TimeSpan result; 

    for (var index = 0; index != 1000000; index++) 
    { 
     count = input.IndexOf("<script", 0, stringComparison); 
    } 

    result = DateTime.UtcNow.Subtract(startTime); 

    Console.WriteLine("{0}: {1}", stringComparison, count); 
    Console.WriteLine("Total time: {0}", result.TotalMilliseconds); 
    Console.WriteLine("--------------------------------"); 
} 

y el resultado es:

CurrentCulture: 
    Total time: 225.4008 

InvariantCulture: 
    Total time: 187.2003 

Ordinal: 
    Total time: 124.8003 

Como se puede ver el rendimiento del ordinal es un poco mejor.