Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
Einen guten Abend wünsche ich Euch
und habe nach langer Zeit wieder mal eine Frage (Achtung! FreePascal):
Zunächst mal: mit dem obigen "FrontEnd" ist keine GUI gemeint, sondern eine Klasse, die als Container für etliche andere "Helfer"-Klassen ("BackEnds") eingesetzt wird und gleichzeitig eine schöne übersichtliche Programmier-Schnittstelle zur Verfügung stellen soll. Die BackEnds sind für die eigentliche Verarbeitung zuständig.
Voraussetzung: die Programmier-Schnittstelle sollte schnell und sicher sein.
Ursprünglich - weil mir auch damals schon nichts Besseres eingefallen ist - habe ich das mittels Object-Properties implementiert, wobei der Programmierer hier auf alle öffentlichen Properties/Methoden des jeweiligen BackEnds zugreifen kann:
Code:
TBackEnd =Class;
TFrontEnd =Class(TObject)
Protected
fBackEnd: TBackEnd;
Public
Property BackEnd: TBackEnd Read fBackEnd;
End;
Das ist schnell, aber hier kann der Programmierer auch auf TBackEnd.Destroy zugreifen, womit man die BackEnds einfach löschen kann. Für eine API ist das meiner Meinung nach ein Disaster.
Weiters habe ich Property-Interfaces ausprobiert, mit dem gleichen Ergebnis wie vorher.
Ich habe mal vor längerer Zeit im Lazarusforum drüben nachgefragt, aber die Antwort war: "Das ist eben so. Kann man nichts machen."
Als Alternative gibt es folgende Standardversion: Der Programmierer ruft "Frontend.DoSomething" auf, welches wiederum "Backend.DoSomething" aufruft. Das ist sicher, aber nicht besonders schnell: zwei Methoden-Aufrufe hintereinander
Mein letzter Versuch benutzt einen Delegaten, dabei ruft die Methode "Frontend.DoSomething" zwar auch die Methode "Backend.DoSomething" auf, aber diesmal direkt über dessen Methodenzeiger, wobei ich hoffe, das das etwas flotter ist.
Google war diesmal auch keine große Hilfe.
Meine Frage lautet jetzt: Ist die Sache mit dem Methodenzeiger schon das Schnellste, was ich kriegen kann, oder habt Ihr noch bessere Ideen?
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Erstmal die obligatorische Frage: Hast du geprofiled und rausgefunden, dass ein doppelter Methodenaufrauf tatsächlich ein Problem ist? Das sollte ein bis zwei Fetches (zwei bei virtuellen Methoden) und ein Call sein, wenn die Argumente in der gleichen Reihenfolge bleiben und du auf 64bit bist (auf 32bit wird noch nen bisschen Stack dazugebaut, um die Argumente zu duplizieren; bei 64bit ab einer gewissen Anzahl auch).
Dann hab ich noch nicht ganz raus was dein Problem mit Interfaces ist. Wenn du das Reference-Counting aushebelst (kannst du in deiner Implementation von IUnknown machen) sollte das sicher sein, solange du den Destruktor nicht mit ins Interface packst. Der Vorteil bei Interfaces ist, dass sie schnell zu bauen sind. Geschwindigkeitstechnisch müssten sie in einer Größenordnung mit dem Methodenzeiger liegen.
Wenn Interfaces keine Option sind, Methodenzeiger, ja. Über Properties gegen Schreibzugriff absichern und dann sollte das hinhauen.
viele Grüße, Horazont
P.S.: Darf man fragen, wofür das ist? Ich mache inzwischen relativ viel Python, und da geht man eher so dran, dass man APIs nicht versteckt, sondern nicht dokumentiert, wenn sie nicht verwendet werden sollen, und schreibt dem Benutzer einen gewissen Common Sense zu.
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
Lord Horazont hat geschrieben:
Erstmal die obligatorische Frage: Hast du geprofiled und rausgefunden, dass ein doppelter Methodenaufrauf tatsächlich ein Problem ist? Das sollte ein bis zwei Fetches (zwei bei virtuellen Methoden) und ein Call sein, wenn die Argumente in der gleichen Reihenfolge bleiben und du auf 64bit bist (auf 32bit wird noch nen bisschen Stack dazugebaut, um die Argumente zu duplizieren; bei 64bit ab einer gewissen Anzahl auch).
Nein, geprofiled habe ich noch nicht. Ich wollte bloß wissen, ob ich irgend etwas Offensichtliches übersehen habe. Ich bin immer noch auf 32 Bit, vermutlich weil ich aus einer Zeit stamme, wo die Leute mit 64k RAM auskommen mussten und ich mich ärgere, was heutzutage für Platz verschwendet wird.
Lord Horazont hat geschrieben:
Dann hab ich noch nicht ganz raus was dein Problem mit Interfaces ist.
Ich habe mir einfach mein FreePascal-LanguageReference-Handbuch hergenommen und eine kleine Probeanwendung erstellt:
Code:
[
{$interfaces corba}
type
IMyInterface =interface
procedure P1;
end;
TDelegateClass =class(TObject, IMyInterface)
private
procedure P1;
end;
TMyClass =class(TInterfacedObject, IMyInterface)
private
FMyInterface: TDelegateClass;// class type
public
property MyInterface: TDelegateClass
read FMyInterface implements IMyInterface;
end;
Und weil das "FMyInterface" wie man oben schön sehen kann, ja nichts anderes ist als das Objekt selbst, wird bei einem Call: TMyClass.MyInterface.Free genauso das SubObjekt freigegeben, als wenn ich gleich ein ganz normales Object Property implementiere. Zwischen Object Property und Interface Property ist *kein* Unterschied, bei beiden kann das implementierende Objekt einfach gelöscht werden. Und genau das will ich ja vermeiden.
Bei Corba-Objekten braucht man das Interface nicht aushebeln, die haben gar keine Referenz-Zählung, lt. Pascal-Handbuch gibt es die nur bei MSWindows COM-Objekten.
Wenn die Interfaces dem Methodenzeiger ungefähr gleich sind, wärs mir recht, denn dann kann ich dabei bleiben. Ich werde aber das Profiling noch nachholen.
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
Lord Horazont hat geschrieben:
Hm, Interfaces haben ein Free? Dann ergibts natürlich Sinn, das wusste ich (noch) nicht.
Nein, Interfaces haben kein Free, aber das Objekt, welches das Interface implementiert, hat ein Free:
Code:
property MyInterface: TDelegateClass
read FMyInterface implements IMyInterface;
Das Ding, das hier "MyInterface" genannt wird, ist eine normale Pascal Klasse und zur Laufzeit also ein Objekt und kann über das Property freigegeben werden.
Lord Horazont hat geschrieben:
Mit deinen Methodenzeigern hast du wieder zwei Calls, da gewinnst du nichts. Ich dachte du würdest die per Property rausreichen:
Nein, das möchte ich nicht, weil dazu folgender Vorgang nötig wäre:
Das FrontEnd müsste in seinem Konstruktor den leeren **öffentlichen** Methodenzeiger mit der Methode "TBackEnd.Print" bestücken. Dann ist es möglich, dass der Programmierer das direkt aufrufen kann. Verstehst Du, einen öffentlichen nackten Methodenzeiger. Das wäre zwar besser, als ein nacktes Subobjekt (wie ich es jetzt habe), aber für eine ordentliche API finde ich das nicht tragbar.
Da bleibt mir wohl nichts übrig als der zweifache Methodenaufruf.
Das ist schade, denn es handelt sich um das Fontend einer, naja, nicht mehr ganz so kleinen Grafik-Engine für OpenGL 3.3. Das ganze war ursprünglich gedacht für die GUI, ist aber total getrennt von ihr und immer weiter gewachsen. Seit ungefähr 2009 beschäftige ich mich damit, und die Version 1.0 hat sie jetzt endlich erreicht. Die Print-Methode habe ich als Beispiel herausgenommen, es gibt noch etliche andere Methoden, die genauso gehandhabt werden. Diese Methoden sammeln die Daten für den nächsten Rendervorgang und es wäre schön, wenn das so schnell wie möglich wäre. Aber im Augenblick ist die Sicherheit (und Lesbarkeit) wichtiger, weil es das eben das Canvas-Container-Objekt ist, das Frontend nach außen.
Nachtrag: Hab's hingekriegt , letztendlich mit Corba-Interfaces und einigem Herumprobieren, weil das FPC-Handbuch bei diesem Thema irgendwie auslässt.
Registriert: Do Sep 25, 2003 15:56 Beiträge: 7804 Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
Über Methodencalls mach ich mir - insbesondere bei Frameworks - keine Gedanken. Es ist viel wichtiger Lesbaren und wiederverwendbaren Code zu produzieren. Wenn es dazu nötig ist 3 Ebenen zu routen, dann ist das eben so.
Das Thema fällt unter "Pre Mature Optimazation" und "YAGNI - You Aint Gonne Need It".
_________________ Blog: kevin-fleischer.de und fbaingermany.com
Mitglieder in diesem Forum: 0 Mitglieder und 46 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.