Conseguir la maxima velocidad del EXE creado por Harbour

Conseguir la maxima velocidad del EXE creado por Harbour

Postby George » Tue Mar 13, 2012 5:07 pm

Hola foro,
Basado en sus experiencias, ¿cuáles son los procedimientos a seguir para conseguir la velocidad máxima del EXE generado por Harbour + FWH?
Por ejemplo, estoy pensando en lo siguiente:
1. Sustituir código Harbour por código C (en funciones que pueden ser, o deberían ser, optimizadas mediante el uso de código C).
2. Usar variables locales en lugar de variables globales.
3. Para bases de datos usar ADS/ADT en lugar de DBF (no estoy seguro cual sera mas rapido).
4. Uso de tablas HASH en lugar de arrays
5. Uso de los bloques de código y funciones eval()

Algunas preguntas:
Fivetechsoft o algun compañero del foro han hecho pruebas comparando las velocidades de EXE de harbour vs. EXE de lenguaje C?
Seria posible llamar codigo "assembler" desde Harbour?
Es el P-Code, generado por Harbour, una gran limitación para obtener EXE que corran rapido, comparable a los EXE generados por el lenguage C?
Se pudiera anular (o disminuir notablemente) , esta limitación, llamando a funciones escritas en lenguaje C (o assembler) desde el codigo Harbour?

Saludos,

George
George
 
Posts: 725
Joined: Tue Oct 18, 2005 6:49 pm

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby hmpaquito » Tue Mar 13, 2012 6:28 pm

George,

La velocidad del .exe depende _MUCHO_ del compilador C que utilices. Te hablo sobre lo que he leído en el foro de Harbour.

Saludos
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby Antonio Linares » Tue Mar 13, 2012 6:44 pm

George,

Una aplicación en Harbour finalmente genera un EXE a partir de lenguaje C, luego no hay diferencia, salvo que usamos una máquina virtual que interpreta pcode, pero esa máquina virtual está programada en C y es muy eficiente, posiblemente de las más rápidas que existan :-)

Pero tu pregunta me hace pensar en por que te planteas esto ? Es que tu aplicación va "lenta" ? De ser asi, en que va lenta ? Que partes de ella ?

Y aqui es en donde aparece el concepto de "profiling": http://en.wikipedia.org/wiki/Profiling_(computer_programming)

Para acelerar la ejecución de una aplicación hay que identificar los "cuellos de botella" que enlentecen la aplicación. Harbour implementa un profiler (yo lo diseñé) que basicamente mide los tiempos de ejecución de cada función (ó método) y además informa de cuantas veces cada proceso es llamado. Los que se llamen más veces obviamente son los mejores candidatos a ser optimizados, para ganar en velocidad de ejecución.

Otra posible razón de lentitud es el consumo de memoria. Los sistemas operativos usan "memoria virtual" que basicamente se traduce en usar tanto la ram como los discos duros para almacenar información. A mayor consumo de memoria, mayor necesidad de memoria virtual y mayor lentitud. Hoy en día parecería que esto es "improbable" pero, por ejemplo, estos dias estoy probando la nueva versión Mountain Lion de OSX y uno puede experimentar esto perfectamente. A mayor complejidad de los sistemas operativos, más consumo de memoria y mayor lentitud. De ahi que se hace necesario el usar de hardware más rápido.
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42122
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby George » Tue Mar 13, 2012 10:09 pm

Antonio,
Como siempre, te agradezco tu pronta respuesta asi como tus consejos tecnicos y tu disposicion de ayuda.
Pero antes de seguir quiero aclararte que TODAS las aplicaciones que he desarrollado con Harbour + Fivewin NO son lentas en modo alguno.
Por el contrario son mas rapidas que todos los otros entornos de desarrollo (IDE) que he utilizado y que no lo voy a mencionar para no entrar en discusiones irrelevantes.

Lo que me refiero es a la *optimizacion* del codigo EXE generado por Harbour (aunque gracias al comentario der hmpaquito y al tuyo creo que entiendo mejor ahora).

Por ejemplo si tenemos una database con decenas de millones de records donde los campos pueden ser tratados como un 'string" de bits, talvez podemos usar FWH64 para representar una informacion "binaria" de longitud 64 bits almacenado como un numero entero en formato hexadecimal.
En este caso podemos aplicar el concepto de Bit Map (como expresado en el libro "Clipper 5.2 - Power Programmer Guide", autor Rick Spencer, pags. 688-694), para accesar directamente (y manipular) esta informacion a nivel de bits.

Supongamos que en nuestra database tenemos un tipo de producto X particular, representado por el string de 64 bits expresado en hexadecimal, y que la posicion No. 32 (dentro de ese string de 64 bits) representa un atributo especifico (del producto X) cuando esta "1" y la carencia de ese atributo cuando esta "0". Aqui podemos, de forma muy rapida, mediante operaciones bitwise (AND, OR, o XOR) investigar cuales productos tienen esa caracteristicas y cuales no,
Lo anterior es posible ya que estas operaciones "bitwise" se ejecutan en "computer's clock cycle" asi que nuestros programas realizarian millones de estas operaciones por segundo.

La pregunta mia es, si debido al factor P-Code, que tipo de limitaciones tendremos al usar las operaciones bitwise desde Harbour64 + FWH64?
Si hay limitaciones, en lo que se refiere a la velocidad, podemos evitarlas desarrollando funciones escritas en lenguaje C y llamandolas desde nuestro programas Harbour64?

Cual es tu opinion referente al codigo siguiente que esta siendo llamado desde Harbour64 + FWH64?
Se estaria ejecutando a una velocidad diferente (menor) que si lo llamaramos desde un programa escrito enteramente en C, aunque utilizemos el mismo compilador?
Code: Select all  Expand view  RUN
#pragma BEGINDUMP
#include <hbapi.h>
#include <math.h>

// Bitwise XOR C Function
HB_FUNC( C_XOR )
{

    long long int nHexadecimal1, nHexadecimal2;
    long long int nXor;

    nHexadecimal1 = hb_parnll(1);
    nHexadecimal2 = hb_parnll(2);

    nXor = nHexadecimal1 ^ nHexadecimal2;

    hb_retnll( nXor );

}

#pragma ENDDUMP
 


Saludos,

George
George
 
Posts: 725
Joined: Tue Oct 18, 2005 6:49 pm

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby Adolfo » Tue Mar 13, 2012 11:47 pm

George...

Si, lo mas problabe es que tengas un optimizacion de 0.00000001 segundo... que lo vas a perder 1.000.000 de veces en el siguiente Do While.. que no esta optimizado.

Lo mejor cuando trabajas con compiladores, especialmente con aquellos que generan codigo C puro y duro, es la optimizacion del procedimiento o funcion que ejecuta ciclos, ahi es donde los problemas aparecen, no en la ejecucion lineal de tu programa.

1.-Evita lo que mas puedas las escrituras y lecturas a disco.
2.-Optimiza al maximo los ciclos. (Do While...End / For...Next etc)
3.-Controla al maximo las excepciones (calculos o recuperaciones de divisiones por cero por ej) Aqui en el foro habia un post muy interesante sobre eso.
4.-Cuida el flujo de tu programa, evita pasos innecesarios, respuestas del usuario.
5.-Menos es mas
6.-Crea consultas inteligentes (en el caso de SQL)
7.-Crea indices de acuerdo a lo que vas a necesitar. Con la seguridad de usarlos y de que te permitan mejorar la respuesta de la DB.

Con eso tienes mas que suficiente para "acelerar" tu exe.
Los internals de C, pueden mejorar tu velocidad, pero en cuanto.. casi imperceptible para el usuario.

Saludos desde Chile
Adolfo
;-) Ji,ji,ji... buena la cosa... "all you need is code"

http://www.xdata.cl - Desarrollo Inteligente
----------
Asus TUF F15, 32GB Ram, 2 * 1 TB NVME M.2, GTX 1650
User avatar
Adolfo
 
Posts: 860
Joined: Tue Oct 11, 2005 11:57 am
Location: Chile

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby George » Wed Mar 14, 2012 12:40 am

Adolfo,
Muy clara tu respuesta.
Estoy de acuerdo contigo en tus sugerencias y sobretodo en que las instrucciones tipo "loops" y de toma de decisiones nos hacen perder en mucho lo poco que gananos.

Por otro lado imaginate un escenario donde usemos unicamente operaciones bitwise, no "for-next" , no "do while - enddo", no "if-else-endif"
Lo que me gustaria saber es como las operaciones "bitwise" se verian afectadas por P-Code.
Bueno, creo que tendria yo mismo que hacer estas pruebas :|

Saludos,


George
George
 
Posts: 725
Joined: Tue Oct 18, 2005 6:49 pm

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby thefull » Thu Mar 15, 2012 1:07 pm

Bueno , algunas mejoras, es insignificante pero, te aseguro que esto es una cuello de botella enorme;

Code: Select all  Expand view  RUN

aArray := Array( 1000000 )
For x := 1 to len( aArray )   // MALO MALISIMO


Optimizado;

Code: Select all  Expand view  RUN
nLen := Len( aArray )
for x := 1 to nLen


La explicación es simple, en el primero , evalua LEN() por cada vuelta, en el otro, no.

Lo mejor que puedes hacer es poner el PROFILE , pero para ello tienes que construir Harbour con ese soporte, porque la aplicación no estará pensada para el cliente final, si no para encontrar los cuellos de botella.
Saludos
Rafa Carmona ( rafa.thefullARROBAgmail.com___quitalineas__)
User avatar
thefull
 
Posts: 731
Joined: Fri Oct 07, 2005 7:42 am
Location: Barcelona

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby George » Thu Mar 15, 2012 4:51 pm

Gracias thefull.
Ire tomando notas de todos los "tips".

Saludos,

George
George
 
Posts: 725
Joined: Tue Oct 18, 2005 6:49 pm

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby hmpaquito » Thu Mar 15, 2012 5:15 pm

George,

Como ha quedado inaugurada la temporada de tips, a vuela pluma va uno:

Code: Select all  Expand view  RUN


Tip. Cuidado con  Aadd() !
// No hacer:
a:= {}
for ni:= 1 to 1000
   Aadd(a, "algo")
next

// Sino hacer mejor asi:
a:= asize(1000)
for ni:= 1 to 1000
   a[ni]:= "algo"
next

 


A ver si la gente se anima a poner aqui sus tips de velocidad.

Saludos
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby MarioG » Fri Mar 16, 2012 12:43 pm

Pues acá les dejo un interesante tip "del dia"
http://forums.fivetechsupport.com/viewtopic.php?f=6&t=23671&p=127486#p127481

Y otros del 2005, publicados por el amigo Walter Negro en su blog
Optimizacion I: http://cosadenegro.blogspot.com/2005/02/optimizacion-i.html
Optimizacion II: http://cosadenegro.blogspot.com/search? ... izacion+II
Optimizacion III: (parece que se perdio, pero les pego el texto, porque "el que guarda siempre tiene" :wink: )
Optimización IV - Walter Negro
605 palabras totales en este texto (24 Lecturas) Versión Imprimible
<modules.php?name=Sections&op=printpage&artid=13>
------------------------------------------------------------------------

Comando With Object

WITH OBJECT ... END

Esta es otro de los nuevos comandos de xHarbour que permiten ahorrar
tiempo en tareas repetitivas.

Este comando realiza una cache de un objeto y junto con algunos PCODEs
permite acelerar el acceso a metodos y datas de un objeto.

La sintáxis más común es:

WITH OBJECT oGet
...:Name := "Nombre"
...:VarPut( "Walter" )
END

También se puede usar la función HB_SetWith().
Al pasarle el dato, lo guarda en la cache, y al llamar a esta función
sin parámetros, lo quita.
Para acceder al objeto en el cache se usa la función HB_QWith().
Hay que destacar, que tanto HB_SetWith() como HB_QWith() no son
funciones reales, sino que en tiempo de compilación se transforman en
los mismos PCODEs usados en WITH OBJECT.

HB_SetWith( oGet )
...HB_QWith():Name := "Nombre"
...HB_QWith():VarPut( "Walter" )
HB_SetWith()

Este código genera el mismo PCODE que el ejemplo anterior.

Es posible usar HB_QWith() dentro de WITH OBJECT, pero no es posible
usar la sintáxis reducida (:Name), con HB_SetWith().

WITH OBJECT oGet
...oOtro := HB_QWith() // Correcto
END

HB_SetWith( oGet )
...? :Name // Error
HB_SetWith()

También es posible usar el WITH OBJECT dentro de una macro.

WITH OBJECT oGet
...&(":Name := 'Nombre'")
...&(":VarPut( 'Walter' )")
END

Y el HB_SetWith() también en una macro

&("HB_SetWith( oGet )")
...HB_QWith():Name := "Nombre"
...HB_QWith():VarPut( "Walter" )
&("HB_SetWith()")

WITH OBJECT y HB_SetWith() soportan hasta 32 niveles de anidamiento.
Cada END y cada HB_SetWith() sin parámetros o con parámetro NIL,
"desanidan" un nivel.

Aquí hay un ejemplo de los usos, tomado desde el CVS de xHarbour
xharbour estswith.prg
<http://cvs.sourceforge.net/viewcvs.py/*checkout*/xharbour/xharbour/tests/with.prg?rev=1.3>

Fuente: Blog "Cosa de Negro" por Walter Negro

Optimizacion IV: http://cosadenegro.blogspot.com/search? ... izacion+IV
Optimizacion IV:
Optimización V - Walter Negro
1107 palabras totales en este texto (31 Lecturas) Versión Imprimible
<modules.php?name=Sections&op=printpage&artid=14>
------------------------------------------------------------------------

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 construcciones 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
<http://cvs.sourceforge.net/viewcvs.py/*checkout*/xharbour/xharbour/tests/foreach.prg?rev=1.1>
objlist.prg
<http://cvs.sourceforge.net/viewcvs.py/*checkout*/xharbour/xharbour/tests/objlist.prg?rev=1.1>
ivarref.prg
<http://cvs.sourceforge.net/viewcvs.py/*checkout*/xharbour/xharbour/tests/ivarref.prg?rev=1.3>

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

Fuente: Blog "Cosa de Negro" por Walter Negro
Resistencia - "Ciudad de las Esculturas"
Chaco - Argentina
User avatar
MarioG
 
Posts: 1380
Joined: Fri Oct 14, 2005 1:28 pm
Location: Resistencia - Chaco - AR

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby hmpaquito » Sat Mar 17, 2012 8:30 am

Amigos,

Me gustan mucho los tips que van al grano, sencillitos y directos...

Aquí va lo que espero que sea uno del estilo

Code: Select all  Expand view  RUN

// 2º Tip de velocidad: Unlock sólo al final del proceso.
// Teniendo un bucle DO WHILE de evaluación de datos con replace, el
// unlock lo haremos al final del bucle y no en cada iteración

do while !eof()
   rlock()
   replace x with y
   skip
enddo
unlock
 


¿ Alguien se atreve a aportar sus tips de velocidad fruto de su ya mucha experiencia en las lides xprogramáticas ?
saludos
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby JmGarcia » Sat Mar 17, 2012 2:46 pm

thefull wrote:
Code: Select all  Expand view  RUN
aArray := Array( 1000000 )
For x := 1 to len( aArray )   // MALO MALISIMO


Y como lo hariamos si dependiendo de un criterio dado tenemos que ir BORRANDO algunos elementos del array.

Yo hago esto:
Code: Select all  Expand view  RUN
for x = 1 to len( aArray )
   if << Aquí la condicion deseada >>
      adel( aArray , x , .T. )
   endif
next x
Mi abuelo decía: Los aviones vuelan porque Dios quiere, y los helicópteros ni Dios sabe porque vuelan.
FWH 16.02, xHarbour 1.2.3, Harbour 3.2.0, WorkShop 4.5, AJ Make 0.30, Borlan BCC 7.00, VisualStudio 2013
User avatar
JmGarcia
 
Posts: 654
Joined: Mon May 29, 2006 3:14 pm
Location: Madrid - ESPAÑA

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby hmpaquito » Sat Mar 17, 2012 3:38 pm

jm,

lo haríamos así:
Code: Select all  Expand view  RUN
nLen:= Len( aArray)
nBorrados:= 0
for x = 1 to nLen- nBorrados
   if << Aquí la condicion deseada >>
      adel( aArray , x )
      nBorrados++
      x--
   endif
next x
aSize(aArray, nLen- nBorrados)


Y en este caso la mejora de velocidad sería doble: primero el Len(aArray) y luego que sólo se hace una operación de redimensionado del array al final del proceso.
Saludos
Last edited by hmpaquito on Sat Mar 17, 2012 6:24 pm, edited 1 time in total.
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby MarioG » Sat Mar 17, 2012 4:29 pm

jm;
Si el array es de muchos registros, como tu sample, te sugiero usar FOR EACH (la explicación en Optimizacion IV).

Saludos
Resistencia - "Ciudad de las Esculturas"
Chaco - Argentina
User avatar
MarioG
 
Posts: 1380
Joined: Fri Oct 14, 2005 1:28 pm
Location: Resistencia - Chaco - AR

Re: Conseguir la maxima velocidad del EXE creado por Harbour

Postby JmGarcia » Sun Mar 18, 2012 10:39 am

MarioG wrote:Si el array es de muchos registros, como tu sample, te sugiero usar FOR EACH (la explicación en Optimizacion IV).
Sí, mis array suelen ser "monstruosos".

He probado el método IV con ForNext, Aeval y ForEach y los resultados sobre un array de 10.000.000 de elementos son estos:
ForNext 5.7 seg.
Aeval 6.8 seg.
ForEach 3.1 seg. (casi la mitad del tiempo)

Aqui el ejemplo probado:
Code: Select all  Expand view  RUN
function main()
local nSegundos , x , n:=0 , nSum:=0 , aArray := array(10000000) , nLen := Len(aArray)
*---
nSegundos:=seconds()
for n:=1 to nLen
   aArray[n] := n
next
for n:=1 to nLen
   nSum += aArray[n]
next
nSegundos:=seconds()-nSegundos
? nSegundos
*---
nSegundos:=seconds()
aeval( aArray, {|a,n| aArray[n]:=n} )
aeval( aArray, {|a,n| nSum += a} )
nSegundos:=seconds()-nSegundos
? nSegundos
*---
nSegundos:=seconds()
for each x in aArray
   x := HB_EnumIndex()
next
for each x in aArray
   nSum += x
next
nSegundos:=seconds()-nSegundos
? nSegundos
*---
return nil
Mi abuelo decía: Los aviones vuelan porque Dios quiere, y los helicópteros ni Dios sabe porque vuelan.
FWH 16.02, xHarbour 1.2.3, Harbour 3.2.0, WorkShop 4.5, AJ Make 0.30, Borlan BCC 7.00, VisualStudio 2013
User avatar
JmGarcia
 
Posts: 654
Joined: Mon May 29, 2006 3:14 pm
Location: Madrid - ESPAÑA

Next

Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: No registered users and 58 guests