Optimization de code pour du Xna rapide et efficace !

Je dois en omettre un grand nombre, si vous en avez d'autres n'hesitez pas à me les soumettre que je les rajoute ici !

Première optimisation, les strings :

Eviter tout indexeur à base de string s’il y’a un équivalent à base d’int.

L'instruction :

myEffect.Techniques["Precompiled"] ;

Est bien moins efficace q'un :

const int TechnikIndex = 0;

myEffect.Technique[TechnikIndex]

Eviter au maximum la class string, si on a besoin de puissance. En C# tout modification d'une string entraine la création d'une nouvelle instance et donc une allocation mémoire. Une string N EST JAMAIS MODIFIABLE et l'allocation mémoire est une opération très couteuse en temps. Il faut donc travailler un maximum avec StringBuilder.

 Besoin de comprendre le string intern pool. Quand on créé une string, le clr regarde dans un pool interne si elle existe avant de la créer. (méthode statique Intern de la classe string).

Donc string jamais collectés comme les objets communs dans le GC.

 

 Boxing/unboxing

Boxing passage d'un value type (exemple int) à une classe objet .

Par exemple :

int a  = 0;

object o = (object)a;

Unboxing est l'opération inverse.

boxed = 20 fois plus de traitement CPU qu'une simple affectation de ref

unboxed = 4 fois plus

= généric autant que faire se peux (optimization en Early binding)

 Pour les struct, implementer la méthode ToString (sinon il y’a boxing en objet pour chaque appel à cette méthode)

 

Array

Jagged Arrays > Rectangular Array ([][] > [,])

Privilegier les for au foreach (moins nécessaire depuis le .Net 3.0)

addrange plutot que add

Range Check important, exemple :

//rapide
for(int i = 0; i < myArray.Length; i++)
{
   Console.WriteLine(myArray.ToString());
}
//plus lent
for(int i = 0; i < myArray.Length + y; i++)

   Console.WriteLine(myArray[i+x].ToString());
}

Dans la seconde boucle le test ne porte pas sur la longueur du tableau traité mais sur une autre valeur, il y’a donc des tests supplémentaire ajouté en IL pour générer une exception en cas de sortir de Bounds.

Methodes :

Il y’a quelque regles simples pour que le compilateur utilise de l’inlined (suppresion à la compilation d'une méthode pour inclure son code en lieu et place de l'instruction appellant la dite méthode) :

  • 32o d'il (8instructions) max par méthode/accesseur/mutateur à "inliner"
  • pas de structures conditionnelles dans le bloc à inliner
  • pas de type struct utilisé dans le bloc à inliner
  • pas de bloc Try/Catch/Finaly dans le bloc à inliner.

Si il n’y a pas besoin de rendre le code compréhensible on priviligiera les chunky call au maximum (méthodes énormes en lieu et place de myriades de petites méthodes qui s’appellent les unes les autres).

 

Thread

Synchroniser les thread : System.Threading.Interlocked class

 

 Exception

retourner des enum plutot que des exception sur les méthodes critiques

Eviter ce genre de code :

public static void Main(string[] args){
  int j = 0;
  for(int i = 0; i < 10000; i++){
    try{   
      j = i;
      throw new System.Exception();
    } catch {}
  }
}

En asp.net, à eviter :

L’instruction : Response.Redirect() elle provoque un ThreadAbortException

Un throw d'exception perturbe gravement l'activité CPU associée à votre applicatif.

 

Objets

Utiliser au maximum les structure là ou la notion de référence n’est pas importante (besoin ponctuelle d’agrégat ordonné de données) en lieu et place d’une class

(Exemple de la structure Point, Rectangle, etc.) . La gestion en mémoire est alors bien plus efficace et rapide.

Apprecier au maximum l’attribut Fieldoffset qui permet de réaliser l’équivalent de l’Union du langage C pour optimiser la mémoire

Utiliser les weakreference notamment pour le cache, afin de ne pas surcharger la mémoire.

 

 Framework :

Working Set : charger une assembly pour profiter d'une seule de ses méthodes c'est pas bien !

Utiliser vadump.exe pour détecter ca dans son framework

Se servir de Perfmon et de ses attributs (nb de classes chargées, nombre de méthodes, temps passé à loader, JITed, etc.)

 

 Executables :

Précompilation avec ngen.exe pour les exes critiques. Permet de précompiler son application pour un premier lancement rapide.

 

SQL 

connexion ouverte longtemps < multiple ouverture /fermeture de connexion

Datareader > dataset

Annuler le transaction enlistment si inutile

 SqlConnection conn = new SqlConnection(

"Server=exasrv01;

Integrated Security=true;

Enlist=false");

Published Sat, Feb 28 2009 12:53 by valentin

Comments

# re: Optimization de code pour du Xna rapide et efficace !

Sunday, March 08, 2009 7:02 AM by Jérémy

Article interessant !

Cependant, j'ai une question en rapport avec l'opération de boxing et unboxing.

Imaginons une texture où nous mettons 5 tiles, nous aurons donc un tableau de rectangles de ce style :

Rectangle[] rc = new Rectangle[5]

{

 new Rectangle(0, 0, 100, 50),

 new Rectangle(0, 51, 100, 50),

 new Rectangle(0, 102, 100, 50),

 new Rectangle(0, 153, 100, 50),

 new Rectangle(0, 204, 100, 50)

};

Ainsi pour avoir le premier élément de la liste, on mettrait 0 comme index. Cependant, si le programmeur ne veut pas regarder perpetuellement plusieurs lignes au dessus de son code pour savoir que l'indice 0 correspond au bouton A, 1 au bouton B, etc..., il serait tenter de créer une énumération comme ceci :

enum GameButton : int

{

 A = 0,

 B,

 X,

 Y,

 Start

}

et ainsi il écrirait ceci :

public void DrawButton(GameButton but)

{

 spriteBatch.Draw(texture, position, rc[(int)but], ...);

}

Maintenant la question que je pose est :

Comment se fait-ce qu'une énum qui est pourtant considérée en int, doit avoir un castage et est-ce qu'il est aussi coûteux qu'un cast int en obj ou obj en int et autres ?

Sinon quand vous parlez de généricité, est-ce que vous parler de l'utilisation d'un truc du style :

public T ConvertToEnum<T>(string value)

{

   return (T)Enum.Parse(typeof(T), value, true);

}

En vous remerciant ...

# re: Optimization de code pour du Xna rapide et efficace !

Sunday, March 08, 2009 7:27 AM by valentin

L'énum est un value type à part entière créé par le développeur. Ce n'est donc pas sémantiquement parlant un int, short, byte etc.

Les valeurs d'une énum ne sont pas 0, 1, 2, 3, ... mais A, B, C, D. On peut faire une correlation avec le type char, 'A' n'est pas un int, mais on sait très bien qu'il équivaut à une valeur entière coté ASCII.

Le compilateur heureusement est au courrant de ça et manipule ce genre de cast en amont. Ce qui fait que écrire rc[(int)but] ou rc[0] est aussi rapide.

# re: Optimization de code pour du Xna rapide et efficace !

Sunday, March 08, 2009 7:27 AM by valentin

Pour la généricité je parle de ça oui effectivement.

# re: Optimization de code pour du Xna rapide et efficace !

Sunday, March 08, 2009 10:29 AM by Jérémy

Merci de ces éclaircissements...

# Goto vs While

Sunday, August 16, 2009 3:18 AM by Idrakis

Bonjour,

J'ai récement découvert dans un projet C# qu'il était possible de faire des goto en C#. Dans aucun livre traitant de C#, aucun tuto ou cours en ligne, et jusqu'a aujourd'hui, dans aucune source je ne l'avais rencontré. Donc je me suis demandé si il étatit plus performant de faire un goto ou un while pour une boucle ? Et voici ce que j'ai obtenu en IL :

Label:

  goto Label;

IL_0000:  nop

IL_0001:  br.s       IL_0001

while(true) {}

 IL_0000:  nop

 IL_0001:  br.s       IL_0005

 IL_0003:  nop

 IL_0004:  nop

 IL_0005:  ldc.i4.1

 IL_0006:  stloc.0

 IL_0007:  br.s       IL_0003

Je ne connais pas bien l'IL, mais a vu de nez, je dirais qu'il faudrait privilégier le goto. Mais alors pourquoi n'en parle-t-on presque pas ? Est-ce considéré comme mauvais pour la lisibilité du code, et donc comme une mauvaise pratique ? Est-ce une source d'erreur difficilement débugable trop importante ? Est-ce que le gain de performance est négligeable ?

Et qu'est ce qui éxplique cette différence d'IL pour un résultat identique ?

Merci.

# re: Optimization de code pour du Xna rapide et efficace !

Sunday, August 16, 2009 6:21 AM by valentin

En fait si on ne priviligie pas le Goto ce n'est pas une question de performances mais plutot une question de lisibilité du code.

Les detracteurs de cette intruction invoque la difficile maintenance d'un code comportant un grand nombre d'étiquette.

Le goto n'est pas une intruction lente.

Moi même qui aime bien le code propre et lisible je l'utilise quelque fois dans des cas bien particuliers. Par exemple pour sortir de plusieurs imbrications de boucles ou dans des switch importants. C'est aussi une instrction qui permet l'inlining.

Leave a Comment

(required) 
(required) 
(optional)
(required) 
Powered by Community Server (Commercial Edition), by Telligent Systems