Autor Tema: Complicando para simplificar  (Leído 16355 veces)

0 Usuarios y 3 Visitantes están viendo este tema.

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: Complicando para simplificar
« Respuesta #15 en: 18 de Marzo de 2008, 15:31:58 »
Sí, maestro, ya se dió cuenta de mi error Sander unos meses atrás  :D :D :D

Aqui hay algo que no me cuadra, la primera instruccion  que pones es variable > 0x0A00 , si variable es igual a 0X0A01 se cumplira la condicion, en tu ultima "optimizacion"  comparas la parte alta de variable con 0x0A y si variable vale  0x0A01 no se no entra el if , entonces lo primero y lo ultimo no es lo mismo.

Desconectado Sispic

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 1685
    • winpic800
Re: Complicando para simplificar
« Respuesta #16 en: 18 de Marzo de 2008, 16:36:45 »
Eso me pasa por leer tanto despues de salir  de farra hasta las 8 AM.  8)

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: Complicando para simplificar
« Respuesta #17 en: 23 de Mayo de 2008, 17:41:24 »
como trabaja #zero_ram

El funcionamiento de #zero_ram, es limpiar la ram a través del direccionamiento indirecto.

En un 16F, con el par INDF + FSR y en un 18F con FSR0H + POSTINC0, si analizan mejor el código generado por #zero_ram, consume mas lineas en asm, porque usan variables para hacer decrementos, en cambio si se le indica al puntero hasta donde debe llegar, menos lineas:

ej: 18F

Código: C
  1. void limpiar_la_ram(){
  2. #asm
  3.       lfsr  FSR0,primer_banco_ram
  4. next: clrf  POSTINC0
  5.       btfss FSR0H,3  // ultimo_banco_ram
  6.       bra   next
  7. #endasm
  8. }

gasté 4 lineas, en contraste con las 13 lineas que genera el #zero_ram

Código: ASM
  1. 0008:  MOVLW  FE
  2. 000A:  MOVWF  00
  3. 000C:  MOVLW  08
  4. 000E:  MOVWF  01
  5. 0010:  MOVLW  02
  6. 0012:  MOVWF  FE9
  7. 0014:  MOVLW  00
  8. 0016:  MOVWF  FEA
  9. 0018:  CLRF   FEE
  10. 001A:  DECFSZ 00,F
  11. 001C:  BRA    0018
  12. 001E:  DECFSZ 01,F
  13. 0020:  BRA    0018


La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado Gonzalo_BlackHawk

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 519
Re: Complicando para simplificar
« Respuesta #18 en: 23 de Mayo de 2008, 23:39:38 »
Hola a todos. Muy interesantes sus opiniones chicos. Yo tambien soy fan de exprimir el C hasta ahorrarme el ultimo microsegundo, sin embargo hay casos donde optimizar en forma viciosa hace que el código sea ilegible y que el mantenimiento del mismo sea un dolor de cabeza, sobre todo cuando hace tiempo que no lo tocamos. Obviamente queda en manos de uno decidir cuando ya hemos optimizado lo suficiente, y esto hay que analizarlo con cada caso.

Un ejemplo de lo que digo es colocar a cero todos los elementos de una matriz. Puedes hacerlo mediante un bucle o bien individualmente, lo cual esta comprobado que reduce el código en ASM. Esto no conlleva importancia cuando tenemos una matriz de 10 elementos, sin embargo, no creo que tener un código en C que borra una matriz de 32 x 128 elementos en forma individual sea lo mas comodo, pero eso ya es ha gusto del programador.

Saludos.
"Siempre piensa si el jugo vale la exprimida..."

"La muerte esta tan segura de vencer que nos da toda una vida de ventaja."

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: Complicando para simplificar
« Respuesta #19 en: 24 de Mayo de 2008, 01:14:28 »
Gonzalo, pero eso depende del tamaño de la matriz. Si quieres poner a 0 una de 32 x 128 obviamente siempre saldrá más corto el ASM con un bucle que de manera individual.

Desconectado Gonzalo_BlackHawk

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 519
Re: Complicando para simplificar
« Respuesta #20 en: 24 de Mayo de 2008, 09:18:11 »
Si Nocturno, menos mal que estas atento a mis errores, eso me pasa por escribir cansado, no se en lo que estaba pensando  :(. Anduve probando con una matriz de 32 x 128 y se puede reducir el tiempo de ejecucion del borrado hasta un cierto punto, luego comienza a subir nuevamente. Fijense, aqui coloca a uno todo los elementos de una matriz 32 x 128 utilizando dos bucles anidados:



Tenemos un tiempo de 76 ms aprox. Ahora elimino el primer bucle y cambio manualmente el primer indice de la matriz, quedandome:



Me he ahorrado unos 16 ms aprox. Ahora bien, en vez de reemplazar el bucle de 32 elementos, voy a eliminar el bucle de 128 elementos y a dejar el anterior como estaba, haciendo que el segundo indice de la matriz cambie individualmente. Lo que obtengo es:



Fijense como el tiempo sube nuevamente a los 76 ms. No hay que ser adivino para imaginarse que cuando tratemos de eliminar ambos bucles y asignar valores individualmente a cada uno de los 4096 elementos de la matriz el tiempo de ejecucion se nos ira por las nubes. Sin lugar a duda Nocturno tenia razon, existe un punto de inflexion a la hora de optimizar una asignacion de valores a elementos de matrices de gran tamaño.  Me disculpo nuevamente por el error infligido.

A modo de comentario, otra forma de optimizar un poco el código es tratar de utilizar siempre el fast_io y de reemplazar cocientes o multiplicaciones con potencias de 2 por desplazamiento de bits, entre otros. He visto gente que hilando fino empieza a reemplazar las funciones built-in de CCS por algunas mas elaboradas pero mas rapidas (por ejemplo sprintf, que segun he visto no es para nada eficiente) pero a mi gusto ya el código deja de ser legible, pero bueno, cada programador tiene sus necesidades y gustos.

Un saludo a todos.

"Siempre piensa si el jugo vale la exprimida..."

"La muerte esta tan segura de vencer que nos da toda una vida de ventaja."

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: Complicando para simplificar
« Respuesta #21 en: 24 de Mayo de 2008, 12:33:21 »
Magnífico análisis el que has hecho, Gonzalo. Nada de pedir disculpas, todo lo contrario, eso nos fuerza a pensar y nos enriquece más.

Habría otra forma de inicializar ese bucle de forma más rápida aún, si no me equivoco: mediante punteros.

Obtenemos el puntero al primer elemento del array y hacemos un bucle que lo vaya incrementando hasta el 4096 (32x128), ya que los elementos del array estarán situados de manera contigua en la RAM.

Desconectado RICHI777

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1498
Re: Complicando para simplificar
« Respuesta #22 en: 24 de Mayo de 2008, 15:12:53 »
Hola, porque no usar un memset de esta manera:
memset( Matriz, 0, sizeof( Matriz ));

Pondra en este caso en 0 todo el array y el compilador calculara exactamente el tamaño de la misma.
Saludos

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: Complicando para simplificar
« Respuesta #23 en: 24 de Mayo de 2008, 15:58:21 »
...Habría otra forma de inicializar ese bucle de forma más rápida aún, si no me equivoco: mediante punteros.
Obtenemos el puntero al primer elemento del array y hacemos un bucle que lo vaya incrementando hasta el 4096 (32x128), ya que los elementos del array estarán situados de manera contigua en la RAM.


con el direccionamiento indirecto se hace mas rápido aún.

La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: Complicando para simplificar
« Respuesta #24 en: 24 de Mayo de 2008, 16:03:44 »
Buen punto el del memset, Richi.

No he entendido tu sugerencia, Pedro.

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: Complicando para simplificar
« Respuesta #25 en: 24 de Mayo de 2008, 16:12:03 »
por el hecho de que los arrays son contiguos, con solo conocer la dirección del primer dato de la ram puedes recorrer el arreglo usando los registros virtuales INDF ó PRE/POSTINC(n)

hasta ahora he concluido que dichos registros son una forma de puntero por hardware, caso contrario, los que hemos conocido siempre al declararlos en c
La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado Gonzalo_BlackHawk

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 519
Re: Complicando para simplificar
« Respuesta #26 en: 24 de Mayo de 2008, 18:02:06 »
Hola chicos, anduve probando las opciones que han expuesto y he obtenido resultados interesantes. Le uso de la instrucción memset tal como lo ha expresado RICHI77/ me ha dejado asombrado, ha podido colocar a uno todos los elementos de una matriz de 32 x 128 en un poco mas de medio milisegundo (Recuerden que con 2 bucles anidados o bien asignacion de valor en forma individual la demora era de entre 70 a 76 ms!!!!!!). Aqui dejo una foto para que vean tanto el código como el stopwatch:



Como mi matriz es de bits y no de bytes, hay que tener cuidado en definir el valor de la instruccion memset pues esta siempre completa bytes, estoy hay que contemplarlo con cualquier tipo de datos que no sea int.

Luego he probado con punteros tal como ha indicado Nocturno, el resultado tambien es extraordinario, el código se ejecuta en unos pocos milisegundos tal como se ve en la siguiente imagen. Aqui tambien hay que tener en cuenta el tipo de matriz que es, el tamaño de los punteros (en los 18F son de 2 bytes), pero con una programación flexible puede lograrse y con grandes beneficios como hemos podido ver.



Quedaria probar lo que ha dicho Pedro, pero la verdad que no entiendo a que se refiere, podrias explicarnos como deberiamos hacerlo Palitroquez??? yo siempre pense que el direccionamiento indirecto es el que se realiza con punteros, como el metodo que postee en este mensaje, tal vez alguien me saque de la confusión.

Un saludo a todos.

"Siempre piensa si el jugo vale la exprimida..."

"La muerte esta tan segura de vencer que nos da toda una vida de ventaja."

Desconectado RICHI777

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1498
Re: Complicando para simplificar
« Respuesta #27 en: 24 de Mayo de 2008, 19:37:11 »
Hola, me alegro mucho de los resultados y tambien felicitaciones por la forma de trabajo, por tu profresionalismo y la forma que expones las pruebas. Con respecto a la mejora puedo detallar, en forma general porque no conozco la arquitectura MicroChip.
Inicializando con Arrays
El compilador en este caso debe efectuar una multiplicación para acceder a cada miembro del array, y si el procesador no la tiene como instrucción nativa debe suplirla con alguna funciíon intrinsica o propia del mismo, por eso esta es la solución mas lenta.

Trabjando con punteros como indico Nocturno
En este caso se define un puntero al comienzo del array, y luego se incrementa el mismo por el sizeof de cada elemento, en este caso la navegacion por cada elemento se hace con aritmetica de punteros que al compilador solo le cuesta una suma ( sizeof de cada elemento )

Memset 
El memset normalmente es implementado directamente en assembler ya que es parte de la libreria RTL ( Real Time library ) entonces el fabricante del compilador deberia aprovechar lo maximo de la arquitectura.

Seria bueno exponer el codigo assembler generado por cada de las opciones para ver los resultados.

Saludos !

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: Complicando para simplificar
« Respuesta #28 en: 27 de Mayo de 2008, 16:08:43 »
Hola.

He pensado mucho en lo escribiré jaja porque cada vez que analizaba el asm generado por el ccs, me cambiaba la perspectiva. En fin en vez de montar un discurso, mejor resumo el análisis:

en base a los 2 ejemplos montados por Gonzalo:

1) con punteros:

Código: C
  1. #include <18F2525.h>
  2. #fuses HS
  3. #use  Delay(clock=20M)
  4.  
  5. short Matriz[32][128];
  6.  
  7. void Main()
  8. {
  9. long *P;
  10. long A;
  11. int16 espacio;
  12.  
  13. P=Matriz;
  14.  
  15. espacio=sizeof(Matriz);
  16.  
  17. for(A=0;A<=espacio;A++){
  18.  *P=0xFFFF;
  19.   P++;
  20. }
  21. delay_cycles(1);
  22. }

no entiendo porque puntero es long ¿? Gonzalo el tamaño del puntero es independiente del tipo de datos que manejes.

en este caso vamos a rellenar bytes con 0xff, el código iría así:

Código: C
  1. #include <18F2525.h>
  2. #fuses HS
  3. #use  Delay(clock=20M)
  4.  
  5. short Matriz[32][128];
  6.  
  7. void Main()
  8. {
  9. int *P;
  10. long A;
  11. int16 espacio;
  12.  
  13. P=Matriz;
  14.  
  15. espacio=sizeof(Matriz);
  16.  
  17. for(A=0;A<=espacio;A++){
  18. *P=0xFF;
  19.   P++;
  20. }
  21. delay_cycles(1);
  22. }

el tiempo: 1,95ms sigue alto. ¿porque? bueno es por el overhead o como lo llamo yo, el factor de multiplicación, que se monta en asm, el contador A se comprueba mediante restas, lo cuál aumenta el tiempo de cálculo y se ve reflejado al final del bucle.




2) con memset

Código: C
  1. #include <18F2525.h>
  2. #fuses HS
  3. #use  Delay(clock=20M)
  4.  
  5. short Matriz[32][128];
  6.  
  7. void Main()
  8. {
  9. int16 espacio;
  10. espacio=sizeof(Matriz);
  11. memset(Matriz,0xFF,espacio);
  12. delay_cycles(1);
  13. }

Digamos que para este ejemplo, el compilador optimiza al máximo en asm con memset, la instrucción con menos factor de multiplicación es decfsz.

En ambos casos, el compilador usa Direccionamiento indirecto, el asunto es que en algunos casos puede no optimizar del todo.


Este estudio me llevó a una conclusión: mientras menos variables usemos para hacer calculos en un bucle, menos tiempo se llevará. porque el factor de multiplicación será menor, (y además que liberas preciada RAM)

pd: a lo que yo llamo factor de multiplicación es la misma operación de cálculo que tiene que hacer el pic en cada pasada del bucle.

La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: Complicando para simplificar
« Respuesta #29 en: 28 de Mayo de 2008, 00:17:44 »
Yo no entiendo eso del factor de multiplicación, ¿podrías explicar qué es?


 

anything