begin process at 2013 06 19 23:25:16
  Trouver un code source :
 
dans
 
Accueil > 

Tutoriels

 > 

Exécution

 > TACTIQUES D’OPTIMISATION DE LA VITESSE D'EXECUTION DU CODE

TACTIQUES D’OPTIMISATION DE LA VITESSE D'EXECUTION DU CODE


 Information sur le tutoriel

Note :
Aucune note


 Description

Présentation de la "philosophie" et de quelques tactiques pour optimiser la vitesse d'exécution d'un code.

Tutorial

TACTIQUES D’OPTIMISATION DE LA VITESSE D'EXECUTION DU CODE






L’optimisation du code est la pratique qui consiste à modifier un code correct pour le rendre plus efficace. Il peut s’agir de la taille ou de la vitesse d’un programme. Mais dans ce tutoriel, nous ne nous occuperons que de la vitesse d’exécution.


On peut considérer ce problème d’optimisation de la vitesse à deux niveaux : stratégique ou tactique.

Au niveau stratégique, il s’agit de modifications obtenues par la refonte de la conception de tout le programme et des données. Autant dire de repartir presque à zéro. Il y a cependant une solution extrêmement efficace qui consiste à acheter (ou à faire acheter par l’utilisateur qui se plaint de la lenteur) un nouvel ordinateur plus rapide et plus puissant (héhéhé).

Je vous laisserai décider de la stratégie à adopter et je ne traiterai que des tactiques. C’est-à-dire de modifications à petite échelle qui ne concerneront que quelques lignes de code.


On a calculé, à propos des programmes Fortran, que 4% d’un programme représentait en moyenne 50% de son temps d’exécution. C’est sans doute vrai pour tous les langages. Nous allons voir comment améliorer cet état de fait.


    D’abord parlons d’un malentendu courant que voici :


«La réduction du nombre de lignes de code conditionne la taille et la vitesse de l’exécutable    


C’est faux !

Une preuve?

- Initialisons un tableau de 10 éléments :


for i := 1 to 10 do MaTable[ i ] :=0;


A votre avis, la méthode ci-dessous sera-t elle plus rapide ou plus lente?


MaTable[1] := 0;

MaTable[2] := 0;

MaTable[3] := 0;

MaTable[4] := 0;

MaTable[5] := 0;

MaTable[6] := 0;

MaTable[7] := 0;

MaTable[8] := 0;

MaTable[9] := 0;

MaTable[10] := 0;


Un test avec Delphi7 donne l’avantage à la deuxième méthode avec un ratio de performance de 1.7 : 1 (1.7 fois plus rapide !)


    J’en profite pour effectuer une petite mise en garde.

Les techniques que je vais présenter ne constituent pas des recettes à appliquer aveuglément. Peu de techniques s’appliqueront d’une façon assez générale pour être directement copiées dans votre code. Ce sera à vous de les tester et de vérifier que votre compilateur n’y est pas allergique. En effet, certaines techniques, paraissant pourtant de bon sens, pourront dégrader les performances après la compilation. J’en montrerai un exemple et je rappellerai ce point très important : « testez, testez et re-testez ! »


    Voici les six commandements à garder en mémoire pour obtenir de bonnes performances. Allez ! Mettons les doigts dans le cambouis !





« LOGIQUE DE DELPHI TU ADOPTERAS »


Je ne doute pas que vous ayez tous une certaine dose de logique. Mais bon… Il ne faut pas craindre l’overdose.

Par exemple, les expressions logiques suivantes sont équivalentes :

(not A) and (not B)

not (A or B)

En choisissant la 2ème, on gagne une opération ‘not’. Le temps gagné sera sans doute infime, mais dans une boucle très longue, ça se sentira.



Sortir d’un test en temps et en heure :


Voici un exemple de code optimisable :


for i := 0 to High( TableTest ) do if (TableTest[ i ] < 0) then NegativeFound := true;


C’est une boucle de recherche, un cas très courant. Le principe d’optimisation consiste à ne pas continuer à tester lorsque le résultat est connu. Appliquons ce principe :


for i := 0 to High( TableTest ) do begin

if TableTest[ i ] < 0 then begin

NegativeFound := true;

Break; //On sort dès que le résultat est connu.

end;

end;


Ici, le gain de performance sera très variable en fonction du contexte. Mais le plus souvent, le gain sera très important. Si on place une valeur négative au milieu du tableau, on peut s’attendre à un temps de traitement deux fois plus rapide. Et c’est bien ce que confirment les tests.


Un autre exemple :


N := Random(101);

if (N>=0) and (N<5) then Categorie := 1;

if (N>=5) and (N<20) then Categorie := 2;

if (N>=20) and (N<101) then Categorie := 3;


Et ce même code partiellement optimisé :


if (N>=20) then Categorie := 3 //On sort dès que le résultat est connu.

else if (N>=5) then Categorie := 2//On sort dès que le résultat est connu.

else Categorie := 1;

Ratio de performance = 1.3 : 1 Mais on peut faire mieux…



Ranger les tests par ordre croissant :


if (N<5) then Categorie := 1

else if N<20 then Categorie := 2

else Categorie := 3;

Maintenant, le ratio de performance = 1.9 : 1


Vous allez me dire : « - Où est la logique dans tout cela ? »

- Bein, c’est la logique du compilateur de Delphi7, tiens !


Je vous avez bien dit : « testez, testez et re-testez ! »

Cet exemple souligne la nécessité de ne pas suivre aveuglément des conseils d’optimisation. Ce qui est valable pour C# ne le sera pas forcément pour Java ou Delphi. Certaines optimisations valables pour un langage dégraderont même très fortement les performances dans un autre langage. De plus, cela peut même varier d’une version à l’autre d’un même compilateur. Vous devrez donc toujours mesurer les performances car les règles du jeu changent lorsque vous changez de langage, de compilateur ou de version d’un même compilateur, de processeur, de mémoire vive, de région (bon, d’accord, peut-être pas de région), etc…


Je cite Olivier DAHAN :

« Prenons l’instruction Case pour exemple. Jusqu’à Borland Pascal 7 (BP7) l’ordre le meilleur des constantes de cas en termes d’efficacité du code devait être celui de l’ordre décroissant de la fréquence d’utilisation : le cas le plus fréquent devant être placé en premier, le moins fréquent en dernier. Cela était lié au code machine généré par le Pascal (une suite de comparaisons et de sauts de cas en cas, du premier au dernier). L’Objet Pascal de Delphi génère un code très différent, et ici le moyen d’optimiser l’efficacité d’un Case est de placer les constantes de cas dans l’ordre numérique croissant (qui est celui de l’ordre de la déclaration dans le type si le sélecteur de cas est de type énuméré). »


J’espère donc avoir réveiller votre méfiance.


Sinon, je rajoute une couche :



Case of meilleur que if then else :


Au lieu d’écrire :


case N of

20..100 : Categorie := 3;

5..19 : Categorie := 2;

else Categorie := 1;

end


On préférera la formulation 1.5 fois plus rapide :


case N of

0..4 : Categorie := 1;

5..19 : Categorie := 2;

else Categorie := 3;

end;

Maintenant, le ratio de performance = 1.95 : 1


Presque deux fois plus rapide que l’exemple de départ ! (Ca tombe bien pour une fois, car Case of est toujours plus lisible que des if then else imbriqués).


-En C#, Case of et if then else sont comparables.

-Par contre, en Java if then else est 6 fois plus rapide qu’un case of.

-Et en Visual Basic, c’est Case of qui est 4 fois plus rapide que if then else (Steve McConnell dixit).


A bon entendeur, salut !



Remplacer des expressions logiques complexes par un tableau :


A titre d’exemple abstrait, supposons que vous ayez à assigner un numéro de catégorie (0, 1, 2, 3) à un objet en fonction de son appartenance à un ou plusieurs ensembles A, B, C :



Exemple extrait du livre de Steve McConnell «CODE COMPLETE» et adapté du C++ à Delphi.

















En général, on écrira un truc du genre :


if ((N in EnsembleA) and not (N in EnsembleC)) or ((N in EnsembleA) and

(N in EnsembleB) and (N in EnsembleC)) then Categorie := 1

else if ((N in EnsembleB) and not (N in EnsembleA)) or ((N in EnsembleA) and

(N in EnsembleC) and not (N in EnsembleB)) then Categorie := 2

else if (N in EnsembleC) and not (N in EnsembleA) and not (N in EnsembleB)

then Categorie := 3

else Categorie := 0;


Avec une recherche dans un tableau, nous n’allons certes pas augmenter la lisibilité du code, mais ce ne sera pas beaucoup plus sibyllin… De toute façon, on peut toujours documenter le code avec des commentaires et un tableau sera plus facile à modifier si nécessaire. Cela donnera :


type

TTab = array[0..1, 0..1, 0..1] of byte;


const

TableCategorie : TTab = // notB,notC notB,C B,notC B,C


(((0, 3), (2, 2)), // notA

((1, 2), (1, 1))); // A


Categorie := TableCategorie[Ord(N in EnsembleA), Ord(N in EnsembleB), Ord(N in EnsembleC)];

Ratio de performance = 9 : 1





« LES BOUCLES TU VERIFIERAS »



Placer les tests à l’extérieur des boucles :


Il est facile de repérer les tests qui ne sont pas modifiés à l’intérieur des boucles :


for i := 0 to High( TableTest ) do begin

if Choix = 0 then TableTest[ i ] := TableTest[ i ] + 1

else TableTest[ i ] := TableTest[ i ] - 1;

end;


Et la même chose optimisée :


if Choix = 0 then

for i := 0 to High( TableTest ) do TableTest[ i ] := TableTest[ i ] + 1

else for i := 0 to High( TableTest ) do TableTest[ i ] := TableTest[ i ] - 1;

Ratio de performance = 1.35 : 1



Déroulez les boucles :


Cette technique se rapproche du premier exemple de ce tutoriel où on déroulait complètement une boucle de 10 itérations. En pratique, ce n’est pas réalisable pour un grand nombre d’éléments ou quand celui-ci nous est inconnu. On peut cependant adapter cette technique. Voici une boucle classique :


i:=0;

while i < High(TableTest) do begin

TableTest[ i ] := 0;

Inc(i);

end;


Et un exemple de la même boucle partiellement déroulée :


i:=0;

while i < High(TableTest)-3 do begin

TableTest[ i ] := 0;

TableTest[ i+1 ] := 0;

TableTest[ i+2 ] := 0;

TableTest[ i+3 ] := 0;

inc(i,4);

end;

{On ajuste les derniers cas:}

if i = High(TableTest)-1 then TableTest[ High(TableTest)-1 ] := 0;

if i = High(TableTest)-2 then TableTest[ High(TableTest)-2 ] := 0;

if i = High(TableTest)-3 then TableTest[ High(TableTest)-3 ] := 0;

Ratio de performance = 1.4 : 1


Mais c’est une technique que je ne recommande pas. D’abord, je la trouve affreuse. Ensuite, il faut vraiment tester le gain de performance dans chaque cas car il peut changer en fonction des calculs effectués. Le compilateur de Delphi fait en général beaucoup mieux qu’un tel bricolage. A tester et n’utiliser que lorsque le gain en vitesse de traitement est crucial.



Les valeurs sentinelles :


Exemple d’une boucle de recherche contenant un test composé :


NegativeFound := False;

i := 0;


while (not NegativeFound) and (i < High(TableTest)) do begin

if TableTest[ i ] < 0 then NegativeFound := true;

inc(i);

end;


Dans ce code, on sort bien de la boucle en temps et en heure, cependant chaque itération de la boucle teste (not NegativeFound) et (i < High(TableTest)). Le test TableTest[ i ] < 0 sert à déterminer si la valeur négative a été trouvée. La boucle exécute donc 3 tests par itération.


Mais on peut n’effectuer qu’un test par itération. Il suffit d’affecter la valeur de recherche à l’élément qui suit immédiatement la fin de la plage de recherche (N’oubliez pas d’allouer de l’espace pour cet élément lorsque vous déclarez le tableau !). Ici, on affectera une valeur négative au dernier élément du tableau. Si on ne trouve pas de valeur négative avant de rencontrer celle qu’on a placée à la fin, on saura que la valeur recherchée ne se trouve pas dans le tableau.

Quelques lignes de code valent mieux qu’une longue explication :



TableTest[ High(TableTest) ] := -1; // Valeur sentinelle.


NegativeFound := false;

I := 0;

while not TableTest[ i ] < 0 do inc(i); //On sort.

{Et on vérifie que la valeur a été trouvée}.

if i < High(TableTest) then NegativeFound := true;

Ratio de performance = 1.7 : 1



Placez la boucle la plus active à l’intérieur :


Lorsque des boucles sont imbriquées, réfléchissez à la boucle que vous allez placer à l’extérieur et à celle que vous voulez à l’intérieur. L’exemple suivant est optimisable :


for Col := 0 to 999 do

for Ligne := 0 to 1 do Total := Total + MaTable[ Col, Ligne ];


La boucle extérieure est exécutée 1000 fois. La boucle intérieure 1000*2 = 2000 fois. Soit un total de 3000 itérations.

En inversant les boucles :


for Ligne := 0 to 1 do

for Col := 0 to 999 do Total := Total + MaTable[ Col, Ligne ];

Ratio de performance = 1.9 : 1


Dans le code optimisé, la boucle extérieure est exécutée 2 fois. La boucle intérieure 2*1000 = 2000. Soit un total de 2002 itérations.





« ATTENTION AUX DONNEES TU FERAS »


Voici quelques lignes qu’on peut souvent trouver dans un code qui affiche une animation à l’écran par exemple :


Var X, Y, Angle, Hypot : Smallint;


for Angle := 0 to 360 do begin

X := Round( cos(DegToRad(Angle)) * Hypot );

Y := Round( sin(DegToRad(Angle)) * Hypot );

end;



Se méfier des fonctions système :


D’abord, l’unité Math nous permet d’utiliser la fonction SinCos() qui est deux fois plus rapide que l’appel à Sin() suivi de l’appel à Cso() :



Var X, Y, Angle, Hypot : Smallint

Sinus, Cosin : Extended;


for Angle := 0 to 360 do begin

SinCos(DegToRad(Angle) ,Sinus, Cosin);

X := Round( Cosin * Hypot );

Y := Round( Sinus * Hypot );

end;


Ratio de performance = 1.3 : 1


DegToRad() est très explicite et favorise la lisibilité du code, mais il est aussi très gourmand en temps de traitement. On le vire et on fait l’opération à la main :



for Angle := 0 to 360 do begin

SinCos(Angle*0.0174532925199433 ,Sinus, Cosin);

X := Round( Cosin * Hypot );

Y := Round( Sinus * Hypot );

end;

Ratio de performance = 1.5 : 1 (Par rapport au premier exemple)



Mise en cache des données :


La véloce fonction SinCos() n’est pas aussi rapide qu’un accès à un tableau. La technique de mise en cache des données consiste à enregistrer systématiquement les valeurs que vous utilisez souvent en mémoire. Ici, nous allons stocker les valeurs trigonométriques dans un tableau :


Var //var globales.

MonCos : array[0..360] of Extended; // Tableau en cache.

    MonSin : array[0..360] of Extended; // Tableau en cache.



procedure TForm1.FormCreate(Sender: TObject);

var Angle : Integer;

Sinus, Cosin : Extended;

begin

for Angle := 0 to 360 do begin

    SinCos(Angle*0.0174532925199433 ,Sinus, Cosin);

    MonSin[ Angle ] := Sinus; //Création des tableaux en cache.

    MonCos[ Angle ] := Cosin;

end;


...


for Angle := 0 to 360 do begin

X := Round( MonCos[ Angle ] * Hypot );

Y := Round( MonSin[ Angle ] * Hypot );

end;

Ratio de performance = 7 : 1 (Par rapport au premier exemple)



La CPU est optimisée pour le 32 bits :


Vous aviez sans doute remarqué qu’on travaillait avec des SmallInt dans la boucle de calcul. C’est justifié car largement suffisant pour calculer des coordonnées écran. Cependant, la CPU est beaucoup plus à l’aise en travaillant avec des Integers. La modification est minime :


Var X, Y, Angle, Hypot : Integer;

Sinus, Cosin : Extended;

Ratio de performance = 9 : 1 (Par rapport au premier exemple)



Précision superflue des fonctions système :


    Tiens, c’est vrai qu’on travaille sur l’écran !

Si une valeur trigonométrique en Extended (15aine de chiffres après la virgule) se justifie pour faire « alunir » un homme, ici avec mon écran de ‘m…’, je n’ai pas besoin d’une telle précision. On va diminuer la précision des tableaux de valeurs trigonométriques en cache.

    Et comme nous venons de voir que notre CPU aimait les 32 bits. Nous allons donc, par la même occasion, transformer les extendeds en Integers.

Dans la foulée, on supprimera le Round, fonction système très coûteuse aussi.



Var //var globales.

MonCosInt : array[0..360] of Integer; // Tableau en cache.

    MonSinInt : array[0..360] of Integer; // Tableau en cache.



procedure TForm1.FormCreate(Sender: TObject);

var Angle : Integer;

Sinus, Cosin : Extended;

begin

for Angle := 0 to 360 do begin

    SinCos(Angle*0.0174532925199433 ,Sinus, Cosin);

    MonSinInt[ Angle ] := Round( Sinus * 1000 );

    MonCosInt[ Angle ] := Round( Cosin * 1000 );

end;


...

for Angle := 0 to 360 do begin

X := MonCosInt[ Angle ] * Hypot div 1000;

Y := MonSinInt [ Angle ] * Hypot div 1000;

end;

Ratio de performance = 300 : 1 (Par rapport au premier exemple)


Parfois, avec cette technique, on aura une petite erreur d'une unité sur une coordonnée à cause de l’arrondi vers le bas de Div. Mais dans une animation sur l’écran cela passera inaperçu et de toute façon tout a un prix !



Rester vigilant à tout :


A votre avis, qu’est-ce qui ne colle pas dans le code suivant ?


Hypothenuse1 := Sqrt( Sqr(CoteA1) + Sqr(CoteB1) );

Hypothenuse2 := Sqrt( Sqr(CoteA2) + Sqr(CoteB2) );


if Hypothenuse1 < Hypothenuse2 then {Faire ceci};


C’est simplement que si Hypothenuse1 < Hypothenuse2, alors

CoteA1 + CoteB1 < CoteA2 + CoteB2

On peut s’épargner les coûteux Sqrt() et Sqr() avec simplement :


if CoteA1 + CoteB1 < CoteA2 + CoteB2 then {Faire ceci};

Ratio de performance = 10 : 1





« SCANLINE() TU UTILISERAS »


Si Canvas.Pixels[] peut être bien pratique pour connaître la couleur d’un pixel isolé, dans le cas où on travaille sur toute la surface d’un Bitmap, il faut utiliser Scanline[] qui est beaucoup plus performant. En effet, les goulets d’étranglement pour ce qui concerne les temps d’exécution se trouvent souvent dans les traitements de Bitmap.


    Je vous renvoie à l’excellente démonstration de Florenth qui semble avoir fait plusieurs fois le tour du problème ;)


http://www.delphifr.com/codes/BON-USAGE-SCANLINE_43222.aspx




« DES IDEES RECUES TU TE MEFIERAS »



Un exemple : le décalage :


On voit et on entend souvent (j’étais dans le lot) que les décalages binaires sont plus rapides pour remplacer une multiplication ou une division par 2, 4, 8, etc :


X := X shl 1

Y := Y shr 2


Pourtant les temps de calcul sont équivalents avec D7. Alors, préférez la lisibilité :


X := X * 2

Y := Y div 4

Ratio de performance = 1 : 1


J’ai demandé une explication à f0xi, pensant que le compilateur de Borland était vraiment excellent. Voici sa réponse :


« C'est surtout les CPU qui deviennent bons.

A l'époque des CPU monocore, faire un shr 1 ou shl 1 était plus rapide qu'une multiplication ou division. Maintenant avec les CPU dual, quad, octo-core, ça devient ridicule ce genre de petite optimisation.

[ ]

L'optimisation "locale" n'est plus vraiment une question à se poser. Aujourd'hui on parle plus d'optimisation "globale" de l'ensemble du programme et de ses éléments et également de plus travailler la méthodologie ; car si le compilo est capable d'optimiser certaines instructions, il n'est pas encore capable d'optimiser tout seul une routine. »





« L’OPTIMALISATION TU EVITERAS »


Optimisation, optimalisation, l’un des deux termes ne vous semble-t il pas démesuré ? ;)


Un logiciel est un fragile équilibre entre sa lisibilité, sa facilité de maintien, sa vitesse d’exécution, sa taille, sa justesse, etc.

Touchez à l’un de ces éléments et il est probable que l’équilibre sera rompu. Ce qui est sûr, c’est que l’optimisation de la vitesse rend souvent le code beaucoup moins lisible et moins facile à maintenir. Il faut donc toujours se demander ce qui est le plus important. Avec les machines modernes c’est finalement rarement la vitesse qui est un problème. De toute façon, ce sera à vous d’arbitrer ce dilemme.





Conclusion :



L’abus nuit en tout. Sachez utiliser ces techniques à bon escient. En cas d’optimisation de votre code, n’hésitez pas à commenter la technique que vous employez. En effet, si les techniques décrites ici étaient toujours ‘cleans’, elles seraient la norme en programmation, non ?


Il existe bien sûr des centaines d’autres techniques plus ou moins performantes qui vont du fait d’éviter les tableaux à plusieurs dimensions jusqu’à utiliser l’assembleur ( ainsi que l’a fait remarqué WHITEHIPPO, l’optimisation ultime restera quand même l'optimisation en assembleur, et je vous renvoie à ce site : http://www.fastcodeproject.org/ pour obtenir les meilleures routines ). Mais le but de ce tutoriel était de vous mettre sur de bons rails, non pas d’établir une liste exhaustive des techniques. J’espère être parvenu à vous donner quelques conseils généraux qui vous aideront.


Et quand vous serez parvenu à multiplier par 300 la vitesse d’exécution d’un code, vous serez content de ce que vous aurez fait à la seule force de nos petits neurones ! ^_^

C’est une des satisfactions qui font partie des joies de la programmation.






Je remercie ceux qui m’ont aidé ou inspiré ce tutoriel:


Steve McConnell (TOUT sur le CODE, en français chez Microsoft)

Olivier Dahan et Paul Toth (Delphi7 Studio chez Eyrolles

fOxi (Administrateur de CS et légèrement obsédé par les optimisations, graphiste oblige ;)

Florenth (Membre de CS, dont le dada est l’optimisation. On se demande d’ailleurs bien pourquoi ce n’est pas lui qui a écrit ce tuto ;)



Caribensila



Commentaires

Commentaire de f0xi le 21/10/2007 19:56:29 administrateur CS

MonCos : array[0..360] of Extended; // Tableau en cache.
MonSin : array[0..360] of Extended; // Tableau en cache.

pour ce genre de truc on peut faire :

type
  TSinCosElements = (mSin, mCos);
  TSinCosArray360s = array[TSinCosElements, 0..359] of single; // 2880 octets
                  // array[TSinCosElements, 0..359] of extended; // 7200 octets
                  // de plus le type extended n'est pas portable a 100%
                  // Single correspond a la norme IEEE 32bits float

const
  cDegToRad = 180/Pi; // l'utilisation de constante fait partis de l'optimisation


procedure PrecalcSCA(var SCA: TSinCosArray360);
// on appel ça au debut du programme et plus de soucis.
var n : integer;
    S,C: extended;
begin
  for N := 0 to 359 do
  begin
    SinCos(N * cDegToRad, S, C);
    SCA[mSin, N] := S;
    SCA[mCos, N] := C;
  end;
end;



for Angle := 0 to 359 do
begin
  Am := Angle mod 360; // empeche le debordement du tableau
  H1 := Hypot div 1000; // calculé une fois pour toute
  X  := MonCosInt[Am] * H1;
  Y  := MonSinInt[Am] * H1;
end;


avec les angles pour sin et cos : 0 = 360 et [0..179] = -[180..359]




Hypothenuse1 := Sqrt( Sqr(CoteA1) + Sqr(CoteB1) );
Hypothenuse2 := Sqrt( Sqr(CoteA2) + Sqr(CoteB2) );

n'est pas faux mais :

C = Sqrt((A*A) + (B*B)); // sera plus rapide si on veux calculer l'hypoténuse.

Commentaire de f0xi le 21/10/2007 21:20:09 administrateur CS



equivalent assembleur pour comprendre :


pour :

if (N >= 0) and (N < 5) then Categorie = 1;
if (N >= 5) and (N < 20) then Categorie = 2;
if (N >= 20) and (N < 101) then Categorie = 3;


equivaux a :

    @@cat1:
      test    eax, eax
      jl      @@cat2           // N >= 0 ?
      cmp     eax, +$05
      jnl     @@cat2           // N < 5 ?
      mov     edx, $00000001   // categorie = 1

    @@cat2:
      cmp     eax, +$05
      jl      @@cat3          // N >= 5 ?
      cmp     eax, +$14
      jnl     @@cat3          // N < 20 ?
      mov     edx, $00000002  // categorie = 2

    @@cat3:
      cmp     eax, +$14
      jl      @@fin           // N >= 20 ?
      cmp     eax, +$65
      jnl     @@fin           // N < 101 ?
      mov     edx, $00000003  // categorie = 3

    @@fin:
      mov     eax, edx
  end;

total : 16 instructions / 7 minimum / 10 moyenne / 14 maximum


pour :

if (N >= 0) and (N < 5) then Categorie = 1 else
if (N >= 5) and (N < 20) then Categorie = 2 else
if (N >= 20) and (N < 101) then Categorie = 3;

equivaux a :

    @@cat1:
      test    eax, eax
      jl      @@cat2         // N >= 0 ?
      cmp     eax, +$05
      jnl     @@cat2         // N < 5 ?
      mov     edx, $00000001 // categorie = 1
      jmp     @@fin          // fin

    @@cat2:
      cmp     eax, +$05
      jl      @@cat3         // N >= 5 ?
      cmp     eax, +$14
      jnl     @@cat3         // N < 20 ?
      mov     edx, $00000002 // categorie = 2
      jmp     @@fin          // fin

    @@cat3:
      cmp     eax, +$14
      jl      @@fin          // N >= 20 ?
      cmp     eax, +$65
      jnl     @@fin          // N < 101 ?
      mov     edx, $00000003 // categorie = 3

    @@fin:
      mov     eax, edx       // fin

total : 18 instructions / 7 minimum / 11 moyenne / 14 maximum / gain 0% et pourtant plus rapide!


pour :

  case N of
    0..4: categorie := 1;
    5..19: categorie := 2;
    20..101: categorie := 3;
  end;

equivaux a :

    sub     eax, +$05      // N = 0..4 ?
    jb      @@cat1         // oui > categorie = 1
    sub     eax, +$0F      // N = 5..19 ?
    jb      @@cat2         // oui > categorie = 2
    sub     eax, +$51      // N = 20..100 ?
    jb      @@cat3         // oui > categorie = 3
    jmp     @@fin          // fin
    @@cat1:
      mov     edx, $00000001 // categorie = 1
      jmp     @@fin          // fin

    @@cat2:
      mov     edx, $00000002 // categorie = 2
      jmp     @@fin          // fin

    @@cat3:
      mov     edx, $00000003 // categorie = 3

    @@fin :
      mov     eax, edx       // arret

total : 13 instructions / 5 minimum / 7 moyenne / 8 maximum / gain 40 a 50% !

Commentaire de Oniria le 22/10/2007 18:17:59

Bonjour,

Je trouve que ce tutoriel est excellent, il ouvre les yeux sur beaucoup de morceaux de code mal ficelé que l'on peux facilement faire.
Un grand bravo !

Juste un petit truc, sur

if (N>=0) and (N<5) then Categorie := 1

else if (N>=5) and (N<20) then Categorie := 2

else Categorie := 3;

On peut supprimer le (N>=5) car si N>=5, on passera à la deuxième ligne. On gagne encore une comparaison et le code devient

if (N>=0) and (N<5) then Categorie := 1

else if (N<20) then Categorie := 2

else Categorie := 3;

Oniria

Commentaire de f0xi le 22/10/2007 20:44:50 administrateur CS

c'est bien Oniria en effet car ça donne :


    @@cat2:
      cmp     eax, +$19
      jnl     @@cat3         // N < 20 ?
      mov     edx, $00000002 // categorie = 2
      jmp     @@fin          // fin

    @@cat3:
      cmp     eax, +$79
      jnl     @@fin          // N < 101 ?
      mov     edx, $00000003 // categorie = 3

soit 4 instructions en moins

bien vus.

Commentaire de Caribensila le 22/10/2007 22:27:22

J'avoue que je m'attendais à des tentatives d'optimisation de ce tuto    lol

Mais je ne suis vraiment pas déçu par la qualité de vos commentaires. On peut dire qu'ils sont parties intrinsèques du tuto et qu'ils en sont la postface.

Grand merci à vous.


PS: On se prend vite au jeu de l'optimisation, hein?  ;)

Commentaire de MAURICIO le 25/10/2007 15:46:59 administrateur CS

Salut Cari!
super, même moi j' ai appris quelques trucs:
- Vitesse integer Vs SmallInt même si l' integer bouffe plus de mémoire, surtout dans un array !!!
- le Case Of qu' on peut booster.

Juste un truc quand même, tu l' expliques très bien: quelques fois il est plus interessant de garder le source de façon compréhensible plutôt que de vouloir gagner 1/1000 de secondes, tout dépend de la nature des testes à faire dans les if etc ...

Exemple:
(not A) and (not B)
not (A or B)

Il est quand même plus simple de comprendre la 1ere   lol

Avec ça, nos utilisateurs n' auront plus le temps de cligner des yeux ou d' aller aux toilettes!

Impecc en tout cas,
A+

Commentaire de WhiteHippo le 25/10/2007 19:41:39

Hello

« OPTIMISATION TROP RAPIDE, TU EVITERAS » et « TUTORIAL TU CORRIGERAS » :P

[Citation]
Un autre exemple :
  if (N>=0) and (N<5) then Categorie := 1;
  if (N>=5) and (N<20) then Categorie := 2;
  if (N>=20) and (N<101) then Categorie := 3;
  
Et ce même code partiellement optimisé :
  if (N>=20) then Categorie := 3 //On sort dès que le résultat est connu.
  else if (N>=5) and (N<20) then Categorie := 2//On sort dès que le résultat est connu.
  else Categorie := 1;

[/Citation]

Attention !! si N=102 et plus, la catégorie sera égale à 3 alors qu'elle ne le devrait pas (selon la premiere portion de code). Idem pour les deux optimisations suivantes.

P.S. Merci à toi d'avoir pris le temps d'écrire ce tutorial Caribensila, dès a présent une référence en la matière.

Cordialement.

Commentaire de WhiteHippo le 25/10/2007 20:01:13

Ne pas oublier que pour les optimisations l'optimisation ultime reste quand même l'optimisation en assembleur.

N.B. On pourra notamment aller sur le site http://www.fastcode.dk/ pour obtenir les meilleures routines écrites en BASM.

Cordialement.

Commentaire de Jean_Jean le 26/10/2007 23:59:11

Salut Cari,

Je découvre ton tuto ce soir, sympa et instructif.
Je venais juste de faire une remarque sur un post à propos des types extended que l'on utilise trop souvent! Car il vaut mieux parfois plus de chiffres significatifs qu'un domaine étendu (exposant puissance).
Je suis allé sur http://www.fastcode.dk/ dont parle WhiteHippo. Je pense que c'est utile en cas de process très répétiif d'appel de fonctions. Par exemple, j'ai comparé les fonctions proposés pour l'ArcCos, on gagne un ns seconde par rapport à celle de l'unité Maths. Je crois donc surtout que l'optimisation vient surtout des bonnes pratiques de programmation.
Ce tuto va bien dans ce sens.
Merci
Jean_Jean  

Commentaire de Albedo039 le 29/10/2007 08:28:01

Salut,

Pour:
  if (N>=0) and (N<5) then Categorie := 1;
  if (N>=5) and (N<20) then Categorie := 2;
  if (N>=20) and (N<101) then Categorie := 3;

On peut par ex.:
Categorie := Ord(n<5) + 2*Ord(n<20) + 3*Ord(n<101);

Je n'ai pas testé "en live"... mais qd même: ça évite tous les sauts conditionnels :)

Si on utilise un array en plus, possibilité de simplification (toujours pas testée en live):
Multiplic: array [False..True, 1..3] of integer;
Multiplic[False,1]:=0;
Multiplic[False,2]:=0;
Multiplic[False,3]:=0;
Multiplic[True,1]:=1;
Multiplic[True,2]:=2;
Multiplic[True,3]:=3;
Categorie := Multiplic[n<5,1] + Multiplic[n<20,2] + Multiplic[n<101,3]; (* !!! *)

Bon: voila que je n'ai plus que 3 additions maintenant :)

Commentaire de Albedo039 le 29/10/2007 08:43:57

** Attention - Sorry **

Categorie := Ord(n<5) + 2*Ord(n<20) + 3*Ord(n<101);

est bien sûr faux !!!!  (:smiley-honteux:)


Je voulais bien sûr dire:
   Categorie := Ord(n>=0) + Ord(n>=5) + Ord(n>=20);

ou encore
   Categorie := 3*Ord(n<101) - Ord(n<20) - Ord(n<5) ;
  

Note 1:
l'array peut rester une technique interessante pour des test plus complexes.
C'est juste une question de "reconnaissance d'algo".

Note2:
voila ce que c'est que de poster un commentaire d'un café internet ;)
Dommage qu'on ne puisse pas éditer ses propres "bévues" .

Commentaire de Caribensila le 29/10/2007 13:35:06

@ ALBEDO039 :
Bien vu !
"Categorie := Ord(n>=0) + Ord(n>=5) + Ord(n>=20);"
fait passer le ration de performance de 2:1 avec le Case Of à 2.4:1 !!!
Mais la lisibilité devient... fumeuse, comme dirait MAURICIO  lol

Par contre :
Categorie := 3*Ord(n<101) - Ord(n<20) - Ord(n<5) ;
est sérieusement handicapé par la multiplication  ;)
Et ton array n'apporte rien dans ce cas. Il reste en effet toujours 3 additions.

J'ai essayé :
for i := 0 to 4    do TabCat[i] := 1;
for i := 5 to 19   do TabCat[i] := 2;
for i := 20 to 100 do TabCat[i] := 3;
et
Categorie := TabCat[N];
Mais on reste à 2.4:1
L'accés au tableau est donc trop pénalisant ici.


@ WHITEHIPPO
J'ai corrigé mon tuto avec :
N := Random(101);    :p   ;)

A ce propos, je ferai une MAJ avec toutes vos bonnes idées plus tard. Pour le moment un bug rend les MAJ impossibles sauf à demander à un administrateur (merci PCPT). Et dans ce cas l'Historique n'est pas complété.

Commentaire de khawarizm le 29/10/2007 19:53:25

Merci cari ; ce n'est qu'aujourd'hui que je vois ton tuto et je le trouve super bien et très intéressant, ça fait un moment que je voulais  te poser quelques question pour pouvoir améliorer un de mes code et voilà que tu  nous (nous= moi et les débutants comme moi) surpris avec ce cadeau qui , effectivement , nous montre les bonnes pratiques , merci encor et STP ne soit pas « radin «  de ton savoir faire et initier nous aux joies de la programmation.

Commentaire de Albedo039 le 30/10/2007 10:26:27

Encore une p'tite chose à propos de:

---------------------------------------------
--- for i := 1 to 10 do MaTable[ i ] :=0; ---
---------------------------------------------

Au lieu de faire du "ligne à ligne" comme proposé... on peut entre autre:
1) utiliser "Fillchar(...);"
2) utiliser "ZeroMemory( MaTable, SizeOf(MaTable) );"

à prendre en compte aussi dans l'exemple "Déroulez les boucles".

---------------------------------------------
--- (not A) and (not B) vs. not (A or B) ----
---------------------------------------------
# si le test est répété plusieurs fois
# si "(not A)" est souvent = false
# et si l'évaluation des expr. bool n'est pas configurée "évaluation complète"
alors il vaut mieux la première version

---------------------------------------------
--- if (N<5) then Categorie := 1          ---
--- else if N<20 then Categorie := 2      ---
--- else Categorie := 3;                  ---
---------------------------------------------
As-tu essayé cette possibilité? :

ND := N Div 5;
Categorie:=1;
If ND>3 then Categorie:=3 else if ND>0 then Categorie:=2;

(*
ND = 0 si N in [0..4]
ND = 1, 2 ou 3 si N in [5..19]
ND = 4 ou plus si N in [20....]
*)

Commentaire de Kenavo le 30/10/2007 22:22:36

Mon Dieu ! Quelle fôte !
----------------------------------------------------------------
A votre avis, qu'est-ce qui ne colle pas dans le code suivant ?


Hypothenuse1 := Sqrt( Sqr(CoteA1) + Sqr(CoteB1) );

Hypothenuse2 := Sqrt( Sqr(CoteA2) + Sqr(CoteB2) );


if Hypothenuse1 < Hypothenuse2 then {Faire ceci};


C'est simplement que si Hypothenuse1 < Hypothenuse2, alors

CoteA1 + CoteB1 < CoteA2 + CoteB2

On peut s'épargner les coûteux Sqrt() et Sqr() avec simplement :


if CoteA1 + CoteB1 < CoteA2 + CoteB2 then {Faire ceci};
--------------------------------------------------------------

Ben non ! Par exemple :

si
  CoteA1 = 1
  CoteB1 = 10

  CoteA2 = 7
  CoteB2 = 7

alors
  (Hypothenuse1)² = 101
  (Hypothenuse2)² = 98
  donc  :Hypothenuse1 > Hypothenuse2

alors que
  CoteA1 + CoteB1 = 11
  CoteA2 + CoteB2 = 14
  donc : (CoteA1 + CoteB1) < (CoteA2 + CoteB2)

Si on peut s'épargner le Sqrt, le Sqr reste bien utile !
Sinon, pour le rotations, le Scanline, etc ... je souscris totalement ...

Bien à toi, Cari

Kenavo

  

Commentaire de florenth le 02/11/2007 18:53:05

Je viens de découvrir ce tuto, et c'est une vraie anthologie a garder sous le coude.

Mais justement, faut faire gaffe quand on optimise :
- D'une part on perd en lisibilité
- On rend le code moins maintenable
- Souvent, ça améliore pas grand chose niveau vitesse (genre 25 millisecondes au lieu de 30), [sauf si c'est dans une boucle bien sûr^^]

- Et d'autre part, on n'est pas à l'abri de conn***** comme celle que te signale Kenavo !

Pour compléter le tuto, je rajouterai que la dérécursification des procédures récursives aide grandement à gagner de la vitesse.
Exemple: la fonction de Fibonacci:

function Fibo(X: Integer): Integer;
begin
  if (X = 0) or (X = 1) then
    Result := 1
  else
    Result := Fibo(X - 1) + Fibo(X - 2);
end;

Elle s'optimise largement ainsi :

function Fibo2(X: Integer): Integer;
var
  I: Integer;
  Pred, PredPred: Integer;
begin
  Pred := 1;
  PredPred := 1;
  for I := 2 to X do
  begin
    Result := Pred + PredPred;
    PredPred := Pred;
    Pred := Result;
  end;
end;

Cette dernière fonction est infiniment plus rapide que l'autre. (pour calculer le 40 ème terme, il lui faut 0ms contre 400ms pour la version récursive)

Enfin:
-------------------------
fOxi (Administrateur de CS et légèrement obsédé par les optimisations, graphiste oblige ;)
Florenth (Membre de CS, dont le dada est l'optimisation. On se demande d'ailleurs bien pourquoi ce n'est pas lui qui a écrit ce tuto ;)
--------------------------

Ce n'est pas moi le fada de l'opti mais f0xi !!!
Et si je n'ai pas écrit ce tuto, c'est justement car tu as été plus rapide que moi, t'as un cerveau plus optimisé... :-)

Commentaire de Caribensila le 03/11/2007 15:34:03

@Flo

« Et d'autre part, on n'est pas à l'abri de conn***** comme celle que te signale Kenavo !»

Wouah! J'sens que ça va me poursuivre pendant 107 ans, le truc du 1² ! (surtout si Francky tombe la-dessus, j'ai pas fini de l'entendre).
De toute façon, je m'autopardonne passe que j'ai pas allé à l'école quand j'étais petit et que je suis autodidacte (surtout auto, d'ailleurs).
J'avais déjà remarqué l'avatar de Kenavo: 1 oeil scrutateur, l'autre inquisiteur...
Et je dirai même plus! Un mec qui se balade avec 1,7182818285¤ en poche, on voit tout de suite que c'est un adepte du coupagedecheveuxenquatrisme ( http://www.delphifr.com/codes/REELS-REALITE_44550.aspx ). Mais, bon... Je corrigerai quand même ma bourde à la prochaine màj.


« je rajouterai que la dérécursification des procédures récursives aide grandement à gagner de la vitesse »

Et moi qui me suis souvent cassé la nenette à récursifier des fonctions en espérant optimiser!!!
Mais, tout bien réfléchi, c'est logique...
"Pour comprendre le principe de récursivité, il faut d'abord comprendre le principe de récursivité". C'est sûr... Mais on peut quand même se demander à quoi ça sert et pourquoi on enseigne la récursivité aux étudiants ingénieurs de 1ère année...


« Ce n'est pas moi le fada de l'opti mais f0xi !!! »

Vous êtes deux fadas, oui!
Yaka regarder le forum:
http://www.delphifr.com/infomsg_COMMENT-CALCULER-RATIO-PERFORMANCE-SELON-TUTORIAL-CARI_1029837.aspx
'manque plus que Albedo039 pour compléter l'équipe...


« t'as un cerveau plus optimisé... :-) »

Récursifiez-vous au début de ce post pour bien comprendre que ce n'est pas exact.


Bon, je n'ai plus qu'à me taper une sacrée MAJ, moi!


PS: Merci à Flo de nous consacrer un peu de ton temps en cette année de Bac.  :)))

Commentaire de florenth le 03/11/2007 20:41:06

Mais non, ça arrive à tout le monde de se tromper...
Et figure toi que si je prends la peine de le mentionner, ce n'est pas pour t'enfoncer, bien au contraire, mais parce que j'ai récemment fait une boulette semblable.
Eh ben je t'assure que j'en ai mis du temps à comprendre, surtout qu'un code optimisé est souvent un peu moins lisible que son homologue lent.

Et ouais, Kenavo, c'est un inquisiteur. Pas rien qu'avec ses 1.7182 ¤ en poche, mais avec tous ces trucs de webcam à manivelle et bidules de résistances... brrr, je me demande à quels genre de torture on a le droit ! ^^

Sinon, ben je dois me résoudre à avouer que je suis (un peu) fada d'optimisation. Mais c'est vraiment parce que tu me récursifie l'affaire !!!
Mais tu comprends, une des clés de la bonne réussite au bac, c'est la rapidité d'esprit. Et qui dit rapidité, dit optimisation... tiens, voila une bonne excuse pour coder au lieu de philosopher...
Nan, je peux pas, (je fais croire que) je suis un élève sérieux !! ;-)

Allez, en bonus, tu as le droit à un test de performance entre entiers, flottants, et tout le toutim:
http://www.delphifr.com/codes/MESURER-PERFORMANCES-VOS-FONCTIONS-ANAYLSE-DIFFERENTES-OPERATIONS-SUR_44599.aspx

Commentaire de Delphiprog le 06/11/2007 22:43:18 administrateur CS

Que dire ?
Chapeau bas Monsieur Cari pour t'être lancé dans cette aventure et avoir placé la barre très haut pour les suivants.
Dans les idées reçues en matière d'optimisation, je n'ai pas vu le réflexe stupide qui consiste à déclarer des identifiants les plus courts possibles. C'est vrai qu'avec un langage compilé, cela n'a pas d'importance. Ce qui m'amène à faire remarquer que l'essentiel des bonnes pratiques ci-dessus s'applique avant tout aux langages compilés.
Alors, à quand un didacticiel consacré aux langages interprétés ?

J'aurais aussi aimé voir un paragraphe sur la manière de transmettre des arguments aux fonctions et procédures. Combien de fois on voit des codes sources avec passage par copie au lieu de transmettre par adresse. Cela fait aussi partie des bons réflexes à adopter.

Enfin, bref, un excellent tuto à garder préciseusement et à relire tous les matins.
Excellent travail.

Commentaire de Kenavo le 08/11/2007 21:09:20

"Pour comprendre le principe de récursivité, il faut d'abord comprendre le principe de récursivité". C'est sûr... Mais on peut quand même se demander à quoi ça sert et pourquoi on enseigne la récursivité aux étudiants ingénieurs de 1ère année...

On l'enseigne parce que c'est une base de la réflexion mathématique, comme le calcul matriciel, mais ça reste de la théorie qui parfois peut s'appliquer (voir la procedure de tri QuickSort). Par contre, pour l'efficacité du code, on est souvent obligé de faire appel à des méthodes numériques plus efficaces ...
Par exemple, pour calculer sur les matrices (determinant, réduction ...) on apprend les méthodes théhoriques qui sont récursives : pour une matrice carrée d'ordre 3, calcul de 3 matrices d'ordre 2 ; pour matrice d'ordre 4 : 4 matrices d'ordre 3 ; etc ... Ce qui pour une matrice carrée d'ordre n donne en gros n! (factorielle n) calculs sur des matrices d'ordre 2. Infaisable dès qu'on manipule des matrices assez grandes. On utilise donc des méthodes moins "élégantes" mais plus rapides, sans lesquelles des applications comme le scanner ou l'IRM par exemple, n'auraient pas été possibles.  
Mais toutes ces méthodes numériques, pour qu'elles soient bien appliquées, ne doivent pas empêcher de connaître la théorie.

Et comme dirait Jappe Volfoni : "Moi quand on m'en fait trop j'correctionne plus, j'dynamite... j'disperse... j'ventile... je dérécurcifie..."  

Et qu'est ce qu'ils ont mes yeux ?

Commentaire de DeltaFX le 04/12/2007 15:29:51

Yo,

A propos de ce bout de code là :

if (N>=0) and (N<5) then Categorie := 1

else if (N<20) then Categorie := 2

else Categorie := 3;

Est- ce qu'on gagne en l'écrivant comme ça :

...
Categorie := 3;
if (N>=0) and (N<5) then Categorie := 1
else if (N<20) then Categorie := 2;
...

ça fait un branchement de moins, non ?

Commentaire de BruNews le 01/02/2008 14:15:42 administrateur CS

DES IDEES RECUES TU TE MEFIERAS
décalage au lieu de division, ABSOLUMENT et TOUJOURS.
Que vous obteniez des temps identiques en écrivant l'un ou l'autre n'est du qu'au fait que vous avez un compilo correct qui fait le remplacement de la div par le shift.

Ce n'est bien entendu pas en comparant les perfs d'un listing de code avec un autre qu'on peut s'en apercevoir mais en ASM, là précisément où le compilo ne peut rien changer.
Voici listing de test, fait sur VC++ mais on s'en fout, c'est pur asm.

http://brunews.com/Decal.zip

__declspec(naked) char* __fastcall bnultoa(unsigned int dwnum, char* szdst)
{ // ECX = dwnum, EDX = szdst
  __asm {
    test     ecx, ecx
    jnz      short L1
    lea      eax, [edx+1]
    mov      byte ptr[edx], 48
    mov      byte ptr[eax], cl
    ret      0
L1:
    mov      [esp-4], edi
    mov      [esp-8], edx
    mov      edi, edx
L2:
    mov      eax, -858993459
    mul      ecx
    mov      eax, edx
    shr      eax, 3
    mov      edx, ecx
    lea      ecx, [eax+eax*8]
    add      ecx, eax
    sub      edx, ecx
    add      dl, 48
    mov      [edi], dl
    mov      ecx, eax
    add      edi, 1
    test     eax, eax
    jnz      short L2
    mov      byte ptr[edi], al
    mov      [esp-12], edi
    mov      eax, [esp-8]
L3:
    sub      edi, 1
    mov      dl, [eax]
    mov      cl, [edi]
    mov      [edi], dl
    mov      [eax], cl
    add      eax, 1
    cmp      eax, edi
    jb       short L3
    mov      eax, [esp-12]
    mov      edi, [esp-4]
    ret      0
  }
}

#pragma comment(linker, "/entry:myWinMain")
__declspec(naked) void __stdcall myWinMain()
{
  __asm {
    lea     edi, [esp-32]
    mov     ebx, 1000
    or      eax, -1
    mov     ecx, 2
    mov     esp, edi
    rdtsc
    mov     esi, eax
    xor     edx, edx
goDIV:
    div     ecx
    sub     ebx, 1
    jne     short goDIV
    rdtsc
    sub     eax, esi
    mov     edx, edi
    mov     ecx, eax
    call    bnultoa
    
    or      eax, -1
    mov     ebx, 1000
    rdtsc
    mov     esi, eax
goSHIFT:
    shr     eax, 1
    sub     ebx, 1
    jne     short goSHIFT
    rdtsc
    sub     eax, esi
    lea     edx, [edi+16]
    mov     ecx, eax
    call    bnultoa
    
    lea     edx, [edi+16]
    push    0
    push    edx
    push    edi
    push    0
    call    dword ptr MessageBox
    add     esp, 32
    push    0
    call    dword ptr ExitProcess
  }
}

RESULTAS DE 5 TESTS CONSECUTIFS:
shr 1 div 2
2967 79220
1811 76007
1811 76007
1819 76007
1810 76016
c'est sans appel, la division reste 40 fois plus lente que le shift, comme d'ailleurs on peut le voir dans les manuels Intel.
Fait sur BIXEON 64, 3.4 Ghz (pas une antiquité).

Plaisanterie ON:
Pour f0xi, lui dire que c'est heureux que les CPUs deviennent bons mais qu'il ferait bien de les suivre.
Bah finalement je lui dirai moi même ce soir sur msn.
Plaisanterie OFF.

Commentaire de matrix1 le 03/02/2008 10:14:47

Avec le metriel qui est disponible! tous ça reste quelque pédagogique.

Commentaire de matrix1 le 03/02/2008 10:17:02

tous ça est pédagogiques*

Commentaire de BruNews le 04/02/2008 13:52:37 administrateur CS

Matrix1 > Il ne faut surtout pas raisonner ainsi sinon tu es mûr pour .NET, JAVA ou tout autre interprété profond. Serait inutile de rester sur un langage permettant au développeur de s'exprimer si c'est pour envoyer l'utilisateur prendre un café à la moindre routine lancée.
Rassurons de suite tout le monde: du taf exigeant des perfs ça existe. Certes c'est du travail de niche mais pas pour demain de le voir disparaitre. Bien plus que pédagogique, ce genre de tuto est une piqûre de rappel aux bonnes méthodes qu'il serait bon d'avoir plus souvent sous les yeux.
Kenavo >
QuickSort est le type même du tri qu'on met en oeuvre quand on veut des perfs et c'est donc justement le cas où la récursivité est à exclure.
EX: http://brunews.com/SortInt.zip
; QueryPerformanceCounter(&liDeb);
; QuickSort(pmem, nelems);
; QueryPerformanceCounter(&liFin);
Regarde en combien ça se fait sans récursivité. Noter que j'ai bloqué l'affichage, inutile ici.

Commentaire de Caribensila le 04/02/2008 15:16:08

« QuickSort est le type même du tri qu'on met en oeuvre quand on veut des perfs et c'est donc justement le cas où la récursivité est à exclure. »

C'est justement en tentant de débrouiller en dilettante le challenge de Brunews :
http://www.delphifr.com/infomsg_COMPARATIFS-PERFS_1061255.aspx?p=4
que je me suis un peu intéressé aux méthodes de tri rapides. Et j'ai été étonné de trouver de nombreux exemples de QuickSort sur Internet utilisant la récursivité. C'est vrai que ça semble un peu absurde vu le but recherché. Mais reconnaissons que la récursivité apporte une touche d'élégance et de professionnalisme à la plus rébarbative des routines.

Tout cela pour dire que c'est Florenth qui nous signalait ici la mollesse de la récursivité et qu'il a gagné le droit de retoucher son excellente source :
http://files.codes-sources.com/fichier.aspx?id=34509&f=Sorts.pas

Et espérons que Kenavo a évité le piège du QuickSort récursif dans sa participation aux COMPARATIFS-PERFS.   lolll

Commentaire de Kenavo le 08/02/2008 18:48:58

"Et espérons que Kenavo a évité le piège du QuickSort récursif dans sa participation aux COMPARATIFS-PERFS. "
Ben non ! En plein dedans !

J'ai jeté un oeuil sur les sources de Brunews, mais là, c'est moi qui ne suis point optimisé pour lire le C !!! J'ai toujours la fâcheuse impression d'avoir une police exotique où manquent toutes les lettres de l'alphabet !
C'est étonnant, ce besoin de ne pas faire de phrases !

Commentaire de Caribensila le 08/02/2008 19:59:20

'faut dire qu'y'a le C... Et y'a le C par Brunews.
Et c'est tomber de Charybde en Scylla pour un delphiste. lolll

QuickSort sans récursivité, très bien commenté et expliqué:

http://delphi.developpez.com/sources/?page=sec_math_algo#JFS_Quicksort

Commentaire de BruNews le 08/02/2008 23:37:08 administrateur CS

Mon clavier est aussi usé que moi, pour ça que je ne fais que C ou ASM, peu de touches suffisent.

Les alertes mail sur les tutos sont encore en rade mais j'ai mis dans la todo list de Nix, espérons.

Commentaire de matrix1 le 26/02/2008 16:57:40

BruNews> tu ma pas compris,premièrement c un trés bon tuto et j'admire ce que à fait Caribensila c quasiment de bon travail qui mérite bc d'applaudissement car c vrais, c choses se font rare de nos jours, mais ce que je voulais dire c que le pratique est tj <> de la théorie, tu c tous ça mais qu'on ton travail sur un projet OOP comme le delphi par exemple, tu ne peut pas faire ça
MaTable[1]  := 0;
MaTable[2] := 0;
MaTable[3] := 0;
MaTable[4] := 0;
MaTable[5] := 0;
MaTable[6] := 0;
MaTable[7] := 0;
MaTable[8] := 0;
MaTable[9] := 0;
MaTable[10] := 0; + si j'essayerais d'apprendre le java en + alors ta pas le temps surtout si tu sais bien que l'utilisateur de ton logiciel a des pc un peut performants, tu ton fiche carrément .
et personnellement j'aime pas le .net ni le java je préfère le TCL/TK ou le python que ces ramasseurs de miettes :D

Commentaire de Caribensila le 29/02/2008 23:41:37

@MATRIX
Je n'ai jamais dit qu'il fallait dérouler les boucles!
Ce n'était qu'un exemple pour illustrer qu'un code court n'est pas toujours le plus performant en vitesse d'exécution.

Et si tu ne prends pas le temps de faire des applications performantes en temps d'exécution, ce seront les utilisateurs qui perdront leur temps à attendre le résultat d'un traitement un peu gourmand. C'est un choix. Mais un programme rapide n'a jamais été flashé pour excès de vitesse, et la route est encore longue.  ;)

Commentaire de acanicio le 28/03/2008 10:07:43

Salut,

Tout d'abord felicitations à Caribensila pour ce tutoriel très efficace !

Ayant connu delphi depuis sa premiere version, j'ai une petite amélioration à proposer à ton exemple de calculs d'angles avec les entiers, elle est aussi très "subtile".
Je suis d'accord avec toi dans le fait que le SHR et le DIV ont à peu près la même vitesse d'execution. Mais le top c'est d'essayer de toujours penser en "binaire", et pas en "décimal".

Ton exemple :

<<
for Angle := 0 to 360 do begin

    SinCos(Angle*0.0174532925199433 ,Sinus, Cosin);

    MonSinInt[ Angle ] := Round( Sinus * 1000 );

    MonCosInt[ Angle ] := Round( Cosin * 1000 );

end;




...

for Angle := 0 to 360 do begin

X := MonCosInt[ Angle ] * Hypot div 1000;

Y := MonSinInt [ Angle ] * Hypot div 1000;

end;

Ratio de performance = 300 : 1 (Par rapport au premier exemple)
>>
Peut être très fortement amélioré en remplaçant le diviseur 1000 par 1024 ou toute autre valeur multiple de 2.

On obtient donc :
<<
for Angle := 0 to 360 do begin

    SinCos(Angle*0.0174532925199433 ,Sinus, Cosin);

    MonSinInt[ Angle ] := Round( Sinus * 1024 );

    MonCosInt[ Angle ] := Round( Cosin * 1024 );

end;




...

for Angle := 0 to 360 do begin

X := MonCosInt[ Angle ] * Hypot div 1024 ;

Y := MonSinInt [ Angle ] * Hypot div 1024 ;

end;
>>

Ratio de performance = 4,5 : 1 (Par rapport à ton propre exemple) Donc 1350:1 par rapport au premier...

Si on travaille avec des entiers, et qu'on a besoin de précision, on se fout que cette précision soit en puissance 10 ou en puissance 2... Et ça fait une sacrée différence !!!

;-)

Cordialement
Axel

Commentaire de Caribensila le 28/03/2008 14:15:48

Whaooo!
Méthode "subtile, harmonieuse et efficace"!
Grand merci.

Commentaire de acanicio le 28/03/2008 14:58:36

Merci !

Mais je viens de refaire un truc, en remplaçant le DIV 1024 par un SHR 10, et je gagne encore un facteur de 1.3 par rapport à mon résultat précédent.
Comme on travaille réellement sur un multiple de 2...

Mais je bosse avec Delphi 5... peut être que sous D7 c'est différent.

Axel

Commentaire de BruNews le 30/03/2008 15:55:37 administrateur CS

RECTIF:
un shift remplace un DIV ou MUL en cas de PUISSANCE de 2 et non multiple de 2.

Commentaire de acanicio le 31/03/2008 12:01:10

T'as raison, merci, je me suis trompé dans le terme, il s'agit bien d'une puissance de 2.

Axel

Commentaire de JulioDelphi le 10/05/2008 18:45:07 administrateur CS

Salut cari,
on peut aussi parler de ce genre d'optimisation :

J'ajoute dans une ListBox 100000 chiffres en random 100, je vais faire un bête :
"
var i: integer; begin for i:=0 to 99999 do listbox1.Items.Add(IntToStr(Random(100))); end;
"
et je vais mettre (chez moi) entre 7 et 8 secondes.
mais si je fais plutot ça :
"
var
i: integer;
temp : TStringlist;
begin
temp := tstringlist.Create;
for i:=0 to 99999 do
  temp.Add(IntToStr(Random(100)));
listbox1.Items.Text := temp.Text;
temp.free;
end;
"
je le fais en moins d'une seconde. Si c'est pas de l'optimisation, je sais pas ce que c'est.
Il faudrait voir au niveau de la mémoire mangée, je pense que ma méthode en mange un peu plus, mais j'ai pas que 2mo de ram chez moi donc je pense que ça passe un peu partout ;)

moralité : n'affichez pas ce ue vous insérez dans une liste visuelle si vous etes dans une boucle, gardez le tout en mémoire et insérez ensuite le tout dans la liste d'un coup.

Commentaire de f0xi le 15/05/2008 19:53:42 administrateur CS

@Julio

ListBox1.Items.BeginUpdate;
try
  for i:=0 to 99999 do
    listbox1.Items.Add(IntToStr(Random(100)));
finally
  ListBox1.Items.EndUpdate;
end;

parfois on doit preserver la memoire.

sinon autant faire :

var S : string;
...
for i := 0 to 99999 do
  S := S + IntToStr(Random(100))+#13#10;
ListBox1.Items.Text := S;

ce qui ne sera pas forcement plus rapide.


Commentaire de saidbelhaj le 23/05/2008 12:46:16

Bravo!

Commentaire de Scaythe le 14/11/2008 09:06:18

Merci pr ce tuto ( oui, un merci de plus)

N := Random(101);
if (N<5) then Categorie := 1
else if N<20 then Categorie := 2
else Categorie := 3;

Je vais essayer une petite optimisation alors :

Categorie :=3;
if ( N<20)
   then
{  Categorie :=2
    if ( N <5 ) Categorie :=1
}

Mon raisonnement : je n'optimise pas pour 1 traitement mais pour n traitements
Théoriquement 80% de nos traitements ne devraient jamais rentrer dans le premier if
J'appellerais cela " L' Optimisation par le context" :D ... un genre d'optimisation a ne pas oublier ??



Commentaire de kelloucheaeh le 24/03/2009 14:09:05

que dire de plus, tout ces conseils et gratuitement grand merci à toi maître.

une chose, si seulement tout les bons commentaires trouvés leur place dans la version 2 du tuto alors se serait génial.

Commentaire de cleecky le 21/06/2010 16:56:10

Merci ;)

Commentaire de Bacterius le 30/12/2010 09:41:22

Salut Caribensila,
je vois que tu as manqué le commentaire de Kenavo ... dans :

"Rester vigilant à tout"

Posons :

Hypothenuse1 := Sqrt( Sqr(CoteA1) + Sqr(CoteB1) );
Hypothenuse2 := Sqrt( Sqr(CoteA2) + Sqr(CoteB2) );

Si Hypothenuse1 < Hypothenuse2 alors on est d'accord que Sqr(CoteA1) + Sqr(CoteB1) < Sqr(CoteA2) + Sqr(CoteB2), cependant cela n'implique absolument pas que CoteA1 + CoteB1 < CoteA2 + CoteB2 :)

On économise quand même une double racine carrée !

Cordialement, Bacterius !

Commentaire de Caribensila le 30/12/2010 15:22:20

Bonjour Bacterius,

Je vois que tu es vigilant !  ;)
Mais, dans ce cas précis où on travaille sur des nombres positifs, ça se tient non ?

Commentaire de Bacterius le 31/12/2010 00:39:11

Non, car prends un exemple simple.

CoteA1 = 3
CoteB1 = 6
CoteA2 = 1
CoteB2 = 7

On a Sqr(CoteA1) + Sqr(CoteB1) < Sqr(CoteA2) + Sqr(CoteB2) qui devient 9 + 36 < 1 + 49, ce qui est vrai, mais ça ne veut pas dire que 3 + 6 < 1 + 7, car ceci devient 9 < 8 ...

Cordialement, Bacterius !

Commentaire de Caribensila le 31/12/2010 01:40:22

Je te remercie pour ce commentaire pertinent qui prouve le besoin urgent (et pas d'hier!) d'un sérieux recyclage de ma part.

Je remercie aussi Kenavo pour son amicale délicatesse ( ou découragement? :))).

Commentaire de barbichette le 20/06/2011 18:03:15

aller, on déterre les vieux tutos... (déjà 4 ans...)
Juste pour rajouter mon grain de sel, et pour ceux qui veulent aller plus loin dans l'optimisation, je dirai qu'une bonne optimisation, c'est une optimisation faite par quelqu'un qui connait bien le language assembleur (proche du language machine) et qui connait surtout la façon donc le compilateur transforme notre oeuvre d'art (notre cher petit programme) en un méli-mélo d'octets.

J'ai souvent remarqué que pour optimiser un code, je devais passer des heures à regarder comment delphi compilait ma source. En général, pour faire mieux, avec un morceau de code gourmant, il suffit de prendre le code assembleur généré par delphi, le mettre directement dans une balise asm...end et en optimiser le code assembleur. Delphi (et les autres languages) sont très performants pour changer des DIV par des SHR si besoin, mais ils n'ont pas toujour une vue globale de la chose. Et Delphi (contrairement au C) ne connais pas bien les variables locals (propre à un seul begin...end).
Pour ma source avec des feux d'artifices, j'ai presque un gain de 2 en passant du pascal à l'assembleur parce que je suis apte, en tant qu'être humain de modifier des passages alrs même que Delphi fera de assembleur "moche" du fait de l'éloignement des languages évolués par rapport au language machine.

Bon, maintenant, on voit de plus en plus d'appli java ou autres, où il faut, avec un 4 coeurs, attendre 4 secondes pour que le menu fichier s'ouvre.., Alors qu'avec mon vieux 286, le menu fichier de la commande Edit s'affichait instantanément. C'est ça le progret...

Barbichette

Commentaire de Denis007 le 25/01/2012 18:09:14

Ce qu'il dit n'est pas complétement inutile mais prenons le premier exemple :

Si vous voulez initialiser un grand tableau avec des indice supérieur à 100 vous

n'allez pas les écrire à la main et la comparaison d'un nombre infime de boucle ne

sert à rien non plus du fait du temps ridiculement faible du temps d'utilisation. C'DB

Commentaire de Denis007 le 25/01/2012 18:13:16

Ce que tu viens de dire pour remplacer Div Par SHR ne sert à rien non plus

car le  compilateur l'interprète déjà comme cela.

C'DB.

Commentaire de Denis007 le 25/01/2012 18:26:32

Autre exemple les optimisations ditent (not A) and (not B) not (A or B) que j'ai

appris en première de techniologie serait optimisable de cette façon si l'on ne tenais

pas compte de l'optimlisation du compilateur et de l'évaluation complète des fonctions

qui parfois ont besoin d'être interprétée. C'DB

Commentaire de Denis007 le 25/01/2012 19:04:13

Ne jammais remplacer DIV par SHR celui-ci rend le code moins compréhensible

et la fonction est la même une fois compilé avec Delphi.

Denis Bertin www.denisdraw.com

Commentaire de Denis007 le 25/01/2012 19:43:42

{Delphi ne connais pas bien les variables locals (propre à un seul begin...end). }

Si tu avais comprix tu isolerais dans des méthodes plus petite tes algorythmes pour

voir que cela reviens au même.  C'dB

Si tu avais de la mémoire tu pourrais aussi en faire abstraction.

Commentaire de Denis007 le 26/01/2012 16:27:47

Et puis aussi on s'était bien demander pourquoi les cercles était
aussi mal dessiné avec des tableaux dont la précision ne dépasser pas le degré.

Commentaire de Bacterius le 26/01/2012 16:45:00

Denis007, tu fais quelques bons points dans tes messages précédents mais la tu deviens franchement aggressif et lourd. Le manque de lissage des cercles dans l'image n'a absolument rien a voir avec la précision des tableaux trigonométriques précalculés. Si tu avais lu, l'image est extraite d'un livre, donc tu es vraiment hors sujet. Ce qui me laisse penser que tu as une dent contre quelqu'un, ou quelque chose. Peut-etre pourrais-tu essayer la critique constructive?

Commentaire de Denis007 le 26/01/2012 17:37:22

Je m'explique : Ces calculs optimisés était nécessaires sur des ordinateurs peut véloce
et d'ailleurs je les ai aussi utilisé dans mon logiciel cependant vous pouvez vous rendre compte
que ceci est une forme de limité à ce sujet et désormais j'utilise toute la précision permise
par des processeurs de calcul très rapide.

Commentaire de Denis007 le 26/01/2012 17:52:29

Et puis aussi le but ultime de l'optimisation consiste à la fois à acélérer les traitements ce qui est utile mais il faut savoir faire la part des choses mais aussi d'économiser de la mémoire alors pour quoi calculer à la fois la table des sinus et celle des cosinus alors que sin(x+pi/2)=cos(x).

Commentaire de Caribensila le 27/01/2012 23:41:11

@Denis007

« Si vous voulez initialiser un grand tableau avec des indice supérieur à 100 vous n'allez pas les écrire à la main »
- Bien sûr que non. Mais dans ce cas, on peut dérouler les boucles partiellement pour optimiser.



« la comparaison d'un nombre infime de boucle ne sert à rien non plus du fait du temps ridiculement faible du temps d'utilisation. »
- Ce n'est peut-être pas précisé dans le tuto, mais on n'optimise que ce qui prend du temps. Je pense que la précision était superflue (du moins, pour le plus grand nombre).
Ceci dit, tout le monde n'utilise pas des mini-boucles.

« remplacer Div Par SHR ne sert à rien non plus »
- Ouais. C'est ce que disent Barbichette et le tuto.
'faut lire !

« (not A) and (not B)  =  not (A or B) »
- Par défaut, le compilateur génère un code d'évaluation optimisé des expressions booléennes. Si tu désires une évaluation complète, il faut utiliser la directive {$B+}.

« Si tu avais comprix tu isolerais dans des méthodes plus petite tes algorythme »
- Découper un algo en plusieurs routines est le moyen le plus sûr pour augmenter le temps de traitement.

« Et puis aussi on s'était bien demander pourquoi les cercles était aussi mal dessiné avec des tableaux dont la précision ne dépasser pas le degré. »
- Tu devrais savoir que l'écran d'un ordinateur est une surface discrète et qu'un tableau trigonométrique précalculé à la seconde d'arc près n'ajoutera pas un seul pixel au cercle !
D'ailleurs, en infographie, on n'utilise pas les fonctions trigonométriques pour tracer les cercles. On utilise l'algorithme de tracé d'arc de cercle de Bresenham pour des raisons d'optimisation, justement.
L'auteur d'un logiciel de DAO devrait savoir ça.

« Ces calculs optimisés était nécessaires sur des ordinateurs peut véloce »
- Tu dis ça à Bacterius qui est en train de travailler sur le RayTracing, et je doute que tu arrives à le convaicre que l'optimisation est désormais obsolète à cause de la vitesse des processeurs.
En revanche, l'économie de mémoire est, elle, passait au second plan. Surtout pour un tableau aussi petit que les sinus.

Commentaire de barbichette le 28/01/2012 00:09:35

« Ces calculs optimisés était nécessaires sur des ordinateurs peut véloce »
Je suis bien d'accord avec Caribensila.
J'ai aussi développé un logiciel de DAO, mais je ne dessine pas dans un Bitmap, parce que je ne peut pas (en ne veux pas). Et pour avoir des fonctions de tracés de cercles, patates remplis avec un paterne, ou encore des ellipses en travers, si je n'avais pas cherché à optimiser mes routines mathématiques, les tracés en direct (lors du déplacement de la souris) donc plusieurs fois par secondes aurai été saccadé, même sur une machine de compétition.

Pour mon écran de veille "feu d'artifice", il y a une grande différence entre les routines pascals et les routines assembleurs avec un rapport de 10. C'est peut-être pas grand chose pour dessiner un clipart, mais de passer de 3 images par secondes à 30 im/s, c'est quand même plus fluide...

Commentaire de Caribensila le 28/01/2012 00:19:13

... D'autant que pour les jeux faisant appel aux réflexes humains, il faut du 60 img/sec !  ;)

Commentaire de Denis007 le 28/01/2012 20:04:22

Je suis curieux de savoir si tu as un site web M. barbichette

Commentaire de Denis007 le 28/01/2012 20:37:14

Je ne suis pas certain de ce que tu avance, car la limite de projection des films traditionnelement à 24 images secondes au cinéma ou 25 pour nos télévisions devrait suffire à tromper la persistance rétinienne, en fait ces fréquences suffisent. denis.

Commentaire de Denis007 le 04/02/2012 03:35:53

Pour revenir sur la démonstration première de cette page:

Imaginer que vous voulez adresser 10 cellules dans un tableau.
Par la suite Imaginer que vous voulez adresser 10 cellules dans un tableau plus le fait de les compter.
Pensez vous réellement que la seconde opération soit plus rapide que la première ?

Commentaire de Bacterius le 04/02/2012 06:40:22

"Je ne suis pas certain de ce que tu avance, car la limite de projection des films traditionnelement à 24 images secondes au cinéma ou 25 pour nos télévisions devrait suffire à tromper la persistance rétinienne, en fait ces fréquences suffisent. denis."
Et non. Les films a 24 ou 25 images par seconde apparaissent fluides car les images sont floues (car elles sont capturees par une camera), et donc contiennent l'information de deux ou trois images "normales". Essaye de faire tourner un jeu video a 24 images par seconde, l'image sera saccadee, car l'image n'est pas floue, et contient seulement l'information d'une seule image. La frequence minimale pour obtenir une image fluide est plus ou moins 50 images (pas floues) par seconde, mais en general on assume 60 images par seconde car c'est la frequence de rafraichissement de la plupart des ecrans LCD. Et d'ailleurs, ce n'est pas parfait a cette vitesse, l'oeil humain peut facilement distinguer entre 60 images par seconde et 120 images par seconde par exemple (au-dela, ca devient difficile).

Commentaire de barbichette le 04/02/2012 09:04:56

La persistance rétinienne évite juste que l'½il humain perçoive les noirs entre chaque image.
Mais il est heureusement bien plus performant que 25 im/s. D'ailleurs, dans la réalité, nous voyons bien tourner les pales et le rotor d'un hélico dans le bon sens (presque 20.000 tour/s) et portant, aucune vidéo n'arrive à ce résultat (soit elles sont flou, soit elles tournent au ralenties, soit elles tournent en sens inverse).
Pour en revenir au sujet

Même l'humain est plus optimisé que la machine, c'est fou...(enfin, pour l'instant...)

Commentaire de Denis007 le 04/02/2012 13:02:34

De ce fait il doit aussi être (la génération de l'écran d'affichage) toujours être synchronisé sur le top de l'écran lui aussi ?

Commentaire de Denis007 le 04/02/2012 18:58:43

MonCos : array[0..360] of Extended; // Tableau en cache.
MonSin : array[0..360] of Extended; // Tableau en cache.

Si tu avais rééllement comprix la fonction modulo
ton tableau irait de 0 à 359 ce qui suffisant.

extrait de mon code souce ci joint

tableau_sinus = array [0 .. 359] of longint;

for i:=0 to 359 do sinus^[i]:=round(sin(i*pisur180)*spline_div);

autre extrait : afin de tracer un arc de cercle.

isin:=(720+isin+pas) mod 360;

{720 pour tenir compte des angle négatifs}

icos:=(isin+90) mod 360;
repeat
    ax:=gx+(Int64(sinus^[icos])*xrayon) div spline_div;
    ay:=gy-(Int64(sinus^[isin])*yrayon) div spline_div;
until abs(isin-finangle)<=abs(pas);

Commentaire de Caribensila le 05/02/2012 03:25:29

« Si tu avais rééllement comprix la fonction modulo ton tableau irait de 0 à 359 ce qui suffisant. »

Mercix de m'expliquer l'utilité de la fonction modulo (qui n'est d'ailleurs pas une fonction mais un opérateurs arithmétique).

Mais sache que si mes tableaux de trigo précalculés vont de zéro à 360, il y a une raison qui concerne directement l'optimisation !
Raison que je te laisse découvrir par toi-même car, lorsqu'on voit comment tu dessines un cercle, on se rend compte que tu as encore beaucoup de progrès à faire en optimisation. Et cela te fera le plus grand bien de chercher un peu.



D'autre part, je te serais reconnaissant de bien vouloir cesser de déblatérer sur mon tuto.

Le principal mérite de ce tuto résidant dans la qualité des commentaires des participants, je pense que tes observations blessantes, insignifiantes et presque toujours réfutables n'y apportent rien.

Tu restes, bien sûr, tout à fait libre d'écrire ton propre tuto sur l'optimisation afin de développer tes idées personnelles.

Merci d'avance.

Commentaire de Denis007 le 05/02/2012 16:50:06

C'était pour faire des ellipses deux rapports différents sont utilisé pour chaque axe!  

Commentaire de Denis007 le 06/02/2012 03:24:45

Je me doit quand même de préciser que sin(0)=sin(360) ainsi que cos(0)=cos(360) que 360 mod 360 = 0 et que dans ce cas,
l'accés optimisé dans le tableau représente une variation qui si elle est parcourue de 0 à 360 peut être préciser plus concisément
comme l'a suggérer f0x qui lui utilise la convertion des degrés en radian et qui peut s'écrire tout bonnement angle*pi/180 plutôt qu'une
constante illisible.db.

Commentaire de Denis007 le 06/02/2012 03:41:46

Un compilateur honnête ne se soucis pas d'optimisation sans but mais le fait pour vous.

Commentaire de barbichette le 07/02/2012 20:51:31

Bon, pour faire un peu de pratique, voir un programme que je viens de poster
http://www.delphifr.com/codes/COMPARATIF-ALGO-CERCLES_54052.aspx
Juste pour voir comment l'optimisation est mise en pratique...

Commentaire de Caribensila le 10/02/2012 17:41:06

... Et une autre démo complémentaire de celle de Barbichette ici :
http://www.delphifr.com/codes/CERCLE-ENCHANTE-ANDRES-GIT-NOS-MEMOIRES_54058.aspx

 Ajouter un commentaire




Nos sponsors


Sondage...

CalendriCode

Juin 2013
LMMJVSD
     12
3456789
10111213141516
17181920212223
24252627282930

Consulter la suite du CalendriCode

Photothèque

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,546 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales