"Cosa" de Negro
agosto 26, 2005
 
Optimización - Profiler
El profiler es una herramienta muy útil para optimizar el funcionamiento de nuestras aplicaciones.
Nos permite obtener datos como cantidad de veces que se ejecuta una función y tiempo total empleado por dicha función.

Estos datos nos permiten conocer los puntos donde debemos apuntar nuestros esfuerzos en las optimizaciones.

El profile está compuesto por una parte por una clase en PRG y por otra parte por una serie de funciones existentes en el corazón del motor de ejecución.

La clase es la encargada de recuperar y ordenar la información generada por las funciones internas.
Las funciones internas se encargan de recabar en bruto los tiempos y cantidad de veces que las funciones son ejecutadas.

Además existen otras 2 clases que usando la información generada por la clase profile, generan reportes en forma de texto o browse.

El único gran problema que tenemos, es que la medición de tiempos tiene una granularidad extremadamente grande.
Se miden los tiempos en base a los ticks de reloj y ocurren 18 ticks por segundo.
Esto hace que cuanto más rápida es la ejecución de una función más error se introduce en la medición de tiempos.

Debemos tener presente, que los tiempos medidos son los tiempos totales de ejecución de cada función.
Esto incluye a los tiempos usados por las funciones a las cuales se llama desde dentro de la primer función.
Si A llama a B, y A tarda 2.1 segundos y B 2 segundos, el tiempo real de ejecución de la función A es 0.1 segundos.

En xharbour\tests\testprof.prg hay un ejemplo muy sencillo de como se usa el profile.
http://cvs.sourceforge.net/viewcvs.py/*checkout*/xharbour/xharbour/tests/testprof.prg

Los pasos básicos son los siguientes:

1- Crear el objeto HBProfile() antes de activar el Profiler.

oProfile := HBProfile():new()

2- Activar el relevamiento de la información.

__setProfiler( .T. )

3- Ejecutar el código que se quiere medir.

4- Desactivar el relevamiento de la información.

__setProfiler( .F. )

5- Obtener una foto del estado de la información relevada.

oProfile:gather()

6- Ordenar la información por:

oProfile:callSort() // Ordenado por cantidad de llamadas
oProfile:nameSort() // Ordenado por nombres de funciones
oProfile:timeSort() // Ordenado por tiempo de ejecución.

7- Generar un reporte

memoedit( HBProfileReportToString():new( oProfile:callSort() ):generate( {|o| o:nCalls > 0 } ), 1,,,, .F. )


Es posible ejecutar arrancar y parar el relevamiento de la información las veces que sea necesario.
Por ejemplo podemos parar el relevamiento mientras esperamos que el usuario ingrese datos.
Sin embargo, al llamar a oProfile:gather(), además de sacar la foto del estado actual, se está eliminando la información releavada hasta el momento.

Hay un uso avanzado del profiler, que sirve para obtener información de los PCODES usados y la cantidad de veces que se ejecutó cada uno.

Para esto se usa

oProfile := HBProfileLowLevel():new()

el resto de la operatoria es similar.


Esta es una herramienta más que podemos usar para optimizar el funcionamiento de nuestras aplicaciones, conocer los cuellos de botella y actuar donde sea necesario.
 
Escribiendo algo
La verdad es que me llevo muy bien escribiendo.
Escribiendo programas, claro, porque escribir en lenguaje "humano" me cuesta bastante.
Lo que más me cuesta es ordenar un pensamiento o una idea y transformarla en frases coherentes y entendibles.
Porque no es solo escribir, además, hay que cuidar el estilo, hacerse entender, evitar los regionalismos para que se entienda en cualquier otro lugar hispanohablante.

Realmente, mantener un blog técnico se parece mucho a escribir documentación.
Siento mucho más complicado escribir "humano", que transformar una idea en un programa.

En este tiempo ausente he recibido varios mensajes extrañando nuevas notas, asi que agradezco a todos los que han leido mis mensajes y espero que no se hayan ido del todo.

Gracias
mayo 04, 2005
 
Paradojas de la tecnología
Dicen que 5 años en computación es una eternidad, pero es así?

Hoy otra vez estoy crítico, o "quejoso" diría alguno.

Leyendo un artículo en la revista Dr Dobbs de Mayo/2005, una nota escrita por un ingeniero de Intel, donde muestra (nuevamente) la necesidad de ir hacia la programación multithread y multiprocesador.
Entre otras cosas, señala tecnologías existentes hace bastante tiempo, como son las instrucciones MMX.

Asi que dije, bueno esas tecnologías ya existen, voy a revisar como puedo hacer un código C que el compilador pueda transformar en instrucciones MMX.
Busco MMX en la ayuda de Visual C++. Resultado? "Microsoft Visual C++ soporta MMX desde la version 4.1 sólo mediante instrucciones ASM". (link)
Asi que, a 8 años de la existencia de procesadores con soporte MMX, todavía muchos compiladores de C no soportan la optimización del código generado para hacer uso de estas instrucciones.

La semana pasada surgió la necesidad de crear mails con soporte MMX, pero el requerimiento era que debía ser procesado por el cliente de correo del usuario para que se guarde constancia en su carpeta de mensajes enviados y para que el usuario pueda hacer agregados o modificaciones antes de su envío.
Despues de arreglar y compilar el código del archivo mapi.c de FWH para Visual C++ que no se encuentra dentro de la librería original, me puse a hacer algunas pruebas.
Todo muy lindo, excepto que no soporta HTML, asi que otra vez a a la web y al Google para encontrar un código que me permita crear un mail con soporte de HTML.
No se, pero parece que hubieran inventado ayer el correo con HTML.
Pueden creer que me pasé UN DIA entero buscando algo que ni los programdores de VB, ni la mismisima Micro$oft "sabe" como hacer por programa. Oh, paradoja, los virus si saben.
Las explicaciones son que cuando se hizo el estandar MAPI, no existian los mails con HTML, pero parece que nunca se les ocurrió hacer un nuevo estandar o una extensión al estandar o una extensión en los programas de correo para soportar algo que el estandar no soporta.

Lo más gracioso fue encontrar dentro de la ayuda de Visual Basic, la forma en que los "ingenieros" de Micro$oft sugieren resolver el problema de agregar imágenes a un mail con HTML.
Sólo para Outlook 98 o superior, porque usa OLE y porque para el Outlook Express todavía no lo resuelven.
Paso 1 - Crear el mail con el texto html usando la propiedad HTMLBody
Paso 2 - Mostrar el mensaje creado con el método Display().
Paso 3 - Cargar la imágen en el portapapeles.
Paso 4 - Enviar la secuencia de caracteres Ctrl-V.

Yo creo que despues de esta solución el equipo de programadores tuvo que tomarse un mes de descanso porque sus neuronas estaban agotadas.
Con "ingenieros" que sugieren esas cosas, creo que es en vano que me queje de los programadores, no?
abril 27, 2005
 
La delgada linea entre reusar y arrastrar & soltar
Es lamentable como la "ley del menor esfuerzo" se apodera cada vez más de las áreas técnicas.
Y parte de ello son los programadores.

Cuál es la línea que separa al programador que reutiliza código del que arrastra y suelta ?

Estoy muy de acuerdo con la reutilización de código, con no reinventar la rueda, con la POO, pero cómo puede ser posible que un programador que dice tener muchos años de experiencia no sepa hacer algo tan básico como relacionar una tabla de facturas cuyos importes están expresados en pesos con una tabla de cambio para obtener el importe en dolares ?

Qué hace un pseudo-programador cuando falla el código predeterminado ?
Por qué hay tanto software "lindo" con un funcionamiento pésimo ?
Alguien se da cuenta de que esta masa de pseudo-programadores que hacen malos programas producen perdidas millonarias en la sociedad?
abril 10, 2005
 
Rescribiendo el motor de objetos
He emprendido un desafio importante, rescribir el motor de objetos de xHarbour.

La idea es obtener mejoras en estos aspectos:

Pero ya que estamos en ritmo de modificaciones, aprovecho para abrir el debate para sugerencias.
Se me ocurren un par que considero importantes.

  1. Método SUPERINIT (se aceptan sugerencias para el nombre).
    La idea es tener un método que SIEMPRE sea llamado al crear una nueva instancia de un objeto.
    Esto incluye las instanciaciones provenientes de la ejecución de __clsInst() y las provenientes de la clonacion de un objeto con OClone() o hb_arrayClone() sobre un objeto.
    Las clases heredadas ejecutan al instanciarse todos los métodos SUPERINIT existentes en su clase, tanto los propios como los heredados.
    El método recibe un parámetro que indica si el objeto fué instanciado o clonado.
    La idea de este tipo de método, no es reemplazar al método CONSTRUCTOR actual, sino funcionar de forma similar a las INIT FUNCTION.

    Algunos posibles usos son:
    • Conteo de referencias o instancias.
    • Inicialización dinámica de variables de instancia.

  2. Métodos delegados que soporten llamadas con cantidad variable de parámetros, ya que hoy sólo soporta llamadas con una cantidad fija de parámetros.

abril 03, 2005
 
Nuevos tipos de datos en xHarbour
Hace un tiempo hubo unos comentarios acerca de que los usuarios de xHarbour no tenían posibilidad de discutir u opinar sobre nuevas sintáxis agregadas al lenguaje.

Asi que aquí tienen la oportunidad de hacerlo.

Clipper tiene el tipo de dato DATE, pero no tiene TIME o DATETIME.
Además para cargar un valor DATE fijo por programa hay que hacerlo mediante una llamada a una función, no existe una constante de tipo DATE.

Esta es la propuesta de constante de tipo DATE, TIME y DATETIME.

DATE : {^2005/04/03}
TIME : {^13:02:03.04} y también {^01:02:03.04PM}
DATETIME : {^2005/04/03 01:02:03:04}

El modificador AM/PM es opcional tanto para TIME como para DATETIME y es válido para horas de entre 1 y 12.

Espero sus opiniones sobre la sintáxis y sobre los tipos de datos a implementar.

- DATE y DATETIME.
- DATE, TIME y DATETIME
marzo 21, 2005
 
Optimización V - FOR EACH
Acelerando los recorridos sobre un array.

Es muy común que tengamos que recorrer arrays para hacer diferentes tipos de tareas, buscar, imprimir, copiar, modificar masivamente, etc.

Además de poder hacerlo con un AEVAL(), podemos usar un bucle FOR-NEXT o un bucle DO WHILE-ENDDO.

En xHarbour se agrega un nuevo bucle exclusivo para la tarea de recorrer arrays, el FOR EACH-NEXT.

Veremos más adelante que este bucle tiene algunas particularidades muy interesantes que son su punto fuerte y razón de ser.

La sintáxis de uso es la siguiente:

FOR EACH _var_ IN _array_
[LOOP]
[HB_ENUMINDEX()]
[EXIT]
NEXT

_array_ puede ser cualquier expresión que retorne un array, un objeto o una cadena de caracteres.

_var_ puede ser cualquier variable previamente declarada, pero es recomendable que sea una variable local.
Esta variable va a apuntar a una posición del array, comenzando por la primera posición.

El FOR EACH va a recorrer todo el array completamente, a menos que se fuerze la salida anticipada con el comando EXIT.
También es posible usar el comando LOOP de la misma forma que se usa en el comando FOR-NEXT.

La pseudo-función HB_ENUMINDEX() retorna el número de posición del array que se está procesando en ese momento.

Funcionamiento:

El comando FOR EACH recorre todo el array _array_ y en cada iteración guarda en la variable _var_ una referencia a la posición actual del array.

Al finalizar el bucle FOR EACH, la variable _var_ queda con valor NIL, asi que si necesitamos preservar el valor de dicha variable para usarlo fuera del bucle, debemos copiarla a otra variable antes de salir.

Qué es eso de que la variable es una referencia ?

La variable del FOR EACH funciona de forma similar a los parámetros de una función que reciben los datos por referencia.

Es como si fuera algo así: (la siguiente sintáxis es de ejemplo, no es válida)

_var_ := @_array_[n]

Qué ventajas tiene ?

La ventaja de tener en _var_ una referencia a la posición del array es que para modificar la posición del array, solo es necesario asignar el dato que queremos a la variable _var_, y lo que realmente estaremos haciendo, es modificar el contenido del array.

Donde aplicarlo?

Este comando está especialmente diseñado para recorrer arrays rápidamente y poder usar o modificar su contenido también de la forma más rápida posible.

Todas los bucles que recorren un array pueden beneficiarse con el uso de For each.
Cualquier construcción del tipo:

for n:=1 to Len(aArray)
...
next

puede ser convertida a

for each x in aArray
...
next

Los que usan Fivewin, encontrarán muchas contrucciones que pueden optimizarse usando FOR EACH, en muchos de los fuentes del sistema.
También en otras clases como TSBrowse el desempeño mejora notablemente al usar FOR EACH.

Encontrarán ejemplos y pruebas aquí: foreach.prg objlist.prg ivarref.prg

Los siguientes ejemplos producen el mismo resultado, pero con tiempos de ejecución diferentes.
El for-next y el aeval, tardan aproximadamente lo mismo, pero el for each demora la mitad de tiempo.

aArray := arrray(500000)
nLen := Len(aArray)

for n:=1 to nLen
...aArray[n] := n
next

for n:=1 to nLen
...nSum += aArrray[n]
next

------------------------

aeval( aArray, {|a,n| aArray[n]:=n} )
aeval( aArray, {|a,n| nSum += a} )

------------------------

for each x in aArray
...x := HB_EnumIndex()
next

for each x in aArray
...nSum += x
next
------------------------

Powered by Blogger