Zeit für Lowlevel Sachen wie if, Funktionsaufruf und Co.



  • Ich stelle nun endlich mal die Frage, die mir im Kopf herumgeistert, seit ich gehört habe, dass ifs lahm sind:
    Wie lahm sind die?
    Wie schnell sind die zB im Vergleich eines Funktionsaufrufes einer Division, etc.?
    Wie teuer ist new eigentlich wirklich?

    Ansatz der Fragen diesmal war folgende Idee:
    Ich hab ein Makro CHECK_NULLPTR(ptr), was (vereinfacht) folgende Funktion aufruft:

    void check_nullptr(void* ptr);
    

    Die wirft ggf. eine Exception.
    Natürlich kann man das via ifdef ausschalten.
    Nun würde ich das gerne dynamisch ausschalten können. Da es dass eh nichts bringt in jedem Fall ein if auszuführen, hatte ich folgende Lösung:
    Anstatt direkt die Funktion check_nullptr aufzurufen, wird ein function Pointer dereferenziert, der bei deaktivierung auf eine Funktion zeigt, die nichts tut:

    void do_check_nullptr(void* ptr) { if (!ptr)...}
    void dont_check_nullptr(void*) {}
    void (*checker) (void*) = ...;
    

    Die Frage ist: Bringt das etwas?



  • ???
    Eine Exception oder gar ein Funktionsaufruf ist doch nicht schneller als eine if-Abfrage. 😕



  • Ja klar, das meinte ich doch nicht.
    Ich wollte nur einen Vergleich haben:
    if x, Funktionsaufruf y; mit y = 3x oder so.



  • Quellen:
    Branch prediction: http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array
    Function pointer: http://stackoverflow.com/questions/10757167/do-function-pointers-force-an-instruction-pipeline-to-clear

    Das Problem ist, dass die Zeit für if extrem schwanken kann. Der Prozessor versucht vorauszusagen, was welchen Weg das if gehen wird und wenn er Recht hat, dann ist if nur 1 Takt, andernfalls wird die ganzen Pipeline geleert, was typischerweise mehr als 1 Takt ist.

    Bei Funktionspointern ist das nicht anders, nur dass es etwas aufwendiger ist, das Ziel vorherzusagen.

    In deinem Fall ist ein "if" schneller. Es wird immer den gleichen Wert haben und der Prozessor ist schlau genug, das herauszufinden. Wenn du es wechselst, ist es die ersten paar Aufrufe vielleicht etwas langsamer, dann merkt er aber, dass es gewechselt wurde und gut ist.

    Es gibt eine Ausnahme, und zwar wenn du sehr viele if-Zweige hast, die sehr viel Code enthalten. Dann muss der Prozessor die ganze Funktion im Instructioncache halten und verwirft etwas mehr von dem anderen Code, der dann wieder neu geholt werden muss etc. Der Effekt tritt aber nur auf, wenn du ganz oft Code ausfühst, der nur knapp in den Instruction-Cache passt. Bei dir würde ich mir darüber keine Sorgen machen.



  • Das heißt, vorm Pointercheck abzufrage, ob enabled, ist nicht viel langsamer?
    Gut, trotzdem frag ich mich, ob es dynamische Check in diesem Fall bringt. bringt.



  • Wenn du dich zur Laufzeit zwischen zwei Codepfaden entscheiden musst ist if *immer* das schnellste was du kriegen kannst.

    Die einzige Optimierung die du hier noch draufwerfen kannst ist jedes Mal zur Laufzeit beide Pfade auszuführen und dann im if nur das richtige Ergebnis zu selektieren. Das kann in wenigen, ganz speziellen Fällen ein Performanceplus bringen, aber wer mit sowas anfängt ohne vorher gründlichst zu profilen macht definitiv was falsch.



  • Nathan schrieb:

    Das heißt, vorm Pointercheck abzufrage, ob enabled, ist nicht viel langsamer?
    Gut, trotzdem frag ich mich, ob es dynamische Check in diesem Fall bringt. bringt.

    Aeh wat?



  • if (enabled)
    if (!ptr)
    ...
    ist - wenn enabled == false - nicht viel langsamer als
    if (!ptr)
    ...
    da enabled ja nur selten geändert wird?
    Trotzdem frage ich mich, ob es das bringt, weil man da auh gleich immer checken kann.o



  • 1.) Dein Beispiel lebt in einem Kontext, der mir nicht bekannt ist.
    2.) 2 Mal if ist doppelt so teuer wie 1 Mal if (abhaengig vom Praedikat).
    3.) if kostet fast nix (abhaengig vom Praedikat).
    4.) 2 Mal von fast nix, ist meistens wieder fast nix.



  • Den Kontext hab ich oben erklärt, dass ist mein Thread. 🙂



  • Und weiter:

    5.) Funktionsaufrufe kosten fast nix (abhaengig von Parametern).
    6.) Leere Funktionsaufrufe kosten fast nix (abhaengig vom Parametern).
    7.) Problem: Spielt fast nix eine Rolle?
    8.) Problem: Wie vergleicht man fast nix?

    Hat ptr einen Wert in Abhaengigkeit von 'enabled' dann sollte bei enabled doch schon klar sein, was ptr ist. D.h. enabled ist ueberfluessig. Umgedreht genauso.



  • Ich plane, nullptr Checks dynamisch ausschaltbar zu machen, via der ..
    im OT erklärten Methode.
    miraculix erklärte aber, dass das wg branch prediction langsamer ist.
    Um das zu vergewissern fragte ich, ob die Methode mit zwei Checks nicht viel langsamer ist, als der ohne den Check, da der äußere Check, ob enabled, sich ja fast nie ändert.
    Trotz allem frage ich mich, ob das dynamische Ausschalten was bringt.



  • Nathan, du verstrickst dich da glaube ich in eine total sinnlose Aufgabe.
    Mach die Nullzeiger-Checks statisch und permanent und nicht deaktivierbar.
    Oder steuere sie über #ifndef DEBUG .
    Aber dynamisch? Wozu?



  • Das frag ich mich wie gesagt mittlerweile auch.
    Naja, es hätte von der Konsistenz her ganz gut in mein Konfigurationskonzept hinein gepasst.
    Mach das Flag jetzt constexpr, das wird wohl optimiert werden.


Log in to reply