2010-08-13 11 views
9

Tomar subseries de una cadena es una operación de manipulación de cadena muy común, pero escuché que podría haber diferencias considerables en el rendimiento/implementación entre la plataforma Java y .NET. Específicamente He oído que en Java, ofrece java.lang.String operación constante de tiempo para substring, pero en .NET, System.String las ofertas lineal Substring rendimiento.Comparación del rendimiento de operación de subcadena entre .NET y Java

¿Son estos realmente el caso? ¿Se puede confirmar esto en la documentación/código fuente, etc.? ¿Es esta implementación específica o especificada por el idioma y/o la plataforma? ¿Cuáles son los pros y los contras de cada enfoque? ¿Qué debería buscar una persona que migra de una plataforma a otra para evitar caer en dificultades de rendimiento?

+1

¿Por qué no ejecuta sus propios microcalibrados para probar esto? ? ¿Puede vincular a fuentes que dicen que tiene un rendimiento "malo"? – Oded

+0

@Oded: fuente es el comentario de Danny Chen aquí http://stackoverflow.com/questions/3474254/how-to-make-a-first-letter-capital-in-c/3474263#3474263; Honestamente, me sorprendería si 'Subcadena' no es' O (1) 'operación de tiempo y espacio (como, por ejemplo, Java), pero le estoy dando el beneficio de la duda ya que no conozco .NET. – polygenelubricants

+1

¿Qué significa "mala actuación"? En relación a qué? .NET también tiene un mal rendimiento si se compara con C++, por ejemplo. ¿Deberíamos soltar .NET por eso? –

Respuesta

11

En .NET, Substring es O (n) en lugar de la junta (1) de Java. Esto se debe a que en .NET, el objeto String contiene todos los datos de caracteres reales en sí - por lo que tomar una subcadena implica copiar todos los datos dentro de la nueva subcadena. En Java, substring puede simplemente crear un nuevo objeto refiriéndose a la matriz de caracteres original, con un índice y longitud de inicio diferente.

Hay pros y los contras de cada enfoque:

  • enfoque de .NET tiene una mejor coherencia de caché, crea menos objetos , y evita la situación en la que una pequeña subserie evita una gran char[] ser basura recogida . Creo que en algunos casos también puede hacer que la interoperabilidad sea muy fácil, internamente.
  • enfoque de Java hace tomando una subcadena muy eficiente, y probablemente algunas otras operaciones también

Hay un poco más de detalle en mi strings article.

En cuanto a la cuestión general de evitar los errores de rendimiento, creo que debería tener una respuesta enlatada lista para cortar y pegar: asegúrese de que su arquitectura es eficiente, y ponerlo en práctica de la manera más legible que pueda. Mida el rendimiento y optimice dónde encuentra los cuellos de botella.


Por cierto, esto hace string muy especial - es el único tipo no-matriz cuyos huella de memoria varía por ejemplo dentro de la misma CLR.

Para cuerdas pequeñas, esta es una gran victoria. Ya es suficientemente malo que exista toda la sobrecarga de un objeto, pero cuando también hay una matriz extra involucrada, una cadena de un solo carácter podría tomar alrededor de 36 bytes en Java. (Ese es un número de "dedo en el aire": no puedo recordar las transparencias exactas de los objetos. También dependerá de la VM que esté utilizando).

2

Usando reflector esto es lo que se obtiene de subcadena (Int32, Int32)

[SecuritySafeCritical, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] 
public string Substring(int startIndex, int length) 
{ 
    return this.InternalSubStringWithChecks(startIndex, length, false); 
} 

si seguir adelante dentro de la última llamada es a un

internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount) 

que copia los caracteres mediante punteros. El código completo parece realmente grande, pero no verá qué tan rápido o lento es hasta que lo ejecute y lo compare.

0

Realmente depende de su carga de trabajo. Si realiza un bucle y realiza muchas llamadas de subcadena, puede tener un problema. Para la publicación SO a la que te refieres, dudo que alguna vez sea un problema. Con esa actitud, sin embargo, siempre podría terminar en una situación de "muerte por mil recortes de papel". En el SO post que se refieren a, tenemos lo siguiente:

String after = before.Substring(0, 1).ToUpper() + before.Substring(1); 

Suponiendo que el compilador no hacer algunas optimizaciones locos, esto creará al menos cuatro nuevas cadenas (2 Substring llamadas, una llamada ToUpper, y el concatenación). Subcadena se implementa exactamente como cabría esperar (copia de cadena), pero tres de esas cadenas asignadas anteriormente se convertirán rápidamente en basura. Hacer mucho de esto creará una presión de memoria innecesaria. Digo "innecesario" porque probablemente pueda encontrar una solución más económica con solo un poco más de inversión de tiempo.

Al final, el perfilador es su mejor amiga :)

Cuestiones relacionadas