Salut tout le monde,
J'ai vu dans les premières pages qu'il fallait régulièrement bencher son code, mais vous le faites comment?
Avec un clock() ?
Merci
Salut tout le monde,
J'ai vu dans les premières pages qu'il fallait régulièrement bencher son code, mais vous le faites comment?
Avec un clock() ?
Merci
J'aurais une petite question :
quelle différence entre int x (65) et int x = 65 ?
Est-ce que ça a le même effet que ça :
MaClasse:MaClasse ()
: x (65) <---- Ca ?
{
}
J'autre part, j'ai vu sur les papiers du prochain standard une rvalue référence, qui permettrait de faire ça :
MaClasse && operator + (MaClasse obj1, MaClasse obj2)
{
return MaClasse (obj1.x + obj2.x, obj1.y + obj2.y);
}
Sans l'appel à un constructeur de copie ? Comme ça se fera internement (ça se dit ?). Provoquera t'il une amélioration des perfs que ce qui se passe actuellement (car d'après ce que j'ai compris, dans les cas comme ci-dessus, le compilo utilise l'optimisation RVO qui annule un peu cette surcharge ?
Salut,
Pour les types de base ((unsigned)char, (unsigned) int, (unsigned) float, (unsigned) double etc), il n'y a pas de différence entre le fait d'utiliser la liste d'initialisation et celui de faire l'initialisation dans le corps du constructeur.
Par contre, là où la différence apparait, c'est dans le cadre où le membre à initialiser est une classe ou une structure.
En effet, en mettant que l'on aie une classe du genre de
qui doit servir de membre dans la classe
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 class Membre { public: Membre(){} Membre(int i, float j) { /* ici, ca ne change pas grand chose */ _i=i; _j=j; } void SetI(int i){_i=i;} void SetJ(float j){_j=j;} int GetI(){return _i;} float GetJ(){return _j;} private: int _i; float _j; };
Le fait d'utiliser la liste d'initialisation apportera un gain de temps sous la forme de
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 class MaClasse { public: MaClasse(Membre& m); MaClasse(int i, float j); /*...*/ private: Membre mem; };
par rapport aux constructeurs sous les formes de
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 MaClasse::MaClasse(Membre& m):mem(m){}/* utilise * Membre::Membre(Membre&) */ /* ou */ MaClasse::MaClasse(int i, float j):mem(i,j){}/* utilise * Membre::Membre(int i, float j) */
pour la simple raison que, les versions utilisant les liste d'initialisation se contenteront de l'appel du constructeur qui va bien selon le cas (par copie, ou en fournissant les valeurs nécessaire) alors que les autres versions vont:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 MaClasse::MaClasse(Membre& m) /* utilise Membre::Membre() */ { mem.SetI(m.GetI());/* il faut encore initialiser les valeurs de mem */ mem.SetJ(m.GetJ()); } /* ou */ MaClasse::MaClass(int i; float j)/* utilise Membre::Membre() */ { mem.SetI(i);/* il faut encore initialiser les valeurs de mem */ mem.SetJ(j); }
- Utiliser des valeurs par défaut pour le constructeur (généralement 0 pour _i et 0.0f pour _j dans l'exemple)
- devoir assigner, pour chacun des membre de la classe membre (ici _i et _j) la valeur que l'on veut leur donner
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Non, Musaran a raison, vous ne parlais pas de la meme chose, toi tu parle de la base, lui de la representation.Envoyé par LeGreg
En effet un nombre peut etre coder classiquement, c'est a dire qu'il est converti dans la base, ainsi en 10 decimal s'ecrit 0000 1010 en binaire et la effectivement >> et << corresponde nt a des multiplicationdivision par puissance de 2.
Maintenant un nombre peut aussi etre representer en BCD (binaire code decimal), 10 en decimal devient alors 0001 0000 et du coup << et >> pour mulitplier/diviser par 2 ne fonction plus. Pourtant on travaille dans les deux cas avec des operateur binaires.
En resume le binaire, decimal, hexadecimal, ... ne sont que de bases et quelque soit la base, le comportement et la valeur d'un nombre ne varie pas.
La numerotation classique, le BCD, ... sont des representations qui influe sur les proprietes du nombre
il ne faut pas etre totalement stupide non plus
<< pour multiplier un float par deux ne fonctionne pas non plus
c'est une grande nouvelle..
Il y a bien adequation entre le but (multiplier par deux)
et la methode (decaler les bits)
parce que l'operateur << utilisé sur des nombres entiers
est documenté comme décalant un nombre de bits sur la gauche
(representation binaire classique d'un nombre entier par un processeur usuel) et que ce décalage correspond effectivement a multiplier par la puissance correspondante de deux.
Vous pouvez decaler les bits d'une banane aussi
mais le resultat ne sera pas garanti..
LeGreg
Mon site web | Mon blog | Mes photos | Groupe USA
> BONJOUR, JE SUIS NOUVEAU SUR CE FORUM
> presse la touche caps lock, stp
> OH.. MERCI C EST BEAUCOUP PLUS FACILE COMME CA
Je sais tres bien que ca ne fonctionne pas avec les float, il n'y a pas de souci.
Musaran donnait les conditions a respecter pour pouvoir utiliser le decalage pour realiser des multiplications ou des divisions.
J'ai juste apporte une petite precision sur la remarque :Sache que tout les type numerique (int, float, ...) quelque soit la representation (BCD, ...) ont une representation binaire mais sur tous les types le resultat n'est pas le meme, il faut en etre conscient, ce qui est apparement ton cas, mais pas forcement celui de tout le monde. Dans ce sens je troucve que le commentaire de Musaran est le bienvenu.les operateurs binaires operent sur les representations binaires des nombres. Point.
Je ne sais pas trop quoi dire sans me répéter...
Il n'y a pas grand mal à utiliser les décalages, il faut juste bien comprendre que ce sont des opérations sur les bits.
Et comme toutes les opération de bits, leur sens sur la valeur arithmétique représentée dépends justement de ce mode de représentation.
Dans cette façon de compter (dont j'ai oublié le nom), le décalage ne vaut pas multiplication/division:
Le bit de poids fort peut être à gauche ou à droite. C'est le "boutisme" ou endianness de bits.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 Dec Bin 0 000 1 001 2 011 3 010 4 110 5 111 6 101 7 100
Si jamais j'ai une précision sur le sens standard de <</>> en C++, j'en parlerai ici.
C'est ça que je voulais dire.
Quant au const...
Je parle des constantes vraies, connues à la compilation, et pas des const extern, membres, ou paramètres de fonctions.
Quand le compilateur sait qu'une valeur ne varies pas, il peut l'incorporer directement dans le code, plutôt que de mettre une adresse pour aller la chercher.
Aussi, elles sont placées dans une page de données read-only, qui n'a pas besoin d'être ré-écrite sur disque en cas de swap de mémoire virtuelle.
Ca s'apelle le binaire reflechi.Envoyé par Musaran
La propriete principal est que lors d'une incrementation un seul bit est modifie. Il est surtout utilise en electronique logique car cette incrementation particuliere permet de compenser dans certains cas la difference de temps de reponse entre deux composants.
là , je viens de relire tout ce sujet et je vous dirais d'arreter avec vos considérations sur la représentation des nombres , ca a plus rien à voir !
surtout que lorsque , en C j'entends , on écrit tartenbouille<<n , eh ben tartenbouille est pris sous sa forme binaire évidement , et les ops << >> sont des ops dits binaires parce qu'ils agissent sur le binaire et un point c'est tout !!!
si vous voulez d'autres representations que celle ci d'ailleurs , il va faloir le coder vous meme ( ou ajouter une lib qqconque ) , donc toute cette discussion , ben , je la trouve inutile de ce point de vue !
Quand, pour optimiser, on remplace un opérateur arithmétique par un de bits, on dépend de la façon dont la machine utilises les bits pour représenter les nombres.
On n'a pas forçément le poids faible à droite (même au niveau des bits), et on n'est pas forçément en complément à 2.
Aller vite, c'est bien.
Être incompatible, c'est pas bien.
Je vais essayer de ne pas revenir là-dessus.
-utiliser des tables plûtot qu'une suite de comparaison.
Les fonctions isspace &co. utilisent le char comme indice pour trouver la réponse dans un tableau.
-utiliser des tables de pointeurs de fonction plutôt que des switch/case:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 //Lent switch(n){ case 1: fonc1(); break; case 2: fonc2(); break; case 3: fonc3(); break; ...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 //Rapide typedef void (*pf)(); //pointeur de fonction pf tabfonc[]= {fonc1, fonc2, fonc3}; ... tabfonc[n]();
Mais toutes ces representations sont des formes binairesEnvoyé par RegBas
non , mais j'veux dire que , en C , la representation utilisée est toujours :
001
010
011
100
101
...
(je parle de BASE , ce ke je voulais dire , c ke qqsoit la representation , l'opération a<<1 DOIT correspondre à a*=2 !! le code de l'operateur est peut etre different suivant la rep , mais pas le résultat ! sinon , y'aurait pas de maths possibles ! )
Non l'operateur <<1 ou >>1 correspond a un decalage de bits, et non a une multiplcation/division par 2, la representation classique des entiers fait que ca correspond a une telle multiplication dans ce cas particulier, mais ce n'est pas une obligation. Essaie avec un float et tu verras que ce n'est pas le cas.Envoyé par RegBas
oui , c'est vrai pour les float ... et surement pour d'autres aussi , en fait ce ke j'ai dit n'est valable que pour tres peu de types et de rep ..
par contre en parlant d'optimisation , j'ai eu un ptit prob :
j'ai programmé une petite démo en opengl , et bien sur , en passant d'une machine à l'autre , il y a des écarts de vitesse hallucinant ( la faute aux drivers ? )
mais il y a aussi le fait de passer la souris au dessus de la fenetre de l'application et là , ca ralentit l'exécution de ma démo !
pourtant , ( j'ai utilisé glut ) , je ne me sers d'aucun call back sur la souris
à quoi c'est dû et comment résoudre le pb ?
Je suis desole, je suis bien incapable de resoudre ton probleme. Mais je te conseillerais de plutot poster cette question sur le forum opengl ou tu trouveras plus facilement des gens capables de t'aider.
Le standard ISO/ANSI définit très clairement qu'il ne définit aucune représentation standard des nombres.
Les chars dépendent des pages de codes. Ils peuvent être signés ou pas, et n'ont pas forcément 8 bits.
Les flottants ne sont pas forcément des IEEE-je-sais-plus-quoi.
Les entiers peuvent être autrement qu'en système de base et/ou complément à 2.
Pour être plus rapide et adaptatif, le C/C++ utilise ce que la machine sait faire.
Si un jour on invente des circuits logiques à trois états, << pourrait multiplier par 3.
Ne pas faire de supositions si ce n'est pas indispensable.
Caclculer tout ce qui peut etre calculé c'est pas indispensable. Ectrivez l'instruction :
sera remplacé a la compilation par
Code : Sélectionner tout - Visualiser dans une fenêtre à part a = b + (3000 * 200);
(En version non optimisée par le compilateur)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5mov eax, 600000 add b, eax mov eax, b mov a, eax
Ce qui veut dire que le compilatuer calcul ce qui est constant. Verifiez de même si vous tapez :
200 * 3000 sera egalement precalculé. Toutes les variables constantes donnent exactement le même effet que les #define puisque le compilateur les remplaces par leur valeur. Juste une difference, si je me souviens bien, les #define sont interpretes par le preprocesseur donc avant la compilation réele. Par contre il faut faire attention :
Code : Sélectionner tout - Visualiser dans une fenêtre à part const int c = 3000;
dans ces deux cas 300 * 200 ne seront pas (toujours) calculé a la compilation, du dans le premier cas au fait que la multiplication s'effectue de gauche a droite donc b * 300 est d'abord calculé et etant non constant, l'instruction resultat * 200 ne peut pas etre calculé a la compilation.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 a = b * 300 * 200; //Attention a = 300 * b * 200; //Aussi ici
Optimisation aussi mais dans la manière dont vous concevez certaines choses. Vous avez vu que l'operateur >> permet des division par deux et que << permet des mulmtiplications par 2. Il faut en tenir compte mais en voyant un peu plus loin. Par exemple si vous programmez un jeu avec des cartes, prevoyez une largeur de carte de 256 par exemple pour que dans vos calculs vosu ppuissiez facilement integrer les decalages. Il y a des milliers de cas ou en choississant des bonnes limites ou un bon systeme de coordonnees, on peut simplifier (mais plus tard) les calculs.
Cet operateur << peut etre utilisé aussi dans des cas plus large. par ex :
Ca ce voit très bien si on decompose 40 en binaire : 40 = 000110000b ou on s'apercoit que 000110000 = (1 << 4) + (1 << 5). Donc on peut décomposer un facteur constant de la multiplication en une somme de puissance de 2. On peut decomposer en deux trois nombres voire plus sachant qu'une multiplication orend (a verifier) 40 cycles pour des 32 bits et une addition ou un decalage prend (a verifier aussi) seulement un cycle.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 a = b * 40; //Equ a : a = (b << 5) + ( b << 4);
Faire un tableau de sin est vraiment bcp mieux que utiliser la fonction sin. Après tests, le calcul est au moins 1000 fois plus rapide. (je peux même vous passer le proghramme de tests si vous voulez verifier.)
Je comprends pas pourquoi << fonctionne pas pour les negatifs. Le compilateur est bien cencé savoir que le nombre est signé et remplacer par l'instruction assembleur corespondante :
devrait etre en toute logique remplacé par :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 int a; unsigned int b; a >>= 4; b >>= 4;
On trouve même dans la doc assembleur
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 sar a, 4 shr a, 4
Donc je comprends pas. C'est tous les compilos qui ne permettent pas la division signée ? (Dès que je rentre chez moi je met le miens a l'epreuve)sar r/m, 1 : signed divided by 2
shr r/m, 1 : unsigned divide by 2
Il existe encore d'autres optimisations encore mais plus sensibles.
Blustuff.
Je pense que tout le monde est d'accord pour dire que dans 95% des projets il vaut mieux passer du temps à faire un code lisible et à optimiser ses algorithmes.
Le temps passé à faire du code C C++( ou autre ) qui utilise au mieux les possibilités du compilateur n'est que rarement rentable... Celà dit pour des programmes tres diffusés et soumis à concurence, comme les jeux, ça peut valoir le coup... (si je ne m'abuse QUAKe comporte pas mal de code assembleur... et aucun malloc)
Mais je serais curieux de connaitre les temps de calcul (en unité arbitraire) de différentes opérations tel que
- - Affectation (=)
- test == , <, <= , ...
- ouveture d'un fichier.
- ecriture/lecture/parcourt de fichier
- Multiplication/division
- etc.
tout ça en fonction du type évidemment...
J'ai déjà eu un document de ce type entre les mains mais je ne l'ai pas retrouvé par google...
Pour un développeur, la "performance" d'un logiciel doit être le cadet
de ses soucis dans la mesure où l'utilisateur constate aucune "lenteur"
C'est le travail d'un bon compilateur des faire toutes les optimisations
qu'il juge nécessaires selon le contexte
Ainsi le développeur peut se consacrer à ses vrais priorités :
faire du code correct, simple et lisible
et donc réaliser des applications plus élaborées
Partager