C pointeurs vs access membre direct pour les structures

Dites que j’ai une structure comme celle-ci …

typedef struct { int WheelCount; double MaxSpeed; } Vehicle; 

… et j’ai une variable globale de ce type (je connais bien les pièges des globals, il s’agit d’un système embarqué, que je n’ai pas conçu, et pour lequel ils constituent un mal malheureux mais nécessaire. ) Est-il plus rapide d’accéder aux membres de la structure directement ou via un pointeur? c’est à dire

 double LocalSpeed = MyGlobal.MaxSpeed; 

ou

 double LocalSpeed = pMyGlobal->MaxSpeed; 

L’une de mes tâches consiste à simplifier et à réparer un système intégré récemment hérité.

    En général, je dirais aller avec la première option:

     double LocalSpeed = MyGlobal.MaxSpeed; 

    Cela a un déréférencement de moins (vous ne trouvez pas le pointeur, vous le déréférencez pour vous rendre à son emplacement). Il est également plus simple et plus facile à lire et à gérer, puisqu’il n’est pas nécessaire de créer la variable de pointeur en plus de la structure.

    Cela étant dit, je ne pense pas que vous constateriez une différence de performances, même sur un système embarqué. Les deux temps d’access seront très très rapides.

    Le premier devrait être plus rapide car il ne nécessite pas de déréférencement de pointeur. Là encore, c’est vrai pour les systèmes x86, pas sûr pour les autres.

    sur x86 le premier se traduirait par quelque chose comme ça

     mov eax, [address of MyGlobal.MaxSpeed] 

    et le second serait quelque chose comme ça

     mov ebx, [address of pMyGlobal] mov eax, [ebx+sizeof(int)] 

    Sur votre plate-forme intégrée, il est probable que l’architecture soit optimisée de telle sorte qu’il s’agisse essentiellement d’un lavage, et même si ce n’était pas le cas, vous ne remarqueriez jamais un impact sur les performances si cela était exécuté dans une boucle très serrée.

    Il existe probablement des zones de performances beaucoup plus évidentes de votre système.

     struct dataStruct { double first; double second; } data; int main() { dataStruct* pData = &data; data.first = 9.0; pData->second = 10.0; } 

    Voici la sortie de l’assemblage utilisant le mode de publication VS2008:

      data.first = 9.0; 008D1000 fld qword ptr [__real@4022000000000000 (8D20F0h)] pData->second = 10.0; 008D1006 xor eax,eax 008D1008 fstp qword ptr [data (8D3378h)] 008D100E fld qword ptr [__real@4024000000000000 (8D20E8h)] 008D1014 fstp qword ptr [data+8 (8D3380h)] 

    démonter, démonter, démonter …

    En fonction des lignes de code que vous ne nous montrez pas, il est possible que si votre pointeur est un peu statique, un bon compilateur le saura et pré-calculera l’adresse pour les deux. Si vous n’avez pas d’optimisations, alors toute cette discussion est muette. Cela dépend également du processeur que vous utilisez, les deux peuvent être exécutés avec une seule instruction en fonction du processeur. Je suis donc les étapes d’optimisation de base:

    1) démonter et examiner 2) le temps de l’exécution

    Comme mentionné ci-dessus, il se peut qu’il s’agisse de deux instructions au lieu d’une seule coûtant un seul cycle d’horloge que vous ne verrez probablement jamais. La qualité de vos choix de compilateur et d’optimiseur aura des conséquences beaucoup plus dramatiques sur les performances que d’essayer d’affiner une ligne de code dans l’espoir d’améliorer les performances. Changer de compilateur peut vous donner 10 à 20% dans les deux sens, parfois plus. Comme il est possible de changer les indicateurs d’optimisation, tout mettre en marche ne produit pas le code le plus rapide, parfois -O1 donne de meilleurs résultats que -O3.

    Comprendre ce que ces deux lignes de code produisent et comment optimiser les performances à partir d’un langage de haut niveau provient de la compilation pour différents processeurs et du désassemblage à l’aide de divers compilateurs. Et plus important encore, le code autour des lignes en question joue un rôle important dans la façon dont le compilateur optimise ce segment.

    En utilisant l’exemple de quelqu’un d’autre sur cette question:

     typedef struct { unsigned int first; unsigned int second; } dataStruct; dataStruct data; int main() { dataStruct *pData = &data; data.first = 9; pData->second = 10; return(0); } 

    Avec gcc (pas très bon compilateur), vous obtenez:

     mov r2, #10 mov r1, #9 stmia r3, {r1, r2} 

    Donc, les deux lignes de code C sont jointes dans un magasin, le problème ici est l’exemple utilisé comme test. Deux fonctions distinctes auraient été un peu meilleures, mais il faut beaucoup plus de code et le pointeur doit pointer vers une autre mémoire pour que l’optimiseur ne se rende pas compte qu’il s’agit d’une adresse globale statique. Pour le vérifier, vous devez passer l’adresse. donc le compilateur (bien gcc) ne peut pas comprendre que c’est une adresse statique.

    Ou sans optimisations, même code, même compilateur, pas de différence entre pointeur et direct.

     mov r3, #9 str r3, [r2, #0] mov r3, #10 str r3, [r2, #4] 

    C’est ce que vous vous attendriez à voir selon le compilateur et le processeur. Pour ce processeur, même si le code de test masquait l’adresse statique du pointeur de la fonction, elle se résumerait à deux instructions. Si la valeur stockée dans l’élément de structure était déjà chargée dans un registre, il s’agirait d’une instruction dans les deux sens, pointeur ou directe.

    Donc, la réponse à votre question n’est pas absolue… ça dépend. démonter et tester.

    Je suppose que si cela faisait une différence, cela dépendrait de l’architecture.

    En général, accéder directement à la structure serait plus rapide, car cela ne nécessiterait pas de déréférencement supplémentaire du pointeur. Le déréférencement du pointeur signifie qu’il doit prendre le pointeur (l’élément dans la variable), charger tout ce qu’il pointe, puis l’exploiter.

    En C, il ne devrait y avoir aucune différence, ou un impact insignifiant sur les performances.

    C les élèves apprennent:

     pMyGlobal->MaxSpeed == (*pMyGlobal).MaxSpeed 

    Vous devriez pouvoir comparer le désassemblage des deux pour vous convaincre qu’ils sont essentiellement les mêmes, même si vous n’êtes pas un programmeur de code d’assemblage.

    Si vous recherchez une optimisation des performances, je chercherais ailleurs. Avec ce type de micro-optimisation, vous ne pourrez pas économiser suffisamment de cycles de processeur.

    Pour des raisons stylistiques, je préfère la notation Structure-Dot, en particulier lorsqu’il s’agit de singleton-globals. Je le trouve beaucoup plus propre à lire.

    L’access direct aux membres est plus rapide (pour les pointeurs, vous obtiendrez généralement une opération de déréférencement de pointeur de plus). Bien que j’aie du mal à l’imaginer dans une situation où ce serait un problème, une performance ou autre.