Pregunta "No use StringBuilder o foreach en esta ruta de código de acceso"


Estoy navegando por el código fuente de Open Source SignalR proyecto, y veo este código diff que tiene derecho "No use StringBuilder o foreach en esta ruta de código de acceso" :

-           public static string MakeCursor(IEnumerable<Cursor> cursors)
+           public static string MakeCursor(IList<Cursor> cursors)
            { 
-               var sb = new StringBuilder();
-               bool first = true;
-               foreach (var c in cursors)
+               var result = "";
+               for (int i = 0; i < cursors.Count; i++)
                {
-                   if (!first)
+                   if (i > 0)
                    {
-                       sb.Append('|');
+                       result += '|';
                    }
-                   sb.Append(Escape(c.Key));
-                   sb.Append(',');
-                   sb.Append(c.Id);
-                   first = false;
+                   result += Escape(cursors[i].Key);
+                   result += ',';
+                   result += cursors[i].Id;
                }
-               return sb.ToString();
+               return result;
            }

Entiendo por qué foreach puede ser menos eficiente a veces y por qué es reemplazado por for.

Sin embargo, aprendí y experimenté que StringBuilder es la forma más eficiente de concatenar cadenas. Así que me pregunto por qué el autor decidió reemplazarlo con concatenación estándar.

¿Qué hay de malo aquí y en general sobre el uso de StringBuilder?


32
2017-09-11 14:39


origen


Respuestas:


Hice el cambio de código y sí, hizo una gran diferencia en el número de asignaciones (GetEnumerator ()) llamadas vs no. Imagine que este código es millones de veces por segundo. La cantidad de enumeradores asignados es ridícula y se puede evitar.

editar: Ahora invertimos el control para evitar cualquier asignación (escribiendo directamente al escritor): https://github.com/SignalR/SignalR/blob/2.0.2/src/Microsoft.AspNet.SignalR.Core/Messaging/Cursor.cs#L36


30
2017-09-11 21:37



Solo espero que el tipo que cambió esto realmente midiera la diferencia.

  • Hay una sobrecarga al crear instancias de un nuevo generador de cadenas cada vez. Esto también ejerce presión sobre la recolección de memoria / basura.
  • El compilador puede generar código 'stringbuilderlike' para concatenaciones simples
  • El FOR podría ser más lento porque podría requerir la comprobación de límites que no es hecho con bucles foreach ya que los compiladores 'saben' que están dentro de los límites.

3
2017-09-11 14:48



Depende de la cantidad de Cursors proporcionado a la función.

La mayoría de las comparaciones entre los dos apporaches parece favorecer StringBuilder sobre la concatinación de cuerdas cuando se concatenan 4-10 cuerdas. Lo más probable es que me gustaría StringBuilder si no tuviera razones explícitas, tampoco (por ejemplo, comparación de rendimiento de los dos enfoques para mi problema / aplicación) Consideraría preasignar un buffer en el StringBuilder para evitar (muchas) reasignaciones.

Ver Concatenación de cadenas frente a String Builder. Actuación y Concatenación con StringBuilders vs. Strings para alguna discusión sobre el tema.


2
2017-09-11 16:47



¿Cuántas concatenaciones estás haciendo? Si es numeroso, use StringBuilder. Si solo unos pocos, la sobrecarga de crear StringBuilder superará cualquier ventaja.


1
2017-09-11 14:43



Pondré mi dinero

           StringBuilder sb = new StringBuilder();
           bool first = true;
           foreach (Cursor c in cursors)
           {
                if (first)
                {
                   first = false;  // only assign it once
                }
                else
                {
                    sb.Append('|');
                }
                sb.Append(Escape(c.Key) + ',' + c.Id);
            }
            return sb.ToString();

Pero pondría mi dinero y la actualización de dfowler. Mira el enlace en su respuesta.


1
2017-09-11 15:46