Wieviel Performance kostet die Objekt orientierte Programmierung?



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



  • Michael E. schrieb:

    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.

    Ja, das Beispiel ist zu klein.



  • otze schrieb:

    hustbaer schrieb:

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

    Elemente des vektor internen Felds? Oder sind Elemente eines Felds keine Member mehr?

    Natürlich nicht, waren sie auch nie.

    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;

    "Genau so schlimm" kann man in C++ immer erreichen, man kann immer memset(&obj, 123, sizeof(obj)) machen, das wird auch kaum ein Objekt "überleben".



  • Ziel von C++ ist es, nur "zero cost abstractions" anzubieten, also Abstraktionen, die im Vergleich zu per Hand nachgebautem C-Gefrickel performanztechnisch nicht schlechter darstehen.

    Es hat ja noch gar keiner den "C++ Performance Report" ausgepackt, der gerade dazu da ist, diese Performanz-Mythen bzgl C++ aus der Welt zu räumen. Dann mach ich das mal:
    http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf



  • Wieviel Performance kostet die Objekt orientierte Programmierung?

    Im Vergleich zu was?



  • krümelkacker schrieb:

    Ziel von C++ ist es, nur "zero cost abstractions" anzubieten, also Abstraktionen, die im Vergleich zu per Hand nachgebautem C-Gefrickel performanztechnisch nicht schlechter darstehen.

    Es hat ja noch gar keiner den "C++ Performance Report" ausgepackt, der gerade dazu da ist, diese Performanz-Mythen bzgl C++ aus der Welt zu räumen. Dann mach ich das mal:
    http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf

    Wow, danke! Das ist sicherlich von unschätzbarem Wert für mich, wenn ich mal C++ lerne! 🙂



  • hustbaer schrieb:

    otze schrieb:

    hustbaer schrieb:

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

    Elemente des vektor internen Felds? Oder sind Elemente eines Felds keine Member mehr?

    Natürlich nicht, waren sie auch nie.

    Sicher sind es Member. Nur eben keine die man kapseln müsste.

    Wenn ich einen vector serialisiere, dann will ich dass er den Inhalt seines Feldes mit-serialisiert. Es ist also Teil der Klasse und dazu sagen wir nunmal Member.

    Ist auch übrigens leicht an dem Variablen Namen zu erkennen den es in der Klassendefinition hat.

    Nur gilt hier die übliche Kapselung von wegen kein Zugriff von aussen nicht, weil es uninteressant ist welchen Wert es dort gibt. Der vector will den Zugriff ja erlauben. Das ist auch vollkommen OK. Man darf Member haben die alle Werte annehmen können.

    Vielleicht besser ersichtlich bei std::array? Da ist ein T arr[NUM]; Member vorhanden. Und auf diesen kann ich zugreifen wie ich lustig bin. Aber das ist by Design so.

    cooky451 hätte keine Referenz zurück geben sollen, aber das bedeutet nicht dass sein Beispiel schlecht war. Es war genau richtig. Eine Diskussion darüber halte ich für fragwürdig. Es geht doch nur um Prinzipien. Keine Referenzen nach aussen leaken lassen ist eine super Regel. Sie stimmt wie alle Regeln nahezu immer. Aber wie alle Regeln gibt es eben Situationen wo sie nicht stimmt.

    zB bei Containern (wozu zB auch shared_ptr zählt).



  • ich habe ein paar studenten an der uni in Utah mal geholfen das anhand eines raytracers herauszufinden. man durfte die sprachen seiner wahl nutzen und das resultierende bild sowie die source daten waren vorgegeben (Standford Bunny mit 5k polys und Utah Teapot mit 500polys).
    ich glaube wir hatten gcc 3.4 und VS2003 benutzt. ich war an c++ und c dran, das resultat war, dass c++/oop (also z.b. vector klassen mit ueberladenen operatoren) ca 5% langsammer war als c/prozedural(z.b. vec_dot_3f(out,va,vb);). (java war ca 230% langsammer und c# 400% langsammer wenn ich mich recht erinnere).
    vom assembler code (aus VS2003) her sah ich, dass einige ueberladene operatoren sich nicht dazu bringen lassen wollten ohne temp copy auszukommen, selbst nach dem inlinen, ansonsten sah der assembler ziemlich gleich aus. (das war simples brute force raytracing damit feinheiten von implementierungen besserer algorithmen keine rollen spielen, z.b. ob man beim kd-tree mit < oder <= vergleicht.)

    ich habe mir den java bytecode angeschaut, das sah sehr unoptimiert aus, vermutlich weil es auch egal ist, da erst der JIT den finalen code erstellen soll und dessen output sah ich nicht).

    c# war ziemlich.. emm.. overengineered, der programmierer hat z.b. fuer die vector klasse, die er hlsl nachempfunden hat, alle permutationen von zwizzling im vector als accessors generieren lassen (da gab es wohl ein extra tool fuer bei c#).

    ich habe ein paar jahre spaeter das gesehen: http://www.flipcode.com/archives/Faster_Vector_Math_Using_Templates.shtml und es fuer diesen test implementiert, das war dann um einiges schneller als prozedural, ich glaube ca 20%.

    es haengt also davon ab wie man seine werkzeuge nutzt.

    aber es gibt ja die regel das 90% der laufzeit im 10% vom code verbracht wird, wenn man also effektiv 90% vom code herstellen kann, dank OOP, hat man am ende vielleicht mehr zeit die 10% genau zu optimieren.
    Ich kenne einige leute die dann fuer die kritischen funktionen den assembler vom compiler nehmen und ihn fuer die ziel cpu optimieren, gerade bei sowas wie Atom, PPU, SPU, ARM-NEON kann man durch besseres instruction scheduling oder bessere wahl von instructions fuer eine gleichmaessige auslastung der pipelines, locker die doppelte performance rausholen (ich weiss nicht weshalb die compiler bei sowas so unglaublich versagen, wenn man sonst sieht, dass sie manchmal 9kb binary replizieren um _ein_ compare+branch zu sparen).



  • rapsos Posting uebersetzt: "Ich bin der geilste"



  • rapso schrieb:

    vom assembler code (aus VS2003) her sah ich, dass einige ueberladene operatoren sich nicht dazu bringen lassen wollten ohne temp copy auszukommen, selbst nach dem inlinen, ansonsten sah der assembler ziemlich gleich aus.

    Kann man das mit dem &&-Opertator aus C++11 nicht schneller hinkriegen?



  • rapso schrieb:

    ich war an c++ und c dran, das resultat war, dass c++/oop (also z.b. vector klassen mit ueberladenen operatoren) ca 5% langsammer war als c/prozedural(z.b. vec_dot_3f(out,va,vb);).

    Ob da viel oop war? Oder bzw. ob C kein oop war? Hört sich auf jeden Fall so an, als ob es in beiden Fällen sehr ählicher Code war.



  • Shade Of Mine schrieb:

    hustbaer schrieb:

    otze schrieb:

    hustbaer schrieb:

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

    Elemente des vektor internen Felds? Oder sind Elemente eines Felds keine Member mehr?

    Natürlich nicht, waren sie auch nie.

    Sicher sind es Member. Nur eben keine die man kapseln müsste.

    Nö, es sind nie und werden nie Member.
    std::vector hat meist einfach drei Member, nen Zeiger und zwei size_t (allocated, used).
    Das sind die einzigen drei Member.

    Die verwalteten Elemente, sind eben Elemente, und zwar eines Arrays, auf das ganz zufälligerweise im Vektor ein Zeiger existiert.

    Das macht sie aber noch lange nicht zu membern.

    Davon abgesehen schliesse ich mir dem an was du geschrieben hast.
    (OK, dem meisten, ich finde das Beispiel nämlich immer noch schlecht, aber egal)

    BTW: bei std::array sieht die Sache schon wieder anders aus, da ist das Arrays selbst Member, dann kann man IMO auch sagen dass die Elemente des Arrays Member sind.



  • dot schrieb:

    Wieviel Performance kostet die Objekt orientierte Programmierung?

    Im Vergleich zu was?

    Das ergibt sich doch eindeutig aus der Frage.



  • &&- schrieb:

    rapso schrieb:

    vom assembler code (aus VS2003) her sah ich, dass einige ueberladene operatoren sich nicht dazu bringen lassen wollten ohne temp copy auszukommen, selbst nach dem inlinen, ansonsten sah der assembler ziemlich gleich aus.

    Kann man das mit dem &&-Opertator aus C++11 nicht schneller hinkriegen?

    Eher nicht. Und es ist kein Operator sondern ein Deklarator. Wenn Deine Vektor-Klasse direkt ein Array wrappt à la

    class vec3d {
      double xyz[3];
      :::
    };
    

    dann kannst du einfach move-technisch nichts besser machen als eine normale Kopie. Die Move-Operationen werden dann interessant, wenn der Zustand des Objekts nicht komplett in den sizeof(T) Bytes enthalten ist, sondern teilweise woanders gespeichert wird (wo auch immer) und die Klasse einen "natürlichen Nullzustand" besitzt (wie z.B. einen leeren Vektor oder ein unique_ptr, der auf nichts mehr zeigt).

    Wenn es um das Rechnen mit kleinen Vektoren/Matrizen und Geschwindigkeit geht, könnte man mal tvmet ausprobieren (tiny vector/matrix expression templates). Das stell ich mir zumindest sehr flott vor. Getestet habe ich das bisher nicht.



  • hustbaer schrieb:

    bei std::array sieht die Sache schon wieder anders aus, da ist das Arrays selbst Member, dann kann man IMO auch sagen dass die Elemente des Arrays Member sind.

    Es ist also für dich relevant ob ich new oder alloca verwende?
    Für mich sind das implementierungsdetails. zB gibt es bei Strings ja eine short-string-optimization die es ermöglich bei kurzen Strings ohne new auszukommen.

    Für mich ist die Implementierung der Klasse eher nebensächlich. Ob ich den Speicher mit einem array allokiere, alloca verwende oder new - das Konzept ist Speicherbeschaffung. Wie die stattfindet ist ein Implementierungsdetail.

    Dass std::array zB die Sachen in einem array hält ist für mich nur ein Detail. std::array wäre genauso OK wenn es die Sachen dynamisch allokiert. uU macht es sogar Sinn std::array zu spezialisieren und bei großen Arrays hier dynamisch zu allokieren...

    Was wäre dann? Member oder nicht member?



  • Shade,
    mich interessiert die Diskussion schon lange nicht mehr.
    Wenn ihr meint so tun zu müssen, als wüsstet ihr nicht was Member sind, OK.
    Wenn ihr meint so tun zu müssen, als wüsstet ihr nicht was ich mit meiner Kritik gemeint habe, auch OK.

    Macht halt.

    Bis demnächst in einem anderen Thread.



  • hmmmmm schrieb:

    rapso schrieb:

    ich war an c++ und c dran, das resultat war, dass c++/oop (also z.b. vector klassen mit ueberladenen operatoren) ca 5% langsammer war als c/prozedural(z.b. vec_dot_3f(out,va,vb);).

    Ob da viel oop war? Oder bzw. ob C kein oop war? Hört sich auf jeden Fall so an, als ob es in beiden Fällen sehr ählicher Code war.

    es war natuerlich kein klassengeruest usw. vorhanden, je mehr man das ausgebaut haette, desto mehr haette die vergleichbarkeit gelitten, somit ist das natuerlich auch kein kompletter c vs c++ vergleich, sollte es ja auch nicht werden.
    die frage war schlicht, ob es egal ist welche sprache und vorgehensweise man nimmt. es gab leute die sagten c/prozedural ist am schnellsten, weil man dort den fuer den compiler einfachsten code schreibt, es quasi high level assembler ist. auf der ganz anderen seite, c#/java das ganze program beim JIT kennt, es ist genau spezifiziert was auf was pointer usw. halten kann und ein compiler kann theoretisch genau wissen wann etwas aliast und wann nicht usw. und dadurch ist es moeglich besseren code zu generieren. auch wird das ueber files hinweg gemacht, waehrend c++ eigentlich nur innerhalb von translation unit optimiert wird (wobei VS2003 afaik, link time code generation eingefuehrt hatte und dadurch z.b. accessor funktionen geinlined werden, selbst wenn sie zur compiletime nicht zu sehen sind.)
    aber naja, die vorgaben waren schon relativ eng gesetzt, der ray/tri intersection algorithm war sogar als c code vorhanden, sodass es nur "portieren" war.
    ich hatte am anfang nicht erwartet, dass man einen unterschied messen koennen wird zwischen c/prozedural und c++/OOP (wie gesagt, nur vector klassen und simple stream in/out, was nicht zur messung gehoerte), es hat mich auch ueber jahre genervt 😡 bis ich den flipcode artikel ausnutzte.

    &&- schrieb:

    rapso schrieb:

    vom assembler code (aus VS2003) her sah ich, dass einige ueberladene operatoren sich nicht dazu bringen lassen wollten ohne temp copy auszukommen, selbst nach dem inlinen, ansonsten sah der assembler ziemlich gleich aus.

    Kann man das mit dem &&-Opertator aus C++11 nicht schneller hinkriegen?

    nein, wie kruemlk sagte, ist es eher fuer klassen mit referencen gedacht. um das zu optimieren wuerde man eher mojo benutzen. damit hatte ich angefangen, aber ueber ein paar hierarchieen hinweg wurden das ziemlich viele spezialisierte klassen werden. ich haette dafuer templates nutzen sollen, waere dann wohl bei der selben idee gelandet wie mein flipcode link.
    ich hatte damals ueberlegt ob ich mojo mit virtuellen operatoren mache und ob der compiler, wenn er den ganzen scope des mojo objektes kennt, den virtuellen function call durch einen direkten call bzw inlining loesen wuerde, aber wenn nichtmal RVO klappte, wollte ich die zeit nicht verschwenden.

    Psychologe22 schrieb:

    rapsos Posting uebersetzt: "Ich bin der geilste"

    suess dass du das ueber einen simplen erfahrungsbericht denkst :), fuehle mich geschmeichelt. :xmas1:


Anmelden zum Antworten