operator+ ohne Kopie?



  • Hallo,

    sorry, wenn ich das schon wieder anreiße, aber:
    Ich will eine Matrix-Klasse schreiben und den +Operator überladen. An sich nicht schwer, aber: Angenommen, ich habe zwei gigantische Matrizen und will die addieren: C = A + B;
    Nun muss ich dazu eine neue (summierte) Matrix anlegen und die zurückgeben, wobei die wieder kopiert wird. Was doof ist, weil es dauert und ggf. auch nicht so viel platz vorhanden ist.
    Kann ich das irgendwie hin bekommen, so dass ich nur 3 statt 4 Matrizen brauche, sprich ohne das Ergebnis kopieren zu müssen?

    Danke & Gruß



  • Das übernimmt der Compiler für gewöhnlich beim Optimieren.

    BTW: Wenn du schon davon redest, dass dir der Speicherplatz ausgehen könnte: Was hast du denn für riesige non-sparse Matrizen?



  • In C++ kannst du nur hoffen, dass der Compiler gut optimiert (was er auch tut, diese Optimierung ist grundlegend).



  • EOutOfResources schrieb:

    In C++0x macht man das mit Move-Semantics:

    Matrix&& operator+ (const Matrix& Lhs, const Matrix& Rhs);
    

    In C++ kannst du nur hoffen, dass der Compiler gut optimiert (was er auch tut, diese Optimierung ist grundlegend).

    Nicht nur, dass du hier die Matrizen per const-Ref nimmst, und somit kopieren musst, gibst du auch noch eine RValue Referenz auf eine lokale Variable zurück.
    Das war nix.



  • 314159265358979 schrieb:

    Nicht nur, dass du hier die Matrizen per const-Ref nimmst, und somit kopieren musst

    Kannst du das bitte ein wenig ausführen?



  • War ein Denkfehler. Punkt 2 stimmt trotzdem :p

    Allerdings würde ich hier 3 Versionen anbieten:

    Matrix operator + (Matrix&& lhs, const Matrix& rhs);
    Matrix operator + (const Matrixt& lhs, Matrix&& rhs);
    Matrix operator + (const Matrix& lhs, const Matrix& rhs);
    


  • EOutOfResources schrieb:

    In C++0x macht man das mit Move-Semantics:

    Matrix&& operator+ (const Matrix& Lhs, const Matrix& Rhs);
    

    In C++ kannst du nur hoffen, dass der Compiler gut optimiert (was er auch tut, diese Optimierung ist grundlegend).

    Du gibst eine Referenz auf ein neues Objekt zurück? Stürz der Nasaldämon dann nicht ab?



  • 314159265358979 schrieb:

    Punkt 2 stimmt trotzdem :p

    Ich war nochmals im Artikel (hier am Board). Hast natürlich recht.



  • yYy schrieb:

    C = A + B;
    Kann ich das irgendwie hin bekommen, so dass ich nur 3 statt 4 Matrizen brauche, sprich ohne das Ergebnis kopieren zu müssen?

    Ja, aber nur, wenn Du A oder B danach nicht mehr brauchst.
    Vielleicht in Richtung 314159265358979 gehen, vielleicht in Richtung expression templates und vielleicht einfach

    A+=B;
    

    schreiben und mit A weiterarbeiten.



  • Michael E. schrieb:

    Das übernimmt der Compiler für gewöhnlich beim Optimieren.

    Irgendwie nicht...
    Ich benutze Visual Studio C++ 2010 Express und habe im Release-Modus gebaut. Ich habe Ausgaben in den Konstruktoren und in den Destruktoren drin. Wenn ich folgendes mache

    cout << (m+m) << endl;
    

    wobei m die Matrix ist, werden 2 Objekte erzeugt und eins zerstört. Oder muss man in Visual Studio die Optimierung irgendwo anschalten?

    Michael E. schrieb:

    BTW: Wenn du schon davon redest, dass dir der Speicherplatz ausgehen könnte: Was hast du denn für riesige non-sparse Matrizen?

    Hab ich nicht. Das ist eine theoretische Grundsatzüberlegung. Sie müssen ja auch nicht unbedingt soo riesig sein, ich könnte ja auch einfach nur wenig Speicher haben.



  • Dein Code wird doch unter der Konfiguration "Release" compiliert? Dann sollten die nötigen Optimierungen an sein.

    Zeig mal den Code des operator+. Nur sichergehen, daß Du RVO-freundlichen Code schreibst, sagen wir mal ganz zu Anfang das Ergebnis anlegen und nur einen Exit-Punkt, das sollte dann jeder Compiler raffen.



  • yYy schrieb:

    Oder muss man in Visual Studio die Optimierung irgendwo anschalten?

    Ich weis nicht wie die Default-Einstellungen aussehen aber man kann bei den Einstellungen die Optimierungen verändern.



  • volkard schrieb:

    Dein Code wird doch unter der Konfiguration "Release" compiliert? Dann sollten die nötigen Optimierungen an sein.

    Zeig mal den Code des operator+. Nur sichergehen, daß Du RVO-freundlichen Code schreibst, sagen wir mal ganz zu Anfang das Ergebnis anlegen und nur einen Exit-Punkt, das sollte dann jeder Compiler raffen.

    Matrix& operator+=(Matrix const &B)
    		{
    			if (m != B.m || n != B.n)
    			{
    				throw std::invalid_argument("Matrizen muessen die selben Dimensionen haben.");
    			}
    			for (unsigned int i = 0; i < m*n; i++)
    				linear[i] += B.linear[i];
    			return *this;
    		}
    
    		const Matrix operator+(Matrix const &B) const
    		{
    			return Matrix(*this) += B;
    		}
    

    EOutOfResources schrieb:

    Ich weis nicht wie die Default-Einstellungen aussehen aber man kann bei den Einstellungen die Optimierungen verändern.

    Sowas dachte ich mir :p Ich finds nur nicht.



  • yYy schrieb:

    Ich finds nur nicht.

    Ich habe nur Visual C++ 2008. Menüleiste -> Projekt -> %Projektname-Eigenschaften... -> Konfigurationseigenschaften -> C/C++ -> Optimierung



  • Du könntest sowas wie´n Proxy bauen, allerdings setzt der voraus, dass die beiden Matrizen A und B mindestens genauso lange leben wie der Proxy:

    class MatrixAdditionProxy
    {
       const Matrix& M1_;
       const Matrix& M2_;
    
    public:
       MatrixAdditionProxy( const Matrix& M1, const Matrix& M2 ) : M1_( M1 ), M2_( M2 )
       {
       }      
    
       double operator()( unsigned int Row, unsigned int Col ) const
       {
          return M1_( Row, Col ) + M2_( Row, Col );
       }
    };
    
    MatrixAdditionProxy operator+( const Matrix& m1, const Matrix& m2 )
    {
       return MatrixAdditionProxy ( m1, m2 );
    }
    

    Damit kommst du sogar ganz ohne Kopien aus, hab mir jetzt aber keine Gedanken darüber gemacht, wie man das transparent umsetzen kann. Vermutlich braucht die Matrixklasse irgendwo einen Zuweisungsoperator, der das alles wieder auflöst. Ob das nur für Rechenoperationen taugt, weil man hinterher ja doch wieder eine Matrix (also ein vollwertiges Matrixobjekt) haben möchte. So Sachen wie

    Matrix D = A + B + C * D;
    

    gingen jedoch deutlich flotter und ohne Kopien.



  • DocShoe schrieb:

    So Sachen wie

    Matrix D = A + B + C * D;
    

    gingen jedoch deutlich flotter und ohne Kopien.

    Sicher auch mit Cache-Effekten?
    Bei E=(A+B)*(C+D) hätte ich aber schon zu viel Angst.





  • yYy schrieb:

    const Matrix operator+(Matrix const &B) const
    		{
    			return Matrix(*this) += B;
    		}
    

    Schreib das mal um zu

    const Matrix operator+(Matrix const &A, Matrix const &B)
    {
    	Matrix tmp(A);
    	tmp += B;
    	return tmp;
    }
    

    Dann sollte auch Visual Studio die Optimierung durchführen.



  • Und lass das const beim Rückgabetyp weg.



  • Nexus schrieb:

    Und lass das const beim Rückgabetyp weg.

    Warum?

    m + m = m;
    

Log in to reply