2010-05-19 18 views
0

J'écris des performances critiques et je voulais savoir quelque chose si elle pouvait faire une différence si je l'utilise:performance lors de l'accès des membres de la classe

int test(int a, int b, int c) 
{ 
    // Do millions of calculations with a, b, c 
} 

ou

class myStorage 
{ 
public: 
    int a, b, c; 
}; 

int test(myStorage values) 
{ 
    // Do millions of calculations with values.a, values.b, values.c 
} 
  • Est-ce essentiellement résultat dans un code similaire? Y a-t-il un frais supplémentaire d'accès aux membres du cours?

Je suis sûr que cela est clair à un expert en C++ je ne vais pas essayer d'écrire une référence irréaliste dès maintenant

+3

Si les performances sont importantes, il n'y a finalement pas d'alternative à l'écriture de tests de performance. –

+3

Micro-optimisation (nous pourrions peut-être même ajouter une balise pour cela) –

+0

@David: Cette balise existait déjà. ':)' – sbi

Répondre

5

Le compilateur les égaliser probablement. S'il a des cerveaux, il copiera values.a, values.b, et values.c dans des variables locales ou des registres, ce qui est aussi ce qui se passe dans le cas simple.

Les maximes pertinentes:

  1. L'optimisation prématurée est la racine de beaucoup de mal. Ecrivez-le afin que vous puissiez le lire à 1h du matin dans six mois à partir de maintenant et comprenez toujours ce que vous essayiez de faire.

  2. La plupart du temps, une optimisation significative vient de la restructuration de votre algorithme, et non pas de petits changements dans la façon d'accéder aux variables. Oui, je sais qu'il y a des exceptions, mais ce n'est probablement pas le cas.

+0

Je suppose que le downvote était trop concis. J'ai élargi la réponse. – egrunin

+0

+1 pour la maxime # 2. La plupart des gens n'écrivent pas de code lisible mais commentent (décrivent) l'intention des opérations. –

4

Cela ressemble à une optimisation prématurée. Ceci étant dit, il existe des différences et des opportunités, mais elles affecteront plusieurs appels à la fonction plutôt que des performances dans la fonction. Tout d'abord, dans la deuxième option, vous pouvez passer MyStorage comme référence constante. En conséquence, votre code compilé va probablement pousser une seule valeur dans la pile (pour vous permettre d'accéder au conteneur), plutôt que de pousser trois valeurs séparées. Si vous avez des champs supplémentaires (en plus de a-c), envoyer MyStorage non comme référence peut vous coûter plus cher parce que vous allez appeler un constructeur de copie et copier essentiellement tous les champs supplémentaires. Tout cela serait des coûts par appel, pas dans la fonction.

Si vous effectuez des tonnes de calculs avec b et c dans la fonction, la façon dont vous les transférez ou y accédez n'a aucune importance. Si vous passez par référence, le coût initial peut être légèrement plus élevé (puisque votre objet, s'il est référencé, pourrait être sur le tas plutôt que sur la pile), mais une fois accédé pour la première fois, la mise en cache et les registres sur votre machine moyen d'accès à faible coût. Si vous avez passé votre objet par valeur, cela n'a pas vraiment d'importance, car même au départ, les valeurs seront proches de la pile.

Pour le code que vous avez fourni, si ce sont les seuls champs, il n'y aura probablement pas de différence. le "valeurs.variable" est simplement interprété comme un décalage dans la pile, pas comme "rechercher un objet, puis accéder à une autre adresse".

Bien sûr, si vous n'achetez pas ces arguments, définissez simplement les variables locales comme première étape de votre fonction, copiez les valeurs de l'objet, puis utilisez ces variables.Si vous les utilisez réellement plusieurs fois, le coût initial de cette copie n'aura pas d'importance :)

0

Non, votre unité centrale mettra en cache les variables que vous utilisez encore et encore.

0

Je pense qu'il y a des frais généraux, mais peut-être pas beaucoup. Étant donné que l'adresse mémoire de l'objet sera stockée dans la pile, qui pointe vers l'objet mémoire tas, vous accédez à la variable d'instance.

Si vous stockez la variable int dans la pile, ce serait vraiment plus rapide, car la valeur est déjà dans la pile et la machine va juste à la pile pour la calculer :).

Cela dépend également de si vous stockez la valeur de la variable d'instance de la classe sur la pile ou non. Si à l'intérieur du test(), vous ne l'aimez:

int a = objA.a; 
int b = objA.b; 
int c = objA.c; 

Je pense que ce serait presque la même performance

0

Si vous écrivez vraiment la performance code critique et vous pensez que une version devrait être plus rapide que la l'autre, écrivez les deux versions et testez le timing (avec le code compilé avec le bon bouton d'optimisation). Vous pouvez même vouloir voir les codes d'assemblage générés. Beaucoup de choses peuvent affecter la vitesse d'un des extraits de code qui sont assez subtiles, comme déversant registre, etc.

0

vous pouvez également démarrer votre fonction avec

int & a = values.a; 
int & b = values.b; 

bien que le compilateur devrait être assez intelligent pour faire ça pour toi dans les coulisses. En général, je préfère faire circuler des structures ou des classes, ce qui rend souvent plus clair ce que la fonction est censée faire, et vous n'avez pas besoin de changer les signatures chaque fois que vous voulez prendre en compte un autre paramètre.

0

Comme pour votre question précédente, similaire: cela dépend du compilateur et de la plate-forme. S'il y a une différence, ce sera très petit. Les valeurs de la pile et les valeurs d'un objet sont communiquées à l'aide d'un pointeur (le pointeur de pile ou le pointeur this) et d'un certain décalage (l'emplacement dans le cadre de pile de la fonction ou l'emplacement dans la classe).

Voici quelques cas où il pourrait faire une différence:

  • En fonction de votre plate-forme, le pointeur de la pile peut être tenue dans un registre CPU, alors que le pointeur this peut-être pas. Si tel est le cas, accéder à this (qui est probablement sur la pile) nécessiterait une recherche de mémoire supplémentaire.

  • La localité de mémoire peut être différente. Si l'objet en mémoire est plus grand qu'une ligne de cache, les champs sont étalés sur plusieurs lignes de cache. Le regroupement des valeurs pertinentes dans un cadre de pile peut améliorer l'efficacité du cache.

Notez cependant, combien de fois j'ai utilisé le mot "might" ici. La seule façon d'être sûr est de le mesurer.

0

Si vous ne pouvez pas profiler le programme, imprimez la langue d'assemblage pour les fragments de code.

En général, moins de code d'assemblage signifie moins d'instructions à exécuter, ce qui accélère les performances.C'est une technique permettant d'obtenir une estimation approximative des performances lorsqu'un profileur n'est pas disponible.

Une liste de langage d'assemblage vous permettra de voir les différences, le cas échéant, entre les implémentations.