Wieviel Performance kostet die Objekt orientierte Programmierung?
-
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 einenfloat
kapseln soll, würde ich das genauso legitim halten. Das hat mitsize
beimvector
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.pdfWow, 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?