Autor Tema: Eficiencia pobre del compilador XC8 con estructuras  (Leído 1634 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado RodrigoAndres

  • PIC16
  • ***
  • Mensajes: 171
Eficiencia pobre del compilador XC8 con estructuras
« en: 23 de Enero de 2016, 13:06:46 »
hhola como estan todos. el problema es que tengo la siguiente estructura:

Código: [Seleccionar]

#ifndef CIRCULAR_BUFFER_SIZE
    #define CIRCULAR_BUFFER_SIZE 32
#endif


typedef struct {
        uint8 address;
        uint8 start;
        uint8 end;
        uint8 count;
        uint8 size;
        bool overflow;
        uint16 data[CIRCULAR_BUFFER_SIZE];           
}buffer_circular;

y la siguiente funcion:

Código: [Seleccionar]
inline uint16 read(buffer_circular *buffer){
    uint16 dato;
   
    if(buffer->start == buffer->end){
        return 0;
    }
   
    dato = buffer->data[buffer->start++];
       
    if(buffer->start == CIRCULAR_BUFFER_SIZE){
        buffer->start = 0;
    }   
    buffer->count--;   
    return dato;
}

y llamo a la funcion de la siguiente manera:


Código: [Seleccionar]
buffer_circular ADC_buffer;

void task_1(void) {
    uint16 lectura;
    lectura = read((buffer_circular*) &ADC_buffer);
}

el problema radica en que la funcion read tarda casi 6 microsegundos en ejecutarse, o sea muy ineficiente.

si vuelvo a escribir la funcion read en forma de macro, quedaria de la siguiente forma, hace lo mismo y solo tarda 2 microsegundos, sin embargo se que se puede hacer todavia mas eficiente con la funcion anterior (debido a POSTINC y POSTDEC) si tan solo el compilador entendiera lo que quiero hacer.

Código: [Seleccionar]

#define ADDRESS_OFFSET 0
#define START_OFFSET 1
#define END_OFFSET 2
#define COUNT_OFFSET 3
#define SIZE_OFFSET 4
#define OVERFLOW_OFFSET 5       
#define DATA_OFFSET 6   

#define fast_read(struct_pointer, dato)                                                                                        \
    if(*(struct_pointer+END_OFFSET) == *(struct_pointer + START_OFFSET)){                               \
        (*((uint8*)dato)) = 0;                                                                                                             \
        (*((uint8*)dato + 1)) = 0;                                                                                                       \
    }else{                                                                                                                                         \
       (*((uint8*)dato)) = *(struct_pointer + DATA_OFFSET + (2* *(struct_pointer+START_OFFSET)));             \
       (*((uint8*)dato + 1)) = *(struct_pointer + DATA_OFFSET + (2* *(struct_pointer+START_OFFSET) + 1));\
       *(struct_pointer+START_OFFSET) = *(struct_pointer+START_OFFSET)+1;                                                \
       if(*(struct_pointer + START_OFFSET) == CIRCULAR_BUFFER_SIZE){                                                         \
            *(struct_pointer + START_OFFSET) = 0;                                                                                                \
       }                                                                                                                                                                  \
       *(struct_pointer+COUNT_OFFSET) = *(struct_pointer+COUNT_OFFSET) - 1;                                              \
    }

el problema esta en que cuando llamo a la funcion read (la normal), es que el compilador cre que el puntero que le estoy pasando es variable y no constante, por eso genera codigo adicional para obtener las direcciones del puntero cuando en realidad no es necesario ya que &ADC_buffer nunca va a cambiar.

lo que intento saber es como puedo hacerle entender al compilador que el parametro que le estoy pasando a la funcion read, es decir, &ADC_buffer es constante ya que es una direccion de memoria fija?? si uso  const buffer_circular *buffer  entonecs el complador da error cuando intento modificar las variables de la estructura, gracias por la ayuda se lo agradesco mucho
« Última modificación: 23 de Enero de 2016, 13:21:15 por RodrigoAndres »

Desconectado RodrigoAndres

  • PIC16
  • ***
  • Mensajes: 171
Re:Eficiencia pobre del compilador XC8 con estructuras
« Respuesta #1 en: 23 de Enero de 2016, 13:39:44 »
Vean, para que observen mejor esta es la diferencia de codigo en ensamblador que genera cada funcion:

De la forma original:


De la forma rapida, con macros:



Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Eficiencia pobre del compilador XC8 con estructuras
« Respuesta #2 en: 25 de Enero de 2016, 21:22:08 »
Pero tenes una diferencia MUY importante entre las 2 cosas que estas haciendo. Un macro no es una funcion como la anterior. Ya que al "llamarse" a esta, no se pone los datos dentro del stack, ni tampoco se procede a sacar las cosas de alli. Es decir no hay interaccion con el stack. La "funcion", o por asi decirlo las lineas de codigo estan en conjunto con lo demas.

Otra cosa, un macro por tener la misma caracteristicas de antes, hace que si lo usas varias veces, las lineas se repitan varias veces. Distinto a una funcion. Hay varias diferencias, espero tratar de llegar al punto. Pero esto no pasa por XC8 , sino por algo mas de C.

Luego veo mas el programa  me extiendo si encuentro algo.

Citar
lo que intento saber es como puedo hacerle entender al compilador que el parametro que le estoy pasando a la funcion read, es decir, &ADC_buffer es constante ya que es una direccion de memoria fija?? si uso  const buffer_circular *buffer  entonecs el complador da error cuando intento modificar las variables de la estructura, gracias por la ayuda se lo agradesco mucho

Const significa que no se puede modificar. En los PIC esto se implementa en la Flash cuando se declara una variable, pero en tiempo de compilacion se revisa y si hay un intento de escritura es un error obviamente. &ADC_Buffer es "constante", en realidad no lo es, es generado por el linker luego de la compilacion. Pero recordar que ese parametro se pasa por el stack. Debido a que es un argumento de una funcion. Ese parametro que vos pasas, no es un valor fijo, es un valor cualquiera que es pasado a traves de la funcion, almacenado en el stack y que ocupa una posicion de la RAM ( es decir la direccion de ADC_Buffer ocupa una posicion de la RAM ) . Entonces tenes al PIC apuntando un FSR al stack + otro FSR a otro lado.


Otra diferencia que no se si se hizo notar en mi anterior parrafo.

1er codigo: Un codigo, multiples de buffers que se pueden usar.
2do codigo: Un codigo, un buffer.

Desventajas:
1er codigo: Manejar stack (tanto de llamada como de vuelta), pasar direccion del buffer + call + return, calcular posicion de los elementos, ya que como puede entrar cualquier direccion, debes calcularlas
2do codigo: Tamaño utilizado de flash al repetirse la "funcion". Si utilizas 2 veces la funcion fast_read() ya estarias ocupando el mismo lugar en flash que el 1er codigo, si lo usas 3 veces ya ocupas mas flash.

Ventajas:
1er codigo: Reutilizacion ( al usar el mismo codigo para cualquier  valor )
2do codigo: Rapidez  ( al no usar stack, ni calls, y ser especifico el codigo para ese bufferen cuestion)

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Eficiencia pobre del compilador XC8 con estructuras
« Respuesta #3 en: 05 de Febrero de 2016, 13:53:13 »
Bueno vuelvo al tema para agregar un poco mas de cosas y el por que no deberias hacerlo como lo hiciste.

En una estructura por "regla" de C los elementos van a estar seguidos (no hablo de cuando se hacen bit-fields en la estructura, que eso es definido por el compilador). Pero vos nunca podes saber si es que C agrego algun padding, asi que hacer:

(struct_pointer+START_OFFSET)+1

Puede llevarte a un fallo y no es lo correcto.
Supongamos que estamos en un micro de 8 bits por lo tanto jamas esto va a ocurrir. Pero... que ocurre si este valor llega a un "limite" de memoria. Los PIC16 tienen bancos supongamos que el banco 0 va desde 0x00 a 0x7F (aunque las ultimas es espacio compartido por todos lso bancos) Supongamos que tu start se encuentre en 0x7F, si utilizas lo anteriores vas a apuntar a 0x80 lo cual no es valido, ya que esas primeras posiciones son usadas por los SFR. En PIC18 no tendrias este problema ( si es que accede con el FSRx )

Estos son los posibles problemas que pueden haber tambien.

PD: Si tu idea es quitar el overhead de los call y el stack. Entonces trata de usar el "inline" en las funciones.
« Última modificación: 05 de Febrero de 2016, 15:24:55 por KILLERJC »