String Builder vs String en ASP.Net

Como ya dije hace unos días, ando algo liado últimamente, aunque también puede que sea que necesito respirar un poco de aire fresco y hacer cosas distintas, por lo que temporalmente voy a pasarme poco por el blog. No obstante os traigo un pequeño experimento que hice el otro día en ASP.Net, en donde a través del sistema de trazas comprobé la eficacia del StringBuilder de .Net.

¿Stringloqué? StringBuilder es una clase de .Net que representa un String como un objeto, se encuentra en System.Text y su principal particularidad consiste en un menor consumo de recursos a la hora de formar una cadena de texto (de ahí su nombre). StringBuilder reserva un buffer de memoria de forma que cuando vas a insertar nuevos caracteres mira si el buffer es suficiente, si lo es los mete, si no lo es, amplia el búffer y los mete.

Pero, ¿que hacía entonces el String normal? El String normal tiene varias formas de ser concatenado, .Concat, .Join, “+” y “&”. Cada una de ellas distinta pero en principio todas iguales, la idea, y a menos que alguien me corrija y me saque del error, es que crea un nuevo objeto a partir de los ya existentes (un nuevo string), lo que parece consumir bastante mas RAM además de tiempo de proceso.

Yo, por curiosidá de esa que sólo un frikie informático tiene y por culpa de la cual nos consideran un reducto mutante de la sociedad (nosotros nos creemos pr0s) decidí generar un algoritmo facilón y activar el sistema de trazas para realizar carreras… ¿Queréis saber quien gana?

Primero que todo os muestro el código fuente, por si alguien ve algún error en el mismo que pueda haber alterado los resultados:

    Private Sub PruebaRendimiento()
        Dim i As Integer
        Dim strTest As String = ""
        Dim strTestBuilder As New StringBuilder
        Dim META As Integer = 50000
        Dim arrStrings() As String = {""}

        Me.Trace.IsEnabled = True
        Me.Trace.Warn("PRUEBA DE RENDIMIENTO. START:")

        'STRING +
        Me.Trace.Warn("Empiezo bucle con String +.")
        For i = 0 To META
            strTest += i.ToString
        Next
        Me.Trace.Warn("Bucle con String + terminado.____________________________")

        'STRING &
        strTest = ""
        Me.Trace.Warn("Empiezo bucle con String &.")
        For i = 0 To META
            strTest = strTest & i.ToString
        Next
        Me.Trace.Warn("Bucle con String & terminado.____________________________")

        'STRING CONCAT
        strTest = ""
        Me.Trace.Warn("Empiezo bucle con String CONCAT.")
        For i = 0 To META
            strTest = String.Concat(strTest, i.ToString)
        Next
        Me.Trace.Warn("Bucle con String CONCAT terminado.____________________________")

        'STRING JOIN
        strTest = ""
        Me.Trace.Warn("Empiezo bucle con String JOIN.")
        For i = 0 To META
            Array.Resize(arrStrings, arrStrings.Length + 1)
            arrStrings.SetValue(i.ToString, arrStrings.Length - 1)
        Next
        strTest = String.Join("", arrStrings)
        Me.Trace.Warn("Bucle con String JOIN terminado.____________________________")

        'STRING BUILDER
        strTest = ""
        Me.Trace.Warn("Empiezo bucle con StringBuilder.")
        For i = 0 To META
            strTestBuilder.Append(i.ToString)
        Next
        Me.Trace.Warn("Bucle con StringBuilder terminado.____________________________")

    End Sub

Tras lo cual, hice 3 pruebas, una facililla de 5 vueltas al bucle, otra algo mas dura con 5000 y finalmente la bestia negra que casi tumba al .Net con 50.000 (y no quiero ni saber cuánto ocupaba ese string). Aquí tenéis los resultados:

Bucle META Segundos
String + 5 0,000032
String & 5 0,000029
String CONCAT 5 0,000029
String JOIN 5 0,000036
StringBuilder 5 0,000028
String + 50 0,111021
String & 50 0,303108
String CONCAT 50 0,842379
String JOIN 50 0,128027
StringBuilder 50 0,003752
String + 50000 25,125145
String & 50000 78,670144
String CONCAT 50000 146,204513
String JOIN 50000 27,010910
StringBuilder 50000 0,046688

Nota: Estas conclusiones las saqué bajo un error, los datos reales están más abajo

En principio no existen demasiadas diferencias entre las pruebas, el tiempo aumenta a la vez que lo hace la variable META, no obstante, al CONCAT y al & se les ha atragantado muchísimo la bestia negra mientras que StringBuilder ni se ha inmutado. StringBuilder es el ganador indiscutible y supera a todos los demás en todas las pruebas con una mayor velocidad de concatenación, pero claro, eso era lo que se esperaba de él… ¿no? (Hay que ver que poca fe tenéis en Microsoft…).

La sorpresa me ha llegado del Concat y del &, por un lado, el & no debería tener tantos problemas al concatenar dos cadenas, o cuanto menos debería tener menos problemas que un + que se puede utilizar para mas cosas, pero sobretodo, sorprende ver cómo el .Concat es el perdedor absoluto de esta carrera de concatenaciones, y eso que se supone que el método fue creado para algo… no parece que fuera para hacer códigos optimizados.

El resto de conclusiones podéis sacarlas vosotros mismos, eso sí, con cadenas se pueden hacer mas cosas, como replaces, substrings, remove, copy, … habría que ver una a una quién resulta mas eficiente, ¿alguien se anima?

Actualización

Parece ser que se me olvió limpiar la variable strTest tras cada bucle, con lo que los resultados anteriores estaban adulterados y eran erróneos. Tras rectificar el pequeño problemilla estos son los resultados de la bestia negra:

Bucle META Segundos
String + 50000 50,524726
String & 50000 49,690219
String CONCAT 50000 49,919286
String JOIN 50000 23,218603
StringBuilder 50000 0,041889


El ganador indiscutible sigue siendo el StringBuilder
, el Join no se vio muy afectado por el error del algoritmo por ser un array y los demás son prácticamente idénticos, así que ya podré volver a usar el & como de costumbre… 😀

Espero que quienes leyeran las conclusiones anteriores se lean las nuevas y no vivan pensando que el .Concat es el anticristo de la optimización de código. :S Por cierto, que si tenemos en cuenta que el error del algoritmo anterior acumulaba las cadenas bucle a bucle, el StringBuilder, por estar al final, se comía la peor cadena de todas y a pesar de eso ofrecía el mejor rendimiento de todos siendo casi instantáneo, un /clap bien grande desde aquí a Microsoft por semejante objeto ultraoptimizado que ponen a nuestra disposición (si, los informáticos decimos /clap, ¿que pasa?).

5 thoughts on “String Builder vs String en ASP.Net”

  1. Facinate, de hecho por falta de curiosidad no sabia que existia el StringBulder en el tiempo pasado (ya saben las prisas). Muchas gracias por este anilisis lo tomare en cuenta para el futuro

  2. Uoooo, me han avisado de que se me había olvidado limpiar la variable tras cada bucle, lo que ha alterado significativamente el experimento, los resultados obtenidos tras el arreglo si son más lógicos, aunque el StringBuilder sigue ganando por goleada.

    Ahora rectifico el post 😉

  3. me parece muy interesante, me gustaria que crearan mas sitios con ejemplos utilizables en la programacion

Comments are closed.