Willkommen ~Gast!
Registrieren || Einloggen || Hilfe/FAQ || Staff
Probleme mit der Registrierung im Forum? Melde dich unter registerEin Bild.
Autor Beitrag
000
11.05.2001, 01:43
Prefect



Ich bin heute über einen Artikel von einem KDE-Entwickler gestolpert. Dieser Artikel behandelt die Geschwindigkeitsprobleme, die bei KDE-Programmen besonders beim Starten auftauchen. KDE-Programme sind komplett in C++ geschrieben, und da liegt wohl zu einem Großteil der Hund begraben....

http://www.suse.de/~bastian/Export/linking.txt

(Ich geh mal davon aus, dass ihr jetzt den Artikel gelesen & verstanden habt)

Komplett in C++ geschriebene .EXEs leiden natürlich nicht unbedingt so sehr unter dem Problem, da sie meist an eine vor-reloziierte Position, so dass weniger Zeit draufgeht. Sobald DLLs verwendet werden hat man aber ein Problem.

Natürlich ist das im weiteren Verlauf des Programms nicht soo kritisch, von daher ist das kein echtes Argument gegen Spiele in C++ (es sei denn es werden ständig DLLs ein/ausgeladen), aber man sollte das doch mal berücksichtigen, wenn's mal wieder um Geschwindigkeit C vs C++ geht...

cu,
Prefect

--

Widelands - Gemütliche Aufbaustrategie, Free Software
Noch ein Blog - Lerne, wie die Welt wirklich ist, aber vergiss niemals, wie sie sein sollte.

zum Seitenanfang zum Seitenende Profil || Suche
001
11.05.2001, 09:48
WareWolf



Ich kann das nur bestätigen. Schuld ist eigentlich die Eigenart der 80x86 Prozessoren mit getrennten Code und Segmentregistern, was auch im Protected Mode immer noch eine Bremse ist.
Aber durch geschickte Programmierung könnte dies weitgehend vermieden werden, nur verhindern dies wieder "moderne" Betriebssysteme wie Winxx die allesamt vermutlich schlampig programmiert sind.
Der Focus der Betriebssysteme ist halt nicht Schnelligkeit, sondern Vielseitigkeit.

Meine Meinung war schon immer: Engines in Assembler pur programmieren. [/wunschdenken]

--

Sig as a brick ┴┬┴┬┴┬┴┬┴┬┴┬┴
WW

zum Seitenanfang zum Seitenende Profil || Suche
002
11.05.2001, 13:48
Kriz



Objektiv gesehen ist alles, was nicht direkt Maschinensprache ist, langsam. Subjektiv werden die Grenzen dort gezogen, wo es auf Echtzeitleistung oder akurate Leistung ankommt. Was im Artikel steht, ist richtig. KDE muß erstmal über Xfree und QT sich selber initialisieren, d.h. die komplette GUI plus die unteren Ebenen laden. Das frißt natürlich Zeit. Der Aufruf direkt in KDE erspart natürlich diese Prozedur (persönlich bin ich eher in einer GUI, über die man ein Terminal aufrufen kann als im Terminal selber), logisch. Ab dann werden Zeiteindrücke nur noch rein subjektiver Natur. Wenn ein KDE-Fenster 0.3 Sekunden im Durchschnitt benötigt, um komplett konstruiert, initialisiert und eingabefähig zu sein, dann ist das in meinen Augen völlig korrekt. Langsamere CPU's mit weniger Durchsatz können dann eben nicht so schnell agieren. Zudem müssen Prozeßtabellen usw. aktualisiert werden, was ja auch je nach Systemauslastung mal schneller und mal langsamer vor sich geht.

Inwiefern C++ samt Overhead diesen Prozeß noch weiter abbremst, kann nur durch Disassemblierung des Codes festgestellt werden. Wenn die Redundanz schlechter Compiler bei 25% oder mehr liegt, dann kann C++ je nach Projektgröße sicherlich als Bremse dienen.

Auf jeden Fall ein interessantes Thema finde ich! Ich glaube, von der reinen Konstruktion eines Fensters ist Windows schneller, da die GUI als Bestandteil des OS agiert und nicht extern geladen werden muß. Wie das dann natürlich bei bereits bestehender KDE-Env aussieht, steht natürlich auf einem anderen Blatt. Viele DLL's (die aber nicht alle beim Start geladen werden, denn dann wäre der Sinn einer DLL für'n Arsch) verzögern dann den Aufbau eines Windows-Fenster und KDE (weiß jetzt nicht, ob es ein DLL Equivalent in KDE gibt) wäre dann schneller...

Wie dem auch sei, ob C wirklich schneller ist (infolge fehlender Typprüfungen usw.) als C++, sei mal dahingestellt. Schließlich sind C++ Klassen (über die ja andauernd gewettert werden) Bestandteil von C++ und kein eigens dafür vom Programmierer entwickelter Code wie C-Funktionen. Ich glaube eher, daß eigene C-Funktionen langsamer ausgeführt werden als implementierte Klassen. Auch das würde einer Prüfung bedürfen :)

Bin mal gespannt, wie das Thema sich weiterentwickelt...

Cu

--

K:R-I)Z++
"CSS ist cascading style sheets. Und nicht so'n Ranzspiel." - dp
In memory of Voice († 2005/03/30)

zum Seitenanfang zum Seitenende Profil || Suche
003
11.05.2001, 14:58
Buy-Menu-Man



Hat Geschwindidkeit mit der Programmiersprache zu tun?
Ja klar, aber theoretisch müsste es der Compiler doch immer in den optimalen Maschienencode umwandeln!
Wenn das so wäre (Was ja nicht der Fall ist) würde es ja nur noch darum gehen mit welcher Sprache man am schnellsten Code schreiben und Debuggen kann und welche am übersichtlichsten ist!

Ist es überhaupt möglich einen Compiler zu programmieren, der einen optimalen Maschienencode erzeugt oder ist das nur möglich, wenn man ihn selbst schreibt?
Also mit Assembler (Ist ja nicht ganz Maschienencode)!

--

http://battletech-hl.teamfortress2.de

zum Seitenanfang zum Seitenende Profil || Suche
004
11.05.2001, 15:54
Prefect



@Buy-Menu-Man: Die Geschwindigkeit hat nix mit dem resultierenden Code, sondern mit den Virtual Method Tables zu tun, die bei C++ verwendet werden. Mehr dazu unten.

@warewolf: Alle modernen Betriebssysteme (in diesem Fall zählt auch Win9x größtenteils als modernes Betriebssystem) verwenden ein sogenanntes Flat Memory Model in dem Segmentregister keine Rolle mehr spielen.

Und jetzt auf zum ausschweifenderen Gesprächspartner ;)

Wie in dem Artikel z.T. gezeigt wurde, liegt das Performance-Fressen nicht an den eigentlichen Systemaufrufen wie zum Updaten der Prozesstabellen. Was auf jeden Fall aber stimmt, ist dass X-Windows totaler Mist ist, einfach veraltete Technologie. Die Netzwerktransparenz ist zwar auf dem Papier recht nett, in der Praxis aber einfach ineffizient. Da hat Windows klare Vorteile. Aber ich will hier keine Linux vs. Windows-Diskussion starten, da haben wir auf dem Off-Topic-Board schon das Potential ;)
Mir geht's hier nur um C und C++.

Ein paar Dinge vorneweg:
Viele DLLs bringen auch dann Sinn, wenn sie alle beim Start geladen werden - nämlich zur Verringerung des Festplattenplatzes. Es ist außerdem recht problematisch, DLLs mit OOP in Runtime zu laden, wegen bestimmten Funktionsabhängigkeiten die schon vorher aufgelöst werden müßten. In was für Streß das ausartet sieht man ja bei COM - ja, COM wurde in Visual Studio mit Klickibunti aufgepeppt und ist leicht zu programmieren. Aber hinter den Kulissen geht einiges an dreckiger Programmierung und Ineffizienz vor sich, so kennt COM z.B. aus technischen Gründen keine nicht-virtuellen Funktionen. Ich könnte jetzt noch länger ausschweifen...
.dll's gibt's auch bei Linux, die heißen da halt .so's (shared objects).
C ist sicher nicht wegen der Typprüfungen schneller als C++. Die Typprüfungen finden nämlich ausschließlich zur Compiletime statt.

So. Jetzt ist die Frage: _Was_ bremst den C++-Code gegenüber C-Code ab?
Natürlich gibt's da auf der Ebene vom Assemblercode ein paar Dinge. So werden z.B. alle virtuellen Funktionen indirekt über die VMT (Virtual Method Table) aufgerufen. Aber das können wir verkraften. Alle Variablen werden auch indirekt aufgerufen, aber das können die Compiler eigentlich sehr effizient optimimieren.
Im Endeffekt ist also bei C++-produziertem Assembler eine kleine Bremse drin, aber im Vergleich zu dem anderen Hemmnis ist das recht gering.

Der Hammer kommt nämlich eigentlich beim Laden des Programms. Da der Linker beim Linken eines Programmes / DLLs die endgültige Position nicht vorhersagen kann, muß der Code beim Laden eigentlich immer entsprechend angepaßt werden. Hier läßt sich einiges optimieren, so werden die eigentlichen Executables bei Linux z.B. immer auf 0x80000000 geladen, aber eine Restaufgabe bleibt immer noch, besonders bei DLLs.
Diese Aufgabe gliedert sich in zwei Bereiche:
1) interne Fixups. CALLs und JMPs sind zwar bei IA32 alle relativ, aber Zugriffe auf globale Variablen (die es nur bei C-Programmen extensiv gibt) sind immer absolut. Diese Zugriffe müssen evtl. angepaßt werden, dieser Vorgang ist aber recht schnell, da diese Zugriffe vorgelinkt sind, und der Runtimelinker nur noch die Differenz zwischen der tatsächlichen und der vorhergesehen (in der .so) Ladeposition ausgleichen muß, d.h. im Endeffekt geht er eine Liste von Speicherpositionen durch, zu denen eine Konstante addiert werden muß. Recht effektiv.
2) externe Fixups. Das sind Funktions- und Speicherzugriffe auf Symbole (Funktionen/Speicherbereiche) aus einem anderen Modul. Da diese Aufrufe überhaupt nicht voroptimiert werden können, muß der Runtimelinker einiges mehr machen: Er geht eine Liste mit externen Fixups durch, in denen jeweils der Symbolname angegeben wird. Für jedes Auftreten eines Fixups muß der Loader nun dieses Symbol in seinen Tabellen nachschlagen und einsetzen. Es ist klar, dass dieser Vorgang einiges länger dauert.

Jetzt ist es an der Zeit zu untersuchen, wie sich C und C++ hier unterscheiden.
Bei C-Programmen treten hauptsächlich interne Fixups auf. Externe Fixups beziehen sich höchstens auf die StdCLib, und auch die halten sich meist in Grenzen - wohl meist auf ein paar 100. Ein interessanter Gedanke hier: Wahrscheinlich wäre es viel effektiver, eine Log()-Funktion die printf() indirekt ausführt in sein C-Programm einzubauen, als den gesamten Code mit printf()s vollzumüllen...

Bei C++-Programmen treten natürlich auch interne Fixups auf, allerdings nur zu nicht-virtuellen Funktionen und globalen Variablen. Virtuelle Methoden sind ja quasi Runtimefixups (indirekte Funktionsaufrufe), und Membervariablen werden ja eh wie Strukturvariablen behandelt.
Zudem werden für VMTs Fixups erstellt, und zwar für jede virtuelle Methode jeder Klasse eins. Im Klartext heißt das, wenn man eine Klasse CBase mit 35 V-Methoden hat, und eine CDerived mit 5 zusätzlichen V-Methoden, dass 35 + (35+5) Fixups ausgeführt werden müssen. Wenn nun die OOP-Strukturen hauptsächlich programmintern sind ist das kein Problem. Wenn aber, wie bei KDE der Fall, sehr sehr viele Klassen aus .so's importiert werden, zählen fast alle dieser Fixups zu den externen Fixups.

Ein C-Programm hat also viele interne und wenige externe Fixups. Da interne Fixups schnell sind haben wir hier kein Problem.

Ein C++-Programm hat dagegen wenige internet und viel mehr externe Fixups. Und da gerade externe Fixups aufwendig sind verlängert sich die Ladezeit dramatisch.

Natürlich sollte man jetzt nicht hier aufhören und sagen: C++ ist scheiße. Denn OOP hat gerade im Bereich von GUIs extreme Boni. Ein paar mögliche Lösungsansätze, die ja eigentlich nicht unbekannt sind:

- Dynamic libraries werden in allen Prozessen an die gleiche Stelle im Speicher geladen. Dadurch verringert sich die Anzahl der Relokationen ein bißchen. Dies wird mit dem sogenannten kdeinit-Hack bei KDE teilweise erreicht. Mehr dazu unten.
- Der Loader sollte das Konzept von VMTs verstehen. Wenn der Loader, nachdem er die VMT von CBase eingerichtet hat, gleich alles, was gleich geblieben ist, in die VMT von CDerived kopieren würde, könnte man die Anzahl der notwendigen Fixups dramatisch reduzieren.
- Der Loader sollte eine Art Cache einrichten, so dass externe Fixups nicht jedesmal von Neuem nachgeschaut werden müssen. Das ist vielleicht sogar schon der Fall, ich hab mich nicht näher mit ld.so (dem Liunx-ELF-Loader) beschäftigt.

Der angesprochene kdeinit-Hack basiert darauf, dass bei KDE im Hintergrund immer der kdeinit-Dämon läuft. Wenn jetzt ein neues Programm gestartet werden soll wird ein entsprechendes Signal geschickt. Daraufhjn fork()t sich kdeinit (= dupliziert sich), und lädt in seinen Kindprozess das aufzurufende Programm - interessanterweise als Dynamische Library (!)
Das hat zur Folge, dass alle Libraries an der gleichen Speicherposition sind und nicht neu reloziiert werden müssen.

Allerdings müssen natürlich die VMTs der abgeleiteten Klassen im Programm selbst immer noch eingerichtet werden. Und das sind recht viele, da jedes eigene Dialogfenster ja z.B. eine eigene Klasse ist. Und so eine Dialogfensterklasse hat recht viele virtuelle Methoden....

Wow, schon mein zweiter Bandwurmthread heute. Ich installier jetzt mal isdn4linux auf meinem LFS-System, ihr habt also wahrscheinlich erstmal Ruhe *g*

cu,
Prefect

--

Widelands - Gemütliche Aufbaustrategie, Free Software
Noch ein Blog - Lerne, wie die Welt wirklich ist, aber vergiss niemals, wie sie sein sollte.

zum Seitenanfang zum Seitenende Profil || Suche
005
11.05.2001, 18:40
TheTinySteini



Hui, echt ein umfangreicher Thread und ein umfangreiches Thema.
Allerdings: man wird die externen Fixups wohl nie wirklich effizient umsetzen können, insofern muss man halt darauf achten, dass das Gesamtsystem wenige Fixups benötigt. Und da wäre es natürlich sehr gut, wenn XFree optimiert werden würde. Zwar ist es gut gedacht, das ganze auf Server-Client Basis aufzubauen, aber es ist für lokale Windowmanager viel zu langsam. Das selbe Problem - allerdings auf einem ganz anderen Niveau - gab's ja schon bei Win 3.11. DOS als Host ist einfach viel zu langsam, Geschwindigkeit gab's allenfalls durch den Protected Mode, der DOS umging. Ähnlich wird es bei Windowmanagern auch sein: Geschwindigkeit gibt's erst, wenn der Windowmanager direkt auf Linux aufsetzen kann. Hier hat man natürlich nochmal das Problem, dass direkte Hardwarecalls noch schneller wären, aber das is unter Linux nunmal nicht wünschenswert.
Das DLLs auf jeden Fall sinnvoll sind, bleibt nunmal umumstritten, in der aktuellen c't steht wieder mal ein Artikel, was man damit so alles anstellen kann (in diesem Fall die Adressen der Importtable mit den Adressen von Thunks füllen, um bestimmte Funktionsaufrufe abzufangen). Ganz zu schweigen von der HD- und RAM-Ersparnis und der standardisierten (naja ;-) Programmschnittstelle...

--

TheTinySteini
Coder Poke646
"Don't Panic" - Hitchhiker's Guide to the Galaxy

zum Seitenanfang zum Seitenende Profil || Suche
006
12.05.2001, 01:20
Prefect



Hmm... hier liegt ein bißchen Konfusion vor. Der Window Manager ist eigentlich nur so ein doofes Programm, dass den Rahmen um ein Fenster zeichnet. Auch wenn man's vielleicht nicht glaubt.
Aber gerade eben da liegt ja das Problem. Jedes einzelne Programm muß eine Verbindung mit dem X-Server aufrecht erhalten und außerdem auch noch mit dem WM kommunizieren. Eigentlich offensichtlich, dass da die Performance nicht so optimal ist.

Was den direkten Zugriff angeht: Im Prinzip ist es bei modernen Grakas möglich, sehr große Funktionsbereiche den Anwendungsprogrammen direkt zur Verfügung zu stellen, allerdings wird eben auf das bei X nicht viel Wert gelegt (seit 4.x hat sich da ein bißchen was getan, wie das jetzt so genau intern abläuft bin ich mir im Moment nicht sicher, aber es ist bestimmt noch Raum zu Verbesserungen).

Andererseits hat das eigentlich nicht viel mit dem Thread zu tun (auch wenn's ein interessantes Thema ist), da XFree selbst rein in C programmiert ist. Nur eben das Qt-Toolkit, das darauf aufbaut, ist dann vollkommener C++-Code. Übrigens das Beste was es so an C++ gibt. Die Technologien mit Slots und Events sind echt genial. Allerdings braucht man auch einen zusätzlichen Precompiler... :)

cu,
Prefect

--

Widelands - Gemütliche Aufbaustrategie, Free Software
Noch ein Blog - Lerne, wie die Welt wirklich ist, aber vergiss niemals, wie sie sein sollte.

zum Seitenanfang zum Seitenende Profil || Suche
007
12.05.2001, 12:29
TheTinySteini



Ich hab mir das noch so ein bisschen durch den Kopf gehen lassen - und irgendwie sehe ich keinen vernünftigen Workaround. Man kann natürlich den Code optimieren (wie ja in der linking.txt beim Vergleich Gnome <-> KDE beschrieben) und somit überflüssige externe Fixups einschränken, aber viel mehr ist da nicht. Interne Fixups könnte man ja sogar mit nem neuen kleinen Prozessorregister (Register? Bin mir nicht ganz sicher, ob ich tatsächlich nen Register meine, halt ne 32 Bit Speicherstelle) namens CSO (Codesegment offset) oder so einrichten, in das der Runtimelinker nur noch den Programmcode Offset schiebt und das dann bei jedem Referenzieren einer globalen Var hinzugezählt wird... Aber da ist der Aufwand sehr viel größer als der Nutzen...
Aber externe Fixups? Ich bin schon jetzt erstaunt, wie schnell eigentlich so ein Code geladen und ausgeführt wird - man muss sich das doch mal durch den Kopf gehen lassen, wieviel tausend von Zeilen Code pro Sekunde allein von Windows/Linux ausgeführt werden - und trotzdem ist die Prozessorlast nahe Null.

--

TheTinySteini
Coder Poke646
"Don't Panic" - Hitchhiker's Guide to the Galaxy

zum Seitenanfang zum Seitenende Profil || Suche