7 de noviembre de 2001

Acelerando los programas

Autor: Jim Booth
Traducido por: Pablo Roca

Cómo hacer tus programas rápidos, o cómo eliminar las partes lentas de las aplicaciones.

Todo el mundo quiere velocidad. Veloz es mejor, dicen. ¿Cómo puedes optimizar tu código? ¿Existe alguna metodología estructurada que nos permita obtener el mejor rendimiento en nuestras aplicaciones? Este mes examinaremos algunos principios que pueden ser "implementados" en el proceso de optimización del código. Este artículo no tratará sobre la tecnología Rushmore, Rushmore ha sido y será cubierta en otros artículos. En vez de eso nos centraremos en prácticas de programación y técnicas para obtener el mejor rendimiento de nuestras aplicaciones.

Datos

Algunos tipos de datos se manejan más rápidos que otros en Visual FoxPro. Esto tiene que ver con la manera en que los datos son almacenados en un fichero dbf contra la forma en que son almacenados en memoria. Por ejemplo, el tipo Entero es almacenado en el DBF tal y como se usa en memoria, sin embargo, el tipo Numérico se almacenada como una cadena de números y se utiliza en memoria como un número binario en coma flotante. El tipo Numérico debe ser convertido de acá para allá una y otra vez cuando lo usamos. Cuando sea posible, usaremos un Entero para los valores numéricos (o un Doble para los números en coma flotante).

Los tipos Fecha y DateTime ocupan la misma cantidad de espacio en un DBF. Sin embargo, las Fechas se almacenan en el formato estandar ANSI AAAAMMDD, mientras que los campos DateTime se almacenan como un número de 8 bytes. En memoria, ambos tipos utilizan el formato de número de 8 Bytes, pero los campos del tipo Fecha tienen que ser convertidos, y los del tipo DateTime no. Podemos utilizar el tipo DateTime para todos nuestros datos de Calendario y obtener lo mejor de VFP. Si sólo necesitamos la Fecha, dejaremos el tiempo en el valor por defecto 00:00:00.

¿Cuándo un número no es un número? Cuando nunca se utiliza en cálculos. Por ejemplo, números de teléfono, códigos postales, números de cliente, números de factura son ejemplos de números que no son números. ¿Cuándo fue la última vez que tuviste que calcular la media de los números de factura en una tabla? Almacenemos esos valores en campos de tipo carácter, y limitemos los caracteres a sólo números. Los datos alfanuméricos se manipulan más rápidos que los datos numéricos, utilicemos datos numéricos sólo cuando sea realmente un número (es el momento de utilizar Enteros o Dobles).

Los campos Memo te permiten almacenar datos de longitud variable, pero podemos abusar de ellos por desidia. No utilices campos Memo sólo porque no tienes ganas de calcular cual es la longitud máxima del dato. Haciendo esto codificaras un poco más rápido pero la aplicación se ralentizará considerablemente. Los campos Memo utilizan más tiempo en las búsquedas y en ser procesados. Utiliza campos Carácter siempre que sea posible.

Construcciones

Algunas construcciones de programación son más rápidas que otras. El bulcle FOR/ENDFOR es más rápido que su equivalente DO WHILE/ENDDO. SCAN/ENDSCAN es más rápido que el equivalente DO WHILE NOT EOF()/SKIP/ENDDO. Utiliza las contrucciones más rápidas para obtener un código más rápido, o como dijo una vez George Goley, "Make your code faster by taking the slow parts out" (Haz tu código más rápido eliminando las partes lentas).

Creación de Objetos

La creación de objetos consume tiempo. Ahí no hay nada que hacer. Entonces, ¿cómo optimizaremos la contrucción de objetos?

No crees un objeto hasta que lo necesites. Cuando utilices objetos de interfaz como Marcos de Página (PageFrames), no llenes las páginas (con los objetos de las mismas) hasta que el usuario entre por primera vez en ellas. Esto se llama "delayed instantiation" ("instanciación retrasada") y puede lograrse creando las páginas con todos sus componentes en el Diseñador de Formularios. Entonces, tomando cada página y salvando el conjunto de controles de cada página como una clase contenedor utilizando la opción Guardar como Clase... (Save As Class) del menú Archivo (File). Borra todos los controles de la página. Pon una etiqueta en la página con la Propiedad Visible puesta a .F., en el evento UIEnable coloca un código como este:

IF TYPE("THIS.Parent.Container1.Name") = "C" 
  RETURN
ENDIF
THIS.Parent.AddObject("Container1","YourSavedClass")
THIS.Parent.Refresh()
Con servidores de automatization utiliza la función GetObject() mejor que la función CreateObject. GetObject() encontrará cualquier instancia existente del ojeto mientras que CreateObject() siempre creará una nueva aunque el objeto ya exista. Si el objeto que buscas existe, puedes evitar la instanciación usando la función GetObject().

Refrescando Pantallas

Si quieres ver algo interesante, prueba a hacer un seguimiento del evento Refresh y ejecuta una de tus aplicaciones. Solo ejecútala un momento haciendo algunas cosas y entonces mira los eventos Refresh que se han "disparado". Te puedes llevar una sorpresa cuando veas cuantas veces se ha ejecutado el evento Refresh.

A menudo es más rápido llamar al Refresh de un control individual que llamar al Refresh del Formulario. Intenta limitar las llamadas a Refresh a aquellas que sean imprescindibles. Recuerda que llamando al Refresh del Formulario se llamarán los eventos Refresh de todos los controles en el mismo. Ocurre lo mismo cuando se "Refresca" (actualiza) una rejilla, una página en un Marco de Páginas y las demás clases contendor.

Expresiones de Nombre en lugar de Macros

Cuando sea posible, utiliza una "name expresion" (Expresión de Nombre) en lugar de una Macro. Las Macros se tienen que expandir e interpretar en tiempo de ejecución cada vez que se encuentran. Las Macros no sólo hacen tu código ilegible y la lógica obscura, sino que además tardan más tiempo en ejecutarse.

Por el otro lado, las Expresiones de Nombre se ejecutan mucho más rápido. Se pueden utilizar en cualquier lugar en el que VFP espere encontrar el nombre de algo. Nombres como nombres de tabla, nombres de fichero, nombres de campo, etc son los candidatos para las expresiones de nombre. Los dos siguientes ejemplos de código muestran la idea:

* Ejemplo 1 Macros
LOCAL lcNombredeCampo
USE AlgunaTablaRealmenteGrande
USE AlguanOtraTabla IN 0
SCAN
  lcNombredeCampo = "AlgunaTablaRealmenteGrande.SumadeAyer"
  SELECT AlgunaOtraTabla
  SCAN FOR AlgunaOtraTabla.ForeignKey = AlgunaTablaRealmenteGrande.PrimaryKey
    REPLACE &lcNombredeCampo WITH &lcNombredeCampo + AlgunaOtraTable.SumadeVentas
  ENDSCAN
ENDSCAN

* Ejemplo 2 Expresión de nombre
LOCAL lcNombredeCampo
USE AlgunaTablaRealmenteGrande
USE AlgunaOtraTabla IN 0
SCAN
  lcNombredeCampo = "AlguanTablaRealmenteGrande.SumadeAyer"
  SELECT AlgunaOtraTabla
  SCAN FOR AlgunaOtraTabla.ForeignKey = AlgunaTablaRealmenteGrande.PrimaryKey
    REPLACE (lcNombredeCampo) WITH EVALUATE(lcNombredeCampo) + AlgunaOtraTabla.SumadeVentas
  ENDSCAN
ENDSCAN

El segundo ejemplo se ejecutará más rápido que el primero. Reseñar el uso de la función EVALUATE() para conseguir el valor almacenado en el campo.

Haciendo Referencia a las propiedades de los objetos

Referirse a Propiedades de Objetos es más lento que referirse a variables de memoria. Cuando necesitas escribir código que usará una Propiedad de un Objeto múltiples veces será más rápido poner el valor de la Propiedad en una variable de memoria y utilizar la variable en el código posterior. Dos ejemplos para ilustrarlo:

* Ejemplo 1
USE AlgunaTabla
SCAN FOR AlgunCampo = THISFORM.AlgunaPropiedad
  Blah
  Blah
  Blah
ENDSCAN

* Ejemplo 2
USE AlgunaTabla
LOCAL lcValor
lcValor = THISFORM.AlgunaPropiedad
SCAN FOR AlgunCampo = lcValor
  Blah
  Blah
  Blah
ENDSCAN
El segundo ejemplo se ejecutará más rápido.

Otro método de optimizar el acceso a las Propiedades es utilizar la instrucción WITH/ENDWITH. Cuando necesites acceder a varias propiedades del mismo objeto puedes hacerlo más rápido usanto WITH/ENDWITH.

 * Ejemplo 1
THISFORM.Propiedad1 = "ABC"
THISFORM.Propiedad2 = "ABC"
THISFORM.Propiedad3 = "ABC"
THISFORM.Propiedad4 = "ABC"
THISFORM.Propiedad5 = "ABC"
THISFORM.Propiedad6 = "ABC"
THISFORM.Propiedad7 = "ABC"
THISFORM.Propiedad8 = "ABC"

* Ejemplo 2
WITH THISFORM
  .Propiedad1 = "ABC"
  .Propiedad2 = "ABC"
  .Propiedad3 = "ABC"
  .Propiedad4 = "ABC"
  .Propiedad5 = "ABC"
  .Propiedad6 = "ABC"
  .Propiedad7 = "ABC"
  .Propiedad8 = "ABC"
ENDWITH


Otras Optimizaciones

Abre las tablas sólo una vez cuando sea posible, abriendo instancias adicionales de una tabla incrementa el consumo de memoria y ralentiza otras operaciones.

Manda la salida a la ventana activa en ese momento siempre que puedas, toma más tiempo actualizar una ventana en segundo plano que actualizar la que está activa.

Puedes utilizar la Propiedad LockScreen de un Formulario para impedir modificaciones interactivas del contenido de un formulario durante las operaciones. Pon LockScreen a .T. antes de comenzar una operación y ponlo a .F. cuando se haya completado. Aunque no nos da una gran mejora de tiempos, hace que el interfaz parezca más concisa, ya que el usuario no ve las actualizaciones como una secuencia.

En el programa de comienzo pon siempre SET DOHISTORY OFF. Además, cuando sea posible, SET TALK OFF. Ambas, DOHISTORY y TALK, gastan tiempo en hacer sus cosas, desactivándolas dejan de gastar ese tiempo.

Los Formularios individuales se cargan antes que los Conjuntos de Formularios. Esto es normal, porque los Conjuntos de Formularios deben crear múltiples formularios. Cuando sea posible utiliza formularios individuales separados de tal manera que sólo crearás aquellos que el usuario vaya a utilizar.

Utilizar el Entorno de Datos de un Formulario o Informe es mucho más rápido abriendo las tablas que codificar instrucciones USE para abrirlas.

Borrar los objetos de la memoria tan pronto como hayas acabado de utilizarlos. Esto libera más memoria para los objetos que están siendo usados todavía.

Escribe las instrucciones IF de manera que su expresión tenga como resultado .T. habitualmente.

Para código condicional en métodos, escribe una instrucción IF al principio que ejecute RETURNS cuando la expresión sea verdadera para salir rápidamente del método. Se ejecutará más rápido que haciendo que VFP analice el método entero para encontrarse con que no necesita ejecutar nada del ese código.

Escribe las instrucciones DO CASE de tal manera que los primeros apartados CASE sean los que se ejecuten más a menudo.

Resumen

Este artículo muestra algunas cosas diferentes que puedes hacer cuando escribes tus aplicaciones para mejorar su rendimiento. Optimizar una aplicación es un proceso laborioso, siguiendo estos principios desde el comienzo de la codificación reducirás la complejidad de optimizar el código después.

No hay comentarios. :

Publicar un comentario