operator+ ohne Kopie?



  • 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;
    


  • Michael E. schrieb:

    Nexus schrieb:

    Und lass das const beim Rückgabetyp weg.

    Warum?

    m + m = m;
    

    Und was soll daran verboten sein? Es ist wirklich keine Gefahr. Erinnere Dich, wie oft Du a+b=c; getippt hast und c=a+b; meintest.

    Das const macht Dir nur Optimierungen kaputt.



  • megaweber schrieb:

    Hier eine Lösung mit ein wenig template Vodoo:

    www.uop.edu.jo/download/PdfCourses/Cplus/sanderson_templates_lecture_uqcomp7305.pdf

    Hihi,

    habe gerade sowas Ähnliches hingefrickelt. Schön isses zwar nicht, aber es zeigt, dass es funktioniert.

    #include <memory>
    
    class Matrix
    {
    	double values_[4][4];
    
    public:
    	Matrix()
    	{
       	std::memset( &values_, 0, sizeof( values_ ) );
    	}
    
    	template<typename T>
    	Matrix( const T& op )
    	{
    		for( unsigned int r = 0; r < 4; ++r )
    			for( unsigned int c = 0; c < 4; ++c )
    				values_[r][c] = op( r,c );
    	}
    
    	double operator()( unsigned int Row, unsigned int Col ) const
    	{
    		return values_[Row][Col];
    	}
    
    	double& operator()( unsigned int Row, unsigned int Col ) 
    	{
    		return values_[Row][Col];
    	}
    
    	template<typename T>
    	Matrix& operator=( const T& op )
    	{
    		for( unsigned int r = 0; r < 4; ++r )
    			for( unsigned int c = 0; c < 4; ++c )
    				values_[r][c] = op( r,c );
    		return *this;
    	}
    };
    
    template<typename T, typename U>
    class MatrixAdditionProxy
    {
    	const T& M1_;
    	const U& M2_;
    
    public:
    	MatrixAdditionProxy( const T& M1, const U& M2 ) : M1_( M1 ), M2_( M2 )
    	{
    	}
    
    	double operator()( unsigned int Row, unsigned int Col ) const
    	{
    		return M1_( Row, Col ) + M2_( Row, Col );
    	}
    };
    
    template<typename T, typename U>
    MatrixAdditionProxy<T,U> operator+( const T& M1, const U& M2 )
    {
    	return MatrixAdditionProxy<T,U>( M1, M2 );
    }
    
    template<typename T, typename U>
    class MatrixSubtractionProxy
    {
    	const T& M1_;
    	const U& M2_;
    
    public:
    	MatrixSubtractionProxy( const T& M1, const U& M2 ) : M1_( M1 ), M2_( M2 )
    	{
    	}
    
    	double operator()( unsigned int Row, unsigned int Col ) const
    	{
    		return M1_( Row, Col ) - M2_( Row, Col );
    	}
    };
    
    template<typename T, typename U>
    MatrixSubtractionProxy<T,U> operator-( const T& M1, const U& M2 )
    {
    	return MatrixSubtractionProxy<T,U>( M1, M2 );
    }
    
    int main()
    {
    	Matrix m1, m2, m3, m4;
    
    	m1( 0,0 ) = 1;
    	m2( 0,0 ) = 2;
    	m3( 0,0 ) = 3;
    	m4( 0,0 ) = 11;
    
    	Matrix m5 = m1 + m2 - (m3 - m4);
    
    	double r = m5( 0,0 );
    }
    

    volkard schrieb:

    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.

    Blitz++ setzt diese oder ähnliche Techniken ein und erreicht dabei Geschwindigkeiten von Fortran, scheint also zu gehen.



  • DocShoe schrieb:

    Blitz++ setzt diese oder ähnliche Techniken ein und erreicht dabei Geschwindigkeiten von Fortran, scheint also zu gehen.

    Die Proxies allein versagen.
    "oder ähnliche Techniken" bräuchte man wohl.
    Auf expression templates wies ich um 09:47 bereits hin, wohl zu leise.



  • volkard schrieb:

    Das const macht Dir nur Optimierungen kaputt.

    Welche genau?



  • Michael E. schrieb:

    volkard schrieb:

    Das const macht Dir nur Optimierungen kaputt.

    Welche genau?

    Das zerstörende Auslutschen von r-values.



  • Michael E. schrieb:

    Welche genau?

    Die Move-Zuweisung, die hier möglich wäre.

    a = b + c;
    

    Diese const -Rückgabe ist ohnehin nur ein verzweifelter Versuch, Klassen-RValues wie skalare RValues zu behandeln (die nicht zugewiesen werden können, obwohl sie nicht const -qualifiziert sind). Wirkliche Fehler vermeidet man nicht.


Anmelden zum Antworten