9
votes

Visual C ++ ~ non inlinfer des appels de pointeur de fonction Const simple

Cher Stackoverflowers,

J'ai reçu un simple code de code que je compilise sur Microsoft Visual Studio C ++ 2012: P>

int main()
{
000000013FBA2430  sub         rsp,28h  
int x = 3;
int y = 2;
int z = A::FP(x, y);
000000013FBA2434  mov         edx,2  
000000013FBA2439  lea         ecx,[rdx+1]  
000000013FBA243C  call        qword ptr [A::FP (013FBA45C0h)]  
return 0;
000000013FBA2442  xor         eax,eax
}


17 commentaires

@ Otávio Décio Je n'ai pas le compilateur GCC alors je suis désolé je ne sais pas!


Puisque vous utilisez un compilateur propriétaire, il est peu probable que quiconque connaisse la réponse sera en mesure de vous dire sans conséquences juridiques.


@Mike Seymour je sais ce que tu veux dire. Je ne m'attends pas vraiment à une explication technique sur le compilateur lui-même. J'espère juste que je code d'une manière ou d'une autre modifie mon code qui rendrait la probabilité que le compilateur augmente plus probable.


Pour ce que ça vaut la peine, GCC supprime le tout puisqu'il n'y a aucun effet secondaire. Si j'ajoute volatile aux trois variables pour le forcer à effectuer le calcul, il définit l'appel de la fonction.


@Mike Seymour Selon Wikipedia, le mot-clé «volatil» est utilisé pour empêcher le compilateur de faire toutes les optimisations. Ne serait-il pas bizarre de l'appliquer à quelque chose que vous voulez réellement être optimisé? Ou je vois ce faux?


@Christianveenman: Je l'utilisais pour empêcher l'optimisation de l'optimisation de la suppression du corps entier de principal en raison de l'absence d'effets secondaires, en le forçant à lire x et y , utilisez les valeurs de lecture dans le calcul et écrivez le résultat sur z . Une fois que je l'ai fait, le code généré contenait une instruction Ajouter (le corps de ajoutez () ) et aucun appel de fonction: la fonction avait été inlinée.


@Mike Seymour sonne logique pour moi! J'ai essayé de faire ça, mais j'ai le même résultat (pourrait différer un peu, mais au moins l'instruction d'appel était toujours là.)


GCC 4.7.2 (avec -O3) optimise tout, revenant simplement 0. Si vous modifiez Main pour retourner z , il charge simplement la constante 5 dans un registre et retourne que (Ainsi, l'appel de la fonction est effectivement inlincé dans GCC). Je ne peux pas commenter pourquoi il pourrait ne pas être inlincé dans MSVC.


@Cameron que vous pourriez m'empêcher d'utiliser GCC pour compiler. Mais j'espère toujours que quelqu'un pourrait avoir une bonne réponse.


Votre cas d'utilisation nécessite deux optimisations: (1) la conversion d'un appel indirect à l'aide d'un pointeur constant dans un appel direct. (2) Amener un appel direct. MSVC fait définitivement la seconde, mais l'inlinge est ce que toute la langue technique de votre question concerne. Je suggère une réécriture qui se concentre sur le premier. Notez que l'optimisation (1) est similaire à mais pas identique à la "dévitutualisation". Pourtant, un autre cas intéressant est lorsque le pointeur de fonction est un argument de modèle de type de type.


@Ben voigt mmm qui semble intéressant! Pensez-vous que MSVC est capable de faire le premier type d'optimisation? Quand j'ai plus de temps, je vais essayer de tester la version du modèle!


@Christianveenman: J'imagine que 99% du code réel utilisant les pointeurs de fonction entraînent un code qui est presque impossible savoir ce qu'il pointe à l'heure de la compilation et est donc impossible la nuit à l'inline. Si j'étais un écrivain du compilateur, j'ignorerais probablement ce cas entièrement aussi.


J'ai fait quelques petits tests. Avec un pointeur de fonction utilisé comme argument de modèle de type sans type avec code très simple, MSVC2010 est capable de l'optimiser et de mettre 5 dans un registre comme une constante. Avec un code plus complexe (code réel que j'utilise réellement) qui implémente les foncteurs utilisant des objets de pointeur de fonction de type non-type, MSVC n'oblise pas l'appel de la fonction (même l'appel d'emballage n'est pas inliné), mais la GCC est à nouveau capable d'optimiser Tout est entièrement (j'ai trouvé un petit bug de modèles dans GCC, mais c'est une autre histoire).


@Cameron merci beaucoup pour les tests! (+1!) Je suppose que je vais essayer d'utiliser des maquillages avec Visual Studio et d'utiliser GCC ou d'essayer le plug-in Intel Compiler!


@MOOINGDUCK DÉDUCTION logique à propos de l'écrivain du compilateur! (+1 aussi!)


@Christian: pas de problème. (Le bug de GCC s'est avéré être un bug assez inoffensif dans MSVC , par Le chemin.) Si vous avez vraiment besoin de la fonction pour être inlinée, allez-y et utilisez GCC. Mais en général, MSVC est en fait assez bon pour la production de code rapide (de même que GCC) - la meilleure option consiste à écrire autant que possible du code multi-plate-forme qui fonctionne raisonnablement rapidement sous de nombreux compilateurs et architectures. Les chances sont des choses comme celle-ci ne sera pas des goulots d'étranglement - et s'ils le sont, voulez-vous vraiment être limité à l'utilisation d'un seul compilateur?


@Cameron Hey Cameron, c'est l'idée que j'avais à l'esprit et pourquoi j'ai posé cette question ici: Stackoverflow.com/questions/16478514/...


4 Réponses :


0
votes

Vous pouvez essayer __ forceinline . Personne ne sera capable de vous dire exactement pourquoi il n'est pas inliné. Le bon sens me dit que cela devrait être, cependant. / O2 devrait favoriser la vitesse du code sur la taille du code (inlinage) ... étrange.


10 commentaires

Je l'ai testé en ajoutant __forceinline à la fonction Ajouter, mais j'ai toujours eu la même instruction d'appel en cours! J'ai également essayé les deux options "faveur à la vitesse" et "favoriser une petite taille", mais ils n'ont pas supprimé la fonction d'appel! Et merci!


Si le pire vient au pire, vous pouvez utiliser une macro au lieu d'une fonction statique :)


Heheh merci, mais je préférerai probablement aller à GCC! (Ou utilisez des maquillages dans Visual Studio)


Le compilateur ne peut pas aligner un appel indirect, peu importe la façon dont vous le commandez. Il faut d'abord supprimer l'indirection.


@Benvoigt Il devrait supprimer l'indirection due à l'indirection étant une constante de temps de compilation.


@Randy: Mais il n'a pas fait (regarder le démontage dans la question).


Bien sûr, cela ne l'a pas fait, mais il devrait donc être demandé la question de la question.


@Benvoigt Je suis d'accord avec Randy ici. Poeple Dites que les compilateurs modernes deviennent plus intelligents et peuvent simplifier des choses connues à la compilation. Parce que le pointeur de fonction ne peut pas changer en raison de la saisie du programme. (Et est const) qu'il serait logique pour un compilateur d'aligner l'appel indirect.


@Christianveenman: Il serait logique que le compilateur reconnaisse d'abord que l'appel indirect va toujours à la même cible et convertit à un appel direct. Puis en ligne l'appel direct. Mais l'affirmation d'un appel indirect n'est jamais possible. Aucune quantité de réglages d'inlinage modification n'aura aucun effet tant que le compilateur génère un appel indirect.


@Benvoigt Je comprends ce que tu veux dire. Vous avez raison de ne pas pouvoir aligner un appel direct. Mais quelle était ma question s'il y avait un moyen de rendre le compilateur convertir un appel indirect en une directe, puis en ligne. Mais merci pour cette correction!



1
votes

Je pense que vous avez raison dans cette conclusion: "... ne peut pas affiner les pointeurs de fonction du tout".

Cet exemple très simple brise également l'optimisation: xxx p> Votre échantillon est encore plus complexe pour le compilateur, il n'est donc pas surprenant.


3 commentaires

Nope, il y a des cas où les pointeurs de fonction sont optimisés.


@Benvoigt Je suis d'accord, je suis d'accord qu'ils sont parfois optimisés, mais selon mes tests (et camérons), ils ne sont jamais inlinés. Ou est-ce que je manque un test spécifique?


@Christianveenman: Oui, les deuxième et troisième cas de test dans ma réponse ont été inlinés.



2
votes

Le problème n'est pas avec l'inlinisation, que le compilateur fait à chaque occasion. Le problème est que Visual C ++ ne semble pas se rendre compte que la variable de pointeur est en réalité une constante de temps de compilation.

Test-cas: p> xxx pré>

Les fonctions sont séparées en plusieurs unités de compilation pour donner un meilleur contrôle sur l'inlinisation. Spécifiquement, je ne veux pas show_int code> inlined, car il rend le code d'assemblage en désordre. P>

La première bouffée de problèmes est que le code valide (la ligne commentée) est rejeté par visuel C ++. G ++ n'a aucun problème avec celui-ci , mais Visual C ++ se plaint "Expression constante de la compilation attendue". C'est en fait un bon prédicteur de tous les comportements futurs. P>

avec une sémantique d'optimisation activée et de compilation normale (sans inlinage de module croisé), le compilateur génère: p> xxx pré> P> Il y a déjà une énorme différence entre utiliser sum_ptr code> et sans utiliser sum_ptr code>. Déclarations utilisant Sum_PTR CODE> Générer une fonction indirecte Call Call DWORD PTR _SUM_PTR CODE> Alors que toutes les autres instructions génèrent un appel de fonction directe appel _sum code>, même lorsque le code source utilisé un pointeur de fonction. p>

Si nous activons maintenant l'inlindicing en compilant fonction de fonction_point_resolution.cpp et sum.cpp avec / gl code> et en liant avec / ltcg code>, nous constatez que le compilateur aligne tous les appels directs. Les appels indirects restent comme. P> xxx pré>

Bottom-line: Oui, le compilateur fait des appels en ligne effectués via un pointeur de fonction constant de compilation, tant que cela Le pointeur de fonction n'est pas lu à partir d'une variable. strong> Cette utilisation d'un pointeur de fonction a été optimisée: p> xxx pré>

mais cela n'a pas: p>

(*sum_ptr)(1, 7);


3 commentaires

Mais un pointeur de fonction n'est-il pas techniquement une variable avec une adresse à une fonction? Et puis-je conclure que MSVC n'est pas capable de voir que ce sont des constantes de temps de compilation?


@Christianveenman: Certainement msvc n'est pas capable de voir que Sum_PTR est une constante de temps de compilation, car elle en dit plus si vous déconnectez la ligne Callt (5, 9)


Merci pour l'explication! Aussi pour les testurs!



0
votes

Ce n'est pas une vraie réponse, mais un "peut-être de la solution de contournement": STL de Microsoft a une fois mentionné que les Lambdas sont plus facilement enrachables que les PTRS afin que vous puissiez essayer cela.

Comme une trivia bjarne mentionne souvent que le tri est plus rapide que QSort, car qsort prend la fonction PTR, mais comme les autres personnes ont noté GCC n'a pas de problèmes sans problème ... Donc, peut-être que Bjarne devrait essayer GCC: P


1 commentaires

Merci !: P Bjarne devrait définitivement. (À mon avis: p)