Wieviel Performance kostet die Objekt orientierte Programmierung?



  • SeppJ schrieb:

    314159265358979 schrieb:

    Objektorientierung kostet grundsätzlich gar nichts.

    Quark, mal wieder, leider.

    cooky451 schrieb:

    Objektorientierung ist halt erst mal nur eine Sichtweise, das beschreibt keinen konkreten Implementierungsweg. Wenn du meckern willst, dann nenne irgendein Konzept das du für sinnlos erklärst. 😉

    Und genau deshalb ist dein Post Quark, SeppJ, und nicht meiner.



  • Wenn ich nur von einer Sichtweise spreche, kann ich aber auch nicht von Performance sprechen.
    Ich muss von einer Implementierung sprechen, um über Performance zu diskutieren - und dann kostet OOP unter Umständen (richtig, Programmierung. Nicht Analyse, nicht Design. Programmierung).

    Absolute statements...:)



  • volkard schrieb:

    SeppJ schrieb:

    314159265358979 schrieb:

    Objektorientierung kostet grundsätzlich gar nichts.

    Quark, mal wieder, leider. Aber nicht so großer Quark wie von dem Herausforderer, der mit allen Mitteln versucht, sich seine Welt so zu verbiegen, wie es ihm passt.

    Es kann technische Nachteile geben bei dem Code der von typischen objektorientierten Ansätzen erzeugt wird.

    Des Herausforderers "nicht objektorientierte" Version dürfte als einzigen Schwachpunkt haben, den der optimierende Compiler nicht sieht, daß der vector dauernd neu angelegt und gelöscht wird. Also ausgerechnet sein Versuch, doch ein wenig OO zu programmieren.

    Das Erstellen und Füllen des vectors kann man aus der Schleife raus nehmen, am Ende ist der vector sowieso wieder so gefüllt wie am Anfang. Bringt aber nur so ca. 7%.

    Wenn ihr meint, dass meine Version objektorientiert ist, wie sieht dann eine nicht objektorientierte Version aus?



  • Herausforderer schrieb:

    Wenn ihr meint, dass meine Version objektorientiert ist, wie sieht dann eine nicht objektorientierte Version aus?

    Genau so. Es ist eine verdammte Sichtweise!! 😃



  • [quote="cooky451"]

    OOP & Performance schrieb:

    Wieso kostet es in C++ nichts?

    Weil aus

    class A
    {
      float value_;
    public:
      A(float value)
        : value_(value)
      {}
      float &value()
      {
        return value_;
      }
    };
    
    int main()
    {
      A a(534.65f);
      std::cout << a.value();
      getchar();
    }
    

    das da wird:

    012D14A0  fld         dword ptr [__real@4405a99a (12EA0D8h)]  
    012D14A6  push        ecx  
    012D14A7  fstp        dword ptr [esp]  
    012D14AA  call        std::basic_ostream<char,std::char_traits<char> >::operator<< (12D1550h)
    

    [QUOTE]

    Damit ist die Datenkapselung aber fürn Arsch:

    #include <iostream>
    #include <stdio.h>
    
    class A
    {
      float value_;
    public:
    
      A(float value)
        : value_(value)
      {}
    
      float &value()
      {
        return value_;
      }
    };
    
    int main()
    {
      A a(534.65f);
      std::cout << a.value();
      float* d = &a.value(); // Oh je...
      *d = 10; // Damit ist die Datenkapselung fürn Arsch
      std::cout << std::endl << *d;
      std::cout << std::endl << a.value(); // LOL
      getchar();
    }
    

    Zudem solltest du mal beschreiben, was genau du mit Objektorientierung meinst. Ist FILE* aus C eine Klasse? Was schlägst du als Alternative vor?

    Die Datenkapselung und das Geheimnisprinzip sollte schon aufrecht erhalten bleiben.
    OOP heißt, daß eine andere Person die Implementierung der Objektdaten und Objektmethoden nicht kennen muß, alles was er kennen darf, sind die Deklarationen der public Methoden und public Objektvariablen.
    Also die Schnittstellen zu dem Objekt.

    D.h. also, wenn er seinen Code so wie hier bei

    float* d = &a.value();
    

    programmiert, dann fliegt der Code mächtig auf die Schnauze, wenn die Klasse intern irgendwann während der Softwareentwicklung verändert wird.

    Deswegen gibt es das Prinzip der Datenkapselung und dem Geheimnisprinzip, sowie der maximalen Entkopplung.

    Dein Code ist also zwar vielleicht schnell, aber er erfüllt die Bedingungen der OOP nicht mehr.
    Da kann man das ganze Klassenkonstrukt auch gleich weglassen.



  • Dann gleich noch eine Frage.

    Wieso heftest du den Adressoperator an den Funktionsnamen und nicht an den Rückgabetyp, so wie es IMO sein sollte?

    cooky451 schrieb:

    float &value()



  • Hehe jo, du hast in völlig zusammenhangslosem Beispielcode einen imaginären Schönheitsfehler gefunden. 👍
    Dass der resultierende Binärcode völlig identisch mit der "nicht Referenz"-Version bleibt, dürfte wohl klar sein.

    Pfusch schrieb:

    Wieso heftest du den Adressoperator an den Funktionsnamen und nicht an den Rückgabetyp, so wie es IMO sein sollte?

    Deswegen: http://ideone.com/NGNYG
    Ich würde Referenzen und Pointer auch lieber dem Datentypen zuschreiben. Das Komitee selbst scheint sich ja nicht mal ganz einig über die Schreibweise zu sein. Früher standen */& beim Variablennamen, jetzt stehen sie öfter beim Typ. Einheitlich machen sie es jedenfalls nicht, von daher zählt für mich vor allem die Eigenschaft von oben.



  • cooky451 schrieb:

    Hehe jo, du hast in völlig zusammenhangslosem Beispielcode einen imaginären Schönheitsfehler gefunden. 👍

    kleine schönheitsfehler, wenn man jenen überhaupt so nennen will, find ich super 👍



  • cooky451 schrieb:

    Hehe jo, du hast in völlig zusammenhangslosem Beispielcode einen imaginären Schönheitsfehler gefunden. 👍

    Der Fehler ist weder ein Schönheitsfehler noch imaginär oder sonstwas.

    Eine mutable Referenz auf ein Member zurückzugeben macht effektiv die Kapselung kaputt. Das Ding erst private zu machen, und dann einen "Referenz-Getter/Setter" anzubieten, halte ich für äusserst albern. Das ist bloss Vorspiegelung falscher Tatsachen.

    Erst Recht in dem Zusammenhang in dem du es gebracht hast: wenn es keine echte Kapselung in dem Code gibt, wie soll er dann als Beispiel dafür dienen, dass Kapselung nix kostet?



  • Welche Tatsachen? Die Klasse heißt A und die einzige Membervariable "value_", es existiert einfach kein Umfeld. Ist std::vector jetzt auch schlecht gekapselt? Das Beispiel sollte nur verdeutlichen, dass Methode sowie Referenz wegoptimiert werden, und das hat es auch geleistet. Dass ohne Referenz nicht plötzlich weniger optimiert wird ist doch klar.



  • cooky451 schrieb:

    Welche Tatsachen? Die Klasse heißt A und die einzige Membervariable "value_", es existiert einfach kein Umfeld.

    Die (falsche) Tatsache "hier ist was gekapselt".

    Ein Member einer Klasse das "private" ist, impliziert, dass es nicht uneingeschränkt von ausserhalb verwendbar ist -- wieso sollte man es sonst "private" machen und nicht gleich "public"?
    Sobald du eine mutable Referenz zurückgibst, ist das aber nicht mehr der Fall, denn dann ist das "private" Member auf einmal uneingeschränkt von ausserhalb verwendbar.

    Ergo: der Kuchen ist eine Lüge.

    Ist std::vector jetzt auch schlecht gekapselt?

    Nein. Bietet denn std::vector Referenzen auf seine Member der über Referenz-Getter an? Nein, tut er nicht. Sonst könnte man vec.size() = 42; oder ähnlich gefährlichen Unsinn schreiben.

    Das Beispiel sollte nur verdeutlichen, dass Methode sowie Referenz wegoptimiert werden, und das hat es auch geleistet. Dass ohne Referenz nicht plötzlich weniger optimiert wird ist doch klar.

    Ein Beispiel das man in Gedanken erstmal abändern muss, und dann Vermutungen darüber anstellen ob die abgeänderte Version sich auch noch so verhält wie die gezeigte, ist irgendwie kein gutes Beispiel.



  • hustbaer schrieb:

    Ergo: der Kuchen ist eine Lüge.

    Ne, es gibt nämlich keinen Kuchen. Der einzige Sinn der Funktion war ja, dass sie wegoptimiert wird.

    hustbaer schrieb:

    Nein. Bietet denn std::vector Referenzen auf seine Member der über Referenz-Getter an? Nein, tut er nicht.

    operator [] ?

    hustbaer schrieb:

    Ein Beispiel das man in Gedanken erstmal abändern muss, und dann Vermutungen darüber anstellen ob die abgeänderte Version sich auch noch so verhält wie die gezeigte, ist irgendwie kein gutes Beispiel.

    So und hier kommen wir wohl zum einzig relevanten Teil der Diskussion. Und nein, ich denke das Beispiel war gut, weil es zeigt, dass Zugriffe über Methoden auch dann komplett wegopimiert werden, wenn man mit non-const Referenzen arbeitet. (Was z.B. beim std::vector nicht ganz unwichtig ist.)



  • cooky451 schrieb:

    hustbaer schrieb:

    Nein. Bietet denn std::vector Referenzen auf seine Member der über Referenz-Getter an? Nein, tut er nicht.

    operator [] ?

    Manchmal fällt es mir wirklich schwer freundlich zu bleiben, da ich mich des Eindrucks nicht erwähren kann verarscht zu werden.

    Aber gut.

    Welche Member werden denn über operator [] zugänglich gemacht?

    Des weiteren halte ich das Beispiel immer noch für Unsinn - warum habe ich ja schon beschrieben, ich werde nicht nochmals darauf eingehen.



  • cooky451 schrieb:

    Pfusch schrieb:

    Wieso heftest du den Adressoperator an den Funktionsnamen und nicht an den Rückgabetyp, so wie es IMO sein sollte?

    Deswegen: http://ideone.com/NGNYG
    Ich würde Referenzen und Pointer auch lieber dem Datentypen zuschreiben. Das Komitee selbst scheint sich ja nicht mal ganz einig über die Schreibweise zu sein. Früher standen */& beim Variablennamen, jetzt stehen sie öfter beim Typ. Einheitlich machen sie es jedenfalls nicht, von daher zählt für mich vor allem die Eigenschaft von oben.

    Das ist aber keine Begründung.
    Und im obigen Falle steht dein & nicht beim Variablennamen, sondern beim Namen der Funktion. Das ist etwas völlig anderes.

    Bei so Sachen, wo Datentyp und Variablennamen in einer Zeile stehen,
    z.b. int &i..., da ist es vielleicht egal, ob der Adressoperator nun beim int oder beim Variablennamen i steht, aber in deinem Fall steht er bei einem Funktionsnamen, der absolut nichts mit dem Rückgabetyp zu tun hat.
    Insofern ist der Code also Irreführend und so mancher Newb läuft Gefahr, das mit Funktionszeigern zu verwechseln, wenn er sich bei der Bedeutung von & und * noch nicht sicher ist.



  • Pfusch schrieb:

    Deswegen gibt es das Prinzip der Datenkapselung und dem Geheimnisprinzip, sowie der maximalen Entkopplung.

    Dein Code ist also zwar vielleicht schnell, aber er erfüllt die Bedingungen der OOP nicht mehr.
    Da kann man das ganze Klassenkonstrukt auch gleich weglassen.

    Ja, die die glauben, dass OO nichts kostet, glauben auch, dass alles objektorietiert wird, wenn man class darum schreibt. Dass der Performance verlust nicht von ein paar Gettern kommt, sondern auf ein anderes Design zurückzuführen ist, haben sie noch nicht durchschaut. Vorallem durch Information Hiding (Geheimnisprinzip) und Entkopplung entsteht anderes Design und anderer Code. Wenn man sich keine sauberen allgemeinen Schnittstellen zwischen den Klassen definiert, sondern Schnittstellen, die nur funktionieren, wenn man den Inhalt der Klasse nicht ändert, dürfte es keinen Performanceunterschied geben. Dann hat man aber auch die ganzen Vorteile von OO, wie Erweiterbarkeit, nicht mehr. Ich will aber nicht empfehlen, wegen der Performance nicht OO zu programmieren, die Vorteile von OO überwiegen deutlich. Vor allem wenn man nicht allein programmiert ist es gut, wenn andere deine Klassen verwenden können, ohne wissen zu müssen was intern passiert.



  • @Herausforderer
    Du willst hier mit einer bestimmten Vorstellung von OO ein bestimmtes Design "erzwingen", das erstmal viel flexibler ist als der von dir gezeigte prozedurale Code, und (Vermutung/Unterstellung) auch Dinge wie Laufzeitpolymorphie verwendet.

    Dass das nicht mehr so schnell sein wird, wie der (einfachere, weniger flexible) prozedurale Code, ist sehr wahrscheinlich. Keine grosse Überraschung.

    Bloss dass deine Vorstellung von OO nicht unbedingt meiner Vorstellung von OO entspricht.



  • hustbaer schrieb:

    Welche Member werden denn über operator [] zugänglich gemacht?

    Elemente des vektor internen Felds? Oder sind Elemente eines Felds keine Member mehr? Ich kann über die Referenz auch den Vektor kaputt machen: ich lösche das mit placement new erzeugte Element einfach. Damit ist die Rückgabe der Referenz des Elements genauso schlimm wie

    vec.size()=42;



  • hustbaer schrieb:

    @Herausforderer
    Du willst hier mit einer bestimmten Vorstellung von OO ein bestimmtes Design "erzwingen", das erstmal viel flexibler ist als der von dir gezeigte prozedurale Code, und (Vermutung/Unterstellung) auch Dinge wie Laufzeitpolymorphie verwendet.

    Nein nicht nur Laufzeitpolymorphie, man hackt nur nicht einfach Abhängigkeiten zwischen Klassen rein bei denen man das Wissen über den internen Aufbau von Datenstrukturen einer anderen Klasse ausnutzt um irgendwas schneller zu machen. OK, viele machen es mehr oder weniger direkt doch und wundern sich dann, dass plötzlich soviel anderes noch angepasst werden muss, wenn man eine Klasse ändert.



  • Herausforderer schrieb:

    Objektorientiert programmieren bedeutet ja nicht, nur ein paar Getter und Setter für meine Arrays zu machen, sondern ein Klassendesign mit Kapselung zu haben und OO Pattern zu verwenden, also z.B. Observer für Schuss-Events oder ähnliches verwenden.

    Pattern bei 40 Zeilen relevantem Code zu verwenden, ist ziemlicher Overkill, meinst du nicht? Das hat dann auch nichts mehr mit guten OO-Design zu tun.

    Zufällig muss ich diesen Quatsch gerade auch für die Uni machen, also auf Teufel komm raus Design Patterns verwenden, die ich eigentlich nicht nehmen möchte.



  • hustbaer schrieb:

    cooky451 schrieb:

    Welche Tatsachen? Die Klasse heißt A und die einzige Membervariable "value_", es existiert einfach kein Umfeld.

    Die (falsche) Tatsache "hier ist was gekapselt".

    Ein Member einer Klasse das "private" ist, impliziert, dass es nicht uneingeschränkt von ausserhalb verwendbar ist -- wieso sollte man es sonst "private" machen und nicht gleich "public"?

    Z.B. bei einer Vektor3D-Klasse würde ich auch vielleicht privat float x, y, z benutzen und diese über Referenzgetter zugänglich machen. Der Grund ist, dass ich so die Implementierung z.B. leicht auf ein Array umstellen kann ohnen die Schnittstelle zu verändern. Auf der anderen Seite sind Referenzen aber kein Problem, da mich eine Änderung seitens des Benutzers nicht interessiert (es gibt keine verletzbaren Invarianten). Und wenn cooky451s Klasse lediglich einen float kapseln soll, würde ich das genauso legitim halten. Das hat mit size beim vector nichts zu tun.


Anmelden zum Antworten