Schneller Code vs. Übersichtlichkeit/Wartbarkeit
-
King George schrieb:
humbug...
Bitte bleib doch im NadRW Forum, da schätzen wir alle deine Meinung sehr.
-
Man schreibt fuer Datenforma A eine Funktion, fuer B, fuer C, ... da wuerde ich dann gemeinsame Teile nicht weiter in Funktionen auslagern. Wenns dann mehrmals ist, ist es halt so. Auf dem Top-Level bleibt es aber uebersichtlich. Dazu braucht man nicht zwingend Templates.
-
Grund: Die Verarbeitung an "einem Stück" ist schnell. Kapselt man nun jetzt gemeinsam genutzte Codeteile bricht die Performance teilweise massiv ein, weil der Compiler dann halt Funktionsaufrufe macht, o.g. Konstanten nicht mehr zur Compilezeit berechnet sondern zur Laufzeit.
Und was ist mit inline?
-
Was soll damit sein?
-
knivil schrieb:
Man schreibt fuer Datenforma A eine Funktion, fuer B, fuer C, ... da wuerde ich dann gemeinsame Teile nicht weiter in Funktionen auslagern. Wenns dann mehrmals ist, ist es halt so. Auf dem Top-Level bleibt es aber uebersichtlich. Dazu braucht man nicht zwingend Templates.
Nö aber mit Templates ist das halt drei mal weniger Code..
-
Naja dann habe ich keine Aufrüfe mehr, sondern direkt "geinlineten" code, oder irre ich mich?
Sodass zwar für den Compiler eine Codeduplizierung existiert, aber nicht für den Programmierer. Die Geschwindigkeit bleibt dennoch gleich.
-
cooky451 schrieb:
Nö aber mit Templates ist das halt drei mal weniger Code..
Haha! Es kommt immer drauf an: http://www.youtube.com/watch?v=zh8W4ZglOlw 49:35 . Ich kann dir auch leicht eine Aufgabe geben, an der du dich probieren kannst.
-
inline ist ein Hinweis, aber zwingt den Compiler zu nichts. Moderne Compiler wie MSVC entscheiden von selbst was geinlined wird und was nicht, unabhängig davon ob inline davor steht oder nicht...
-
slax schrieb:
Die if Abfrage in der inneneren Schleife ist völlig ineffizient, weil die Schleifen entsprechend oft durchlaufen werden. Die Bedingung kann sich aber auch während dem Funktionsaufruf nicht ändern.
Du schreibst die Bedingung ändert sich nicht.
Angenommen du hilfst dem Compiler etwas indem du die Bedingung in ein eigenes bool speicherst ala
// ... bool const someCondition = PossiblySomeComputationsOrComparisons; for (...) { // ... if (someCondition) { // ... } else { // ... } // ... }
Dann ist schonmal sichergestellt dass es wirklich nur mehr um einen Conditional-Branch geht, und nicht diverse Berechnungen bei jedem Schleifendurchlauf neu angestellt werden. (Das könnte sonst nämlich passieren, z.B. weil der Compilier auf Grund von möglichem Aliasing nicht sicher weiss, dass die Variablen in der Bedingung in der Schleife garantiert nicht verändert werden können).
Dann hast du also einen Conditional-Branch in der Schleife, der immer immer immer gleich ausgeht.
Genau dafür wurde Branch-Prediction erfunden, und genau das macht sie sensationell gut. Zumindest auf den CPUs wo ich es ausprobiert habe (Pentium 4, Core 2).
D.h. mach erstmal nen Versuch mit "if vs. kein-if", und wenn er so ausgeht wie ich es mir erwarte (=kein relevanter Unterschied messbar), dann schreib es einfach mit nem normalen "if".Falls der Unterschied doch gut spürbar ist (z.B. weil du CPUs unterstützen musst die keine (gute) Branch-Prediction haben), dann würde ich die Template-Variante empfehlen.
Ansonsten gibt es immer noch den Präprozessor:
// foo_variant_impl.hpp // Kein Include-Guard! namespace { void FOO_VARIANT_NAME() { // ... for (...) { // ... DoStuffWith(FOO_VARIANT_SOME_CONSTANT); #if FOO_VARIANT_SOME_CONDITION != 0 { // ... } #else { // ... } #endif // ... } } } // namespace #undef FOO_VARIANT_NAME #undef FOO_VARIANT_SOME_CONDITION #undef FOO_VARIANT_SOME_CONSTANT
// foo.cpp #define FOO_VARIANT_NAME FooA #define FOO_VARIANT_SOME_CONDITION 1 #define FOO_VARIANT_SOME_CONSTANT 12 #include "foo_variant_impl.hpp" #define FOO_VARIANT_NAME FooB #define FOO_VARIANT_SOME_CONDITION 0 #define FOO_VARIANT_SOME_CONSTANT 42 #include "foo_variant_impl.hpp" void Foo(bool someCondition) { if (someCondition) FooA(); else FooB(); }
Bietet sich auf für C-Projekte an, wenn man sich nicht auf das Inlining des Compilers verlassen will.
(Wenn schon, dann kann man ja genau das selbe machen wie mit Templates, nur nen normalen bool Parameter übergeben statt des Template-Arguments. Wenn der Compiler das inlinen tut, sieht er ja dass die Condition Konstant ist, und wird entsprechend den "if" oder den "else" Zweig wegwerfen)
Ist mMn. auf jeden Fall alles besser als den Code mehrfach zu schreiben (Wartbarkeit!). Speziell wenn es mehr als ein paar Zeilen sind, doppelt speziell wenn es Code ist der nicht ganz einfach zu verstehen ist (was bei SSE-Code ja oft der Fall ist).
-
knivil schrieb:
cooky451 schrieb:
Nö aber mit Templates ist das halt drei mal weniger Code..
Haha! Es kommt immer drauf an: http://www.youtube.com/watch?v=zh8W4ZglOlw 49:35 . Ich kann dir auch leicht eine Aufgabe geben, an der du dich probieren kannst.
Wir haben hier doch schon einen ganz konkreten Fall?
-
hustbaer schrieb:
Ist mMn. auf jeden Fall alles besser als den Code mehrfach zu schreiben (Wartbarkeit!). Speziell wenn es mehr als ein paar Zeilen sind, doppelt speziell wenn es Code ist der nicht ganz einfach zu verstehen ist (was bei SSE-Code ja oft der Fall ist).
Ok, das mit dem conditinal branch habe ich nicht bedacht, aber ist logisch.
Ich habe es jetzt die Schleifen in Templatefunktionen gepackt, und über Template Parameter "nach außen gezogen". Funktioniert soweit gut. Das Problem ist nur, den Funktionen muss man jetzt ~10 Parameter übergeben. Das führt dazu das der Compiler das nicht mehr freiwillig inlined. Performanceverschlechterung: +100%.
Wenn man den Compiler mittels Tricks wie "__forceinline" zum inlinen zwingt habe ich Performanceverschlechterung: +0%Das ärgert mich das man wohl einen Tod sterben muss.
Entweder man verwendet schick templates, riskiert dann aber, dass der Compiler nicht mehr so toll optimiert.Oder man bastelt es sich über Präprozessor Makros zusammen, was auch nicht so wirklich toll ist.
Die Codeduplizierung, die hier vorgeschlagen wurde, würde ich gleich mal vergessen, das kostet einfach viel zu viel Zeit und wird elendig unübersichtlich.
-
@slax: Bis du sicher, dass der Perfomance-Verlust an den Funktionen liegt, an denen du grade rumbastelst und nicht woanders ist? Wie hast du es bestimmt? Profiler? Zeitstempel? Ticks? Wie misst du die Zeit?
-
slax schrieb:
Das Problem ist nur, den Funktionen muss man jetzt ~10 Parameter übergeben. Das führt dazu das der Compiler das nicht mehr freiwillig inlined. Performanceverschlechterung: +100%.
Die 10 Parameter vor der Übergabe in ein Zeigerarray packen und nur dieses übergeben.
-
knivil schrieb:
cooky451 schrieb:
Nö aber mit Templates ist das halt drei mal weniger Code..
Haha! Es kommt immer drauf an: http://www.youtube.com/watch?v=zh8W4ZglOlw 49:35 . Ich kann dir auch leicht eine Aufgabe geben, an der du dich probieren kannst.
Der Link, von Youtube, der rechts bei mir mit aufgeführt ist, ist ebenfalls einen Blick wert:
http://www.youtube.com/watch?v=4F72VULWFvcAber...*erinner* wer hatte da noch mal so ein wundervolles zum Zitieren Zitat rausgehauen das anfängt mit premature optimization is...wie gehts weiter?
Oder man meditiert statt dessen ein bißchen über den Inhalt dieser Seite:
http://www.thc.org/root/phun/unmaintain.html
-
Welcher Compiler?
Ich bin mir zu 100% sicher dass der gcc besser deinen Code optimieren kann als du mit all deinem Wissen über die Funktionsweise. Ernsthaft.inline ist ein Hinweis, aber zwingt den Compiler zu nichts. Moderne Compiler wie MSVC entscheiden von selbst was geinlined wird und was nicht, unabhängig davon ob inline davor steht oder nicht...
zb der GCC kennt
__attribute__((always_inline))
Dann wird immer inlined. Wenn die Performance durch den achso-schlechten-Optimizer so leidet, dann kann man ja auch compilerspezifische Features nutzen.
-
abc.w schrieb:
@slax: Bis du sicher, dass der Perfomance-Verlust an den Funktionen liegt, an denen du grade rumbastelst und nicht woanders ist? Wie hast du es bestimmt? Profiler? Zeitstempel? Ticks? Wie misst du die Zeit?
Zeitstempel + Betrachtung des Disassembly
Ethon schrieb:
Welcher Compiler?
Ich bin mir zu 100% sicher dass der gcc besser deinen Code optimieren kann als du mit all deinem Wissen über die Funktionsweise. Ernsthaft.inline ist ein Hinweis, aber zwingt den Compiler zu nichts. Moderne Compiler wie MSVC entscheiden von selbst was geinlined wird und was nicht, unabhängig davon ob inline davor steht oder nicht...
zb der GCC kennt
__attribute__((always_inline))
Dann wird immer inlined. Wenn die Performance durch den achso-schlechten-Optimizer so leidet, dann kann man ja auch compilerspezifische Features nutzen.
Compiler war der VC++ 2008. Mit dem GCC habe ich keine Messung durchgeführt.
Fakt ist, die ursprüngliche Version wurde mittels inline assembler implementiert. Der erste Schritt war eine Simple Portierung zu intrinsischen Funktionen, da x64 Compiler kein inline assembler unterstützen. Die Ur-Inline Assembler Version war immer die schnellste, selbst die intrinsic Portierung mit Codeduplizierung war etwas langsamer (nicht viel).
Der VC hat inline einfach ignoriert, obwohl in den Funktionen massig SSE intrinsics verwendet werden, von denen der Compiler ja ausgehen könnte, dass das Performancekritisch ist und es bei "Optimize Speed" doch bitteschön auch schnell machen sollte.
-
slax schrieb:
abc.w schrieb:
@slax: Bis du sicher, dass der Perfomance-Verlust an den Funktionen liegt, an denen du grade rumbastelst und nicht woanders ist? Wie hast du es bestimmt? Profiler? Zeitstempel? Ticks? Wie misst du die Zeit?
Zeitstempel + Betrachtung des Disassembly
Ethon schrieb:
Welcher Compiler?
Ich bin mir zu 100% sicher dass der gcc besser deinen Code optimieren kann als du mit all deinem Wissen über die Funktionsweise. Ernsthaft.inline ist ein Hinweis, aber zwingt den Compiler zu nichts. Moderne Compiler wie MSVC entscheiden von selbst was geinlined wird und was nicht, unabhängig davon ob inline davor steht oder nicht...
zb der GCC kennt
__attribute__((always_inline))
Dann wird immer inlined. Wenn die Performance durch den achso-schlechten-Optimizer so leidet, dann kann man ja auch compilerspezifische Features nutzen.
Compiler war der VC++ 2008. Mit dem GCC habe ich keine Messung durchgeführt.
Fakt ist, die ursprüngliche Version wurde mittels inline assembler implementiert. Der erste Schritt war eine Simple Portierung zu intrinsischen Funktionen, da x64 Compiler kein inline assembler unterstützen. Die Ur-Inline Assembler Version war immer die schnellste, selbst die intrinsic Portierung mit Codeduplizierung war etwas langsamer (nicht viel).
Der VC hat inline einfach ignoriert, obwohl in den Funktionen massig SSE intrinsics verwendet werden, von denen der Compiler ja ausgehen könnte, dass das Performancekritisch ist und es bei "Optimize Speed" doch bitteschön auch schnell machen sollte.
Hast du mal
forceinline
(oder so ähnlich) versucht?