Reutilizando algo no relacionado como un sumador de hardware para la optimización de código

1

Me estoy topando con algunos problemas de velocidad en el ISR. Debido a la naturaleza de mi código, necesito agregar 20 valores (16 bits) en un ISR. El código se ve así

 #define Element_size 4
 #define Chunk_size 5
 void add(void)
 { 
   int i;
   int j;

   for (i=0;i<Element_Size;i++)
   { 
     sum[i]=0;
     for (j=0;i<Chunk_Size;j++)
     {
       sum[i]+=data[j*4+i];
     }
    }
  } 

Este no es el código exacto pero el algo es idéntico. (los datos se presentan como fragmentos con tamaño de 4, en este caso tengo 5 fragmentos de datos. Agrego el primer elemento de cada fragmento, luego el segundo elemento, etc.) Supongamos que la suma y los datos son globales. Esto lleva más tiempo del que tengo. Estoy buscando una solución para acelerar las cosas. Hasta ahora, puedo pensar en:

  • Mejore el algo: puedo hacer esto un turno (o sin un bucle) en lugar de dos, pero si cambian los tamaños de trozos (ahora una opción de tiempo de compilación), se interrumpirá. Haré esto como último recurso una vez que el código sea definitivo y no se esperen otros cambios.

  • Mover al ensamblaje: haré esto pero tomará tiempo, una opción viable

  • Haga la función en línea: Esto ahorrará algo de tiempo, pero no mucho, ya que la mayoría del tiempo se pasa durante la suma, lo haré de todos modos.

  • Use algún bloque de hardware para la suma: estoy usando la familia STM32F2 (Arm Cortex M3), tiene varias cosas buenas allí, pero no ALU. Miré las librerías crc, hash y cyrpto, pero no pude encontrar nada que salte y que pueda usarse como un sumador de hardware.

Especialmente, si uno de los maestros entre ustedes sugiere una forma asistida por hardware para hacer este cálculo, estaría en deuda. Estoy buscando una forma de hacer esto fuera de la caja, no solo la optimización de código. Esta es realmente la pregunta clave que tengo.

    
pregunta Frank

2 respuestas

4

Como primer paso, observaría el ensamblaje generado para ver qué tiempo lleva más tiempo. A continuación, determine cuánto tiempo tiene disponible y haga una estimación aproximada de si la solución más optimizada (ensamblaje, ambos bucles sin enrollar) puede ser lo suficientemente rápida. Si no es así, busque una solución muy diferente (chip más rápido, algoritmo diferente, haga que el código que cambia los arreglos funcione de manera precomputa como lo sugirió davidcarry, etc.).

Al desenrollar, al menos puede incluir una aseveración de que los # define aún son el valor que asumió cuando desenrolló. Pero el desenrollamiento razonable del lenguaje macro puede ser realizado por una macro que tenga en cuenta el # define. PERO después de cada oportunidad de los # define, tendrías que volver a probar si cumples tus restricciones de tiempo, así que tal vez no quieras ninguna adaptación automática.

No entiendo por qué está buscando un "se puede usar como un sumador de hardware". ¡Su CPU puede agregar dos valores de 32 bits tan rápido como puede suministrarlos! Que sugiere una posible optimización: empacar dos valores de 16 bits en una palabra de 32 bits. Si puede garantizar que la suma de 16 bits nunca se desbordará, esto podría acelerarlo en un factor de 2. Tal vez más, este es un procesador de 32 bits que probablemente se sienta más cómodo con el acceso a datos de 32 bits en lugar de datos de 16 bits. . Incluso sin el truco de empaquetamiento, podría ser una buena idea intentar usar enteros de 32 bits para acelerar el proceso.

En cuanto al desenrollado: desenrollar el bucle interno será más efectivo que desenrollar el bucle externo. Puede intercambiar los bucles, luego desenrollar para el tamaño del elemento y mantener el for () para el tamaño del fragmento. De esta manera, j * 4 se puede calcular una vez.

Utiliza el estilo x [i] de los elementos de la matriz de acceso. No sé qué tan inteligente es su compilador, si toma sus expresiones de índice literalmente generará un código mucho más eficiente si usa el estilo * p ++. Pero nuevamente: primero comprueba lo que hace el compilador, de lo contrario, podrías estar haciendo el trabajo por nada.

Ensamblador contra C: nunca subestimes un compilador. Antes de probar el ensamblaje, primero verifique qué hace el compilador y piense por qué. Es mejor darle al compilador todas las oportunidades para optimizar que hacerlo usted mismo en ensamblaje.

    
respondido por el Wouter van Ooijen
0

Incluso ese bucle corto ya parece demasiado para un controlador de interrupciones de primer nivel. Quizás uno de estos dos enfoques ayude:

actualización parcial

¿De dónde vienen estos números que estás sumando? ¿Hay alguna manera de actualizar de alguna manera la suma al valor correcto sin tener que ejecutar los 20 valores cada vez?

Es decir, algo como

#define Element_size 4
#define Chunk_size 5
void update( int x, int y, int new_value ){
    int old_value = data[x*Element_size+y];
    sum[y] -= old_value;
    sum[y] += new_value;
    data[x*Element_size+y] = new_value;
}

controlador de interrupciones de segundo nivel

¿Hay alguna manera de hacer lo menos posible en el controlador de interrupciones de primer nivel, y luego hacer la suma en el controlador de interrupción de segundo nivel (tal vez en el fondo "bucle principal"?) La Wikipedia: "controlador de interrupciones" y Wikilibros: diseño de sistemas de control embebido / patrones de diseño Menciona mover lo más posible el controlador de interrupciones de primer nivel al controlador de interrupciones de segundo nivel.

    
respondido por el davidcary

Lea otras preguntas en las etiquetas