Zwei Codes, einer ist schneller, wieso?



  • Hi, also ich wollte eine kleine Anwendung schreiben und die Auswahl in der Konsole farbig hinterlegen damit der Nutzer weiß was derzeitig ausgewählt ist. Ich habe den Code anders als in der Anwendung für meinen Test auch in eine For-Schleife gesetzt um zu Testen wie Lange es dauern würde durch alle durchzuwechseln.

    SetColor() ist gekürzt, müsste ich auch noch zur Case funktion machen don't judge

    die "..." sind Platzhalter für Strings

    Anfangs nutzte ich diesen Code (Vereinfacht dargestellt um Problem zugänglicher zu machen).

    #include <iostream>
    #include <windows.h>
    
    int SetColor(int Color)
    {
        HANDLE han;
        han = GetStdHandle(STD_OUTPUT_HANDLE);
        if(han == INVALID_HANDLE_VALUE){}
       
    	else if (Color == 7)
    	{
    	SetConsoleTextAttribute(han, 0x07);//Weiß
    	}
    	else if (Color == 16)
    	{
    	SetConsoleTextAttribute(han, 0x70);//Schwarz mit Weißem Hintergrund
    	}	
    } 
    
    using namespace std;
    int main ()
    {
    	int X = 1;
    	
    	for(X = 1;X <= 10;X++) {
    	switch(X){
    		case 1:
    			SetColor(16);
    			cout << "..."<< endl;
    			SetColor(7);
    		break;
    		case 2 ... 10:
    			cout << "..." << endl;
    		break;
    		
    		default:
    		break;
    	}
    	
    	switch(X){
    		case 1:
    			cout << "..." << endl;
    		break;
    		
    		case 2:
    			SetColor(16);
    			cout << "..." << endl;
    			SetColor(7);
    		break;
    		case 3 ... 10:
    			cout << "..." << endl;
    		break;
    		
    		default:
    		break;
    	}
    	
    	switch(X){
    		case 1 ... 2:
    			cout << "..." << endl;
    		break;
    		
    		case 3:
    			SetColor(16);
    			cout << "..." << endl;
    			SetColor(7);
    		break;
    		case 4 ... 10:
    			cout << "..." << endl;
    		break;
    		
    		default:
    		break;
    	}
    	
    	switch(X){
    		case 1 ... 3:
    			cout << "..." << endl;
    		break;
    		
    		case 4:
    			SetColor(16);
    			cout << "..." << endl;
    			SetColor(7);
    		break;
    		case 5 ... 10:
    			cout << "..." << endl;
    		break;
    		
    		default:
    		break;
    	}
    	
    	switch(X){
    		case 1 ... 4:
    			cout << "..." << endl;
    		break;
    		
    		case 5:
    			SetColor(16);
    			cout << "..." << endl;
    			SetColor(7);
    		break;
    		case 6 ... 10:
    			cout << "..." << endl;
    		break;
    		
    		default:
    		break;
    	}
    	
    	switch(X){
    		case 1 ... 5:
    			cout << "..." << endl;
    		break;
    		
    		case 6:
    			SetColor(16);
    			cout << "..." << endl;
    			SetColor(7);
    		break;
    		case 7 ... 10:
    			cout << "..." << endl;
    		break;
    		
    		default:
    		break;
    	}
    	
    	switch(X){
    		case 1 ... 6:
    			cout << "..." << endl;
    		break;
    		
    		case 7:
    			SetColor(16);
    			cout << "..." << endl;
    			SetColor(7);
    		break;
    		case 8 ... 10:
    			cout << "..." << endl;
    		break;
    		
    		default:
    		break;
    	}
    	
    	switch(X){
    		case 1 ... 7:
    			cout << "..." << endl;
    		break;
    		
    		case 8:
    			SetColor(16);
    			cout << "..." << endl;
    			SetColor(7);
    		break;
    		case 9 ... 10:
    			cout << "..." << endl;
    		break;
    		
    		default:
    		break;
    	}
    	
    	switch(X){
    		case 1 ... 8:
    			cout << "..." << endl;
    		break;
    		
    		case 9:
    			SetColor(16);
    			cout << "..." << endl;
    			SetColor(7);
    		break;
    		case 10:
    			cout << "..." << endl;
    		break;
    		default:
                    break;
    	}
    	
    	switch(X){
    		case 1 ... 9:
    			cout << "... end" << endl;
    		break;
    		
    		case 10:
    			SetColor(16);
    			cout << "... end" << endl;
    			SetColor(7);
    		break;
    		
    		default:
    		break;
    	}
    	
    	}
    }
    

    Dann stellte ich aber fest das statt der vielen einzelnen Switch Funktionen ich ja auch eine große nehmen könnte also wechselte ich auf diesen Code(auch vereinfacht dargestellt:

    #include <iostream>
    #include <windows.h>
    
    int SetColor(int Color)
    {
        HANDLE han;
        han = GetStdHandle(STD_OUTPUT_HANDLE);
        if(han == INVALID_HANDLE_VALUE){}
       
    	else if (Color == 7)
    	{
    	SetConsoleTextAttribute(han, 0x07);//Weiß
    	}
    	else if (Color == 16)
    	{
    	SetConsoleTextAttribute(han, 0x70);//Schwarz mit Weißem Hintergrund
    	}	
    } 
    
    using namespace std;
    int main()
    {
    	int X = 1;
    	
    	for(X = 1;X <= 10;X++) {
    	switch (X) { 
    	case 1:
    		SetColor(16);
    		cout << "..." << endl;
    		SetColor(7);
    		cout << "..."  	<< endl;
    		cout << "..."  	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."  	<< endl;
    		cout << "..." 	<< endl;
    		cout << "... end" 	<< endl;
    	break;
    	
    	case 2:
    		cout << "..." 	<< endl;
    		SetColor(16);
    		cout << "..." 	<< endl;
    		SetColor(7);
    		cout << "..."  	<< endl;
    		cout << "..."  	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."  	<< endl;
    		cout << "... end" 	<< endl;
    	break;
    	
    	case 3:
    		cout << "..."  	<< endl;
    		cout << "..." 	<< endl;
    		SetColor(16);
    		cout << "..."  	<< endl;
    		SetColor(7);
    		cout << "..."  	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."  	<< endl;
    		cout << "... end" 	<< endl;
    	break;
    	
    	case 4:
    		cout << "..." 	<< endl;
    		cout << "..."  	<< endl;
    		cout << "..." 	<< endl;
    		SetColor(16);
    		cout << "..." 	<< endl;
    		SetColor(7);
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."  	<< endl;
    		cout << "... end" 	<< endl;
    	break;
    	
    	case 5:
    	cout << "..."	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."  	<< endl;
    		cout << "..." 	<< endl;
    		SetColor(16);
    		cout << "... end"  	<< endl;
    		SetColor(7);
    		cout << "..." 	<< endl;
    		cout << "..."	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."  	<< endl;
    		cout << "...end" 	<< endl;
    	break;
    	
    	case 6:
    		cout << "..." 	<< endl;
    		cout << "..."	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."  	<< endl;
    		cout << "..." 	<< endl;
    		SetColor(16);
    		cout << "..." 	<< endl;
    		SetColor(7);
    		cout << "..."	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..."  	<< endl;
    		cout << "... end" 	<< endl;
    	break;
    	
    	case 7:
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		SetColor(16);
    		cout << "..." 	<< endl;
    		SetColor(7);
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "... end" 	<< endl;
    	break;
    	
    	case 8:
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		SetColor(16);
    		cout << "..." 	<< endl;
    		SetColor(7);
    		cout << "..." 	<< endl;
    		cout << "... end" 	<< endl;
    	break;
    	
    	case 9:
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		SetColor(16);
    		cout << "..." 	<< endl;
     		SetColor(7);
    		cout << "... end" 	<< endl;
    	break;
    	
    	case 10:
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		cout << "..." 	<< endl;
    		SetColor(16);
    		cout << "... end" 	<< endl;
    		SetColor(7);
    	break;
    	
    	default:
    	break;
    	}
    	
    	}
    }
    

    Beim Ausführen hatte ich in der Orginal-Anwendung das Gefühl das der neue Code langsamer lief also hatte ich beide Varianten in der vereinfachten Form verfasst (die man auch oben sehen kann) um die Geschwindkeit zu testen und tatsächlich ist der erste Code schneller ausgeführt. Auf meinem Rechner

    Code 1: 0.17 (sec)
    Code 2: 0.25 (sec)
    =Durchschnittswerte aus 10 Maligem Testen=

    Zwar hat sich damit für mich die Frage beantwortet welcher Code schneller ist, aber jetzt frage ich mich. Wieso? Müsste nicht eine einzige Case Funktion schneller sein als mehrere einzelne?

    Danke schon mal im voraus.

    P.s

    Wenn es eine noch schnelle Möglichkeit gibt, immer her damit. Aber hauptsächlich würde ich gerne einfach nur wissen warum gerade Code 1 schneller ist, obwohl mehr Switch Funktionen vorhanden sind.



  • @FP-C-Neuling
    cout << "..." und SetConsoleTextAttribute sind verglichen mit dem bisschen if und switch was du machst um Grössenordnungen langsamer. Also ist der Code langsamer der mehr davon macht.



  • @hustbaer aber an sich machen doch beide das gleich oft. Code 1 sowie Code 2 führen (pro Durchlauf) 10 mal cout durch und 2 mal SetConsoleTextAttribute. Der einzige Unterschied liegt hier in der anzahl der Switch funktionen. Da ja in Code A für jede Zeile einzeln überprüft wird wo in Code B alles auf einmal ausgespuckt wird.



  • Womit es richtig schnell werden sollte ist wenn du einen eigenen ScreenBuffer anforderst und diesen dann direkt beschreibst. Siehe
    https://docs.microsoft.com/en-us/windows/console/reading-and-writing-blocks-of-characters-and-attributes
    https://docs.microsoft.com/en-us/windows/console/writeconsoleoutput

    Wichtig dabei: Nicht Stück für Stück in den Screen-Buffer schreiben, sondern mach dir eine Kopie im Speicher die du direkt beschreiben kannst (normale Array-Zugriffe). Und erst wenn du ein Frame fertig hast, schreibt du diese Kopie mit einem einzigen WriteConsoleOutput Aufruf ins Konsolenfenster.



  • This post is deleted!


  • @FP-C-Neuling sagte in Zwei Codes, einer ist schneller, wieso?:

    @hustbaer aber an sich machen doch beide das gleich oft. Code 1 sowie Code 2 führen (pro Durchlauf) 10 mal cout durch und 2 mal SetConsoleTextAttribute. Der einzige Unterschied liegt hier in der anzahl der Switch funktionen. Da ja in Code A für jede Zeile einzeln überprüft wird wo in Code B alles auf einmal ausgespuckt wird.

    OK, so genau hatte ich mir den Code nicht angesehen 🙂
    Dann wird's vermutlich einfach nur ein Messfehler sein.



  • @hustbaer Wie meine Name schon vermuten lässt bin ich absolute planlos. Was ist eine "Messfehler"?


  • Mod

    @FP-C-Neuling sagte in Zwei Codes, einer ist schneller, wieso?:

    @hustbaer Wie meine Name schon vermuten lässt bin ich absolute planlos. Was ist eine "Messfehler"?

    Ein Fehler beim Messen. Wird sehr oft (eigentlich sogar immer) von Leuten mit solchen Fragen wie den deinen gemacht. Da nach deiner Aussage beide Codes das gleiche ausgeben und sich nur in der if-switch-Logik unterscheiden, können wir auch bei dir davon ausgehen, denn (wie hustbaer schon sagte) die Ausgaben dauern so unendlich viel länger als alle ifs und switches, dass sie mindestens 99.99% der Programmlaufzeit ausmachen. Wenn du beide Male das gleiche ausgibst, aber trotzdem etwas unterschiedliches misst, hast du wohl falsch gemessen.

    Typische Fehlerquellen:

    • Es wird nur ein einzelner Programmablauf vermessen
    • Es wird nicht in Isolation vermessen, d.h. irgendein Hintergrundprogramm funkt dazwischen.
    • Es wird von mehreren Programmabläufen nicht das Minimum der Laufzeit (korrekt), sondern die durchschnittliche Laufzeit (falsch) als Messwert angenommen.
    • Es wird ein ohne Optimierungen übersetztes Programm vermessen, schlimmstenfalls sogar ein Debugbuild. Niemanden interessiert es, wie schnell ein unoptimiertes Programm läuft.
    • Die Programme tun doch nicht ganz das gleiche

    Da du weder deine Messmethode noch deinen genauen Code zeigst, ist nicht zu sagen, welche(n) dieser Fehler du machst.



  • @SeppJ Mein Genauer Code ist dem OP zu entnehmen, ich hatte ihn gekürzt da wahrscheinlich keiner lust hat sich mit meinem Unfertigen Code zu befassen. Solltest du diesen Code in eine X beliebiegen Compiler Copy and Pasten wirst du ihn ausführen können.

    Als Messdaten nahm ich die Beendungs Zeit des Programms nach seinem Start die mir mein Compiler in Sec angibt

    Da du vermutlich ebenfalls einen C++ Compiler besitzt gibt den Code doch einfach mal ein, die Problematik mit den Zeiten bleibt die gleiche.



  • Und noch ein Hinweis: std::endl gibt einen Zeilenumbruch aus und macht danach einen Flush. Gerade in Fällen, wo man in eine Datei schreibt, kann das ein Performancehindernis sein (bei cout kann es sein, dass ein \n automatisch ein Flush verursacht). Siehe auch https://www.youtube.com/watch?v=GMqQOEZYVJQ


  • Mod

    @FP-C-Neuling sagte in Zwei Codes, einer ist schneller, wieso?:

    @SeppJ Mein Genauer Code ist dem OP zu entnehmen, ich hatte ihn gekürzt da wahrscheinlich keiner lust hat sich mit meinem Unfertigen Code zu befassen. Solltest du diesen Code in eine X beliebiegen Compiler Copy and Pasten wirst du ihn ausführen können.

    Und somit ist es eben nicht der Code, den du vermisst, sondern nur einer, der nach deiner Meinung nach äquivalent ist. Also nutzlos zum Nachvollziehen.

    Als Messdaten nahm ich die Beendungs Zeit des Programms nach seinem Start die mir mein Compiler in Sec angibt

    Da merkt man schon sofort, dass etwas nicht stimmt, denn dein Compiler sagt dir garantiert nichts über die Ausführungszeit deines Programms.

    Da du vermutlich ebenfalls einen C++ Compiler besitzt gibt den Code doch einfach mal ein, die Problematik mit den Zeiten bleibt die gleiche.

    Da sagt man dir, was an deiner Frage fehlt, damit man dir helfen kann, und dann kommt so etwas als Antwort. Du willst also wohl keine Hilfe bekommen. Kannst du haben.



  • @SeppJ
    Code 1: Siehe Block 1 im OP
    Code 2: Siehe Block 2 im OP

    Die Codes sind von der Ausgabe gleich, beide haben genau 10 cout ausgaben pro Durchlauf beide haben genau 2 SetTextColorAttribute. Beide sind bis auf die if-switch Logik gleich.

    Ich verstehe nicht was an meiner Frage fehlen soll, beide Codes sind Äquivalent und beide Codes werden das gleiche in der Konsole anzeigen.

    Die Zeiten werden mir von meinem Compiler angezeigt nach Ausführung des Programmes, glaub es oder glaub es mir nicht, aber mein Compiler hat die Funktion.

    Da sagt man schon alles was man weiß und schreibt den ganzen Problemteil und ändert extra alles so dass nur die Kern Problematik erhalten bleibt, schreibt seine eigene Funktion um damit Leute sich nicht durch alles durchgraben müssen, testet beide unter gleichen Bedingungen und wird dann von einem Mod dumm angemacht weil etwas fehlen soll, obwohl der gesamt Code vorhanden ist.

    Wenn du keine Hilfe geben willst, dann lass es.



  • Nimm eine high_resolution_clock aus std::chrono und miss vernuenftig.

    // Edit: Ausserdem, welchen compiler benutzt du der angeblich sowas tut und mit welchen parametern compilst du?



  • @FP-C-Neuling: Du weißt aber schon, daß man deinen Code viel eleganter (d.h. kürzer) programmieren kann (ohne switch) - stattdessen mit 2 weiteren Schleifen?
    Dies spart unnötige Cache-Misses und sollte bei einem auf Geschwindigkeit optimierten Build auch schneller sein.

    Oder wolltest du nur testen, wie gut ein switch optimiert werden kann?

    PS: Außerdem ist case N ... M: kein Standard-C++, sondern eine compilerspezifische Erweiterung.



  • @Th69 Die "..." sind Platzhalter für Strings, wenn du das meintest die "..." werden die einzelnen Optionen sein die man auswählen kann, wenn du das nicht meintest. Nein weiß ich tatsächlich nicht, könntest du mir das erklären.

    und zum thema case N . . . M, wusste ich nicht dachte das wäre was aus C übernommenes 😅



  • @Cardiac Ich benutze DevC++ welcher dir nach jedem Ausführen des Programms die Runtime angibt, selbst ohne Return 0; befehl (oder exit();)

    //Edit: Was den Parameter angeht habe ich nie was geändert und ich hab auch keine Ahnung wo ich das nachgucken kann.



  • Die Ranges sind kein Standard, das ist eine GNU C Erweiterung, d.h. ich kann das nicht einfach via copy und paste ausprobieren. Einem online gnu compiler kann ich das auch nicht so einfach geben, weil der natürlich die windows.h nicht kennt. Dann sollte deine SetColor Funktion auch was zurück geben. Ist also doch was spezieller, bevor man den Code ans laufen bekommt. 😉

    Wenn du Geschwindigkeit testen willst, musst du das schon mehrfach ausführen und dann z.B. die Zeiten mitteln. Dabei sicher stellen, dass keine Prozesse im Hintergrund die Ausführung beeinflussen, soweit wie möglich.
    Insgesamt sind für Geschwindigkeitsbeurteilungen wichtig:

    • welcher Compiler
    • welche Compiler Flags
    • welches OS
    • welche Hardware


  • Nicht mitteln, sondern die kürzeste Laufzeit nehmen. Das wird dann die sein, wo du die wenigsten Störeffekte durch andere Prozesse hast.



  • @Schlangenmensch

    Ok das ist doch schonmal hilfreich.

    • DevC++;
    • (Wie mein Name schon vermuten lässt planlos, hab keine Ahnung)
    • Windows 10 (Redstone 1) 64bit
    • Ich Programmiere sowohl auf meinem Dell Latitnde 3380 Laptop, sowie auf meinem Desktop PC, könntest du mir sagen welche Specs du genau brauchst damit ich keinen Roman schreiben muss?


  • Ich meinte so etwas:

    const int MaxX = 10;
    for (int x = 0; x < MaxX; ++x)
    {
    	for (int y = 0; y < x; y++)
    		cout << "..."<< endl;
    	
    	SetColor(16);
    	cout << "..."<< endl;
    	SetColor(7);
    
    	for (int y = x + 1; y < MaxX; ++y)
    		cout << "..."<< endl;
    }
    

    (selbst wenn die "..." unterschiedliche Texte (je nach x) sind, dann können die mittels eines Arrays adressiert werden)

    PS: Und du mußt selbstverständlich einen Release-Build auf Performance testen (und am besten außerhalb deiner IDE).
    Trotzdem testest du damit nur (wie @SeppJ schon geschrieben hat) die Geschwindigkeit der Konsolenausgabe...


Log in to reply