Code mit paralleler For-Schleife langsamer?



  • Guten Tag, ich habe folgenden Code:

    internal void Win32_UpdateEventList(platform_controllerevents *TargetControllerEventList, XINPUT_STATE OldStates[4])
    {
    	//TODO: Second version of this, to set start values
    	XINPUT_STATE XinputState;
    
    	int16 i;
    #pragma omp parallel for
    	for (i = 0; i < 8; i++)
    	{
    		if(XInputGetState(i, &XinputState) ==S_OK){
    			TargetControllerEventList->ControllerEvents[i].IsConnected = true;
    
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[0], XINPUT_GAMEPAD_A);
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[1], XINPUT_GAMEPAD_B);
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[2], XINPUT_GAMEPAD_X);
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[3], XINPUT_GAMEPAD_Y);
    
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[4], XINPUT_GAMEPAD_DPAD_UP);
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[5], XINPUT_GAMEPAD_DPAD_DOWN);
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[6], XINPUT_GAMEPAD_DPAD_LEFT);
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[7], XINPUT_GAMEPAD_DPAD_RIGHT);
    
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[8], XINPUT_GAMEPAD_RIGHT_THUMB);
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[9], XINPUT_GAMEPAD_LEFT_THUMB);
    
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[10], XINPUT_GAMEPAD_START);
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[11], XINPUT_GAMEPAD_BACK);
    
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[12], XINPUT_GAMEPAD_RIGHT_SHOULDER);
    			Win32_ProcessDigitalButton(XinputState, OldStates[4], TargetControllerEventList->ControllerEvents[i].ButtonEvents[13], XINPUT_GAMEPAD_LEFT_SHOULDER);
    
    			Win32_ProcessAnalogButton(XinputState.Gamepad.sThumbLX, 32767, 32768, &TargetControllerEventList->ControllerEvents[i].LeftThumb, XAxis, DEAD_ZONE_THUMB);
    			Win32_ProcessAnalogButton(XinputState.Gamepad.sThumbLY, 32767, 32768, &TargetControllerEventList->ControllerEvents[i].LeftThumb, YAxis, DEAD_ZONE_THUMB);
    
    			Win32_ProcessAnalogButton(XinputState.Gamepad.sThumbRX, 32767, 32768, &TargetControllerEventList->ControllerEvents[i].RightThumb, XAxis, DEAD_ZONE_THUMB);
    			Win32_ProcessAnalogButton(XinputState.Gamepad.sThumbRY, 32767, 32768, &TargetControllerEventList->ControllerEvents[i].RightThumb, YAxis, DEAD_ZONE_THUMB);
    
    			Win32_ProcessAnalogButton(XinputState.Gamepad.bLeftTrigger, 255, 1, &TargetControllerEventList->ControllerEvents[i].LeftTrigger, YAxis, DEAD_ZONE_TRIGGER);
    
    			Win32_ProcessAnalogButton(XinputState.Gamepad.bRightTrigger, 255, 1, &TargetControllerEventList->ControllerEvents[i].RightTrigger, YAxis, DEAD_ZONE_TRIGGER);
    
    			OldStates[i] = XinputState;
    		} else
    		{
    			TargetControllerEventList->ControllerEvents[i].IsConnected = false;
    		}
    	}
    }
    

    Wie kann es sein, dass dieser Code ohne die Nutzung von

    #omp parallel for
    

    schneller ist, als mit?



  • Ohne zu wissen, was Win32_ProcessDigitalButton macht, und als jemand, der sich mit OpenMP nicht auskennt (wohl aber mit Threading-Programmierung), gibt es da mehrere mögliche Ursachen:

    - OpenMP ist doof und optimiert nicht ordentlich an der Stelle, erstellt die Threads an Ort und Stelle, obwohl es vielleicht schlauer wäre, diese zum Anfang des Prozesses zu erstellen.
    - Das Windows-API zur Threading-Kontrolle ist doof und nicht optimiert für so einen Code (weil dieser nur geringe Speicherressourcen benötigt und die Threads aber nicht minimalistisch erstellt werden).
    - Win-API und OpenMP sind schnell genug, aber der Code selbst ist so trivial, dass die Context-Switches am Ende mehr weh tun als die durch die Parallelisierung gewonnene Laufzeit

    Was es genau ist, oder ob es überhaupt eines der genannten ist, können wir dir allerdings nicht sagen. Profiler drüberlaufen lassen, oder generell mal den Code anschauen und prüfen, wo der Flaschenhals liegt.



  • Ach, und noch 'ne Sache zum Stil: Ich kriege langsam Anfälle, weil ich merke, wie wenig Programmierer darauf achten, dass ihr Code lesbar ist. So was zum Beispiel:

    for(i = 0;i < 8;i++)
    {
            if(x == 0)
            {
                    /*Tausendundeine Codezeilen*/
            }
            else
            {
                    /*Eine Zeile*/
            }
    }
    

    Wenn ich sowas sehe, werde ich seelisch grausam den Leuten gegenüber, die das geschrieben haben. Macht es lieber so:

    if(x != 0)
    {
            /*Eine Codezeile*/
            continue;
    }
    
    /*Tausendundeine Codezeilen*/
    

    Das ist dann auch gleichzeitig tausendundeinmal leichter nachzuvollziehen. Ich habe mal ein Perl-Modul gesehen, in denen die Programmierer 7 if-Abfragen gemacht haben und in den Blocks dann der Hauptcode drin war. Am Ende war dann nur ein return . Und nirgendwo gab es auch nur einen else -Block Die Abfragen zu negieren und dann jedes Mal zu return en ist wesentlich verständlicher und schreckt den Leser nicht noch ab.

    Und eigentlich könnte man diesen Post auch direkt als Sticky einrichten, für all die Nasen, die sich dessen noch immer nicht bewusst sind.

    EDIT: Und wo wir schon mal dabei sind ... pack deine XINPUT_GAMEPAD_ *-Konstanten in ein Array, welches du dann in einer Schleife innerhalb deiner Schleife durchläufst. Wenn der Compiler gut ist, merkt er, dass die Schleifendurchläufe hier fix sind, und unrolled das, wenn er denkt, dass das besser ist. Und wenn der Compiler scheiße ist, dann hast du einen Grund mehr, gegen $Compilerhersteller ordentlich zu schimpfen. 🙂



  • OldStates[4] greift einen daneben. 😞



  • OldStates[4]
    

    sollte eigentlich

    OldStates[i]
    

    sein.

    Ich habe nun die Konstanten in ein Array gepackt und durchlaufe dieses jetzt innerhalb der Schleife. Jetzt läuft das Programm auch schneller mit der Verwendung von

    #pragma omp parallel for
    

    als ohne, warum auch immer 😕 .



  • Ohne zu wissen, was Win32_ProcessDigitalButton macht

    Füllt die im dritten Parameter Übergebene struct anhand der anderen Parameter.



  • Moin

    Also sind das nur einfache Funktionen, die nichts aufwendiges machen?
    Du darfst nicht vergessen, dass Threads erstellen und löschen auch seine Zeit kostet. Wenn die Funktion selber so gut, wie keine Zeit verbraucht, dann lohnt es sich einfach nicht zu parallelisieren.



  • Ja, sind alles nur sehr einfache Funktionen. Muss wohl am erstellen der threads liegen. Danke für die Hilfe


Log in to reply