Unterschied Templates und Generics?



  • hustbaer schrieb:

    dot schrieb:

    Wenn man's genau nimmt, geht es in der Regel nie wirklich um GC, sondern eigentlich um das Management der Lebensdauer von Objekten, die aus einer Datenstruktur entfernt wurden, aber potentiell noch nebenläufig gerade von jemandem verwendet werden. Das ist eigentlich ein Problem, das völlig orthogonal – wenn nicht sogar antigonal – ist zu GC...

    Ich weiss nicht genau wie ich darauf jetzt antworten soll...
    Der GC ist dafür zuständig Müll wegzuräumen. Dafür muss er wissen welche Objekte noch referenziert werden. Und wenn er als Teil eines Systems funktionieren muss welches Threads "hat" und es explizit erlaubt Referenzen ohne extra Synchronisierung einfach so zu verbiegen, dann muss er halt auch damit klarkommen. Und wenn Referenzen in Registern gehalten werden dürfen auch damit und und und. Ich verstehe also nicht wieso das "nicht mit dem GC zu tun" haben soll 😕

    Automatisches Aufräumen unreferenzierter Objekte ist eine mögliche Lösung, aber das eigentliche Problem hier hat lediglich mit Garantien bezüglich der Lebensdauer von Objekten zu tun und das ist ein zu GC völlig orthogonales Problem; GC an sich gibt einem normalerweise überhaupt keine Garantien bezüglich ob und wann Objekte zerstört werden. Und der Preis für GC ist mir persönlich mitunter viel zu hoch. Und dabei meine ich nicht nur irgendwelchen Performanceoverhead, sondern vor allem die Tatsache, dass die Verwendung eines GC sämliche Garantien bezüglich Objektlebensdauern als Opfer fordert, was eine jede solche Sprache einfach nur verkrüppelt. In vielen Fällen gibt es elegantere Lösungen wie z.B. dieser RCU Kram im Linux Kernel. Und abgesehen davon ist lock-free sowieso nicht die Lösung als die es immer angepriesen wird. Im Gegenteil. Mag sein, dass ich da nur einen Extrem zu sehen bekomme weil ich mit GPUs zu tun hab, aber meiner Erfahrung nach skalieren lock-free Algorithmen einfach hoffnungslos aus Prinzip nicht. Das Grundproblem scheint mir, dass praktisch alle lock-free Algos die wir heute so haben (zumindest die, mit denen ich bisher zu tun hatte) optimistisch sind, d.h. auf der Annahme basieren, dass Operationen selten interferieren. Dann können wir unsere Operationen clever so designen, dass bei parallelen Modifikationen nur eine erfolgreich ist und der Rest fehlschlägt ohne zu einem undefinierten Zustand zu führen und dann wieder versucht werden kann. Das funktioniert vielleicht ganz toll so lange du 4 Threads hast. Sobald du ein paar hundert Threads hast, die alle gleichzeitig eine Datenstruktur benutzen wollen, kannst du das knicken weil allein der Memory Transfer durch fehlgeschlagene und wiederversuchte Operationen das Ganze gegen die Wand fährt...



  • Erstmal vorweg: Ich bin auch nicht unbedingt der grösste Fan von Systemen mit GC. Speziell nicht von bestehenden gerne verwendeten Implementierungen wie JVM oder .NET. Ich sehe allerdings auch Stärken. Und keinen Sinn darin diese zu ignorieren oder niederzureden damit ja nix gutes dran sein kann.

    dot schrieb:

    Automatisches Aufräumen unreferenzierter Objekte ist eine mögliche Lösung,

    Was jetzt genau das ist was ich behauptet habe, oder?

    dot schrieb:

    aber das eigentliche Problem hier hat lediglich mit Garantien bezüglich der Lebensdauer von Objekten zu tun und das ist ein zu GC völlig orthogonales Problem;

    Er... nein. Es ist definitiv nicht völlig orthogonal. Mag jetzt sein dass du ohne weitere Erklärung eine sehr spezielle Interpretation von "Lebensdauer von Objekten" verwendest, aber nichtmal wenn ich davon ausgehe dass du deterministische Finalisierung meinst...
    Viele Objekte haben (im C++ Standard Sinne des Wortes) "triviale" Finalizer. Also auf Deutsch: keine. In einem System mit GC hat das "verschwinden" solcher Objekte keine beobachtbaren Effekte. Das Objekt muss existieren so lange es verwendet werden kann, und verwendet werden kann es so lange es referenziert wird. (Bzw. auch weniger lang, wenn man beweisen kann dass die Referenz ab Punkt X sicher nicht mehr verwendet wird. Nur da es ja keine beobachtbaren Effekte gibt wenn das Objekt "zerstört wird" (verschwindet)... ist es egal dass man es früher wegräumen könnte.)

    Und dass das den GC betrifft ist hoffentlich klar, denn der darf natürlich nichts tun was diese Garantie ("ein Objekt existiert so lange es verwendet werden kann") "zerstört".
    Natürlich kann man das auch ohne GC lösen, z.B. indem man Speicher einfach gar nicht freigibt. Nur das ist selten praktikabel, und wenn man einen GC verwendet, dann betrifft es ihn. Ganz massiv. Und dann zu sagen es wären völlig orthogonale Prinzipien ist IMO einfach falsch.

    Falls ich dich falsch verstanden habe, bitte beschreib genauer was du meinst. Einfach nur zu wiederholen was du bereits geschrieben hast bringt mir nix, ich hab' deinen Beitrag sowieso schon 3x gelesen, ein 4. oder 5. mal wird dann nix bringen. 🙂

    dot schrieb:

    GC an sich gibt einem normalerweise überhaupt keine Garantien bezüglich ob und wann Objekte zerstört werden.

    Doch, klar. Also konkrete, praktikable Implementierungen tun das. Nämlich dass sie Objekte nicht zu früh zerstören. Was auch immer jetzt mit "zerstören" gemeint ist (Freigabe des Speichers bzw. Finalisierung). Das ist vielleicht nicht die Garantie die du dir wünscht (deterministische Finalisierung ala C++ Destruktoren?), aber es ist eine sehr wichtige Garantie, denn ohne sie wäre jedes System mit GC IMO völlig unbrauchbar.

    dot schrieb:

    Und der Preis für GC ist mir persönlich mitunter viel zu hoch. Und dabei meine ich nicht nur irgendwelchen Performanceoverhead, sondern vor allem die Tatsache, dass die Verwendung eines GC sämliche Garantien bezüglich Objektlebensdauern als Opfer fordert, was eine jede solche Sprache einfach nur verkrüppelt.

    Jain.
    GC muss ja nicht unbedingt heissen dass man nicht trotzdem bestimmte Dinge deterministisch machen kann. Ich kenne zwar auch keine Sprache die da wirklich gut brauchbaren Support dafür hat, aber es spricht grundsätzlich nix dagegen. Im Prinzip kannst du aber jederzeit ne C++ Implementierung mit GC machen, wo der GC sich einfach nur darum kümmert den Heap zu verwalten. Also keine Finalizer o.ä. Damit könnte man Objekte mit trivialem Dtor dann einfach vom GC collecten lassen - und hätte damit auch das was man benötigt um bestimmte Dinge "lock free" zu implementieren. Für einfache POD-structs müsste man dann nämlich nimmer delete/free aufrufen, und das reicht für viele Sachen schon.

    dot schrieb:

    In vielen Fällen gibt es elegantere Lösungen wie z.B. dieser RCU Kram im Linux Kernel.

    RCU ist doch das beste Beispiel für etwas was mit GC deutlich einfacher und vermutlich auch deutlich effizienter wird 😕

    dot schrieb:

    Und abgesehen davon ist lock-free sowieso nicht die Lösung als die es immer angepriesen wird. Im Gegenteil.

    Es war auch nur ein Beispiel. Ein weiteres hast du ja selbst gerade gebracht: RCU. Wobei man eine RCU-Implementierung die readerseitig keinerlei Synchronisierung erfordert auch als spezialisierten mini-GC bezeichnen könnte.

    dot schrieb:

    Das Grundproblem scheint mir, dass praktisch alle lock-free Algos die wir heute so haben (zumindest die, mit denen ich bisher zu tun hatte) optimistisch sind, d.h. auf der Annahme basieren, dass Operationen selten interferieren. Dann können wir unsere Operationen clever so designen, dass bei parallelen Modifikationen nur eine erfolgreich ist und der Rest fehlschlägt ohne zu einem undefinierten Zustand zu führen und dann wieder versucht werden kann. Das funktioniert vielleicht ganz toll so lange du 4 Threads hast. Sobald du ein paar hundert Threads hast, die alle gleichzeitig eine Datenstruktur benutzen wollen, kannst du das knicken weil allein der Memory Transfer durch fehlgeschlagene und wiederversuchte Operationen das Ganze gegen die Wand fährt...

    Nur wenn sich dabei häufig Writer in die Quere kommen. Wenn primär gelesen wird ...
    Und das selbe Problem hast du bei RCU genau so. Wenn da recht oft geschrieben wird skaliert das ganz schlecht.



  • SideWinder schrieb:

    Können Templates eigentlich co/contravariance?

    Nein, aber das können Java Generics auch nicht (warum auch immer, es gibt eigentlich keinen technischen Grund).

    Was man allerdings mit Templates nicht kann sind virtuelle Methoden:

    #include <iostream>
    
    class Fass {
    public:
      template <class T>
      virtual void quiek (T t) = 0;
    };
    
    class Ramelucke : public Fass {
    public:
      template <class T>
      void quiek (T t) {
        std::cout << t << std::endl;
      }
    };
    
    int main (void) {
      Fass* r = new Ramelucke ();
      r->quiek("Hallo Welt");
      delete r;
    }
    

    wird dir nicht compilieren, das Analogon in Java

    class VirtualTemplate {
        static abstract class Fass {
            abstract <T> void quiek (T t);
        }
        static class Ramelucke extends Fass {
    	public Ramelucke () {}
    	<T> void quiek (T t) {
    	    System.out.println(t);
    	}
        }
    
        public static void main (String[] args) {
    	Fass a = new Ramelucke ();
    	a.quiek("Hallo Welt");
        }
    }
    

    tuts :3

    Achso, und was auch nur die Wenigsten wissen ist, dass C++-Schablonen lazy ausgewertet werden. Hier ist ein schönes Rätsel dazu: http://tinyurl.com/gsw8xle



  • Dein Kritikpunkt macht ueberhaupt keinen Sinn; um virtuelle Templates zu unterstuetzen, muesste ein JIT compiler zur runtime dabei sein und auch noch Fehler entsprechend behandeln koennen, was fundamental C++' Natur widerspricht. Aber wir brauchen dieses Feature gar nicht. I.e. es ist nicht so, das wir es ergaenzen koennten: wir brauchen es gar nicht.

    Was Java kann, kann C++ auch, entweder mit virtuellen Funktionen oder type erasure, aber oft (immer?) kann man das auch eleganter loesen.



  • 𝔑&#120094 schrieb:

    SideWinder schrieb:

    Können Templates eigentlich co/contravariance?

    Nein, aber das können Java Generics auch nicht (warum auch immer, es gibt eigentlich keinen technischen Grund).

    Danke für die Info, ja, aber ich setzte statt "Java" auch immer "C#" ein 😉

    Wie es in diesem Thread schon wieder um den GC gehen kann ist mir allerdings ein Rätsel...

    MfG SideWinder


Anmelden zum Antworten