IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Structure d'un programme d'émulation


Sujet :

C

  1. #1
    Membre averti

    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2013
    Messages
    182
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Pérou

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2013
    Messages : 182
    Points : 375
    Points
    375
    Par défaut Structure d'un programme d'émulation
    Je cherche à ce que ça exécute viiiiite !

    Une bonne technique pour accélérer l'exécution d'un code écrit en C, pourrait résider dans les faits :
    - d’éviter d'utiliser les switchs. En fait, je ne sais pas comment est implémenté un switch, mais je ne vois pas comment faire sans une (+ ou - longue) série de "if else if"
    - d'éviter le passage de paramètre aux fonctions.
    Ceci oblige a utiliser des viables globales, ou une structure pointée par une globale.

    J'ai écrit un émulateur. Il reçoit en entrée des codes compris en 0 et 256. Le logiciel actuel comporte près de 300 fonctions. Pour régler le problème des switchs, j'ai utilisé intensivement les tableaux de pointeurs sur fonctions, et c'est la variable qui est entre parenthèse dans :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    switch (variable) { ... }
    qui sert d'index dans ce tableau. Chaque fois qu'on déclare une variable locale à une fonction (dite automatique), il faut réserver de la place dans la pile, ce qui est probablement ridicule en terme de temps d'exécution. J'ai réglé le problème de la façon suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
      // inclusion de headers, déclaration des types de structures, 
      // fonction etc : équivalent à un ".h"
      // Le code C de l'émulateur ne comporte qu'une fonction visible :
    void make_a_step (void) {
      register MyStruct_t *GlobStruct;  // Notez le modificateur "register"
      int local1, local2, etc...
      static void (*functTable[4])();   // "static" modificateur obligatoire
     
      void funct1 (void) { ... }
      void funct2 (void) { ... }
      void funct3 (void) { ... }
      void funct4 (void) { ... }
     
      void init (void) {
        functTable[0] = funct1; // Ce n'est pas écrit ainsi,
        functTable[1] = funct2; // ça revient au même.
        functTable[2] = funct3;
        functTable[3] = funct4;
      }
     
      // le corps de la fonction "make_a_step" commence ici...
      if (GlobStruct->_flagInited) {
       init();
       GlobStruct->_flagInited = 1;
      }
      ...
      ...
     
     
    } // Fin de make_a_step()
    La liste de ce bout de code est extrêmement simplifié, puisque le code actuel comporte plus de 3000 lignes.

    Pour pouvoir mettre au point cet émulateur, il m'aura fallu écrire un moniteur (environs mille lignes). Je peut ainsi "programmer l'émulation en saisissant des codes que je fait exécuter en pas à pas, tout ceci grâce au moniteur. Ce moniteur est supposé permettre l'éxécution de code en pas à pas, ainsi que l'exécution d'une liste de codes en continu. Évidemment, la mise au point du code c'est fait en mode "pas à pas". Mais fait étrange, le programme se plante quand je passe de "pas à pas" en "run", OU quand je passe de "run" en "pas à pas". Nemiver (la "belle et vielle promesse non tenue") ou gdb n'y ont rien fait : je ne vois qu'une perte de contexte mais sans pouvoir corriger en gardant la structuration actuelle décrite plus haut.

    J'ai essayé sans succès :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void make_a_step (register MyStruct_t *GlobStruct) {
      int local1, local2, etc...
      static void (*functTable[4])();   // Static obligatoire
      ...
    }
    La mémoire affectée à la variable pointée par GlobStruct l'a été par.le monitor, avec malloc. Il est peut probable (impossible) qu'elle soit déplacée dans la mémoire par le système.

    Finalement, ça fonctionne,ainsi, avec un minimum de modif de la source, mais c'est philosophiquement insatisfaisant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
      MyStruct_t *GlobStruct;       // Notez le modificateur "register"
      int local1, local2, etc...    // Elles ne sont plus locales (mal nommées!
      void (*functTable[4])();      // Static PLUS obligatoire, puisque globale !
     
      void funct1 (void) { ... }
     
      ...
     
        functTable[3] = funct4;
      }
     
    void make_a_step (void) {
      if (GlobStruct->_flagInited) {
       init();
       GlobStruct->_flagInited = 1;
      }
      ...
      ...
     
     
    } // Fin de make_a_step()
    Quelqu'un pourrait-il me dire pourquoi Linux perd les pédales ? Je pense que les programmeurs réguliers de threads pourraient sentir l'origine du problème et ainsi pouvoir m'indiquer comment le résoudre...

    J'aurais préféré la première forme de structure décrire plus haut. J'ai bien vu que le logiciel perd vraiment les pédales entre "run" et "faire un pas" sans comprendre pourquoi. Je suppose que le code (la fonction make_a_step() complète) n'est pas "déplacé" par le système, à quelque occasion, auquel cas la fonction init serait à relancer ? J'ai vérifier en écrivant ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
      // inclusion de headers, déclaration des types de structures, 
      // fonction etc : équivalent à un ".h"
      // Le code C de l'émulateur ne comporte qu'une function visible :
      void *gFuncPosRef = NULL;
     
    void make_a_step (void) {
      register MyStruct_t *GlobStruct;  // Modificateur "register"
      int local1, local2, etc...
      static void (*functTable[4])();   // "static" obligatoire
     
      void funct1 (void) { ... }
     
      ...
     
        functTable[3] = funct4;
      }
     
      // le corps de la fonction "make_a_step" commence ici...
      if (!gFuncPosRef || gFuncPosRef!= &make_a_step) {
       init();
       gFuncPosRef = &make_a_step;
      }
      ...
      ...
     
     
    } // Fin de make_a_step()
    Mais ça marche pas.

    EDIT :
    Mais j'y pense : peut-être qu'écrire cette "fonction" comme étant une librairie résoudrait mon problème ?

  2. #2
    Expert éminent sénior
    Avatar de Kannagi
    Homme Profil pro
    cyber-paléontologue
    Inscrit en
    Mai 2010
    Messages
    3 214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : cyber-paléontologue

    Informations forums :
    Inscription : Mai 2010
    Messages : 3 214
    Points : 10 140
    Points
    10 140
    Par défaut
    Citation Envoyé par Paul_Le_Heros Voir le message
    Je cherche à ce que ça exécute viiiiite !

    Une bonne technique pour accélérer l'exécution d'un code écrit en C, pourrait résider dans les faits :
    - d’éviter d'utiliser les switchs. En fait, je ne sais pas comment est implémenté un switch, mais je ne vois pas comment faire sans une (+ ou - longue) série de "if else if"
    - d'éviter le passage de paramètre aux fonctions.
    Ceci oblige a utiliser des viables globales, ou une structure pointée par une globale.
    Alors non c'est faux , aucun rapport entre le switch et les variables globales (encore heureux qu'on peut faire un switch sans variable global).
    Bref tout dépend de ce que tu veux émuler , si tu veux émuler une machine avec une dizaine de Mhz , alors le switch marche sans soucis , et ça marchais sans soucis sur mon ancien Pentium 3 qui émuler facilement une SNES
    (On peut prendre l’exemple d'un RasPi qui émule en software tout les consoles 8/16 bits et les anciennes borne d'arcade aussi).
    Par info MAME émule quasiment tout en software (avec if etc etc) de ce que j'ai pu remarquer.
    Si par exemple on prend une Neo Geo qui est une machine assez puissante pour l'époque (12 Mhz) , elle ne fournissait que 2,4 MIPS (millions d'instructions par seconde) , ce qui devrait posait aucun souci à émuler sur un ordi moderne :p
    Cela dépend si tes instructions sont facile à décodé ,certain type de proc le sont plus facilement que d'autre.

    Pour les machines un peu plus puissante (donc si on prend comme les consoles un peu plus puissante comme la dreamcast / PS2/ Gamecube et autre) , on fait du JIT donc de la compilation à la volée.

  3. #3
    Membre averti

    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2013
    Messages
    182
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Pérou

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2013
    Messages : 182
    Points : 375
    Points
    375
    Par défaut
    Bonjour et merci Kannagi, pour votre réponse.
    Citation Envoyé par Kannagi Voir le message
    Alors non c'est faux , aucun rapport entre le switch et les variables globales (encore heureux qu'on peut faire un switch sans variable global).
    Je sais ça, bien sûr.

    Je crois que j'aurais dû écrire (premier listing de code, ligne 5):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    register MyStruct_t *GlobStruct = gGlobStruct; // Un registre pointe toute les infos de l'émulateur.
    Ce qui permets à toutes les autres fonctions qui se trouve entre cette ligne 5 et le corps de fonction "make_a_step", ligne 22, de disposer d'un accès aux infos sans :
    1. ou recevoir un paramètre pointant sur ces infos, genre
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      void fonctTruc (MyStruct_t *sPtr) { ... }
    2. ou créer une variable locale initialisée de sorte à pointer sur ces infos, genre
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      void fonctTruc (void) { lMyStruct_t *lSPtr = gGlobStruct; ... }
    Ces deux possibilités ne consomment certainement pas beaucoup de code (donc de temps), mais certainement plus qu'un registre pointant cette structure et disponible pour toutes les fonctions "internes" ou "privées" de make_a_step().

    Une lecture très attentive de ces pseudos "listings" est nécessaire pour la compréhension du problème...

  4. #4
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Paul_Le_Heros Voir le message
    Je cherche à ce que ça exécute viiiiite !
    Bonjour à toi aussi

    Citation Envoyé par Paul_Le_Heros Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void make_a_step (void) {
    	static void (*functTable[4])();   // "static" modificateur obligatoire
    	...
    	void init (void) {
    		functTable[0] = funct1; // Ce n'est pas écrit ainsi,
    		functTable[1] = funct2; // ça revient au même.
    		functTable[2] = funct3;
    		functTable[3] = funct4;
    	}
    	...
    } // Fin de make_a_step()
    La liste de ce bout de code est extrêmement simplifié, puisque le code actuel comporte plus de 3000 lignes.
    Moi j'aurais plutôt écrit ça ainsi: static void (*functTable[4])()={funct1, funct2, funct3, funct4}. Mais surtout, comment "init()" peut connaitre le tableau "funcTable" ? Et plus généralement, on a le droit, en C, de définir une fonction dans une fonction ???

    Citation Envoyé par Paul_Le_Heros Voir le message
    Mais j'y pense : peut-être qu'écrire cette "fonction" comme étant une librairie résoudrait mon problème ?
    Une librairie possède un index qui optimise les appels. Donc effectivement ça peut améliorer les accès...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  5. #5
    Membre averti

    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2013
    Messages
    182
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Pérou

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2013
    Messages : 182
    Points : 375
    Points
    375
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Moi j'aurais plutôt écrit ça ainsi: static void (*functTable[4])()={funct1, funct2, funct3, funct4}. Mais surtout, comment "init()" peut connaitre le tableau "funcTable" ?
    Je suis surpris par la question : la déclaration est faite ligne 2 ! Mais vous avez pré-sentez le problème : le compilateur ne peut pas générer le code d'initialisation du tableau tel que vous l'avez écrit...

    Citation Envoyé par Sve@r Voir le message
    Et plus généralement, on a le droit, en C, de définir une fonction dans une fonction ???
    Probablement oui, puisque cela a fonctionné. Je vous invite à écrire un bout de code pour vérifier.

    Il se passe quelque chose à un moment qui fait que les tableaux ne pointent plus ce qu'il devraient et l'on fait après exécution de la fonction d'initialisation. C'est certainement très intime : je ne comprends pas pourquoi, donc encore plus difficile de remédier.

    Citation Envoyé par Sve@r Voir le message
    Une librairie possède un index qui optimise les appels. Donc effectivement ça peut améliorer les accès...
    Pour l'instant je garde la situation actuelle, mais j'essayerai.

  6. #6
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Paul_Le_Heros Voir le message
    Probablement oui, puisque cela a fonctionné. Je vous invite à écrire un bout de code pour vérifier.
    Non, les fonctions imbriquées ne sont pas permises en C standard (sauf à avoir changer récemment). Mais c'est proposé comme extension par certains compilateurs dont gcc.

    Citation Envoyé par Paul_Le_Heros Voir le message
    Il se passe quelque chose à un moment qui fait que les tableaux ne pointent plus ce qu'il devraient et l'on fait après exécution de la fonction d'initialisation. C'est certainement très intime : je ne comprends pas pourquoi, donc encore plus difficile de remédier.
    Sans un code minimal qui reproduit le problème c'est difficile de voir ce qui peut se passer.
    Pour ce genre de problème, les outils classiques : compilation avec tous les warning activés (et en les corrigeant) sur différents compilateurs, analyse statique, exécution sous valgrind ou outils du même type, exécution sous debugger.


    Quelques remarques : le register est probablement inutile, l'utilisation de globales ne devrait pas te faire gagner grand chose, l'élimination des switch guère plus, les manipulations mémoires que tu fais sont compliquées (et potentiellement source d'erreur) pour pas grand chose.
    Je doute que la méthode soit bonne. Avant d'utiliser des constructions tordues pour gagner en perf, fais un code simple qui fonctionne et mesure. Ensuite, si besoin, tu peux introduire des constructions plus tordues et mesurer si tu gagnes effectivement (il faut toujours mesurer, les a priori sur les performances sont généralement assez mauvais et on est régulièrement surpris de ce qu'arrive à faire le compilateur).
    Ah, et pour, penses à compiler en activant les optimisation (au moins O2, à tester en O3).

  7. #7
    Membre expérimenté Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2011
    Messages : 625
    Points : 1 559
    Points
    1 559
    Par défaut
    Hello,

    Alors, oui, on a le droit de mettre une fonction dans une fonction (gcc 6.3.0), mais il faut l'appeler.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <stdio.h>
     
    int main(void) {
    	int d=0;
     
    	void setD(void) {
    		d=1;
    	}
     
    	printf("d=%d",d);
     
    	return(0);
    }
    Sortie: d=0
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <stdio.h>
     
    int main(void) {
    	int d=0;
     
    	void setD(void) {
    		d=1;
    	}
    	setD();
    	printf("d=%d",d);
     
    	return(0);
    }
    sortie: d=1
    On écrit "J'ai tort" ; "tord" est la conjugaison du verbre "tordre" à la 3ème personne de l'indicatif présent

  8. #8
    Membre averti

    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2013
    Messages
    182
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Pérou

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2013
    Messages : 182
    Points : 375
    Points
    375
    Par défaut
    Citation Envoyé par gl Voir le message
    Non, les fonctions imbriquées ne sont pas permises en C standard (sauf à avoir changer récemment). Mais c'est proposé comme extension par certains compilateurs dont gcc.
    Bon, ben je crois que c'est là que ça se passe. Cette extension est disponible en GCC, mais c'est pas finie et/ou stable.

    Citation Envoyé par gl Voir le message
    Sans un code minimal qui reproduit le problème c'est difficile de voir ce qui peut se passer.
    Je ne vais pas essayer parce que les tartines tombant toujours coté beurre, il me faudra des milliers de lignes pour que ça puisse planter et ça plantera pas. Non, pas d'investigation avec cette méthode pour moi.

    Citation Envoyé par gl Voir le message
    Pour ce genre de problème, les outils classiques : compilation avec tous les warning activés (et en les corrigeant) sur différents compilateurs, analyse statique, exécution sous valgrind ou outils du même type, exécution sous debugger.
    Je suis exactement dans ce cahier des charges du développement, et les avertissements convertis en erreurs. pour forcer la correction ! Valgrind est un outil merveilleux ! Mais là : il ne dit pas grand chose d'aidant.

    Citation Envoyé par gl Voir le message
    Quelques remarques : le register est probablement inutile, l'utilisation de globales ne devrait pas te faire gagner grand chose, l'élimination des switch guère plus, les manipulations mémoires que tu fais sont compliquées (et potentiellement source d'erreur) pour pas grand chose.
    Puisque ça tourne, j'ai même constaté que ce dernier code fonctionnel utilisant des globales (tous O0) est plus petit qu'alors que cela plantait ce qui va à l'opposé de l'objectif de cette forme d'écriture. Je n'ai pas essayé d'optimisation par le compilateur, et pour ce qui est de la vitesse d'exécution, je dois encore chercher pourquoi dans une console lambda et sans priorités particulières, le code s'exécute à raison de 10^^6 pas en 2 secondes, alors et le même code sous Némiver vas au moins 10 fois plus vite (évaluation à la louche). Par ailleurs, ces manipulations mémoires ne me satisfont pas non plus, s'il s'agit de ce qui est fait pour initialiser. Par contre, l'utilisation de tableaux de pointeurs sur fonctions pour accélérer le traitement me semble correcte et irréprochable si ce n'est que l'écriture n'étant pas conventionnelle, c'est plus difficile à lire et donc à maintenir, sans bons commentaires.

    Citation Envoyé par gl Voir le message
    Je doute que la méthode soit bonne. Avant d'utiliser des constructions tordues pour gagner en perf, fais un code simple qui fonctionne et mesure. Ensuite, si besoin, tu peux introduire des constructions plus tordues et mesurer si tu gagnes effectivement (il faut toujours mesurer, les a priori sur les performances sont généralement assez mauvais et on est régulièrement surpris de ce qu'arrive à faire le compilateur).
    Les torsions sont conséquences de vouloir un registre pointant la structure disponible pour toutes les fonctions, plutôt que de charger un variable locale (éventuellement dans un registre), à cet effet, pour chaque fonction.

    Citation Envoyé par gl Voir le message
    Ah, et pour, penses à compiler en activant les optimisation (au moins O2, à tester en O3).
    J'étais convaincu que les optimisations n'étaient pas propices au bon déroulement du développement. Je me tromperais donc ? Pourriez-vous m'en dire plus sur ce point ?

    Merci pour ces réponses. Je me demande si je dois marquer ce fil de discutions comme résolu...

  9. #9
    Membre averti

    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2013
    Messages
    182
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Pérou

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2013
    Messages : 182
    Points : 375
    Points
    375
    Par défaut
    Merci pour votre post.

    Citation Envoyé par edgarjacobs Voir le message
    Hello,
    Alors, oui, on a le droit de mettre une fonction dans une fonction (gcc 6.3.0), mais il faut l'appeler.
    Il me semble assez logique qu'il faille l'appeler !

    Merci pour cet exemple de code, mais la fonction main() n'est-elle pas un peu "particulière" ? Je proposerais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #include <stdio.h>
     
    int gD = 7;
     
    int emu (int step) {
    	void setGdTo0 (void) {
    		gD = 0;
    	}
     
    	void setGdTo1 (void) {
    		gD = 1;
    	}
     
    	void (*SetGdTable[2])(void) = { setGdTo0, setGdTo1 };
    #if 0
    	switch (step) {
    		case 0; SetGdTo0(); break;
    		default : SetGdTo1();
    	}
    #else
    	SetGdTable[step & 1]();
    #endif
    	return 0;
    }
     
    int main(void) {
     
    	printf("d=%d\n",gD);
    	emu(0);
    	printf("d=%d\n",gD);
    	emu(1);
    	printf("d=%d\n",gD);
    	return(0);
    }
    Dont la sortie est :

  10. #10
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Paul_Le_Heros Voir le message
    Bon, ben je crois que c'est là que ça se passe. Cette extension est disponible en GCC, mais c'est pas finie et/ou stable.
    A priori si elle fonctionne. Il faut voir si tu es bien dans un cas où tu peux l'utiliser mais normalement il n'y a pas de souci.
    Globalement, quand un code ne fonctionne pas, il est largement plus probable que l'erreur soit dans notre code que dans le compilateur ou la bibliothèque standard (ou dans d'autres bibliothèques largement éprouvées).

    Citation Envoyé par Paul_Le_Heros Voir le message
    Je suis exactement dans ce cahier des charges du développement, et les avertissements convertis en erreurs. pour forcer la correction ! Valgrind est un outil merveilleux ! Mais là : il ne dit pas grand chose d'aidant.
    C'est à dire ? Qu'entends tu par cahier des charges du développement ici, quelles options as tu utiliser ? Quels compilateurs ? Que sort valgrind ? Que dit le debuger ? As-tu mis un point d'arrêt sur le changement de valeur de la variable ?

    Citation Envoyé par Paul_Le_Heros Voir le message
    Par contre, l'utilisation de tableaux de pointeurs sur fonctions pour accélérer le traitement me semble correcte et irréprochable si ce n'est que l'écriture n'étant pas conventionnelle, c'est plus difficile à lire et donc à maintenir, sans bons commentaires.
    Peut-être ! As-tu seulement mesuré si tu gagnais vraiment quelque chose ?

    Citation Envoyé par Paul_Le_Heros Voir le message
    J'étais convaincu que les optimisations n'étaient pas propices au bon déroulement du développement. Je me tromperais donc ? Pourriez-vous m'en dire plus sur ce point ?
    Pour la mise au point et le debug du code, ce n'est pas utile voire nuisible. Mais si tu commences à vouloir optimiser et gagner en vitesse, c'est la première étape (une fois que le code, dans sa forme simple, fonctionne bien sur), et il aura probablement un impact plus important que toutes les tentatives d'optimisation que tu vas faire.

  11. #11
    Membre averti

    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2013
    Messages
    182
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Pérou

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2013
    Messages : 182
    Points : 375
    Points
    375
    Par défaut
    Citation Envoyé par gl Voir le message
    A priori si elle fonctionne. Il faut voir si tu es bien dans un cas où tu peux l'utiliser mais normalement il n'y a pas de souci.
    Globalement, quand un code ne fonctionne pas, il est largement plus probable que l'erreur soit dans notre code que dans le compilateur ou la bibliothèque standard (ou dans d'autres bibliothèques largement éprouvées).
    Je n'hésite jamais à me responsabiliser quand quelque chose va mal. Les erreurs étaient soit des adresses erronées aboutissant à des erreurs de segmentation, soit des instructions illégales rencontrée par le processeur lui-même (un Amd XP).


    [QUOTE=gl;10817637]C'est à dire ? Par "cahier des charges", il ne s'agit que de celles que je m'impose pour écrire, rien de plus.



    Citation Envoyé par gl Voir le message
    Peut-être ! As-tu seulement mesuré si tu gagnais vraiment quelque chose ?
    J'aurais 3 switchs à écrire : un avec 240 possibilités sur 256, un autre de 130 sur 256 et un dernier de 90 possibilités sur 256. De toute façon, des switchs de telles tailles c'est pas facile à lire non plus.

    Citation Envoyé par gl Voir le message
    Pour la mise au point et le debug du code, ce n'est pas utile voire nuisible. Mais si tu commences à vouloir optimiser et gagner en vitesse, c'est la première étape (une fois que le code, dans sa forme simple, fonctionne bien sur), et il aura probablement un impact plus important que toutes les tentatives d'optimisation que tu vas faire.
    Ça colle avec les illusions dans les-quelles je vis. Je ne suis pas rendu à ces essais d'otimisation.

    Vous en conviendrez : il est possible de mal écrire du code fonctionnel. Alors autant s'intéresser à la méthode à utiliser avant d'écrire une ligne.

    Oui, je suis sacrilège si souhaiter un registre pointant en permanence une structure sans avoir à le charger pour chaque fonction est un sacrilège.

    Je trouve que les échanges au long de ce fil tournent trop mal.

    Merci pour à tous vos interventions.

  12. #12
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Paul_Le_Heros Voir le message
    Je ne suis pas rendu à ces essais d'otimisation.
    Vu que tu ouvrais le premier fil en indiquant la volonté d'obtenir un code rapide et les stratégies mises en place dans le code, je pensais que tu en étais à l'optimisation. Mais OK.

    Citation Envoyé par Paul_Le_Heros Voir le message
    Oui, je suis sacrilège si souhaiter un registre pointant en permanence une structure sans avoir à le charger pour chaque fonction est un sacrilège.
    Rien à voir avec un sacrilège, c'est juste assez peu usuel et pas forcément recommandé (en moyenne, le compilateur est meilleur que la plupart des développeurs pour déterminer s'il est efficace de mettre une variable dans un registre).

    Au passage, rien ne garanti que le compilateur va effectivement mettre la variable dans le registre, c'est juste une indication qu'il peut ne pas suivre (le seul effet obligatoire semble être d'empêcher de prendre l'adresse d'une variable marquée register).


    Citation Envoyé par gl Voir le message
    Pour la mise au point et le debug du code, ce n'est pas utile voire nuisible. Mais si tu commences à vouloir optimiser et gagner en vitesse, c'est la première étape (une fois que le code, dans sa forme simple, fonctionne bien sur), et il aura probablement un impact plus important que toutes les tentatives d'optimisation que tu vas faire.
    J'apporte un petit complément d'info : sous gcc, la compilation en O2 ou O3 permet parfois de mettre en évidence des comportements douteux et lève des avertissement qui ne sont pas indiqués avec un niveau d'optimisation plus bas. C'est à mon avis, une bonne raison de compiler, au moins de temps en temps, avec ces options indépendamment des volontés d'optimisation.

  13. #13
    Membre averti

    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2013
    Messages
    182
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Pérou

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2013
    Messages : 182
    Points : 375
    Points
    375
    Par défaut
    À toutes fins utiles pour ceux qui seront "tombés" sur ce fil de discution, et grâce à une réponse sur un autre site_:
    il existe des options de compilation (ces options étant elles-mêmes parfois appelées switch dans le MAN, pas pratique pour la recherche). J'ai noté_:
    -ftree-switch-conversion
    -mbig-switch
    -mbigtable
    -fno-jump-tables
    sans être plus entré dans les détails (je me demande s'il n'y en a pas de spécifiques à certains langages).

    Il semble que la génération du code des switchs soit l'objet d'une attention particulière que je n'imaginais pas. J'ai trouvé un article quasiment hors de ma porté : https://www.researchgate.net/publica...ode_Generation

    Je conclue qu'il peut valoir le coup de ré-écrire le code existant de sorte à ce que mes tableaux de pointeurs sur fonctions et leurs utilisations soient remplacés pas des switch...

  14. #14
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Paul_Le_Heros Voir le message
    Je conclue qu'il peut valoir le coup de ré-écrire le code existant de sorte à ce que mes tableaux de pointeurs sur fonctions et leurs utilisations soient remplacés pas des switch...
    C'est possible, ou pas. La seule solution que je connaisse, c'est de mesurer.

  15. #15
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Il y a de grandes chances que les switch soient plus optimisables que les tableaux de pointeurs. Si nécessaire, le compilateur pourra optimiser en remplaçant le switch par une table de pointeurs mais il ne pourra pas faire l'inverse. Et le branchement prédictif des processeurs ne fonctionne pas ou très mal avec des pointeurs de fonction.

    Les variables globales peuvent même être plus lente d'accès que les locales. Car une locale peut être stockée dans un des nombreux registres du processeur. Sur processeur ARM c'est plus fréquemment le cas que sur Intel/AMD.

    Il ne faut pas avoir d'a-priori sur le résultat optimisé, à moins d'être un grand expert des assembleurs. Comme l'a dis gl, écris un code simple et ensuite cherche les goulets d'étranglement du code compilé en optimisé avec un analyseur de performance. Il faudra ensuite agir sur l'algorithme ou à la rigueur sur l'organisation du code si tu te sens meilleur que l'optimiseur.

  16. #16
    Membre averti

    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2013
    Messages
    182
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Pérou

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2013
    Messages : 182
    Points : 375
    Points
    375
    Par défaut Solution définitive
    Finalement, c'était qu'il fallait regarder.

    Je repasserai éventuellement ici donner des détails sur les différences des temps d'exécution...

  17. #17
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Ça m'étonnerais que cela soit plus optimal qu'un switch (car le switch c'est plus qu'une table de sauts), par contre ça sera non portable et moins lisible. En attendant les temps d'exécutions mesurés.

  18. #18
    Membre averti

    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2013
    Messages
    182
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Pérou

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2013
    Messages : 182
    Points : 375
    Points
    375
    Par défaut
    Dans mon cas, je n'ai rien de moins qu'avec un switch.

    Il y a beaucoup de subjectivité dans le postulat "c'est moins lisible". Perso, je ne trouve pas le switch très lisible. Le switch me semble déjà le lieu de prédilection des goto.

    Pour être portable et fonctionnel sur 99% des machines, ne suffit-il pas que Gcc soit porté sur deux OS ? Je ne dispose pas de Windows, mais Gcc n'est pas disponible sur cette plateforme ?

    J'ai déjà une idée des différences de vitesse : 0,5-1% de gain de temps entre Switch et la même source modifiée en table de saut (à partir de quel pourcentage ne serez-vous plus étonné ?). Il me semble avoir lu qu'on peut gagner jusqu'à 15%, mais c'est évidemment très dépendant du contenu de la source. Ma machine a un AMD XP comme processeur et roule OSS Leap 42.3 avec KDE3 comme gestionnaire de fenêtres. Le logiciel est lancé dans une fenêtre XTerm (qui fait le même boulot que Konsole, mais au moins 10 fois plus vite, sans intervenir sur les priorités d'exécution). Je détaillerai l'exécution le moment venu.

  19. #19
    Membre expérimenté
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    543
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : No Comment
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2010
    Messages : 543
    Points : 1 745
    Points
    1 745
    Par défaut
    Bonjour,
    Un tableau de sauts d'adresse avec goto peut avoir une nette efficacité qu'un switch. Vu sous le capot, le switch peut également tout aussi bien faire et être efficace que goto .De toute façon, l'un ou l'autre, le choix d'optimisation est laissé à la discrétion du compilateur. Cependant attention avec goto il n'est pas à utiliser à tort et à travers, il faut savoir ce que l'on fait. Généralement, on utilise goto pour des branchements sur des instructions de traitement d'erreurs, en d'autres termes à utiliser comme des exceptions/dans des cas bien particuliers.

    Là où je ne suis pas d'accord, c'est de penser que le goto peut être la bonne solution ou bonne approche, mais à mon sens, c'est une très bonne fausse idée, car cela peut très vite devenir problématique et ça ne m'étonnerait pas que le débogage du code/sa maintenance ne soit pas aisé. Personnellement, je resterais sur la première idée de @Sve@r avec un non emploi de switch, mais uniquement d'une variable qui sert d'offset, exemple du ci-dessous.
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    #include <stdio.h>
    #include <stdlib.h>
     
     
    typedef void (*TabPtrFunc)(void);
     
    void function_A(void){
        (void)puts("Bonjour fonction A");
    }
     
    void function_B(void){
        (void)puts("Bonjour fonction B");
    }
     
    void function_C( void ){
        (void)puts("Bonjour https://www.developpez.net");
    }
     
    const TabPtrFunc TabJump[4] = {
        &function_A,
        &function_B,
        &function_C,
    };
     
    int main( void ){
     
        size_t i = 0;
     
        if( 0x0 >= i && i <= 256 )
            TabJump[i]();
        else
            (void)puts("Autre cas");
        return (EXIT_SUCCESS);
     
    }

    Cas contraire, si c'est l'emploi du goto et tableau de d'adresse de saut qui vous intéresse et qui vous paraît une solution envisageable, l'exemple ci-dessous peut être une approche, mais cela reste de mon point de vue à prendre avec des pincettes : parce qu'avecgoto, ça marche sous réserve que les fonctions que l'on fait appel soient des noreturn et ou que l'on gère autrement le flux d'exécution et prévoir une sortie de fonction par d'autres moyens que return. Cas contraire, le résultat sera une/la perte de stack frame dont le résultat sera purement et simplement un segment default ; (Pour illustrer les propos que j'avance en me basant sur mon exemple ci-dessous retiré l'instruction "exit(EXIT_SUCCESS) dans les fonction_A,B et C)

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
     
    #include <stdio.h>
    #include <stdlib.h>
     
    typedef void (*TabPtrFunc)(void);
     
    void function_A(void){
        (void)puts("Bonjour fonction A");
        exit(EXIT_SUCCESS);
    }
     
    void function_B(void){
        (void)puts("Bonjour fonction B");
        exit(EXIT_SUCCESS);
    }
     
    void function_C( void ){
        (void)puts("Bonjour https://www.developpez.net");
        exit(EXIT_SUCCESS);
    }
     
    const TabPtrFunc TabJump[] = {
        &function_A,
        &function_B,
        &function_C,
    };
     
    int main( void ){
     
        size_t i = 0;
     
        if( 0x0 >= i && i < 0x4 )
            goto *((void*)TabJump[i]);
        else
            (void)puts("Autre cas");
     
        return (EXIT_SUCCESS);
    }



    Citation Envoyé par Paul_Le_Heros Voir le message
    J'ai déjà une idée des différences de vitesse : 0,5-1% de gain de temps entre Switch et la même source modifiée en table de saut (à partir de quel pourcentage ne serez-vous plus étonné ?). Il me semble avoir lu qu'on peut gagner jusqu'à 15%, mais c'est évidemment très dépendant du contenu de la source. Ma machine a un AMD XP comme processeur et roule OSS Leap 42.3 avec KDE3 comme gestionnaire de fenêtres. Le logiciel est lancé dans une fenêtre XTerm (qui fait le même boulot que Konsole, mais au moins 10 fois plus vite, sans intervenir sur les priorités d'exécution). Je détaillerai l'exécution le moment venu.
    Permettez-moi d'en douter surtout que l'optimisation pour l'obtention d'un gain très significatif de rapidité de temps d'un programme écris en C, ne se limite pas qu'avec une légère modification de code ; il y a d'autres facteurs à prendre en compte surtout si l'on cherche que ça exécute viiiiite.


    Citation Envoyé par Paul_Le_Heros Voir le message
    Quelqu'un pourrait-il me dire pourquoi Linux perd les pédales ? Je pense que les programmeurs réguliers de threads pourraient sentir l'origine du problème et ainsi pouvoir m'indiquer comment le résoudre...

    J'aurais préféré la première forme de structure décrire plus haut. J'ai bien vu que le logiciel perd vraiment les pédales entre "run" et "faire un pas" sans comprendre pourquoi. Je suppose que le code (la fonction make_a_step() complète) n'est pas "déplacé" par le système, à quelque occasion, auquel cas la fonction init serait à relancer ? J'ai vérifier en écrivant ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
      // inclusion de headers, déclaration des types de structures, 
      // fonction etc : équivalent à un ".h"
      // Le code C de l'émulateur ne comporte qu'une function visible :
      void *gFuncPosRef = NULL;
     
    void make_a_step (void) {
      register MyStruct_t *GlobStruct;  // Modificateur "register"
      int local1, local2, etc...
      static void (*functTable[4])();   // "static" obligatoire
     
      void funct1 (void) { ... }
     
      ...
     
        functTable[3] = funct4;
      }
     
      // le corps de la fonction "make_a_step" commence ici...
      if (!gFuncPosRef || gFuncPosRef!= &make_a_step) {
       init();
       gFuncPosRef = &make_a_step;
      }
      ...
      ...
     
     
    } // Fin de make_a_step()
    Mais ça marche pas.

    EDIT :
    Mais j'y pense : peut-être qu'écrire cette "fonction" comme étant une librairie résoudrait mon problème ?
    Linux ne s'emballe/ perdent les pédales a ce que je sache il n'y pas eu de kernel panic, C'est jusque votre GDB /Nemiver n'est juste pas en mesure de réaliser/interpréter l'action souhaitée que l'on passe de débogage pas à pas ou run. En temps normal, les débogueurs ont normalement les informations sur l'ensemble de l'exécutable du début à la fin et si par exemple, il perd le stack frame et qu'il il s'emballe, (c'est le cas par exemple de mon exemple si vous retirez l'instruction exit(EXIT_SUCCESS). C'est qu'il a perdu ou il lui manque une ou des informations ( non de variable local , adresse retour de fonction etc..).

    Ceci dit, on manque d'informations sur ce que vous appelez, Linux s'emballe et les diverses informations de sortie que les debogeurs donnent, y a-t-il un coredump de généré ou pas ?


    À bientôt.
    Celui qui peut, agit. Celui qui ne peut pas, enseigne.
    Il y a deux sortes de savants: les spécialistes, qui connaissent tout sur rien,
    et les philosophes, qui ne connaissent rien sur tout.
    George Bernard Shaw

  20. #20
    Expert éminent sénior
    Avatar de Kannagi
    Homme Profil pro
    cyber-paléontologue
    Inscrit en
    Mai 2010
    Messages
    3 214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : cyber-paléontologue

    Informations forums :
    Inscription : Mai 2010
    Messages : 3 214
    Points : 10 140
    Points
    10 140
    Par défaut
    Citation Envoyé par Paul_Le_Heros Voir le message
    Pour être portable et fonctionnel sur 99% des machines, ne suffit-il pas que Gcc soit porté sur deux OS ? Je ne dispose pas de Windows, mais Gcc n'est pas disponible sur cette plateforme ?
    Oui il a un autre nom MinGW.

    Sinon pour les perfs 90% du temps c'est de l'algo ,cela ne sert à rien (surtout si on met un -O3) de changer le code C pour optimiser (je parle ici de penser optimiser les instructions on codant différemment) ,le compilateur se permettra quelque fois de faire un truc totalement différence de ce qui est écrit.
    Sinon ce qui aide pas mal est si les opcodes sont cohérents , alors on peut émulé rapidement avec un switch et quelque and.

    Et le switch est conseillé , vu que comme les pointeur de fonctions , il existe en assembleur des pointeurs de saut ce qui est plus rapide qu'un pointeur de fonction et peut être que GCC l'utilise pour les optimisations sur le switch.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. [STRUCTURE] Structure d'un programme JAVA
    Par pflany dans le forum Langage
    Réponses: 11
    Dernier message: 18/04/2020, 22h05
  2. Réponses: 18
    Dernier message: 04/06/2007, 00h55
  3. Structure d'un programme
    Par bontempf dans le forum SQL Procédural
    Réponses: 6
    Dernier message: 10/03/2007, 23h48
  4. Apprendre la structure d'un programme en Prolog
    Par minen dans le forum Prolog
    Réponses: 3
    Dernier message: 26/02/2007, 09h48
  5. [debutant] structure d'un programme
    Par poukill dans le forum Débuter
    Réponses: 17
    Dernier message: 19/05/2006, 15h33

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo