Lese-/Schreib-Unterschiedung beim []-Operator, die 2.



  • Das was du zeigst ist jetzt nicht COW. Das ist ein Doublebuffer. Oder man kann es Write-on-Copy* nennen. Du erstellst eine Kopie, arbeitest auf der und tauschst das dann in einem einzigen Schritt auf. Doublebuffer-Pattern, siehe hier: http://gameprogrammingpatterns.com/double-buffer.html

    COW, Copy-on-Write, ist: Wenn du ein Objekt kopierst haben erst einmal alle Kopien einen Zeiger auf dieselbe Resource. Erst wenn eine der Kopien etwas ändern will, wird für dieses Objekt die tiefe Kopie durchgeführt.

    *Im Sinne von "Schreiben auf Kopie", COW ist hingegen "Kopie wenn geschrieben wird"



  • Nathan schrieb:

    Das was du zeigst ist jetzt nicht COW. Das ist ein Doublebuffer. Oder man kann es Write-on-Copy* nennen. Du erstellst eine Kopie, arbeitest auf der und tauschst das dann in einem einzigen Schritt auf. Doublebuffer-Pattern, siehe hier: http://gameprogrammingpatterns.com/double-buffer.html

    COW, Copy-on-Write, ist: Wenn du ein Objekt kopierst haben erst einmal alle Kopien einen Zeiger auf dieselbe Resource. Erst wenn eine der Kopien etwas ändern will, wird für dieses Objekt die tiefe Kopie durchgeführt.

    *Im Sinne von "Schreiben auf Kopie", COW ist hingegen "Kopie wenn geschrieben wird"

    Ich frage mich wo man da die Genze der beiden Definitionen zieht. Meines erachtens führt ein COW-String die selben Operationen auf seinem internen char* durch, wie ich in meinem Beispiel mit dem shared_image .
    Wenn ich den Code von Thread 3 jetzt in einen Member-Funktion von Image packe, und dessen andere non- const Member-Funktionen nach dem gleichen Schema implementiere, habe ich dann ein COW-Image oder DoubleBuffer-Image
    Ist der Unterschied dass es intern und transparent geschieht, oder liegt der woanders?

    Auch bei "intern und transparent" sehe ich die Vorgehensweise als Optimierung bei Operationen auf dem gesamten Bild - natürlich ist das ändern eines einzelnen Pixels kein guter Kanditat dafür (was wieder in Richtung des operator[] des eigentlich Themas geht).

    Finnegan



  • Nein, der Unterschied liegt in der Anwendung:
    Du willst eine Änderung allen Usern bekannt machen, COW eine Änderung nur für einen User.



  • Das Gebiet auf dem ich mich momentan bewege, scheint ein brenzliches Pflaster zu sein. Hier gehts ja immer rund... 😉

    Ich dachte COW und ref-counting gehörten zusammen.. Ich wüsste zumindest nicht, wozu ich sonst ref-counting benötigen würde.

    sebi707 schrieb:

    Und dann als nächstes dann die Operatoren []++ und []-- oder was?

    Gibt doch nichts was wirklich dagegen spricht. Das Programm würde nicht langsamer und es zu schreiben würde wesentlich schneller gehen.
    Es muss ja nicht gleich der "[]=-OP" sein. Ich denke, es wäre aber schon von Vorteil, wenn man direkt sagen könnte welches xyz-value man mit seinen selbst definierten OPs meint. Der Compiler weiß das schließlich normalerweise auch.
    Wenn man am Ende etwas hat, das wirklich wie gewünscht funktioniert, ist mir egal, wieviele OPs noch dazu kommen.
    Am einfachsten wären evtl. zusätzliche Spezifizierer wie "read"/"write", aber das ist eh graue Theorie...

    Tja leider muss man es erst implementieren, bevor man es testen kann. Ich glaube, ich schmeiße die Geschichte mit dem []-Proxy-OP wieder übern Haufen und nehmen statt dessen get()- und set()-Methoden.
    Ich dachte ja, ich würde hier etwas halbwegs professionelles machen, indem ich dann den altbekannten Syntax verwenden könnte, aber so wie es aussieht ist es wirklich nicht mehr als ein Workaround - also Murks!
    Mit Funktionen, die eine nicht-konstante Referenz erwarten, kommt der Proxy überhaupt nicht klar, und sonst muss man an fast jeder Ecke zwischendurch casten, selbst wenn man auf Methoden der Element-Klassen zugreifen will.
    Sowas wie
    array[3].machSchon(); //( "Proxy hat kein machSchon()" ) geht nur als
    ((elementKlasseMitExtraLangemNamenUndNochAls<TemplateVom<Template> >)array[3]).machSchon();

    Das sieht doch echt nicht mehr professionell/schön aus, oder?!
    Ich hatte mir erhofft, dass der Compiler den Cast eigenständig ausführt, wenn er nicht weiter weiß - sieht aber nicht so aus..
    Also da hätte ich von der Maschine aber etwas mehr eigenständiges Denken erwartet...

    Wenn ich darf, hier noch eine Frage zum eigentlichen Thema ;):
    Kennt jemand eine Möglichkeit, wie man es irgendwie doch hinbekommt, oder kann mir bestätigen, dass es wirklich nicht besser geht?!

    Denn, wenns nicht besser wird, geht die Idee übern Jordan.



  • Vielleicht erzählst du uns endlich mal was du mit der Unterscheidung zwischen Lesen und Schreiben überhaupt erreichen willst. Vielleicht gibts dafür eine viel bessere Lösung. Mit get() und set() Methoden kommst du übrigens auch nicht weit oder was machst du dann wenn eine Funktion eine nicht konstante Referenz nimmt? get() geht nicht weil es eine konstante Referenz/Kopie zurückgibt und set() geht auch nicht.

    Und mit deinem Cast sieht wirklich nicht schön aus. Was für Objekte soll dein Proxy überhaupt speichern? Scheinbar irgendwelche Klassen weil du Memberfunktionen aufrufen willst? Da auch wieder die Frage: Zählt der Aufruf einer Memberfunktion als Lesen oder Schreiben? Du könntest hier vielleicht den operator-> Überladen um den Cast loszuwerden. Statt

    array[3].machSchon();
    

    hättest du dann

    array[3]->machSchon();
    


  • @Finnegan
    Ich würde das auch nicht COW nennen. Ich würde hier den Begriff "latch" verwenden - und zwar für den shared_ptr<Image> shared_image .



  • Vielleicht erzählst du uns endlich mal was du mit der Unterscheidung zwischen Lesen und Schreiben überhaupt erreichen willst.

    Naja für die COW-Geschichte/Ref-Counting eben.. Ich will ja nicht "copy-on-read-or-write" haben.

    Mit get() und set() Methoden kommst du übrigens auch nicht weit

    Dass das mit den get()/set() und "nicht-const &" auch nicht geht, ist mir schon klar. Aber da geht man dann erst garnicht davon aus, dass es gehen könnte, einfach schon wegen des Syntax'.

    Was für Objekte soll dein Proxy überhaupt speichern?

    Der Proxy soll alles mögliche speichern können, nen Template eben..
    Wenn es etwas bestimmtes wäre, gäbe es deutlich weniger Probleme.

    Das mit dem "->"-OP müsste gehen.. Aber so richtig konsistent mit dem üblichen Code ist das auch nicht, wenn nicht sogar irreführend. Leider gibts keinen "."-OP..
    Irgendwie werde ich das Gefühl nicht los, dass man hier so schnell nicht aus dem "Around-worken" raus kommt, wenn man erstmal damit begonnen hat.



  • Spaghettimann schrieb:

    Vielleicht erzählst du uns endlich mal was du mit der Unterscheidung zwischen Lesen und Schreiben überhaupt erreichen willst.

    Naja für die COW-Geschichte/Ref-Counting eben.. Ich will ja nicht "copy-on-read-or-write" haben.

    Wenn du COW willst: COW macht keinen Sinn bei Multithreading, a.k.a. lass es.

    Was für Objekte soll dein Proxy überhaupt speichern?

    Der Proxy soll alles mögliche speichern können, nen Template eben..
    Wenn es etwas bestimmtes wäre, gäbe es deutlich weniger Probleme.

    Das mit dem "->"-OP müsste gehen.. Aber so richtig konsistent mit dem üblichen Code ist das auch nicht, wenn nicht sogar irreführend. Leider gibts keinen "."-OP..

    Es gibt einen Proposal für operator., kenn den Status nicht.



  • Spaghettimann schrieb:

    Naja für die COW-Geschichte/Ref-Counting eben.. Ich will ja nicht "copy-on-read-or-write" haben.

    Ich fürchte es ist nicht Möglich mit den aktuellen C++ Möglichkeiten aus jedem beliebigem Typ ein Typ mit COW zu machen. Fürs Reference Counting gibts shared_ptr.

    Leider gibts keinen "."-OP..

    Gibts schon, kann man aber nicht überladen.



  • Nathan schrieb:

    Wenn du COW willst: COW macht keinen Sinn bei Multithreading, a.k.a. lass es.

    Och aber damit umgehe ich doch das lästige kopieren. Ich habe mir den alten Artikel nicht wirklich durchgelesen, schon alleine, weil sich hier nicht alle einig waren, aber wenn ich jetzt nicht 'multithreadde' ist das doch schon ein Vorteil oder?

    Spontan fällt mir z.B. dies ein:

    array<int> ai;
    ai.InitAlleInts();
    
    array<array<int> > aai;
    aai.SetzeAnIdx0(ai);
    

    Wen ich das jetzt ohne COW mache, habe ich nachher 2 ais. 1 (im Array-Array) wäre mir aber genug. Aber fürs Init muss ich ja i.A. immer erst ein "echtes" Objekt erstellen..

    Nathan schrieb:

    Es gibt einen Proposal für operator., kenn den Status nicht.

    So lange (wie lange?) kann ich nicht warten.. 😉

    sebi707 schrieb:

    Ich fürchte es ist nicht Möglich mit den aktuellen C++ Möglichkeiten aus jedem beliebigem Typ ein Typ mit COW zu machen. Fürs Reference Counting gibts shared_ptr.

    Hmhmhm, dann werde ich mir den mal ansehen..

    sebi707 schrieb:

    Gibts schon, kann man aber nicht überladen.

    So wie z.B. in
    ((elementKlasseMitExtraLangemNamenUndNochAls<TemplateVom<Template> >)array[3]).machSchon();
    ? -Höhöö



  • Spaghettimann schrieb:

    Nathan schrieb:

    Wenn du COW willst: COW macht keinen Sinn bei Multithreading, a.k.a. lass es.

    Och aber damit umgehe ich doch das lästige kopieren. Ich habe mir den alten Artikel nicht wirklich durchgelesen, schon alleine, weil sich hier nicht alle einig waren, aber wenn ich jetzt nicht 'multithreadde' ist das doch schon ein Vorteil oder?

    Spontan fällt mir z.B. dies ein:

    array<int> ai;
    ai.InitAlleInts();
    
    array<array<int> > aai;
    aai.SetzeAnIdx0(ai);
    

    Wen ich das jetzt ohne COW mache, habe ich nachher 2 ais. 1 (im Array-Array) wäre mir aber genug. Aber fürs Init muss ich ja i.A. immer erst ein "echtes" Objekt erstellen..

    Wie viele Stunden hängst du da jetzt schon dran?
    Und die Optimierung wirst du im seltensten Fall merken.
    Lass es einfach, wenn du wirklich Performanceprobleme hast, die an zu vielen Kopien liegen, komm nochmal.



  • Wie viele Stunden hängst du da jetzt schon dran?

    Jaaa - zu viele, besonders an der Proxy(-Proxy)-Geschichte. Vor allem, wenn man mal davon ausgeht, dass ich es jetzt wohl doch 'billig' machen werde, oder doch gleich nen vector nehme (...leider...;) )..

    Und die Optimierung wirst du im seltensten Fall merken.

    Das war mir schon bewusst. Mir ging es eher ums Prinzip. Zur Übung sozusagen.. Viel mehr werde ich leider aus der Geschichte auch nicht mitnehmen können, wies aussieht.



  • Nathan schrieb:

    Wenn du COW willst: COW macht keinen Sinn bei Multithreading, a.k.a. lass es.

    Wieso denn das?
    Klar macht COW auch Sinn bei MT. Über shared_ptr geht das sogar recht schön.

    void write()
    {
       if (!m_guts.unique())
          m_guts.reset(new Guts(*m_guts));
    
       m_guts->write();
    }
    

    Ist einzig doof wenn die write Operationen ziemlich billig und ziemlich häufig sind. Dann könnte der Overhead des threadsicheren shared_ptr bemerkbar werden.


Anmelden zum Antworten