Version imprimable du sujet

Cliquez ici pour voir ce sujet dans son format original

Forums MacBidouille _ La Programmation En Général _ Optimiser les calculs en C

Écrit par : Anard 10 Mar 2018, 15:06

Bonjour,

Je travaille sur un µC qui doit lire du MIDI en temps réel.
Il est programmé en C et je débute un peu dans ce language. Lors de changements de tempo, j'ai besoin de faire des calculs mathématiques qui prennent beaucoup de temps au processeur (je pense que je m'y prend mal, j'ai l'impression que certaines instructions seraient à éviter).
Le fichier est écrit ainsi :



Mon programme actuel fonctionne comme ça :
Le microcontrôleur dispose d'un timer interne. Il s'incrément automatiquement tous les 64 coups d'horloge et il déclenche une action quand il atteint une limite configurable entre 1 et 255.
J'essaie donc de le calculer de telle manière à ce qu'il corresponde au plus près de la durée d'un "tick" et quand je recois un nombre de ticks à attendre, j'attends qu'il se déclenche autant de fois avant de lire la suite.
Mon calcul fonctionne, mais si le tempo change par exemple de manière progressive dans un morceau (en gros, il y a un changement de tempo avant chaque note), la lecture s'en trouve très ralentie. C'est embêtant.

Voici ce que j'utilise en cas de changement de tempo :
Code
// variable globale
    unsigned int baseDelay;
        float baseFreq;
// variables locales
    long Finstruction;
    float base, limite, calcul;

// A l'ouverture du fichier, je déduis du nombre de "ticks" par noire (une seule fois) la fréquence du timer dont j'ai besoin
    Finstruction = _XTAL_FREQ / 4000000; //MHz
    baseFreq = 256 * (long)DivisionTps; // 256 = limite max d'explosion du timer
    baseFreq = Finstruction / baseFreq; // = FmaxTimer / DivisionTps
    baseFreq /= 16;    // multiplicateur interne du timer

// Configuration lors d'un changement de Tempo

        // souvent la limite de 256 du timer ne suffira pas, donc j'utilise un multiplicateur supplémentaire
        base = ceil(Tempo * baseFreq);

        // calcul de la valeur d'Overflow (PR2)
        calcul = baseFreq * 256 / base;
        limite = round (Tempo * calcul);
        
        // config
        baseDelay = (int)base;
        PR2 = (int)limite - 1; // le timer déclenche un évènement au moment où il passe la limite PR2, pas quand il l'atteint

        // je dois aussi récupérer le Tempo en BPM :
        TempoBPM = 60000000/Tempo;

// Lorsque je reçois un délai à attendre, il est simplement calculé comme ceci :
        return (DelaiLu * baseDelay);
// puis le timer est démarré si le résultat n'est pas nul.


Pourriez-vous m'expliquer quelles sont les opérations les plus lourdes là-dedans pour essayer de faire en sorte de les supprimer ?
Merci beaucoup.

Écrit par : ntx 10 Mar 2018, 22:23

Es-tu obligé de travailler avec des float ? Les calculs sur les entiers sont plus rapides. 1/10 de Hz sera-t-il entendu à l'oreille ?
Avec des entiers, les "*256" et "/16" seront fait avec des décalages de bits et cela sera bien plus rapide qu'une multiplication ou une division sur des flottants.

Sinon pour des calculs sur des flottants, utilise un DSP qui est fait pour cela.

Écrit par : Jaypee 11 Mar 2018, 07:16

Une autre option serait des configs/patterns pré-définis :
- le calcul permet de proposer tous les cas possibles, mais tous les cas ne sont pas utiles

Donc si les cas les plus probables ou utiles sont prévisibles, on peut pré-calculer les valeurs et les stocker, et les relire sans calculs au moment de les utiliser
- Il faut alors pouvoir trouver le "pattern pré-calculé" qui va bien en fonction du fichier à transformer
- il faut relire les valeurs pré-calculées.

Mais l'idée est là : séparer la fabrication des paramètres de leur utilisation pour transformer un fichier

J-P

Écrit par : Anard 13 Mar 2018, 10:07

Bonjour et merci pour vos réponses.
J'ai réussi à revoir mes calculs pour n'utiliser que des entiers. J'arrive en fonction de la situation à une erreur finale de 0 à -5‰ au lieu de ±2,2‰ avec des float. Ce n'est en effet pas si dramatique et ça tourne beaucoup plus vite.
J'ai également essayé d'écrire les données en dur en créant d'énormes tableaux, pour 7 valeurs de "ticks"/noire (120, 192, 240, 384, 480, 768, 960) souvent rencontrées et pour des tempos allant de 45 à 299 :

Code
const unsigned int limites[7][255] =  {{252,252,252,253,254,249,..},\
                                    {...},{...},{...},{...},{...},{...}};

const unsigned int bases[7][255] =    {{ 44, 43, 42, 41, 40, 40, ..},\
                                      {...},{...},{...},{...},{...},{...}};


C'est bourrin et ça prend pas mal de place en mémoire, mais en effet, ça fonctionne et va encore bien plus vite. Ca m'a surtout permis de constater que certains ralentissements venaient d'ailleurs dans le programme. Par exemple au moment d'afficher la modification à l'écran, en SPI, ce qui ralenti considérablement le temps de traitement s'il y a beaucoup de modifications à suivre (par exemple comme je le disais lors d'un changement de tempo progressif).

Un DSP ? Je ne connais pas. C'est un composant externe qui s'occupe uniquement du calcul ?

En tout cas, c'est déjà un peu mieux, merci beaucoup pour vos conseils, c'est exactement ce dont j'avais besoin pour avancer.

Écrit par : ntx 13 Mar 2018, 18:14

https://fr.wikipedia.org/wiki/Processeur_de_signal_numérique

Propulsé par Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)