Konzept Multiplikationsoperator einer Matrix



  • EOutOfResources schrieb:

    neuling21 schrieb:

    try
    {
    new ...
    }
    catch(std::bad_alloc &e)
    {
     exit(-1);
    }
    

    Schon besser. Ich würde es in ein Log schreiben oder eine Meldung bringen. Und die abzufangende Exception muss keinen Namen haben.

    neuling21 schrieb:

    Könnte man irgendwie diese Matrix Kopiererei (1 Code Zeile 1 bezüglich Zeile 37) irgendwie durch Referenzen unterbinden?

    Nein, denn es sind lokale Instanzen. In C++0x gibt es Move-Semantics für exakt diesen Fall.

    Danke dir schonmal für deine Hilfe.

    Meinst du mit Move Sematics so etwas?

    constexpr lapack_matrix lapack_matrix::operator*(lapack_matrix &rhs) const
    

    Dieses Rumkopieren ist bisher der einzige Wehmutstropfen. Man spürt eine deutliche Verzögerung bei größeren Matrizen gegenüber nativem LAPACK



  • Da wäre es eventuell eine Idee, deine Matrix-Klasse mit Referenzzhälung und copy-on-write Funktionalität auszustatten. (d.h. der Speicher wird erst wirklich kopiert, wenn einer der Verwender etwas daran ändern will)



  • neuling21 schrieb:

    constexpr lapack_matrix lapack_matrix::operator*(lapack_matrix &rhs) const
    

    Nein:

    lapack_matrix&& lapack_matrix::operator*(lapack_matrix &rhs) const
    

    Und wie ich bereits gesagt habe (lesen?) sollst du den Parameter und den Rückgabewert const machen!



  • CStoll schrieb:

    Da wäre es eventuell eine Idee, deine Matrix-Klasse mit Referenzzhälung und copy-on-write Funktionalität auszustatten. (d.h. der Speicher wird erst wirklich kopiert, wenn einer der Verwender etwas daran ändern will)

    Ist es so etwas wie ein proxy pattern? Im worst case hätte ich aber immer noch dieses herumkopiere..



  • EOutOfResources schrieb:

    neuling21 schrieb:

    constexpr lapack_matrix lapack_matrix::operator*(lapack_matrix &rhs) const
    

    Nein:

    lapack_matrix&& lapack_matrix::operator*(lapack_matrix &rhs) const
    

    Und wie ich bereits gesagt habe (lesen?) sollst du den Parameter und den Rückgabewert const machen!

    Wurde sofort gemacht 🙂 Nur habe ich es hier im Forum aus dem oberen Beitrag kopiert. Danke



  • neuling21 schrieb:

    Ist es so etwas wie ein proxy pattern? Im worst case hätte ich aber immer noch dieses herumkopiere..

    Bei den Pattern bin ich nicht 100% sattelfest, aber in dieser Anwendung ersparst du dir das Kopieren auf jeden Fall (das Original, von dem kopiert wird, verschwindet sofort im Nirvana - und am Ende ist C der einzige, der die Daten verwendet).

    In den Worst-Case-Fällen, wo du um die tatsächliche Kopie nicht herumkommst, hättest du vermutlich auch mit dem C-Code, den du hier versteckst, deinen Spiecherbereich kopieren müssen, also dürfte das kein so großes Proble sein:

    lapack_matrix M1 = ...;
    lapack_matrix M2 = M1;
    M2(0,0) = -1;//hier wird tatsächlich eine Kopie notwendig
    


  • EOutOfResources schrieb:

    lapack_matrix&& lapack_matrix::operator*(lapack_matrix &rhs) const
    

    Und wie ich bereits gesagt habe (lesen?) sollst du den Parameter und den Rückgabewert const machen!

    Wurde sofort gemacht 🙂 Nur habe ich es hier im Forum aus dem oberen Beitrag kopiert. Danke[/quote]

    Entschuldige dass ich nochmal hinterfragen muss aber ich bekomme im oberen Code diesen fehler. Selbst wenn ich const lapack_matrix tmp(...) mache.

    error C2440: 'return' : cannot convert from 'lapack_matrix' to 'const lapack_matrix &&'
    You cannot bind an lvalue to an rvalue reference
    

    Gibt es schon ein Buch dass in den neuen Standard reinschnüffelt solche tips gut beschreibt und zudem möglichst deutsch (einfaches englisch) ist?



  • Der Standard ist noch nicht verabschiedet. Welchen Compiler hast du?



  • CStoll schrieb:

    Bei den Pattern bin ich nicht 100% sattelfest, aber in dieser Anwendung ersparst du dir das Kopieren auf jeden Fall (das Original, von dem kopiert wird, verschwindet sofort im Nirvana - und am Ende ist C der einzige, der die Daten verwendet).

    In den Worst-Case-Fällen, wo du um die tatsächliche Kopie nicht herumkommst, hättest du vermutlich auch mit dem C-Code, den du hier versteckst, deinen Spiecherbereich kopieren müssen, also dürfte das kein so großes Proble sein:

    lapack_matrix M1 = ...;
    lapack_matrix M2 = M1;
    M2(0,0) = -1;//hier wird tatsächlich eine Kopie notwendig
    

    Das hört sich gut an. Auch die Klammer als Elementzugriffsoperator zu gebrauchen... hab gar nicht daran gedacht.

    Ich führe also in jeder Klasse einen Zeiger auf lapack_data der ersten Matrix:

    class lapack_data
    {
      public:
       double *data;
       unsigned int ref;
    }
    

    und im Destruktor

    lapack_matrix::~lapack_matrix
    {
      if( ((*lapack_data)--) == 0 )
      {
        delete lapack_data;
      }
    }
    

    Bei lesenden Funktionen greife ich ganz normal auf double * zu. Sobald ich einmal etwas ändere Rufe ich eine Funktion auf die:
    -mir den double Zeiger kopiert.
    -Referenz herunterzählt.
    -Wenn Referenz == 0 dann lapack_data löscht
    Habe ich dich richtig verstanden?



  • EOutOfResources schrieb:

    Der Standard ist noch nicht verabschiedet. Welchen Compiler hast du?

    MS Visual Studio 2010 SP1



  • neuling21 schrieb:

    und im Destruktor

    lapack_matrix::~lapack_matrix
    {
      if( ((*lapack_data)--) == 0 )
      {
        delete lapack_data;
      }
    }
    

    eher (--lapack_data->ref)==0 , aber ansonsten sieht es so richtig aus.
    Du solltest nur daran denken, daß Kopier-Konstruktor und operator= das Spiel auch mitmachen.

    Bei lesenden Funktionen greife ich ganz normal auf double * zu. Sobald ich einmal etwas ändere Rufe ich eine Funktion auf die:
    -mir den double Zeiger kopiert.
    -Referenz herunterzählt.
    -Wenn Referenz == 0 dann lapack_data löscht
    Habe ich dich richtig verstanden?

    Fast - wenn ref==1 ist, brauchst du nicht zu kopieren, sondern kannst normal mit den vorhandenen Daten arbeiten.



  • EOutOfResources schrieb:

    Nein:

    lapack_matrix&& lapack_matrix::operator*(lapack_matrix &rhs) const
    

    Wie kommst du nur auf die Idee, eine RValue Referenz auf eine lokale Variable zurückzugeben. RValue Referenzen _sind_ auch nur Referenzen! Eine ganz normale Wert-rückgabe, allerdings mit move, gehört hier her.

    Move-Semantiks bedeutet, dass es einen Move Ctor gibt, der Move op = kann über den Move Ctor sehr einfach implementiert werden, indem man das Objekt per Value nimmt.



  • 314159265358979 schrieb:

    EOutOfResources schrieb:

    Nein:

    lapack_matrix&& lapack_matrix::operator*(lapack_matrix &rhs) const
    

    Wie kommst du nur auf die Idee, eine RValue Referenz auf eine lokale Variable zurückzugeben. RValue Referenzen _sind_ auch nur Referenzen! Eine ganz normale Wert-rückgabe, allerdings mit move, gehört hier her.

    Move-Semantiks bedeutet, dass es einen Move Ctor gibt, der Move op = kann über den Move Ctor sehr einfach implementiert werden, indem man das Objekt per Value nimmt.

    Wäre dir für ein Beispiel sehr dankbar.



  • Move-Konstruktor:

    Object(Object&& other)
        : some_variables(other.some_variables)
    {
        other.some_variables = some_default_value;
    }
    

    Hier werden die Ressourcen von other "geklaut". Wichtig ist dabei, dass other weiterhin ein gültiges Objekt ist, mit dem gearbeitet werden kann.

    op =:

    Object& operator = (Object rhs) // ganz normale Werte-übergabe
    {
        rhs.swap(*this);
        return *this;
    }
    

    Diese operator = kann sowohl kopieren, als auch moven. Er wird deswegen auch gerne als "unified assignment-operator" bezeichnet. Wenn ein R-Value übergeben wird, wird das rhs-Objekt move-constructed, sprich, der Move-Konstruktor wird für rhs aufgerufen. Wird ein L-Value übergeben, dann wird der normale Copy-Ctor aufgerufen.

    Mit diesem assignment operator entfällt auch der nervige Check, ob other == *this, außerdem ist er Excepion-Save, ein swap() sollte niemals etwas werfen.



  • CStoll schrieb:

    Da wäre es eventuell eine Idee, deine Matrix-Klasse mit Referenzzhälung und copy-on-write Funktionalität auszustatten. (d.h. der Speicher wird erst wirklich kopiert, wenn einer der Verwender etwas daran ändern will)

    Oder nur C++98-Move-Semantik à la auto_ptr , sofern man keine Wertsemantik benötigt. Ist noch etwas günstiger als intrusive_ptr , was hier ja im Prinzip nachgebaut wird.

    314159265358979 schrieb:

    Eine ganz normale Wert-rückgabe, allerdings mit move, gehört hier her.

    Ohne std::move() , eine funktionslokale Variable existiert nicht länger. Move-Konstruktoren können implizit generiert und aufgerufen werden.

    Aber heutige Compiler können schon recht gut RVO. Allerdings muss man ein paar Dinge beachten, um die Optimierung nicht zu verhindern.


Anmelden zum Antworten