C++ Optimierungsfrage: Koennen Compiler das?



  • Koennen Compiler derartiges wegoptimieren:

    struct StructMitZweiAttributen
    {
        public: 
            int a,b;
    }
    
    class Foo
    {
       private:
           StructMitZweiAttributen bar;
    
       public:
           StructMitZweiAttributen baar()
           {
               return bar;
           }
    }
    
    //irgendwo anders im code
    Foo foo;
    int ergebnis = foo.baar().a + foo.baar().b / (foo.baar().a + foo.baar().b);
    

    Machen aktuelle Compiler (gcc, msvc, etc.) mit Optimierung daraus sowas aehnliches:

    Foo foo;
    StructMitZweiAttributen tmp = foo.baar();
    int ergebnis = tmp.a + tmp.b / (tmp.a + tmp.b);
    


  • gcc mingw 4.5.0

    #include <iostream>
    using namespace std;
    
    struct StructMitZweiAttributen
    {
    public:
        int a,b;
    };
    
    class Foo
    {
    public:
        StructMitZweiAttributen bar;
    
    public:
        StructMitZweiAttributen baar()
        {
            return bar;
        }
    };
    
    int main()
    {
        Foo foo;
        cin>>foo.bar.a;
        cin>>foo.bar.b;
    
        int ergebnis = foo.baar().a + foo.baar().b / (foo.baar().a + foo.baar().b);
        cout<<ergebnis;
    }
    

    wird zu

    leal	24(%esp), %eax
    	movl	$__ZSt3cin, (%esp)
    	movl	%eax, 4(%esp)
    	call	__ZNSirsERi
    	leal	28(%esp), %eax
    	movl	$__ZSt3cin, (%esp)
    	movl	%eax, 4(%esp)
    	call	__ZNSirsERi
    	movl	28(%esp), %eax
    	movl	24(%esp), %ecx
    	movl	$__ZSt4cout, (%esp)
    	leal	(%eax,%ecx), %ebx    //addition
    	cltd
    	idivl	%ebx                 //division
    	leal	(%eax,%ecx), %ecx    //addition
    	movl	%ecx, 4(%esp)
    	call	__ZNSolsEi
    

    Also klares ja.
    Der sieht hier den Code von baar(), macht ihn inline und optimiert alles platt.
    Anders sieht es aus, wenn er baar() nicht sehen kann.

    Dazu muß ich baar() in eine andere *.cpp-Datei machen. In der selben wird es optimiert, auch wenn ich sie unter der main() hinschreibe.

    movl	%ebx, 4(%esp)
    	movl	$__ZSt3cin, (%esp)
    	call	__ZNSirsERi
    	leal	44(%esp), %eax
    	movl	$__ZSt3cin, (%esp)
    	movl	%eax, 4(%esp)
    	call	__ZNSirsERi
    	movl	%ebx, (%esp)
    	call	__ZN3Foo4baarEv
    	movl	%ebx, (%esp)
    	movl	%eax, %edi
    	call	__ZN3Foo4baarEv
    	movl	%ebx, (%esp)
    	movl	%edx, 28(%esp)
    	call	__ZN3Foo4baarEv
    	movl	%ebx, (%esp)
    	movl	%eax, %esi
    	call	__ZN3Foo4baarEv
    	movl	28(%esp), %eax
    	addl	%edx, %esi
    	movl	$__ZSt4cout, (%esp)
    	cltd
    	idivl	%esi
    	leal	(%eax,%edi), %edi
    	movl	%edi, 4(%esp)
    	call	__ZNSolsEi
    

    Haha!

    Der Compiler weiß nicht, daß die Funktion immer das selbe berechnet. Könnte ja auch immer ein anderes erzeugen.

    Schade.

    Mit

    StructMitZweiAttributen const& baar();
    

    ist es auch unoptimiert.

    Mit

    StructMitZweiAttributen const& baar() __attribute__ ((const));
    

    ist es dann aber wieder optimiert.



  • Übrigens, mit c++filt lassen sich die komischen Symbole durch lesbare ersetzen, z.B. aus diesem Assembler-Ausschnitt:

    volkard schrieb:

    ...

    leal	24(%esp), %eax
    	movl	$__ZSt3cin, (%esp)
    	movl	%eax, 4(%esp)
    	call	__ZNSirsERi
    	leal	28(%esp), %eax
    	movl	$__ZSt3cin, (%esp)
    	movl	%eax, 4(%esp)
    	call	__ZNSirsERi
    	movl	28(%esp), %eax
    	movl	24(%esp), %ecx
    	movl	$__ZSt4cout, (%esp)
    	leal	(%eax,%ecx), %ebx
    	cltd
    	idivl	%ebx
    	leal	(%eax,%ecx), %ecx
    	movl	%ecx, 4(%esp)
    	call	__ZNSolsEi
    

    ...

    wird durch Anwendung von c++filt

    > cat tmp.s | c++filt
    

    das hier:

    leal    24(%esp), %eax
         movl    std::cin, (%esp)
         movl    %eax, 4(%esp)
         call    std::basic_istream<char, std::char_traits<char> >::operator>>(int&)
         leal    28(%esp), %eax
         movl    std::cin, (%esp)
         movl    %eax, 4(%esp)
         call    std::basic_istream<char, std::char_traits<char> >::operator>>(int&)
         movl    28(%esp), %eax
         movl    24(%esp), %ecx
         movl    std::cout, (%esp)
         leal    (%eax,%ecx), %ebx
         cltd
         idivl    %ebx
         leal    (%eax,%ecx), %ecx
         movl    %ecx, 4(%esp)
         call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
    


  • volkard schrieb:

    Der Compiler weiß nicht, daß die Funktion immer das selbe berechnet. Könnte ja auch immer ein anderes erzeugen.

    Schade.

    Wenn VC verwendet wird und LTCG (Link Time Code Generation) verwendet wird, dann wird auch das wegoptimiert.

    LTGC erzuegt und optimiert den Code erst während der Linker-Phase und dadurch kenn der Compiler "alle" Implementierungen als ob diese inline wären.

    Leiderist das ziemlich lahm bei großen Projekten. Bei kleinen Projekten leistet LTGC allerdings irrsinniges.


Anmelden zum Antworten