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?).
















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