regarding practices example correct best and c# string stringbuilder

practices - stringbuilder list c#



StringBuilder.Append Vs StringBuilder.AppendFormat (9)

1 debería ser más rápido porque simplemente agrega las cadenas mientras que 2 tiene que crear una cadena basada en un formato y luego agregar la cadena. Entonces hay un paso adicional allí.

Me preguntaba sobre StringBuilder y tengo una pregunta que esperaba que la comunidad pudiera explicar.

Olvidémonos de la legibilidad del código, ¿cuál de estos es más rápido y por qué?

StringBuilder.Append :

StringBuilder sb = new StringBuilder(); sb.Append(string1); sb.Append("----"); sb.Append(string2);

StringBuilder.AppendFormat :

StringBuilder sb = new StringBuilder(); sb.AppendFormat("{0}----{1}",string1,string2);


Asumiría que fue la llamada la que menos trabajo hizo. Append solo concatena cadenas, donde AppendFormat realiza sustituciones de cadenas. Por supuesto, en estos días, nunca se puede decir ...


Es imposible de decir, sin saber el tamaño de string1 y string2 .

Con la llamada a AppendFormat , preasignará el búfer una vez dada la longitud de la cadena de formato y las cadenas que se insertarán, y luego concatenará todo e insertará en el búfer. Para cadenas muy grandes, esto será ventajoso en llamadas separadas a Append que podría hacer que el búfer se expanda varias veces.

Sin embargo, las tres llamadas a Append pueden desencadenar o no el crecimiento del búfer y esa verificación se realiza en cada llamada. Si las cadenas son lo suficientemente pequeñas y no se desencadena la expansión del búfer, será más rápido que la llamada a AppendFormat porque no tendrá que analizar la cadena de formato para averiguar dónde hacer los reemplazos.

Se necesitan más datos para una respuesta definitiva

Cabe señalar que hay poca discusión sobre el uso del método estático de Concat en la clase String ( la respuesta de Jon utilizando AppendWithCapacity me lo recordó). Los resultados de sus pruebas muestran que es el mejor caso (suponiendo que no tenga que aprovechar el especificador de formato específico). String.Concat hace lo mismo, ya que predeterminará la longitud de las cadenas para concatenar y preasignar el búfer (con un poco más de sobrecarga debido a las construcciones de bucle a través de los parámetros). Su rendimiento será comparable al método AppendWithCapacity de Jon.

O simplemente, el operador de adición simple, ya que compila una llamada a String.Concat todos modos, con la advertencia de que todas las adiciones están en la misma expresión:

// One call to String.Concat. string result = a + b + c;

NO

// Two calls to String.Concat. string result = a + b; result = result + c;

Para todos aquellos que ponen código de prueba

Debe ejecutar sus casos de prueba en ejecuciones separadas (o al menos, realizar un GC entre la medición de las ejecuciones de prueba por separado). La razón de esto es que si dices, 1,000,000 ejecuciones, creando un nuevo StringBuilder en cada iteración del ciclo para una prueba, y luego ejecutas la siguiente prueba que se repite el mismo número de veces, creando 1,000,000 instancias adicionales de StringBuilder , Es muy probable que el GC intervenga durante la segunda prueba y dificulte su sincronización.


Más rápido es 1 en su caso, sin embargo, no es una comparación justa. Debería preguntar StringBuilder.AppendFormat () vs StringBuilder.Append (string.Format ()) - donde el primero es más rápido debido al trabajo interno con la matriz char.

Sin embargo, tu segunda opción es más legible.


Por supuesto perfil para saber con certeza en cada caso.

Dicho esto, creo que, en general, será el primero porque no está analizando repetidamente la cadena de formato.

Sin embargo, la diferencia sería muy pequeña. Hasta el punto de que realmente debería considerar usar AppendFormat en la mayoría de los casos de todos modos.


CasperOne es completamente exacto de que depende de los datos . Sin embargo, suponga que está escribiendo esto como una biblioteca de clase para consumir de terceros, ¿cuál usaría?

Una opción sería obtener lo mejor de ambos mundos: calcular la cantidad de datos que realmente va a tener que agregar, y luego usar StringBuilder.EnsureCapacity para asegurarse de que solo necesitemos un solo tamaño de búfer.

Sin embargo, si no me molestara demasiado , usaría Append x3; parece "más probable" que sea más rápido, ya que el análisis de los tokens de formato de cadena en cada llamada es claramente de "make-work".

Tenga en cuenta que le pedí al equipo de BCL una especie de "formateador en caché" que podríamos crear utilizando una cadena de formato y luego reutilizarlo repetidamente. Es una locura que el framework tenga que analizar la cadena de formato cada vez que se usa.

EDITAR: De acuerdo, he editado el código de John un tanto por la flexibilidad y he añadido un "AppendWithCapacity" que primero calcula la capacidad necesaria. Aquí están los resultados para las diferentes longitudes: para la longitud 1 utilicé 1,000,000 de iteraciones; para todas las demás longitudes utilicé 100.000. (Esto fue solo para obtener tiempos de ejecución razonables.) Todos los horarios están en milis.

Desafortunadamente, las tablas realmente no funcionan en SO. Las longitudes fueron 1, 1000, 10000, 20000

Veces:

  • Adjuntar: 162, 475, 7997, 17970
  • AppendFormat: 392, 499, 8541, 18993
  • AppendWithCapacity: 139, 189, 1558, 3085

Así que, como sucedió, nunca vi AppendFormat beat Append, pero vi ganar AppendWithCapacity por un margen muy sustancial.

Aquí está el código completo:

using System; using System.Diagnostics; using System.Text; public class StringBuilderTest { static void Append(string string1, string string2) { StringBuilder sb = new StringBuilder(); sb.Append(string1); sb.Append("----"); sb.Append(string2); } static void AppendWithCapacity(string string1, string string2) { int capacity = string1.Length + string2.Length + 4; StringBuilder sb = new StringBuilder(capacity); sb.Append(string1); sb.Append("----"); sb.Append(string2); } static void AppendFormat(string string1, string string2) { StringBuilder sb = new StringBuilder(); sb.AppendFormat("{0}----{1}", string1, string2); } static void Main(string[] args) { int size = int.Parse(args[0]); int iterations = int.Parse(args[1]); string method = args[2]; Action<string,string> action; switch (method) { case "Append": action = Append; break; case "AppendWithCapacity": action = AppendWithCapacity; break; case "AppendFormat": action = AppendFormat; break; default: throw new ArgumentException(); } string string1 = new string(''x'', size); string string2 = new string(''y'', size); // Make sure it''s JITted action(string1, string2); GC.Collect(); Stopwatch sw = Stopwatch.StartNew(); for (int i=0; i < iterations; i++) { action(string1, string2); } sw.Stop(); Console.WriteLine("Time: {0}ms", (int) sw.ElapsedMilliseconds); } }


CasperOne es correcto . Una vez que alcanza un cierto umbral, el método Append() se vuelve más lento que AppendFormat() . Aquí están las diferentes longitudes y tics transcurridos de 100.000 iteraciones de cada método:

Longitud: 1

Append() - 50900 AppendFormat() - 126826

Longitud: 1000

Append() - 1241938 AppendFormat() - 1337396

Longitud: 10,000

Append() - 12482051 AppendFormat() - 12740862

Longitud: 20,000

Append() - 61029875 AppendFormat() - 60483914

Cuando se introducen cadenas con una longitud cercana a 20,000, la función AppendFormat() superará ligeramente a Append() .

¿Por qué pasó esto? Ver la respuesta de CasperOne .

Editar:

Volví a realizar cada prueba individualmente en Configuración de versión y actualicé los resultados.


Append será más rápido en la mayoría de los casos porque hay muchas sobrecargas en ese método que le permiten al compilador llamar al método correcto. Como está utilizando Strings StringBuilder puede usar la sobrecarga String para Append .

AppendFormat toma una String y luego un Object[] que significa que el formato tendrá que ser analizado y cada Object en la matriz tendrá que ser ToString''d antes de que pueda ser agregado a la matriz interna StringBuilder''s .

Nota: Para el argumento de casperone: es difícil dar una respuesta exacta sin más datos.


StringBuilder también tiene agregados en cascada: Append() devuelve StringBuilder , para que pueda escribir su código de esta manera:

StringBuilder sb = new StringBuilder(); sb.Append(string1) .Append("----") .Append(string2);

Limpio, y genera menos código IL (aunque eso es realmente una micro-optimización).