Fragwürdiger Mangel an optimierung - g++



  • Hai,
    Ich bin durch Zufall hierauf gestoßen. Ich schreibe in einen array einen haufen int-große rgba Werte in der Hauptschleife meines Programms.

    In der folgenden Version des Codes,

    for(core::integer x = 0; x < 5; ++x)
            {
                for(core::integer i = 0; i < wnd.get_surface().size(); ++i)
                {
                    wnd.get_surface()[i] = core::rgba(255, 0, 33);
                }
                wnd.get_surface()[x].g = x;
            }
    

    erhalte ich rund ~28 fps

    mit dem folgenden Codeausschnitt jedoch,

    core::rgba dummy = core::rgba(255, 0, 33);
            for(core::integer x = 0; x < 5; ++x)
            {
                for(core::integer i = 0; i < wnd.get_surface().size(); ++i)
                {
                    wnd.get_surface()[i] = dummy;
                }
                wnd.get_surface()[x].g = x;
            }
    

    erhalte ich die (maximalen) 60 fps.

    Der Unterschied ist natürlich, dass die Variable nicht mehr in jedem Schleifendurchlauf auf dem stack neu erzeugt wird, was im Konstruktor die initialisierung der rgb-Komponenten zur Folge hat.

    Compilerflags für den Vergleich waren

    g++ -std=c++11 -Wall -DSRLLINUX -O3
    

    und die Compilerversion ist 4.8.2.

    Meine Erwartung ist, dass g++ an dieser Stelle erkennt dass immer ein und dasselbe Objekt zugewiesen wird, und er dieses dann automatisch zur Seite legt. Warum nicht?



  • Sieht der Compiler die Definition von core::rgba ? Ich nehme an nicht. Das würde erklären, dass er die Funktion in jedem Schleifendurchlauf erneut aufrufen muss, da er nicht davon ausgehen kann, dass sie keine Seiteneffekte hat.



  • Ja, die Headerdatei für core:rgba ist nur eine handvoll Zeilen über main.

    Ich dachte auch zuerst an Seiteneffekte die der Compiler annimmt, aber wenn der höchst unwahrscheinliche Fall, dass der Konstruktor von rgba das Objekt wnd.get_surface() verändert, automatisch von g++ angenommen wird ohne das zu prüfen, dann macht das Optimierungen in inneren Schleifen in den meisten Fällen unmöglich.

    Das heißt ich würde erwarten dass g++ dann im Konstruktor nachschaut. Oder wird sowas nicht gemacht, da schon zuviel Codeanalyse?



  • randa schrieb:

    Ja, die Headerdatei für core:rgba ist nur eine handvoll Zeilen über main.

    Das war nicht die Frage.

    Ich dachte auch zuerst an Seiteneffekte die der Compiler annimmt, aber wenn der höchst unwahrscheinliche Fall, dass der Konstruktor von rgba das Objekt wnd.get_surface() verändert, automatisch von g++ angenommen wird ohne das zu prüfen, dann macht das Optimierungen in inneren Schleifen in den meisten Fällen unmöglich.

    Es geht nicht um Wahrscheinlichkeiten. Wenn eine Programmtransformation nicht sicher zu einem semantisch äquivalenten Programm führt, darf sie nicht durchgeführt werden. Das Verändern von wnd.get_surface() ist auch nicht der einzige mögliche Seiteneffekt, denkbar wäre z.B. schlicht und einfach, dass das Resultat von core::rgba nicht immer das gleiche ist.

    Das heißt ich würde erwarten dass g++ dann im Konstruktor nachschaut.

    Welcher Konstruktor, ist core::rgba eine Klasse? OK, gut zu wissen, ändert aber nichts prinzipielles. [edit: Oh, das ergibt sich ja schon aus dem Code. Hatte ich nicht gesehen.]

    Oder wird sowas nicht gemacht, da schon zuviel Codeanalyse?

    Das kommt wie gesagt primär darauf an, ob die Definition (= der Inhalt des Konstruktors) sichtbar ist, oder ob nur eine Deklaration existiert und die Definition extern liegt. Falls eine Definition sichtbar ist, ist die nächste Frage, wie komplex sie ist. Im Idealfall wäre core::rgba inline und würde nur aus einer Folge von Zuweisungen bestehen.



  • Bashar schrieb:

    randa schrieb:

    Ja, die Headerdatei für core:rgba ist nur eine handvoll Zeilen über main.

    Das war nicht die Frage.

    Ah. Definition war der Funktionsrumpf...gut gut...ich habe das abgeändert, sie liegt nun im Header, aber es macht einen Unterschied von 3 - 4 fps, was wohl am inline liegt.

    Falls eine Definition sichtbar ist, ist die nächste Frage, wie komplex sie ist

    Der Konstruktor setzt nur das Objekt auf 0.

    Wenn eine Programmtransformation nicht sicher zu einem semantisch äquivalenten Programm führt, darf sie nicht durchgeführt werden. Das Verändern von wnd.get_surface() ist auch nicht der einzige mögliche Seiteneffekt, denkbar wäre z.B. schlicht und einfach, dass das Resultat von core::rgba nicht immer das gleiche ist.

    In der Tat.
    Gut dass ich mir angewöhnt hab, den code wie im 2ten Fall zu schreiben. Dieser Optimierer ist zeimlich schwach. Auch constexpr hilft nicht.



  • randa schrieb:

    Dieser Optimierer ist zeimlich schwach.

    *lach*



  • randa schrieb:

    Bashar schrieb:

    randa schrieb:

    Ja, die Headerdatei für core:rgba ist nur eine handvoll Zeilen über main.

    Das war nicht die Frage.

    Ah. Definition war der Funktionsrumpf...gut gut...ich habe das abgeändert, sie liegt nun im Header, aber es macht einen Unterschied von 3 - 4 fps, was wohl am inline liegt.

    3-4 fps besser als vorher oder 3-4 fps schlechter als die "handoptimierte" Variante?

    Falls eine Definition sichtbar ist, ist die nächste Frage, wie komplex sie ist

    Der Konstruktor setzt nur das Objekt auf 0.

    Und mit den Parametern macht er nichts? Komisch.

    Vielleicht sollte man mal genauer forschen, woher der Unterschied noch kommt, d.h. den Assemblercode angucken. Eigentlich steht dem Herausziehen aus der Schleife ja nichts mehr im Wege.



  • Bashar schrieb:

    3-4 fps besser als vorher oder 3-4 fps schlechter als die "handoptimierte" Variante?

    besser als vorher, also die langsame 27fps variante.

    den Assemblercode angucken.

    Jo.



  • Wäre es nicht besser, erst mal sinnvollen Code zus chreiben, bevor die Geschwindigkeit gemessen wird?

    for(core::integer x = 0; x < 5; ++x)
            {
                for(core::integer i = 0; i < wnd.get_surface().size(); ++i)
                {
                    wnd.get_surface()[i] = core::rgba(255, 0, 33);
                }
                wnd.get_surface()[x].g = x;
            }
    

    Wenn ich mal annehme, dass get_surface hier immer das gleiche liefert, dann sind die ersten 4 Durchläufe der äusseren Schleife völlig sinnlos, weil im folgenden Durchgang nochmal alles überschrieben wird.



  • camper schrieb:

    Wäre es nicht besser, erst mal sinnvollen Code zus chreiben, bevor die Geschwindigkeit gemessen wird?

    for(core::integer x = 0; x < 5; ++x)
            {
                for(core::integer i = 0; i < wnd.get_surface().size(); ++i)
                {
                    wnd.get_surface()[i] = core::rgba(255, 0, 33);
                }
                wnd.get_surface()[x].g = x;
            }
    

    Wenn ich mal annehme, dass get_surface hier immer das gleiche liefert, dann sind die ersten 4 Durchläufe der äusseren Schleife völlig sinnlos, weil im folgenden Durchgang nochmal alles überschrieben wird.

    Du bist außergewöhnlich intelligent, Camper. Du könntest fast eine Karriere als Programmierer einschlagen! 🤡



  • Was genau soll die Schleife (nach Korrigierung des genannten Fehlers) tun? Die ersten Fünf Elemente von wnd.get_surface() auf (255, 0, 33) , (255, 1, 33) , ..., (255, 4, 33) setzen und den Rest auf (255, 0, 33) ?


Log in to reply