g012 | Bonsoir bonsoir.
Bin alors je lis partout: le C++ peut être quasi aussi rapide que du C si on évite la ligature dynamique, telle lib dit: on a pas utilisé l'héritage virtuel pour éviter la chute de perfs, etc...
Alors moi je me suis posé la question: pourquoi c'est réputé si lent ?
Test:
Code :
- class Parent
- {
- public:
- virtual int getValue(int a) { return a+a; }
- virtual int getSecondValue(int a) { return 2*a-a; }
- };
- class Fils : public Parent
- {
- public:
- virtual int getValue(int a) { return a*a; }
- virtual int getSecondValue(int a) { return 4*a-a; }
- };
- int main()
- {
- Parent* p;
- Fils f;
- p = &f;
- p->getValue(3);
- p->getSecondValue(3);
- }
|
(je sais y'a pas de return dans main mais c pas la question)
Après compilation avec les 4 virtual, sous vc7.1 en mode debug sans optimisations, on obtient pour main:
Code :
- _main PROC NEAR
- ; 16 : {
- push ebp
- mov ebp, esp
- sub esp, 8
- ; 17 : Parent* p;
- ; 18 : Fils f;
- lea ecx, DWORD PTR _f$[ebp]
- call ??0Fils@@QAE@XZ
- ; 19 : p = &f;
- lea eax, DWORD PTR _f$[ebp]
- mov DWORD PTR _p$[ebp], eax
- ; 20 : p->getValue(3);
- push 3
- mov ecx, DWORD PTR _p$[ebp]
- mov edx, DWORD PTR [ecx]
- mov ecx, DWORD PTR _p$[ebp]
- call DWORD PTR [edx]
- ; 21 : p->getSecondValue(3);
- push 3
- mov eax, DWORD PTR _p$[ebp]
- mov edx, DWORD PTR [eax]
- mov ecx, DWORD PTR _p$[ebp]
- call DWORD PTR [edx+4]
- ; 22 : }
- xor eax, eax
- mov esp, ebp
- pop ebp
- ret 0
- _main ENDP
|
En virant les 4 virtual:
Code :
- _main PROC NEAR
- ; 16 : {
- push ebp
- mov ebp, esp
- sub esp, 8
- ; 17 : Parent* p;
- ; 18 : Fils f;
- ; 19 : p = &f;
- lea eax, DWORD PTR _f$[ebp]
- mov DWORD PTR _p$[ebp], eax
- ; 20 : p->getValue(3);
- push 3
- mov ecx, DWORD PTR _p$[ebp]
- call ?getValue@Parent@@QAEHH@Z ; Parent::getValue
- ; 21 : p->getSecondValue(3);
- push 3
- mov ecx, DWORD PTR _p$[ebp]
- call ?getSecondValue@Parent@@QAEHH@Z ; Parent::getSecondValue
- ; 22 : }
- xor eax, eax
- mov esp, ebp
- pop ebp
- ret 0
- _main ENDP
|
Moi les différences que je vois sont:
- appel d'un constructeur de Fils, qui appelle un constructeur de Parent en virtual, ce qui n'est pas fait en non virtual. Vlà le code du constructeur de Fils au cas où on me le demande:
Code :
- ??0Fils@@QAE@XZ PROC NEAR ; Fils::Fils, COMDAT
- ; _this$ = ecx
- push ebp
- mov ebp, esp
- push ecx
- mov DWORD PTR _this$[ebp], ecx
- mov ecx, DWORD PTR _this$[ebp]
- call ??0Parent@@QAE@XZ
- mov eax, DWORD PTR _this$[ebp]
- mov DWORD PTR [eax], OFFSET FLAT:??_7Fils@@6B@
- mov eax, DWORD PTR _this$[ebp]
- mov esp, ebp
- pop ebp
- ret 0
- ??0Fils@@QAE@XZ ENDP ; Fils::Fils
|
- un appel des fonctions via un offset depuis this, et une écriture d'ecx 2 fois pareil... donc une inutile.
Soit au coût de l'appel, 2 mov en plus et un call indirect, ce qui devrait pas faire plus de 3-4 cycles en plus par appel, ce que je trouve super négligeable comparé au temps d'exécution d'une fonction normalement longue/complexe... Je vois pas trop la différence avec utiliser un pointeur de fonction en C, qui serait le moyen que j'utiliserais normalement pour remplacer ces virtual.
Quelqu'un peut m'expliquer pourquoi on dit que c'est lent alors ?
Merci ! |