IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Est-il possible de faire un inline partiel d'une fonction ?


Sujet :

Langage C++

  1. #1
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut Est-il possible de faire un inline partiel d'une fonction ?
    Bonjour, je viens de me poser une question simple :

    Est-il possible de faire un inline partiel d'une fonction ?

    J'aimerais savoir s'il est possible lors de l'appel d'une fonction de choisir si on veut que le compilateur copie le code ou alors appelle la fonction.
    Car j'ai une fonction dans ma librairie que j'appelle de l'extérieur donc elle ne doit pas être inline de ce côté mais je l'appelle beaucoup en interne dans ma boucle principale dont j'essaie de réduire le temps de calcul.

  2. #2
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Normalement le compilo est capable de décider cela.

    Sinon, au pire tu la déclares inline et tu exposes un wrapper...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Le langage n'interdit pas qu'une fonction de l'API d'une bibliothèque soit inline.

    Cela dit, est-ce certain que c'est le temps d'appel de cettte fonction qui coûte le plus cher dans ta boucle?
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  4. #4
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut
    Merci de vos réponse.

    Tout d'abord, je ne sais pas ce qu'est un wrapper .
    Et le inline fonctionne aussi si la librairie est en .dll ou .a? N'y a-t'il pas des cas ou cela poserais des problèmes?
    Sinon non, je ne pense pas que ce soit l'appel de ma fonction qui ralentisse le temps exécution, je me posais juste la question car je suis en train de programmer en C pour GPU et j'ai besoin que mon code soit très rapide.

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 965
    Points
    32 965
    Billets dans le blog
    4
    Par défaut
    Tu peux laisser le compilateur décider pour toi, il aura raison dans un bon 95% des cas.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Matthieu76 Voir le message
    Tout d'abord, je ne sais pas ce qu'est un wrapper
    Wikipédia anglais donne une décision très claire :
    A wrapper function is a subroutine in a software library or a computer program whose main purpose is to call a second subroutine or a system call with little or no additional computation.
    Voici un exemple simpliste de code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <iostream>
    using namespace std;
     
    void real_function()
    {
        cout << "real_function" << endl;
    }
     
    void wrapper()
    {
        real_function();
    }
     
    int main()
    {
        wrapper();
    }
    Le même article Wikipédia donne des raisons d'utiliser des wrappers. Souvent, il s'agit de simplifier des appels ou de faire des adaptations de prototypes de fonctions. Un exemple concret peut-être la création de pthread. Quand tu crées un thread, tu dois lui passer un pointeur sur fonction avec un prototype comme celui-ci : void * (* start_routine) (void *). Comme ta fonction est peut-être void do_job()void, tu vas vouloir écrire un wrapper avec le bon prototype. Tu passeras un pointeur vers ton wrapper à pthread_create() et dans ce wrapper tu feras justement un appel à do_job() dedans.

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,

    IMHO, l'inlining d'une fonction vient en numéro 257 dans l'ordre des priorités pour obtenir de meilleures performances. C'est te dire que c'est de la micro optimisation, et qu'il y a sans doute 256 choses bien plus intéressantes à faire avant d'y penser.

    En effet, le processus mis en oeuvre pour faire appel à une fonction non inline se réduit à trois (allez, peut être quatre) instruction processeur; auxquelles il faut (ne les oublions pas) sans doute ajouter un nombre équivalent d'instructions pour pouvoir reprendre l'exécution de la fonction appelante.

    En termes de temps d'exécution, cela se traduit vraiment par peanuts au vu des performances des processeurs acutels (y compris si on regarde du coté des smartphones!). Bien avant d'en arriver à un point où ce genre d'optimisation produira un effet "palpable", il y a donc pas mal de choses susceptibles d'améliorer les performances de manière bien plus significatives, par exemple:
    • éviter les copies inutiles
    • éviter les allocations de mémoire inutiles
    • améliorer tes algorithmes en évitant parcourir une boucle qui provoque de nombreux calculs
    • améliorer tes algorithmes en évitant de calculer des valeurs qui n'ont aucune chance d'aboutir
    • améliorer tes algorithmes en préférant une complexité moindre (en O(N) au lieu d'en O(N2), en O(log(N)) au lieu d'en O(N), en O(1) au lieu d'en O(log(N)), ...)
    • améliorer tes structures afin de pouvoir améliorer la complexité de tes algorithmes
    • améliorer l'organisation de tes données afin d'éviter les cache misses
    • ...


    (ce n'est pas forcément dans l'ordre, et surement pas exhaustif )

    De plus, il faut bien te rendre compte que l'inlining n'est pas sans "effets secondaires indésirables". Le plus important étant une augmentation potentiellement très grande de la taille de ton binaire.

    Un autre aspect important à prendre en compte étant que la définition (l'implémentation pour être précis) d'une fonction inline doit être accessible à toutes les unités de compilation (tous les fichier *.cpp) dans laquelle elle est appelée sous sa forme inline. Et ca, ca peut rapidement devenir problématique, surtout si tu souhaite placer un wrapper qui te permettra d'y accéder de manière classique. Un petit exemple (tiré de l'exemple de Bktero) pour te faire comprendre:

    Imaginons un cas simple pour commencer: la fonction inline ne doit être appelée que dans une seule unité de compilation. Nous pourrions exposer le wrapper au travers de la bibliothèque dynamique sous la forme d'un fichier d'en-tête proche de

    wrapper.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #ifndef WRAPPER_HPP_INCLUDED
    #define WRAPPER_HPP_INCLUDED
    void MY_LIB_API wrapper()
    #endif
    et avoir une unité de compilation proche de
    compile_unit.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #include <wrapper.hpp>
    /* la fonction que l'on veut inliner */
    inliine function(){
        /* blabla */
    }
    /* la fonction wrapper */
    void wrapper(){
        function();
    }
     
    /* d'autres fonctions, exposées ou non, qui font appel à function
     * (dans sa version inline)
     */
    void foo(){
        /*.... */
        function();
       /* ...*/
    }
    void void bar(){
        /*.... */
        function();
       /* ...*/
    }
    void doSomething(){
        /*.... */
        function();
       /* ...*/
    }
    Ca parait cool, hein D'autant plus que l'utilisateur de notre dll pourra créer son propre programme sous la forme de

    main.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int main(){
        /* ... */
        wrapper();
        /* ... */
        return 0;
    }
    Jusque là, tout est absolument génial! Mais, imaginons maintenant que nous voulions utiliser la version inline de la fonction dans une autre unité de compilation, par exemple dans le fichier

    other.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
     
    void doSomethingElse(){
        /* comment avoir l'implémentation de fonction ici ????*/
    }
    Il y a plusieurs solutions, à vrai dire. La pire d'entre elle serait de copier l'implémentation de la fonction inline dans ce fichier, sous une forme qui deviendrait proche de
    other.cpp (V2)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    /* la fonction que l'on veut inliner */
    inliine function(){
        /* blabla */
    }
    void doSomethingElse(){
        /* et oui, ca marche!!! */
        function()
    }
    Pourquoi essentiellement parce que, si tu décide à un moment quelconque de modifier (par exemple, pour corriger un bug) le comportement de function, il y a toutes les chances pour que tu oublie de modifier ce comportement dans (au moins) un fichier d'implémentation. Tu n'auras pas respecté le DRY (Don't Repeat Yourself ou "ne vous répétez pas") et le résultat sera que "assez bizarement, parfois, la fonction marche très bien, et parfois, elle nous fait n'importe quoi"

    Une autre solution, moins mauvaise car elle respecte le DRY, consisterait à placer l'implémentation de la fonction inline dans un fichier séparé, sous une forme qui serait alors proche defunction.hpp (ou function.impl, par exemple)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #ifndef FUNCTION_HPP_INCLUDED
    #define FUNCTION_HPP_INCLUDED
    inline void function(){
       /* ...  bla, bla */
    }
    le fichier wrapper.hpp ne serait pas modifié, et les unités de compilations ressembleraient alors à quelque chose comme
    compile_unit.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #include <wrapper.hpp>
    /* fournit tout à la fois la déclaration et l'implémentation de la fonction inline */
    #include <function.hpp>
    /* la fonction que l'on veut inliner */
    inliine function(){
        /* blabla */
    }
    /* la fonction wrapper */
    void wrapper(){
        function();
    }
     
    /* d'autres fonctions, exposées ou non, qui font appel à function
     * (dans sa version inline)
     */
    void foo(){
        /*.... */
        function();
       /* ...*/
    }
    void void bar(){
        /*.... */
        function();
       /* ...*/
    }
    void doSomething(){
        /*.... */
        function();
       /* ...*/
    }
    other.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    /* fournit tout à la fois la déclaration et l'implémentation de la fonction inline */
    #include <function.hpp>
    void doSomethingElse(){
        function()
    }
    DRY est respecté, et nous pourrions donc croire que "tout va pour le mieux dans le meilleur des mondes", non

    Hé bien, justement, non! Car la loi de Finagle nous dit que si tu laisses à "quelqu'un" (au développeur qui utilise ta bibliothèque, dans le cas présent) la possibilité de faire une connerie, ce n'est qu'une question de temps avant qu'il ne la fasse; et ce sera toujours au pire moment qui soit.

    En l'occurrence, si le développeur (ce sera peut-être toi-même!) qui utilise ta bibliothèque prend conscience d'une manière ou d'une autre de l'existence du fichier function.hpp, tu dois t'attendre à ce que ce ne soit qu'une question de temps avant qu'il ne décide d'utiliser function au lieu d'utiliser le wrapper.

    Cela ne devrait (théoriquement) pas trop porter à conséquence, mais un fait demeurera: ce n'est pas sous cette forme que tu avais prévu que ta bibliothèque serait utilisée.

    NOTA: il y aura forcément à un moment ou à un autre des situations dans lequelles tu n'auras pas d'autre choix que d'utiliser des fonctions inline (je pense, entre autres, au fonctions et aux classes template). Mais, tant que tu n'en as pas vraiment besoin, tu auras sans doute bien plus facile de ne pas t'en inquiéter; de faire "comme si cette possibilité n'existait pas"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #8
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 469
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 469
    Points : 6 102
    Points
    6 102
    Par défaut
    Citation Envoyé par koala01 Voir le message
    En l'occurrence, si le développeur (ce sera peut-être toi-même!) qui utilise ta bibliothèque prend conscience d'une manière ou d'une autre de l'existence du fichier function.hpp, tu dois t'attendre à ce que ce ne soit qu'une question de temps avant qu'il ne décide d'utiliser function au lieu d'utiliser le wrapper.
    Si l'utilisateur a tout le code source de la bibliothèque, ce sera un problème de visibilité publique/privée, pas un problème de en ligne/pas en ligne.
    Si l'utilisateur a seulement un fichier compilé et des fichiers d'entête, on n'est pas obligé de fournir à l'utilisateur tous les fichiers d'entête : on peut lui cacher "function.hpp" de la même manière qu'on lui cache les ".cpp".

    ----------

    Le principal inconvénient potentiel des fonctions en ligne, c'est le temps de compilation.
    Si l'implémentation d'une fonction en ligne nécessite d'inclure certains entêtes pour que ça compile, alors toutes les unités de compilation qui accèdent à cette fonction en ligne devront inclure ces entêtes.

    Par exemple, soit le bout de code très simple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
     
    inline void say_hello()
    {
    	std::cout << "Hello world!\n";
    }
     
    int main()
    {
    	say_hello();
    	return 0;
    }
    J'utilise GCC 7.1.0 via MinGW. J'ai lancé uniquement le préprocesseur avec la commande suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    g++ -E -P main.cpp -o main.ii
    Dans le fichier "main.ii", la ligne #include <iostream> a été remplacée par... 17 681 lignes de code !!!

    Si je mets say_hello dans un entête, je vais être obligé d'inclure l'entête <iostream> dans cet entête et toutes les unités de compilation qui incluront l'entête contenant say_hello vont se farcir l'entête <iostream>.

    Remarque 1 :
    Plus tard, quand on aura enfin la fonctionnalité des modules dans le standard C++, on pourra éviter de parser plusieurs fois les mêmes entêtes. En attendant, on a des solutions non standard comme les entêtes précompilés.

    Remarque 2 :
    Quand on a un simple getter de la forme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    inline Machin getMachin() const { return m_machin; }
    qui ne nécessite pas d'ajouter d'inclusion, alors le fait qu'il soit en ligne ne posera pas vraiment de problème de temps de compilation. Dans ce genre de cas, je n'hésite pas à le mettre en ligne.

  9. #9
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
     
    inline void say_hello()
    {
    	std::cout << "Hello world!\n";
    }
     
    int main()
    {
    	say_hello();
    	return 0;
    }
    Ne donne pas juste ça ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include <iostream>
     
    int main()
    {
    	std::cout << "Hello world!\n";
    	return 0;
    }
    Je ne vois pas pourquoi le inline fait que "iostream" soit aussi dans le main.cpp.

    Après dans mon code j'ai l'appelle en boucle des fonctions avec une 10e de paramètres donc j'ai peur que cela ralentisse une peu le code si ce n'est pas inline.

  10. #10
    Expert éminent sénior
    Avatar de Kannagi
    Homme Profil pro
    cyber-paléontologue
    Inscrit en
    Mai 2010
    Messages
    3 214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : cyber-paléontologue

    Informations forums :
    Inscription : Mai 2010
    Messages : 3 214
    Points : 10 140
    Points
    10 140
    Par défaut
    Ben la meilleur réponse est de regarder le code assembleur.

    Pour :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    #include <iostream>
     
    inline void say_hello()
    {
    	std::cout << "Hello world!\n";
    }
     
    int main()
    {
    	say_hello();
    	return 0;
    }
    On a :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
     
    	.file	"test.cpp"
    	.local	_ZStL8__ioinit
    	.comm	_ZStL8__ioinit,1,1
    	.section	.rodata
    .LC0:
    	.string	"Hello world!\n"
    	.section	.text._Z9say_hellov,"axG",@progbits,_Z9say_hellov,comdat
    	.weak	_Z9say_hellov
    	.type	_Z9say_hellov, @function
    _Z9say_hellov:
    .LFB971:
    	.cfi_startproc
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	movl	$.LC0, %esi
    	movl	$_ZSt4cout, %edi
    	call	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    	popq	%rbp
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE971:
    	.size	_Z9say_hellov, .-_Z9say_hellov
    	.text
    	.globl	main
    	.type	main, @function
    main:
    .LFB972:
    	.cfi_startproc
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	call	_Z9say_hellov
    	movl	$0, %eax
    	popq	%rbp
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE972:
    	.size	main, .-main
    	.type	_Z41__static_initialization_and_destruction_0ii, @function
    _Z41__static_initialization_and_destruction_0ii:
    .LFB976:
    	.cfi_startproc
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	subq	$16, %rsp
    	movl	%edi, -4(%rbp)
    	movl	%esi, -8(%rbp)
    	cmpl	$1, -4(%rbp)
    	jne	.L4
    	cmpl	$65535, -8(%rbp)
    	jne	.L4
    	movl	$_ZStL8__ioinit, %edi
    	call	_ZNSt8ios_base4InitC1Ev
    	movl	$__dso_handle, %edx
    	movl	$_ZStL8__ioinit, %esi
    	movl	$_ZNSt8ios_base4InitD1Ev, %edi
    	call	__cxa_atexit
    .L4:
    	leave
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE976:
    	.size	_Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
    	.type	_GLOBAL__sub_I_main, @function
    _GLOBAL__sub_I_main:
    .LFB977:
    	.cfi_startproc
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	movl	$65535, %esi
    	movl	$1, %edi
    	call	_Z41__static_initialization_and_destruction_0ii
    	popq	%rbp
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE977:
    	.size	_GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
    	.section	.init_array,"aw"
    	.align 8
    	.quad	_GLOBAL__sub_I_main
    	.hidden	__dso_handle
    	.ident	"GCC: (GNU) 4.7.2 20121109 (Red Hat 4.7.2-8)"
    	.section	.note.GNU-stack,"",@progbits
    Pour :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    #include <iostream>
     
     
     
    int main()
    {
    	std::cout << "Hello world!\n";
    	return 0;
    }
    On a :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
     
    	.file	"test2.cpp"
    	.local	_ZStL8__ioinit
    	.comm	_ZStL8__ioinit,1,1
    	.section	.rodata
    .LC0:
    	.string	"Hello world!\n"
    	.text
    	.globl	main
    	.type	main, @function
    main:
    .LFB971:
    	.cfi_startproc
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	movl	$.LC0, %esi
    	movl	$_ZSt4cout, %edi
    	call	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    	movl	$0, %eax
    	popq	%rbp
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE971:
    	.size	main, .-main
    	.type	_Z41__static_initialization_and_destruction_0ii, @function
    _Z41__static_initialization_and_destruction_0ii:
    .LFB975:
    	.cfi_startproc
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	subq	$16, %rsp
    	movl	%edi, -4(%rbp)
    	movl	%esi, -8(%rbp)
    	cmpl	$1, -4(%rbp)
    	jne	.L3
    	cmpl	$65535, -8(%rbp)
    	jne	.L3
    	movl	$_ZStL8__ioinit, %edi
    	call	_ZNSt8ios_base4InitC1Ev
    	movl	$__dso_handle, %edx
    	movl	$_ZStL8__ioinit, %esi
    	movl	$_ZNSt8ios_base4InitD1Ev, %edi
    	call	__cxa_atexit
    .L3:
    	leave
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE975:
    	.size	_Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
    	.type	_GLOBAL__sub_I_main, @function
    _GLOBAL__sub_I_main:
    .LFB976:
    	.cfi_startproc
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	movl	$65535, %esi
    	movl	$1, %edi
    	call	_Z41__static_initialization_and_destruction_0ii
    	popq	%rbp
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE976:
    	.size	_GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
    	.section	.init_array,"aw"
    	.align 8
    	.quad	_GLOBAL__sub_I_main
    	.hidden	__dso_handle
    	.ident	"GCC: (GNU) 4.7.2 20121109 (Red Hat 4.7.2-8)"
    	.section	.note.GNU-stack,"",@progbits
    niveau compilation j'ai fait ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    [kannagi@kannagi cpp]$ g++ test.cpp -S
    [kannagi@kannagi cpp]$ g++ test2.cpp -S
    Pour ce qui pige que dalle a l'assembleur (et de plus le C++ en assembleur est pas évident) .
    Ben quand on fait ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    #include <iostream>
     
    inline void say_hello()
    {
    	std::cout << "Hello world!\n";
    }
     
    int main()
    {
    	say_hello();
    	return 0;
    }
    c'est équivalent a :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    #include <iostream>
     
    void say_hello()
    {
    	std::cout << "Hello world!\n";
    }
     
    int main()
    {
    	say_hello();
    	return 0;
    }
    Mais rien d'étonnant c'est ce que t'ont expliqué plein de personne dans le post , le compilateur choisira de remplacer le inline par une fonction ou non dans ton code , et il est très probable qu'il optimisera quand il verra une boucle :p

    Et donc faire un wrapper d'un inline est sûrement une mauvaise idée , il y'aura forcément une double appelle de fonction , parce que je doute que le compilo va optimiser quand il verra le inline seul dans la fonction.

    Pour ton souci d'optimisation , si ta boucle doit être optimisé (parce que ton programme fait appelle a cette boucle et doit le fait 1 million de fois )effectivement elle doit etre optimisé et donc en admettant que ton algo est le bon :
    -ne faire appel a aucune fonction
    -éviter de faire du calcul (préférer des valeurs pré calculer si possible).
    -déplier la boucle si le compilateur ne le fait pas (mais normalement c'est une optimisation vielle comme le monde ) , mais si tu fait un if qui est particulier dans ta boucle il est plus judicieux de le faire hors de ta boucle et de faire des test a part.
    -mettre les options du compilateur pour l'optimisation (parce que sans cela le code fourni par le compilateur est médiocre).

  11. #11
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Il est à noter que le constat de "non inlining" de la fonction de Kannagi est certainement lié à la non optimisation (g++ -S et non g++ -O2 -S ou g++ -O3 -S).

    On en revient toujours à la même conclusion: c'est l'algorithmie et la représentation des données qu'il faut améliorer.
    Ca peut être l'occasion de regarder Boost.Container (notamment les flat_map par exemple) ou std::unordered_map.

    Autant que possible, il faudrait ne pas modifier les conteneurs, c'est à dire ne pas ajouter, supprimer ou déplacer des éléments.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  12. #12
    Expert éminent sénior
    Avatar de Kannagi
    Homme Profil pro
    cyber-paléontologue
    Inscrit en
    Mai 2010
    Messages
    3 214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : cyber-paléontologue

    Informations forums :
    Inscription : Mai 2010
    Messages : 3 214
    Points : 10 140
    Points
    10 140
    Par défaut
    @ternel Exact avec -O3 , il est "inlining" et le code fait 50 lignes

    Mais je pense que le mieux c'est qu'il montre son code source , on pourra l’aiguiller plus facilement sur les optimisations a faire.

  13. #13
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    En fait, ce qu'il faut comprendre, c'est que le mot clé inline ne fait que demander au compilateur d'envisager la possibilité de remplacer l'appel d'une fonction par le code binaire correspondant à celle-ci.

    Le truc, c'est que le compilateur va trouver tout un tas de raisons de ne pas le faire. Avec Gcc, on peut citer dans l'ordre:
    • cela n'a rien à voir avec une option quelconque, mais une fonction virtuelle ne sera jamais inlinée, à cause du mécanisme même mis en oeuvre par la virtualité
    • si l'option -fno-inline est active ou si l'option -finline-functions (qui n'est fournie "par défaut" qu'avec l'option -O3 minimum) n'a pas été fournie
    • si -finline-function est fournie, en fonction du nombre de pseudo instructions de la fonction, qui ne peut pas dépasser la valeur définie au travers de l'option -finline-limits (il semblerait qu'elle soit par défaut définie à 600)
    • Si le nombre d'instructions dans la représentation interne de Gcc est supérieur à certaines valeurs:
      • max-inline-insns-single (généralement la valeur de -finline-limits/2) pour les fonctions définies directement dans le corps des classes
      • max-inline-insns-auto (généralement la valeur de -finline-limits/2) pour les fonctions marquées inline, à condition que -finilne-function soit défini
      • large-function-insns qui limite le nombre d'instructions obtenu de la fonction appelante (définie à 2700)
      • large-function-growth qui limite l'augmentation de la taille de la fonction appelante du fait de l'inining (par défaut, cette taille peut doubler)
      • et bien d'autres encore


    Si tu prends la fonction say_hello indiquée par Kannagi, tu remarqueras par exemple qu'il y aura systématiquement un appel à ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc alors que, du fait même que std::cout n'est qu'une instance globale d'une spécialisation totale de la classe template std::basic_ostream.

    Or, a priori, toutes les fonctions template sont forcément inline, car c'est le seul moyen de permettre au compilateur de générer le code binaire correspondant. Cela nous démontre bien que l'on se trouve bel et bien dans une situation dans laquelle au moins l'une des limites (qu'elle ait été citée ou non) a été atteinte

    Maintenant, on peut aussi forcer le compilateur à inliner les fonctions (du moins, le fonctions non virtuelles) en leur donnant l'attribut __forceinline sous VC++ ou l'attribut __attribute__((always_inline)). Mais cela n'aura d'effet que sur la fonction qui dispose de cet attribut (jamais sur les fonctions appelées par la fonction).

    Voici à peu près ce qu'il faut savoir sur les fonctions inline.
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  14. #14
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Kannagi Voir le message
    @ternel Exact avec -O3 , il est "inlining" et le code fait 50 lignes

    Mais je pense que le mieux c'est qu'il montre son code source , on pourra l’aiguiller plus facilement sur les optimisations a faire.
    Je pense que le mieux, c'est surtout qu'il ne commence pas à se casser la tête avec l'éventuelle optimisation due à l'inining dans un premier temps.

    Il y a tant de choses à faire et qui auront un impact bien plus significatif avant d'en arriver à cette extrêmité qui ne pourra -- de toutes manières -- n'être validée que par des benchmarks précis et fiables!
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  15. #15
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 469
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 469
    Points : 6 102
    Points
    6 102
    Par défaut
    Matthieu76, je vais traduire un extrait de mon message :
    Citation Envoyé par Pyramidev Voir le message
    Si je mets say_hello dans un entête, je vais être obligé d'inclure l'entête <iostream> dans cet entête et toutes les unités de compilation qui incluront l'entête contenant say_hello vont se farcir l'entête <iostream>.
    Si on a un entête "say.h" qui contient le code de la fonction say_hello :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #ifndef SAY_H
    #define SAY_H
     
    #include <iostream>
     
    inline void say_hello()
    {
    	std::cout << "Hello world!\n";
    }
     
    #endif
    alors "say.h" a besoin d'avoir #include <iostream>. Sinon, le compilateur risquera d'afficher une erreur en disant qu'il ne connaît pas std::cout.

    Par contre, si "say.h" ne fait que déclarer say_hello sans la définir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #ifndef SAY_H
    #define SAY_H
     
    void say_hello();
     
    #endif
    alors "say.h" n'aura pas besoin d'avoir #include <iostream>. C'est "say.cpp" qui aura #include <iostream>.

    A présent, il faut se rappeler que, lors de la compilation, avant l'étape de l'édition des liens, chaque ".cpp" est compilé en un fichier objet (qui a l'extension ".o" si le compilateur est GCC), indépendamment des autres ".cpp". Ensuite, l'édition des liens travaillera avec les fichiers objets pour former l'exécutable.
    Or, au début de la compilation d'un ".cpp", il y a une étape qui s'appelle la précompilation qui est exécutée par le préprocesseur.
    Quand le préprocesseur voit une ligne #include "nomFichier", il la remplace par le contenu du fichier. Il fait cela récursivement.
    Ainsi, quand le préprocesseur lit #include "say.h", il la remplace par le contenu de "say.h". Mais si "say.h" contient #include <iostream>, il va remplacer cette ligne par le contenu de <iostream>. <iostream> contient aussi des #include qui seront pris en compte à leur tour, et ainsi de suite jusqu'à avoir plusieurs milliers de lignes de code.
    Après la précompilation, l'étape suivante de la compilation du ".cpp" qui a inclus #include "say.h" se retrouve alors à gérer toutes ces lignes de code.

    Par contre, si "say.h" contient juste :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #ifndef SAY_H
    #define SAY_H
     
    void say_hello();
     
    #endif
    alors faire un #include "say.h" ne coûtera pratiquement rien en temps de compilation.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [POO] Est-il possible de récupérer les arguments muets d'une fonction ?
    Par RomainVALERI dans le forum Général JavaScript
    Réponses: 24
    Dernier message: 16/11/2009, 13h51
  2. [Tkinter] Est-il possible de faire pivoter des PhotoImage ?
    Par sigmar_avenger dans le forum Tkinter
    Réponses: 9
    Dernier message: 06/01/2007, 15h18
  3. Est il possible de faire une fusion
    Par boy569 dans le forum Composants VCL
    Réponses: 3
    Dernier message: 07/02/2006, 13h58
  4. Est il possible de faire planter un système Unix
    Par Patrick PETIT dans le forum Administration système
    Réponses: 15
    Dernier message: 15/06/2004, 15h16
  5. est il possible de faire un trie sur un paramètre donné
    Par chtiboss dans le forum XSL/XSLT/XPATH
    Réponses: 8
    Dernier message: 17/03/2004, 11h51

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo