"Cosa" de Negro
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
------------------------
marzo 16, 2005
64 bits necesidad o marketing
Un poco de cada cosa, en cada caso puede haber más o menos necesidad.
A las nuevas tecnologías siempre hay que darles un tiempo para que se estandarizen y se
masifiquen, a fin de que bajen los costos y compremos algo compatible con el resto.
Y los 64 bits no escapan a esa norma.
Veo a esta primer serie de procesadores de 64bits como los 386 en la época de los 16bits.
Pero con algunas diferencias.
Hoy el mercado esta un poco saturado de ofertas y con un tiempo de recambio tecnológico
que en promedio es superior al de la salida de nuevas tecnologías.
Uno de los motivos de estos nuevos procesadores es movilizar un poco el mercado.
Qué sentido tiene desarrollar memorias de 2Gb si las placas de las PC apenas soportan
3 o 3.5 Gb y para mayor performance hay que usar 2 en paralelo?
Mucho menos sentido tendría desarrollar memorias de 4Gb.
Con un procesador capaz de usar esa memoria y mucho más, tiene otro sentido.
Otro de los motivos es el techo físico que están alcanzando los procesadores de hoy en día. Los 64bits brindan un mayor poder de cálculo y de transferencia de datos a menores frecuencias.
Y aunque suene paradójico, el estandar es y seguirá siendo 32bits por mucho tiempo más gracias a los 64bits.
Esto es porque un sistema operativo de 64bits podrá administrar la memoria más allá de los 4Gb y cada programa de 32bits podría usar hasta 4Gb de memoria sin problemas.
Esto permitiría volver a los viejos tiempos del mainframe, con servidores de alta potencia y gran cantidad de memoria con terminales ejecutando programas de 32bits en forma remota sobre el servidor.
Por el lado de los 64bits nativos, el manejo de grandes cantidades de datos serán las principales necesidades. (SQL, video, etc)
Sin embargo, no todo es gratis en los 64bits, el consumo de memoria de un programa nativo de 64bits puede llegar a ser 80% superior que el mismo programa en 32bits.
Esto no será mayor problema cuando las memorias cuesten la mitad de lo que hoy cuestan y existan modelos del doble o más capacidad.
El aumento en el consumo de memoria se debe principalmente a que los punteros crecen un 100%, de 32bits a 64bits y lo mismo ocurre con el tipo de datos LONG.
Debido al mayor consumo de memoria, una aplicación de 64bits puede tener un rendimiento similar a una de 32bits, esto es porque se tendrán que transferir cantidades mayores de datos, y porque la cache podrá almacenar menor cantidad de información efectiva, obligando a acceder a la memoria principal una mayor cantidad de veces.
Para reducir este problema, se han hecho algunas cosas como:
- el agregado 8 registros de proposito general y 8 para instrucciones SIMD (MMX, SSE, etc).
Algo que creo debería haberse hecho hace bastante.
- el controlador de memoria integrado en el procesador, que permite reducir o eliminar las latencias (demoras) en el acceso a la memoria, reduciendo notablemente el tiempo de acceso.
Los comentarios que he leido sobre la performance de las aplicaciones de 64bits nativas, son que corren a una velocidad similar a una aplicación de 32bits con un mayor consumo de memoria.
Pero es de esperar que en el futuro próximo se mejoren los compiladores, los procesadores y resto de los componentes que intervienen (memorias, chipset, discos, etc).
Por el momento, no existen grandes motivos para desarrollar aplicaciones nativas en 64bits, excepto en casos puntuales o de desarrollo de software de base y sistemas operativos.
Y aunque los 32bits todavía tienen mucha vida, no debemos dejar de preocuparnos por mejorar el desempeño de nuestros sistemas y no derrochar procesamiento innecesario, porque los 64bits no son la panacea ni la gallina de los huevos de oro.
AHORRE, OPTIMIZE SUS SISTEMAS.
marzo 11, 2005
Optimización descartada?
En uno de los primeros post se discutió el tema de optimizaciones sobre arrays. Optimizacion II - Arrays
Jose F. Gimenez sugirió que sería interesante tener un mecanismo para que el crecimiento del array fuera escalonado y evitar las continuas llamadas a realloc. Con este mecanismo, las realocaciones se harian menos frecuentes.
Finalmente hice los cambios necesarios. El siguiente paso fué hacer las pruebas, para ver cuanto se había mejorado la performance.
Cual fué mi sorpresa al descubrir que no hay diferencias y hasta es más lento.
Sólo para arrays grandes (100.000) y escalonados grandes (1.000) obtuve importantes diferencias, cerca de la mitad de tiempo.
Todavía tengo que hacer más pruebas, pero hasta el momento parece ir contra la lógica, o más bien que mi lógica no es la usada por el administrador de memoria del sistema operativo, lo cual es altamente probable.
Largo del array | Bloque | Tiempo |
100.000 | 0 | 7.04 |
100.000 | 16 | 7.25 |
100.000 | 100 | 7.18 |
100.000 | 1.000 | 3.88 |
100 | 0 | 0.26 |
100 | 16 | 0.22 |
100 | 100 | 0.22 |
marzo 03, 2005
Distribución xHarbour de PuertoSUR
Se encuentra disponible la distribución de PuertoSUR basada en xHarbour 99.3
Existen dos compilaciones:
- Compatible con FWH 2.4
- Compatible con FWH 2.5
La mezcla de versiones de FWH y xHarbour no compatibles puede ocasionar errores y GPFs aleatorios.