Algunos años atrás, todo lo relacionado con Visual Basic (VB) 6.0 tendía a ser menospreciado o subvalorado. Los desarrolladores que utilizábamos VB 6.0 no éramos los primeros en levantar la mano para decir orgullosos que lo utilizábamos, como sí lo hacían los que usaban C o C++.
Una pequeña fracción de esa baja estima se mantuvo aún cuando apareció .net. Era cierto que teníamos un nuevo lenguaje (o un lenguaje muy remozado) que permitía lograr cosas impensadas en VB 6.0, tales como programar realmente orientado a objetos, crear threads, o deshacernos por fin de los problemas de los componentes marcados como Apartment, pero seguía existiendo “algo.”
A mi entender, algunos de los problemas que NO ayudaron a la transición real de un lenguaje limitado a un lenguaje completo, se pueden desglosar en la siguiente lista:
La existencia Option Explicit en vb.net. No existe programación seria sin declaración e inicialización de variables.
La existencia de Option Strict en vb.net.
Microsoft.VisualBasic.dll, librería para ayudar a la migración de proyectos, que implementa esas terribles funciones como Len, Left, Right, Trim, Mid, Replace y otras.
Respecto a este último punto, siempre que tenía la dicha de ver algún proyecto usándola, terminaba recomendado que no se use y que use las propiedades de los tipos de .net. (Ej: en vez de usar Len(variable) usar variable.length).
Ante la obvia pregunta de ¿por qué no debo usarla?, venía una respuesta que obtuve de variados lugares, pero nunca comprobé empíricamente, y que sostenía que tenía menor rendimiento que las nativas de los tipos.
A veces incluso utilizaba [reflector] para justificar mi teoría, como muestro en la siguiente imagen. ¿Para qué usar Trim(variable) si al final lo que se ejecuta es variable.trim? Mejor hacerlo directamente.
En esta oportunidad he decidido hacer pruebas sobre las funciones más utilizadas de VB 6.0, pero que fueron reescritas en este ensamblado (Microsoft.VisualBasic.dll) y determinar bajo pruebas empíricas si son “tan” malas como aparentan.
Quiero hacer hincapié en algo muy importante. Las funciones antes mencionadas, implementadas en VB 6.0, y que califiqué como “terribles”, justifican el calificativo asignado. Muchas de ellas, si es que no todas, reciben como parámetro de entrada un tipo de datos variant y retornan un tipo variant.
Esta conversión a variant penaliza el rendimiento de la aplicación, y peor aún, si se realiza en en ambos sentidos (entrada y salida). Siempre hablando en el contexto de VB 6.0, algunas funciones tenían variantes que terminaban en $, como Left$, y que tenían la gracia de retornar un tipo string, pero seguían recibiendo un variant. No entiendo que costaba hacer una que recibiese string y retornase string, pero ya está. Ya fue.
Por suerte, ahora en .net, las implementaciones en Microsoft.VisualBasic.dll incluyen parámetros correctamente tipificados, con la consecuente mejora en rendimiento.
Vamos a la prueba y a los sorprendentes resultados.
Código a ejecutar
Todas las funciones a medir fueron llamadas desde una única función general para cada lenguaje, llamadas ProcesarComoVB6 y ProcesarComoVBNET. No es mi intención medir y comparar el rendimiento de cada función específica sino que medir en general que sucede si se utilizan funciones de Microsoft.VisualBasic.dll o las nativas de los tipos en forma directa.
Cada función general recibe una cadena de 41.000 bytes y la procesa siguiendo la misma regla, detallada en el código de más abajo.
El siguiente es el código a ejecutar, utilizando Microsoft.VisualBasic.dll
El mismo código, utilizando las llamadas nativas de los tipo
La prueba comprende la ejecución una cantidad de 200 veces cada una de las funciones, midiendo los tiempos en terminar de procesar todas las instrucciones. Antes de iniciar las 200 iteraciones de cada una de las funciones, estas son llamadas un par de veces antes para JITtearlas (bonita palabra, no?)
Resultados de las pruebas
Ejecutando la prueba en un proyecto desarrollado en Visual Studio 2003 con el Framework 1.1, compilado en modo release, se obtienen los resultados de la siguiente tabla:
Ejecución N° | Microsoft.VisualBasic.dll | Métodos nativos |
1 | 28,51 segundos | 16,20 segundos |
2 | 27,76 segundos | 16,15 segundos |
3 | 30,45 segundos | 17,40 segundos |
Wow…(no estoy haciendo propaganda a Vista [;)]), impresionante. No nos engañemos todavía. Aún hay mucho que descubrir.
Analicemos antes de seguir las funciones que estamos midiendo y qué podría justificar la diferencia.
Tanto Left, Right como Mid debieran tener costos similares a la representación en VB.NET utilizando Substring. Técnicamente lo que hacen es obtener un grupo de caracteres de una cadena. No hay mucha ciencia.
Conversiones de caracteres a números y viceversa no son costosos, o no debieran serlo. Entonces, Convert.ToChar y Convert.ToInt32 debieran tener similar rendimiento a Asc y Chr respectivamente. Esto último no es necesariamente cierto para el resultado de la transformación. Los resultados de Asc y Chr pueden diferir de los de Convert.* de acuerdo a la cultura que se utilice.
¿Que nos queda?…Replace…el dolor de cabeza de un procesador….
Nuevas pruebas, sin Replace
El siguiente es el resultado de las pruebas removiendo el Replace del final, de ambas funciones. Los resultados son nuevamente sorprendentes.
Ejecución N° | Microsoft.VisualBasic.dll | Métodos nativos |
1 | 7,59 segundos | 7,46 segundos |
2 | 9,20 segundos | 8,73 segundos |
3 | 8,98 segundos | 8,07 segundos |
Interesante, no? ¿Qué se puede concluir ahora?
Conclusiones aventuradas
Descartando la función Replace, como supusimos, no hay mucha diferencia en el tiempo entre utilizar el ensamblado Microsoft.VisualBasic.dll y las funciones nativas. Replace hace la diferencia. Veamos por qué.
Si se utiliza [Reflector] para ver el código de String.Replace se obtiene esto.
Si se utiliza Reflector para ver el código de Replace desde Microsoft.VisualBasic.dll, se obtiene esto, y pongan atención a la zona enmarcada en rojo. Ouch!! Eso afecta cualquier procesador.
Más aún, Strings.Split y String.Join son funciones implementadas en Microsoft.VisualBasic.dll y no las nativas del tipo string.
Consumo de recursos
Veamos ahora como anda el consumo de recursos y uso del procesador para cada ejecución. Para eso utilizaremos performance monitor y revisaremos contadores de CPU, proceso, excepciones en .net y memoria en .net.
Los contadores a medir son:
Uso del procesador para el proceso, tanto para tiempo de proceso de usuario como de kernel
Excepciones lanzadas por segundo
Colecciones en cada generación
Colecciones inducidas
Total de bytes utilizados
Bytes pedidos por segundo
Tiempo utilizado por el GC
Bytes privados del proceso
Grande fue mi sorpresa cuando los contadores de rendimiento de la memoria en .net, todos marcaban cero durante la ejecución. ¿El motivo? No se si es el motivo “oficial,” pero al menos a mi me bastó como auto-explicación. El Framework 1.1 no corre de forma nativa en 64 bits ya que este sólo puede generar código ensamblado de 32 bits. Como mi sistema operativo es 64 bits, es posible que la instrumentación de 1.1 no funcione. Por lo tanto, a mover todo al Framework 2.0 y Visual Studio 2005.
Nota: Si te preguntas por que no hice todo el post utilizando 2.0, la respuesta es porque quería mostrar cual es el impacto de correr aplicaciones 32 bits sobre 64 bits emulando 32, tanto para el Framework 1.1 como 2.0. También haré la prueba con 2.0 compilado en 32 bits, que es lo que viene justo ahora.
Compilando para 32 bits (x86) una aplicación .net 2.0
En esta oportunidad no haremos mucho análisis. Me interesa dejar los tiempos como referencia y poder comparar en los diferentes ambientes.
Versión con Replace
Ejecución N° | Microsoft.VisualBasic.dll | Métodos nativos |
1 | 23,32 segundos | 25,15 segundos |
2 | 23,29 segundos | 25,16 segundos |
3 | 23,28 segundos | 25,46 segundos |
Versión sin Replace
Ejecución N° | Microsoft.VisualBasic.dll | Métodos nativos |
1 | 7,40 segundos | 7,37 segundos |
2 | 7,17 segundos | 7,09 segundos |
3 | 7,18 segundos | 7,78 segundos |
¿Conclusiones?…mmm, no gracias.. No tengo nada inteligente que aportar, como tampoco quiero hacer suposiciones. Full 64 bits por favor!!
Compilando para 64 bits (x64) una aplicación .net 2.0
Veamos los resultados de la misma prueba compilado para 64, o mejor dicho, compilado para cualquier CPU. Total, una vez que se JITtee (bonita palabra nuevamente, no?), lo hará para el procesador en que se está ejecutando.
Versión con Replace
Ejecución N° | Microsoft.VisualBasic.dll | Métodos nativos |
1 | 14,07 segundos | 19,35 segundos |
2 | 13,98 segundos | 18,79 segundos |
3 | 14,25 segundos | 19,12 segundos |
Versión sin Replace
Ejecución N° | Microsoft.VisualBasic.dll | Métodos nativos |
1 | 5,86 segundos | 5,96 segundos |
2 | 5,93 segundos | 5,68 segundos |
3 | 5,79 segundos | 5,75 segundos |
¿¿¿¿Qué????
Parece que el equipo de desarrollo de VB.NET se tomó en serio las críticas y decidieron mejorar el producto.
Veamos con reflector el código de Replace de Microsoft.VisualBasic.dll, para la versión 2.0 del Framework.
Interesante. Cambiaron esa idea de hacer splits y joins e implementaron una función con ese fin específico. ReplaceInternal utiliza un Stringbuilder para cumplir su labor. Bien.
Volvamos ahora que tenemos contadores al análisis del consumo de recursos.
Consumo de recursos, V 2.0
Analicemos la utilización de recursos para ambas versiones, con y sin Replace. En todos los contadores colocaré el valor por defecto, que en algunos casos es el promedio y otros el máximo.
Versión con Replace
Contador | Microsoft.VisualBasic.dll | Métodos nativos |
# Exceptions Thrown/sec | 0 | 0 |
# Gen 0 Collections | 23.236 | 15.134 |
# Gen 1 Collections | 2 | 61 |
# Gen 2 Collections | 0 | 12 |
# Induced GC | 0 | 0 |
# Total Commited Bytes | 1,3 MB | 3 MB |
% Time en GC | 7,1 % | 2,8 % |
Allocated bytes/sec | 1,1 GB (así es) | 512 MB |
% User Time | 89 % | 85 % |
% Privileged Time | 3 % | 12 % |
Private Bytes | 23 MB | 24,8 MB |
Mini conclusiones versión con Replace
La utilización de métodos nativos consume menos recursos en general. Hay una reducción importante en las recolecciones de primera generación (gen 0), pero las recolecciones en esa generación son muy económicas, al igual que las de la generación 1. Las de generación 2 son caras y hay que evitarlas, aunque 12 es un número pequeño.
Lo anterior se traduce en que la versión con métodos nativos consume menos procesador recolectando “basura,” y genera mucho menos “basura” (Allocated bytes/sec).
En la versión con métodos nativos, podría existir una relación entre la cantidad de bytes commited y la cantidad de recolecciones de segunda y tercera generación (gen 1 y gen 2) ya que hay memoria que se mantiene ocupada más tiempo y no es liberada en recolecciones de primera generación. Esto es solo una suposición.
Versión sin Replace
Contador | Microsoft.VisualBasic.dll | Métodos nativos |
# Exceptions Thrown/sec | 0 | 0 |
# Gen 0 Collections | 14.305 | 12.654 |
# Gen 1 Collections | 0 | 52 |
# Gen 2 Collections | 0 | 10 |
# Induced GC | 0 | 0 |
# Total Commited Bytes | 1,3 MB | 3 MB |
% Time en GC | 9,3 % | 8,2 % |
Allocated bytes/sec | 2,0 GB | 1,5 GB |
% User Time | 93 % | 94 % |
% Privileged Time | 4 % | 4 % |
Private Bytes | 23,5 MB | 25,9 MB |
Mini conclusiones versión sin Replace
El consumo de recursos es equivalente. Llama la atención el aumento en ambas versiones de la cantidad de bytes pedidos por segundo (allocated bytes/sec).
El resto de los contadores no presenta diferencias importantes entre ambas funciones para la versión sin Replace.
Conclusiones
Las siguientes conclusiones puedo obtener del análisis.
1.- No correr aplicaciones 1.1 en 64 bits
2.- No correr aplicaciones compiladas para x86 en 64 bits
Tristemente, mi intención inicial de encontrar algún motivo para que no se utilice Microsoft.VisualBasic.dll en los proyectos no ha podido ser cumplida. [:(]
En el único escenario donde encarecidamente recomendaría no usarla es en aplicaciones 1.1, pero en aplicaciones 2.0, la diferencia es irrelevante.
Saludos,
Patrick.
como hago para ver el codigo de io.dll es para hacer un trabajo de la universidad y me urge…
muchas gracias
mi correo es alevilla86@hotmail.com
como hago para ver el codigo de io.dll es para hacer un trabajo de la universidad y me urge…
muchas gracias
mi correo es alevilla86@hotmail.com
Alejandro,
puedes usar reflector para ver el codigo IL de los ensamblados. Me imagino que te refieres a system.io.dll.
Saludos,
Patrick
HOLA HE TENIDO LA INQUIETUD DE IMPLEMENTAR UN SISTEMA DE VISION PERO ME HAN MENCIONADO QUE LA LLAMADA A LIBRERIAS DLL ME SERIAN MUY UTILES PERO PARA SER SINCERO SOY UN PRINCIPIANTE EN EL AREA DE VISUAL BASIC NO SE SI PODRIAS AYUDARME CON ESA DUDA, COMO LLAMAR A DLL CON VISUAL BASIC
AL52848,
por supuesto que puedo ayudarte. Necesito eso si que seas más específico en cómo quieres que sea. ¿Qué andas buscando?
Saludos,
Patrick
If you are already refused there exists still the opportunity you can get one not fake
a fico credit score of a minimum of 620 is necessary to acquire your house.
If you want to increase your knowledge just keep visiting this site and be updated with the hottest gossip posted here.
Sports entertainment are actually high in knnowledge when seen in tthe correct way.
Don’t fear making decisions even iif you’re not that informed about them.
So, even if youu took time offf juxt to watch the game live, you
can still ean some money and maybe more while
hqving fun on the stadiums chering and screaming together
with thousands of fans.
Hi there! I understand this is somewhat off-topic but I needed to ask.
Does building a well-established website such as yours take a
massive amount work? I am completely new to writing a
blog but I do write in my diary daily. I’d like to start a blog so I can easily share my experience and feelings online.
Please let me know if you have any kind of ideas or tips for brand new
aspiring blog owners. Thankyou!
Fascinating blog! Is your theme custom made or did you download it from somewhere?
A theme like yours with a few simple adjustements would really make my blog jump out.
Please let me know where you got your design. Kudos
I will right away grab your rss feed as I can’t
find your email subscription hyperlink or
newsletter service. Do you’ve any? Kindly allow me recognise in order that I may just subscribe.
Thanks.
Excellent site you have got here.. It’s hard to find excellent writing like yours nowadays.
I really appreciate individuals like you!
Take care!!
Wow that was unusual. I just wrote an very long comment but after I clicked submit my comment didn’t appear.
Grrrr… well I’m not writing all that over again.
Anyways, just wanted to say fantastic blog!
It is perfect time to make a few plans for the long run and it’s
time to be happy. I have read this submit and if I could I
wish to suggest you few fascinating things or advice.
Maybe you could write next articles relating to this article.
I wish to learn even more things about it!
Appreciate this post. Will try it out.
Hello are using WordPress for our site platform? I’m new
to the blkg world but I’m trying to get started and create my own.
Do you require anyy coding expertise to make your own blog?
Any help would be really appreciated!
Hello my friend! I want to say that this post is awesome,
great written and come with almost all vital infos.
I would like to look extra posts like this .
You’re so interesting! I do not believe I’ve read through
something like this before. So good to find another person with a few original thoughts on this issue.
Seriously.. thanks for starting this up. This web site is one thing that
is needed on the web, someone with a little originality!
conversation on the topic of this piece of writing here at this weblog, I have read all that, so at this time me also commenting at this place.|
I am sure this post has touched all the internet
viewers, its really really pleasant paragraph on building up new weblog.|
Wow, this post is fastidious, my sister is analyzing such things, so I
am going to inform her.|
Saved as a favorite, I like your blog!|
Way cool! Some very valid points! I appreciate you
http://libertyeducationforum.org/michael-kors-outlet-online.html