Kuriose Optimierungen / Laufzeitverhalten



  • Heyo,

    bezüglich folgendem Code:

    template<SizeT N, typename InputIteratorT, typename OutputIteratorT>
    	void Run(BasicTapeT<N>& Tape, InputIteratorT&& InputFirst, InputIteratorT&& InputLast, OutputIteratorT&& Output, PolicyT const& Policy)
    	{
    		// std::ofstream timings("timings.log", std::ios_base::trunc);
    
    		while(this->IsInstrPtrValid())
    		{
    			this->SingleStep(Tape, std::forward<InputIteratorT>(InputFirst), std::forward<InputIteratorT>(InputLast), std::forward<OutputIteratorT>(Output), Policy);
    		}
    	}
    

    Die Memberfunktion Run wird genau einmal pro Ausführung des Programms aufgerufen. Das kuriose an der Geschichte, ist, dass es einen Unterschied macht, ob ich den Code nach dem // auskommentiert habe oder nicht. Und der Unterschied ist nicht gerade zu vernachlässigen:

    .Run              14362847.700us (auskommentiert; Zeile inaktiv)
    .Run               7160195.400us (nicht auskommentiert; Zeile aktiv)
    

    Das ist ein Performance-Unterschied von 100%, nur weil ich in einer Funktion eine Datei öffne. Was noch viel merkwürdiger ist, ist die Tatsache, dass der eine Wert ganz genau das Doppelte des anderen ist (wenn man für Hintergrundprozesse etc. ein wenig Ungenauigkeit einkalkuliert).

    Aufgefallen ist mir das Ganze, als ich den Code manuell profilen wollte und die Messungen die Laufzeit des gesamten Programms merkwürdigerweise drastisch verbesserten. Das konnte ich dann soweit reduzieren, bis nur noch diese eine Zeile im obigen Code übrig war (als Unterschied zum originalen Code).

    Meine Vermutung war, dass der Compiler irgendwelche Inlining-Entscheidungen oder so etwas anders fällt. Wie dieser dubiose Faktor von genau 2 zustande kommt, ist mir jedoch schleierhaft. Ich bin auf Windows 10 64-bit und habe GCC 5.3.0 mit MinGW installiert. Meine Flags für compilen sind:

    g++ -Wall -Wextra -Wno-missing-field-initializers -std=c++14 -Ofast -march=native -fwhole-program -DNDEBUG -s
    

    Vielleicht kann mir da ein Compiler-Guru ein bisschen auf die Sprünge helfen. 🙂

    Gruss

    PS: in MSVC14 macht es keinen Unterschied, beide Varianten gleich viele Welten langsamer als GCC 😉



  • Ich habe eben MinGW mit GCC 6.2.0 installiert und alles ist immer noch beim Alten...


  • Mod

    Können wir nicht nachvollziehen.

    Nebenbei halte ich

    InputIteratorT&& InputFirst, InputIteratorT&& InputLast
    

    für die Runfunktiosnparamter für fehlerhaft: da aus beiden Argumenten der gleiche Templateparameter ermittelt wird, kann hier nicht l- und rvalue gemischt verwendet werden, also z.B.:

    Run(tape, it, it + 10, out, policy)
    

    Üblicherweise werden Iteratoren per Value Übergeben, andernfalls wäre es zweckmäßig, verschiedene Templateparameter first und last zu verwenden.



  • camper schrieb:

    Können wir nicht nachvollziehen.

    Den Code habe ich hier abgelegt. Ich habe ihn ursprünglich nicht verlinkt, weil es m.E.n. nicht eine Sache des Codes ist, sondern vermutlich auf abstruse Compiler-Geschichten zurückgeht.

    camper schrieb:

    Nebenbei halte ich[...]

    Du hast recht, werde ich ändern. Danke.


Anmelden zum Antworten