Prueba este código.
Es una versión ligeramente modificada de su código.
1. Eliminé Console.WriteLine, ya que probablemente sea un orden de magnitud más lento que el que estoy tratando de medir.
2. Estoy iniciando el cronómetro antes del ciclo y deteniéndolo justo después, de esta manera no estoy perdiendo precisión si la función toma, por ejemplo, 26,4 ticks para ejecutarse.
3. La forma en que dividió el resultado por algunas iteraciones fue incorrecta. Vea lo que sucede si tiene 1000 milisegundos y 100 milisegundos. En ambas situaciones, obtendrá 0 ms después de dividirlo por 1000000.
Stopwatch s = new Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;
string result;
s.Start();
for (var i = 0; i < n; i++)
result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();
Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);
Esos son mis resultados:
1000000 x resultado = string.Format ("{0} {1}", p.Primer nombre, p.LastName); tomó: 618ms - 2213706 ticks
1000000 x resultado = (p.Primer Nombre + "" + p.LastName); tomó: 166ms - 595610 garrapatas
string.Format
que no utiliza ninguna función de formato compuesto (es decir, simple{0}
) y reemplazarla con la concatenación de cadenas considerablemente más rápida. Me pregunto que tal hazaña se pueda lograr con una reescritura IL existente como PostSharp.Me sorprende que tanta gente quiera encontrar de inmediato el código que se ejecuta más rápido. Si UN MILLÓN de iteraciones TODAVÍA tardan menos de un segundo en procesarse, ¿será esto en CUALQUIER FORMA notable para el usuario final? No muy probable.
Optaría por la
String.Format
opción, solo porque tiene más sentido desde el punto de vista arquitectónico. No me importa el rendimiento hasta que se convierta en un problema (y si lo hiciera, me preguntaría: ¿Necesito concatenar un millón de nombres a la vez? Seguramente no encajarán en la pantalla ...)Considere si su cliente más tarde quiere cambiarlo para que pueda configurar si se muestra
"Firstname Lastname"
o no."Lastname, Firstname."
Con la opción Formato, esto es fácil: simplemente cambie la cadena de formato. Con el concat, necesitarás un código extra. Claro que eso no suena como un gran problema en este ejemplo en particular, sino extrapolar.fuente
Dios mío, después de leer una de las otras respuestas, intenté invertir el orden de las operaciones, así que primero realicé la concatenación, luego el String.Format ...
Entonces, el orden de las operaciones hace una GRAN diferencia, o más bien la primera operación es SIEMPRE mucho más lenta.
Aquí están los resultados de una ejecución en la que las operaciones se completan más de una vez. He intentado cambiar los pedidos, pero las cosas generalmente siguen las mismas reglas, una vez que se ignora el primer resultado:
Como puede ver, las ejecuciones posteriores del mismo método (refactoré el código en 3 métodos) son incrementalmente más rápidas. El más rápido parece ser el método Console.WriteLine (String.Concat (...)), seguido de la concatenación normal, y luego las operaciones formateadas.
El retraso inicial en el inicio es probablemente la inicialización de Console Stream, como colocar una Console.Writeline ("Start!") Antes de que la primera operación vuelva a poner en línea todos los tiempos.
fuente
Las cadenas son inmutables, esto significa que la misma pequeña porción de memoria se usa una y otra vez en su código. Agregar las mismas dos cadenas juntas y crear la misma cadena nueva una y otra vez no afecta la memoria. .Net es lo suficientemente inteligente como para usar la misma referencia de memoria. Por lo tanto, su código no prueba realmente la diferencia entre los dos métodos concat.
Probar esto para el tamaño:
Salida de muestra:
fuente
string.Format
vale la pena usar el pequeño rendimiento aquí. Arquitectónicamente es mejor ya que significa que puedes alterar el formato más fácilmente. Pero el generador de cadenas realmente no veo el punto. Todos los otros hilos aquí dicen que debes usar Stringbuilder en lugar de concatenar cadenas. Cual es la ventaja? Claramente no es velocidad, como lo demuestra este punto de referencia.Lástima los pobres traductores
Si sabe que su aplicación permanecerá en inglés, entonces bien, guarde los tics del reloj. Sin embargo, muchas culturas generalmente verían Apellido Nombre en, por ejemplo, direcciones.
Así que úsala
string.Format()
, especialmente si alguna vez vas a hacer que tu aplicación vaya a un lugar donde el inglés no sea el primer idioma.fuente
string.Format()
comportaría diferente en diferentes culturas? ¿No seguiría imprimiendo el nombre y luego el apellido? Parece que tendría que tener en cuenta la cultura diferente en ambas situaciones. Siento que me falta algo aquí.string.Format()
estás usando un nombre para una dirección? Si sestring.Format()
cambia{0} {1}
según la cultura, lo consideraría roto.Aquí están mis resultados en más de 100,000 iteraciones:
Y aquí está el código de banco:
Entonces, no sé de quién es la respuesta para marcar como respuesta :)
fuente
Concatenar cadenas está bien en un escenario simple como ese: es más complicado con algo más complicado que eso, incluso Apellido, Nombre. Con el formato que puede ver, de un vistazo, cuál será la estructura final de la cadena al leer el código, con la concatenación se vuelve casi imposible discernir inmediatamente el resultado final (excepto con un ejemplo muy simple como este).
Lo que eso significa a largo plazo es que cuando regrese para hacer un cambio en su formato de cadena, tendrá la capacidad de aparecer y hacer algunos ajustes en la cadena de formato, o arrugar la frente y comenzar a moverse. tipos de accesores de propiedades mezclados con texto, lo que es más probable que presente problemas.
Si está usando .NET 3.5, puede usar un método de extensión como este y obtener una sintaxis fluida y sencilla como esta:
Finalmente, a medida que su aplicación crece en complejidad, puede decidir que para mantener cuerdas en su aplicación desea moverlas a un archivo de recursos para localizarlas o simplemente a un ayudante estático. Esto será MUCHO más fácil de lograr si ha usado formatos consistentemente, y su código puede ser simplemente refactorizado para usar algo como
fuente
Para una manipulación muy simple, usaría la concatenación, pero una vez que supere los 2 o 3 elementos, el formato se vuelve más apropiado para la OMI.
Otra razón para preferir String.Format es que las cadenas .NET son inmutables y hacerlo de esta manera crea menos copias temporales / intermedias.
fuente
Si bien entiendo totalmente la preferencia de estilo y la concatenación elegida para mi primera respuesta, en parte basada en mi propia preferencia, parte de mi decisión se basó en el pensamiento de que la concatenación sería más rápida. Entonces, por curiosidad, lo probé y los resultados fueron asombrosos, especialmente para una cadena tan pequeña.
Usando el siguiente código:
Obtuve los siguientes resultados:
¡Usar el método de formateo es más de 100 veces más lento! La concatenación ni siquiera se registró como 1 ms, por lo que también envié los tics del temporizador.
fuente
A partir de C # 6.0 se pueden usar cadenas interpoladas para hacer esto, lo que simplifica aún más el formato.
Las cadenas interpoladas tienen un rendimiento similar al de String.Format, pero una legibilidad mejorada y una sintaxis más corta, debido al hecho de que los valores y expresiones se insertan en línea.
Consulte también este artículo de dotnetperls sobre interpolación de cadenas.
Si está buscando una forma predeterminada de formatear sus cadenas, esto tiene sentido en términos de legibilidad y rendimiento (excepto si los microsegundos harán una diferencia en su caso de uso específico).
fuente
Para la concatenación básica de cadenas, generalmente utilizo el segundo estilo, más fácil de leer y más simple. Sin embargo, si estoy haciendo una combinación de cadenas más complicada, generalmente opto por String.Format.
String.Format ahorra en muchas citas y ventajas ...
Solo se guardaron unos pocos caracteres, pero creo que, en este ejemplo, el formato lo hace mucho más limpio.
fuente
Una mejor prueba sería mirar su memoria usando Perfmon y los contadores de memoria CLR. Tengo entendido que la razón por la que desea utilizar String.Format en lugar de simplemente concatenar cadenas es que, dado que las cadenas son inmutables, está cargando innecesariamente al recolector de basura con cadenas temporales que deben reclamarse en la próxima pasada.
StringBuilder y String.Format, aunque son potencialmente más lentos, son más eficientes en memoria.
¿Qué tiene de malo la concatenación de cadenas?
fuente
En general, prefiero el primero, ya que especialmente cuando las cadenas se alargan puede ser mucho más fácil de leer.
El otro beneficio es que creo que es el rendimiento, ya que este último realmente realiza 2 declaraciones de creación de cadena antes de pasar la cadena final al método Console.Write. String.Format utiliza un StringBuilder debajo de las cubiertas, creo, por lo que se evitan múltiples concatenaciones.
Sin embargo, debe tenerse en cuenta que si los parámetros que está pasando a String.Format (y otros métodos similares como Console.Write) son tipos de valor, se incluirán en un recuadro antes de pasarlos, lo que puede proporcionar sus propios resultados de rendimiento. Publicación de blog sobre esto aquí .
fuente
Dentro de una semana a partir del 19 de agosto de 2015, esta pregunta tendrá exactamente siete (7) años. Ahora hay una mejor manera de hacer esto. Mejor en términos de mantenibilidad, ya que no he hecho ninguna prueba de rendimiento en comparación con solo concatenar cadenas (pero ¿importa en estos días? ¿Unos pocos milisegundos de diferencia?). La nueva forma de hacerlo con C # 6.0 :
Esta nueva característica es mejor , en mi opinión, y en realidad mejor en nuestro caso, ya que tenemos códigos donde construimos cadenas de consulta cuyos valores dependen de algunos factores. Imagine una cadena de consulta donde tenemos 6 argumentos. Entonces, en lugar de hacer un, por ejemplo:
se puede escribir así y es más fácil de leer:
fuente
fuente
Elijo en función de la legibilidad. Prefiero la opción de formato cuando hay texto alrededor de las variables. En este ejemplo:
entiendes el significado incluso sin nombres de variables, mientras que el concat está lleno de comillas y signos + y confunde mis ojos:
(Tomé prestado el ejemplo de Mike porque me gusta)
Si la cadena de formato no significa mucho sin nombres de variables, tengo que usar concat:
La opción de formato me hace leer los nombres de las variables y asignarlos a los números correspondientes. La opción concat no requiere eso. Todavía estoy confundido por las comillas y los signos +, pero la alternativa es peor. ¿Rubí?
En cuanto al rendimiento, espero que la opción de formato sea más lenta que la concat, ya que el formato requiere que se analice la cadena . No recuerdo haber tenido que optimizar este tipo de instrucción, pero si lo hiciera, vería
string
métodos comoConcat()
yJoin()
.La otra ventaja del formato es que la cadena de formato se puede colocar en un archivo de configuración. Muy útil con mensajes de error y texto de interfaz de usuario.
fuente
Usaría el String.Format, pero también tendría el formato de cadena en los archivos de recursos para que pueda ser localizado para otros idiomas. Usar una cadena simple concat no te permite hacer eso. Obviamente, si nunca necesita localizar esa cadena, esta no es una razón para pensar. Realmente depende de para qué es la cadena.
Si se va a mostrar al usuario, usaría String.Format para poder localizarlo si lo necesito, y FxCop lo corregirá por mí, por si acaso :)
Si contiene números o cualquier otra cosa que no sea de cadena (por ejemplo, fechas), usaría String.Format porque me da más control sobre el formato .
Si es para construir una consulta como SQL, usaría Linq .
Si para concatenar cadenas dentro de un bucle, usaría StringBuilder para evitar problemas de rendimiento.
Si es para algún resultado que el usuario no verá, y no va a afectar el rendimiento, usaría String.Format porque tengo la costumbre de usarlo de todos modos y simplemente estoy acostumbrado :)
fuente
Si está tratando con algo que necesita ser fácil de leer (y esto es la mayoría del código), me quedaría con la versión de sobrecarga del operador A MENOS QUE:
En al menos dos de estas circunstancias, usaría StringBuilder en su lugar.
fuente
Si tiene la intención de localizar el resultado, String.Format es esencial porque los diferentes lenguajes naturales podrían no tener los datos en el mismo orden.
fuente
Creo que esto depende en gran medida de lo complejo que sea el resultado. Tiendo a elegir el escenario que funcione mejor en ese momento.
Elija la herramienta adecuada según el trabajo: D ¡El que se vea más limpio!
fuente
Prefiero el segundo también, pero no tengo argumentos racionales en este momento para apoyar esa posición.
fuente
¡Buena esa!
Recien agregado
Y es aún más rápido (supongo que string. Concat se llama en ambos ejemplos, pero el primero requiere algún tipo de traducción).
fuente
string.Concat(...)
. Se realiza durante la compilación, por lo que no tiene impacto en el rendimiento en tiempo de ejecución. Si ejecuta sus pruebas varias veces o las ejecuta en muestras de prueba más grandes, verá que son idénticas.Como no creo que las respuestas aquí cubran todo, me gustaría hacer una pequeña adición aquí.
Console.WriteLine(string format, params object[] pars)
llamadasstring.Format
. El '+' implica concatenación de cadenas. No creo que esto siempre tenga que ver con el estilo; Tiendo a mezclar los dos estilos dependiendo del contexto en el que me encuentre.Respuesta corta
La decisión que enfrenta tiene que ver con la asignación de cadenas. Trataré de hacerlo simple.
Di que tienes
Si ejecuta esto, se evaluará de la siguiente manera:
tmp
aquí no es realmente una variable local, pero es temporal para el JIT (se inserta en la pila IL). Si empuja una cadena en la pila (comoldstr
en IL para literales), coloca una referencia a un puntero de cadena en la pila.El momento en que llama a
concat
esta referencia se convierte en un problema, porque no hay ninguna referencia de cadena disponible que contenga ambas cadenas. Esto significa que .NET necesita asignar un nuevo bloque de memoria y luego llenarlo con las dos cadenas. La razón por la que esto es un problema es porque la asignación es relativamente costosa.Lo que cambia la pregunta a: ¿Cómo puede reducir el número de
concat
operaciones?Entonces, la respuesta aproximada es:
string.Format
para> 1 concats, '+' funcionará bien para 1 concat. Y si no te importa hacer optimizaciones de micro rendimiento,string.Format
funcionará bien en el caso general.Una nota sobre cultura
Y luego hay algo llamado cultura ...
string.Format
le permite usarCultureInfo
en su formato. Un operador simple '+' usa la cultura actual.Esto es especialmente un comentario importante si está escribiendo formatos de archivo y f.ex.
double
valores que 'agrega' a una cadena. En diferentes máquinas, puede terminar con diferentes cadenas si no lo usastring.Format
con un explícitoCultureInfo
.F.ex. considere lo que sucede si cambia un '.' para un ',' mientras escribe su archivo de valores separados por comas ... en holandés el separador decimal es una coma, por lo que su usuario podría recibir una sorpresa 'divertida'.
Respuesta más detallada
Si no conoce el tamaño exacto de la cadena de antemano, es mejor usar una política como esta para sobreasignar los búferes que usa. El espacio flojo se llena primero, después de lo cual se copian los datos.
Crecer significa asignar un nuevo bloque de memoria y copiar los datos antiguos al nuevo búfer. El antiguo bloque de memoria se puede liberar. En este punto se obtiene el resultado final: el crecimiento es una operación costosa.
La forma más práctica de hacerlo es utilizar una política de sobreasignación. La política más común es sobreasignar buffers en potencias de 2. Por supuesto, debe hacerlo un poco más inteligente que eso (ya que no tiene sentido crecer de 1,2,4,8 si ya sabe que necesita 128 caracteres) ) pero te haces una idea. La política garantiza que no necesita muchas de las costosas operaciones que describí anteriormente.
StringBuilder
es una clase que básicamente sobreasigna el búfer subyacente en potencias de dos.string.Format
usosStringBuilder
debajo del capó.Esto hace que su decisión sea una compensación básica entre sobreasignar y agregar (-múltiple) (sin cultura) o simplemente asignar-y-agregar.
fuente
Personalmente, el segundo, ya que todo lo que está usando está en el orden directo en el que saldrá. Mientras que con el primero, debe hacer coincidir los {0} y {1} con la var adecuada, lo que es fácil de estropear.
Al menos no es tan malo como el sprintf de C ++, donde si se equivoca el tipo de variable, todo explotará.
Además, dado que el segundo es todo en línea y no tiene que buscar y reemplazar todas las {0} cosas, este último debería ser más rápido ... aunque no estoy seguro.
fuente
De hecho, me gusta el primero porque cuando hay muchas variables entremezcladas con el texto, me parece más fácil de leer. Además, es más fácil tratar con comillas cuando se usa la cadena. Formato (), uh, formato. Aquí hay un análisis decente de la concatenación de cadenas.
fuente
Siempre he seguido la ruta string.Format (). Poder almacenar formatos en variables como el ejemplo de Nathan es una gran ventaja. En algunos casos, puedo agregar una variable, pero una vez que se concatena más de 1 variable, refactorizo para usar el formato.
fuente
Ah, y solo para completar, los siguientes son algunos tics más rápidos que la concatenación normal:
fuente
El primero (formato) me parece mejor. Es más legible y no está creando objetos de cadena temporales adicionales.
fuente
Tenía curiosidad sobre dónde estaba StringBuilder con estas pruebas. Resultados a continuación ...
Resultados:
fuente
Según el material de preparación de MCSD, Microsoft sugiere usar el operador + cuando se trata de un número muy pequeño de concatenaciones (probablemente de 2 a 4). Todavía no estoy seguro de por qué, pero es algo a considerar.
fuente