Miniprogramm mit 50% CPU-Auslastung?

RoninSL

Grünschnabel
Ich hab erst vor kurzem angefangen mich ein bisschen mit C++ auseinanderzusetzen.
Jetzt habe ich mir ein wirklich kleines Programm geschrieben, das aber eine CPU-Auslastung von 50 - 70% erzeugt, was viel zu viel ist (wenn ich überlege, dass ich High-End-Games spielen kann und die gerade mal 15-20% brauchen?).

Ich weiß aber auch nicht wie ich das hinsichtlich der Performance hinbekomme, deswegen habe ich hier mal einen Quellcode und hoffe, dass ihr mir einen Tipp geben könnt, wo mein Problem liegt und wie ich das in den Griff bekomme:

C++:
#define WIN32_LEAN_AND_MEAN
#include <iostream>
#include <string>
#include <Windows.h>
#include <thread>

class Funktionen {
public:
    Funktionen();
    void Funktionen::SimMausklicks();
    ~Funktionen();
};

Funktionen::Funktionen()
{
}

void Funktionen::SimMausklicks() {

    while (true) {

        // Simuliere langen Mausklick mit Druck auf Linke Alt-Taste
        if (GetAsyncKeyState(18)) {
            mouse_event(MOUSEEVENTF_LEFTDOWN, NULL, NULL, NULL, NULL);
            Sleep(60000);
            mouse_event(MOUSEEVENTF_LEFTUP, NULL, NULL, NULL, NULL);
        }
    }

}

Funktionen::~Funktionen()
{
}

int main()
{

    Funktionen Funktionen;

    std::thread thread1(&Funktionen::SimMausklicks, &Funktionen);
    thread1.detach();

    bool run = true;

    while (run)
    {
        // Programm schließen mit Q
        if (GetAsyncKeyState(81))
        {
            run = false;
        }

    }

    return 0;

}

Im Moment soll da also nur eine Minute die Maus geklickt werden, es sollte noch eine zweite Funktion rein (die musste ich erstmal löschen weil mein Programm dann komplett abstürzt) deswegen möchte ich auch einen thread damit dann beide funktionen zeitgleich laufen können.
(Und ja: Ich möchte es benutzen um in Minecraft zu bescheißen - man muss da ewig lange mit der Maus klicken. Shame on me! Mir fiel einfach sonst kein sinnvoller Zweck für ein Programm ein um zu lernen und n Taschenrechner fand ich langweilig *g*)

Aber es kann doch nicht richtig sein, dass so ein Mini-Programm so eine hohe CPU-Auslastung hat? Was genau mach ich denn da falsch?
 
Hi,

deine beiden "while(true)" laufen ununterbrochen durch, ohne Pause und Ablass. Du wirst wohl 4 Kerne haben, macht also zwei Schleifen = 50% Auslastung.
Bau in die Schleifen auch noch ein Sleep ein wenn dein if nicht trifft, damit gibt dein Programm die CPU wieder für eine gewisse Zeit frei.

Jetzt prüfen deine Schleife so oft sie können, ob eine Taste gedrückt wurde. Und das somit viele Tausende Male pro Sekunde. Durch das sleep "bremst" du das halt auf ein paar Mal pro Sekunde, was auch auf alle Fälle ausreichend sein dürfte.

Grüsse,
BK
 
edit: Und zu langsam...

Hi

Programme werden in der Regel so schnell wie möglich ausgeführt. Im "while (run)" - Teil gibt es nichts, worauf gewartet werden muss (Usereingaben, Netzwerkdaten, andere Threads, usw.usw.), deshalb nimmt sich das Programm so viel Prozessorleistung wie es bekommen kann (dH. wie viel das Betriebssystem zuweist. Zumindest das Betriebssystem selber braucht für Verwaltungssachen auch immer wieder ein paar Mikrosekunden)

Dein Prozessor hat mehr als einen "Kern", daher ist es nicht 100% (ein einzelner Thread, wie hier der main-Thread, kann nicht auf mehreren Kernen gleichzeitig laufen. Das ist einer der Hauptgründe, Threads zu verwenden; mehrere Threads können eben mehrere Kerne auslasten (können, nicht müssen)).

Warum das andere Programme besser können? Was du willst ist im wesentlichen, auf einen Tastendruck zu warten und erst dann was zu tun. Dafür ist es auf den üblichen Betriebssystemen nicht nötig, dauerhaft den Tastenzustand zu prüfen. Stattdessen gibt es Funktionen, die beim Aufruf prozessorschonend auf "Ereignisse" vom Betriebssystem warten 8es gibt eine Reihen von verschiedenen, Tastendrücke sind eins davon). In Windows-Konsolenprogrammen ist es leider nicht ganz ideal gelöst, aber mit WH_CALLWNDPROC, SetWindowHookEx, GetConsoleWindow sollte was machbar sein (für die abgefragte Fenster-ID der Konsole einen Hook, also eine vom OS aufgerufene Funktion, vom Typ WH_CALLWNDPROC registrieren. In der Funktion, wo nicht nur Tastenevents ankommen, danach filtern, und dann was tun. Geht aber nicht so direkt im Main, sondern wird Kommunikation zwischen deinen Threads nötig machen, und da kommen dann Mutexe usw. ins Spiel...)

Einfach Sleeps einbauen wird die Prozessorlast verringern, aber dafür gibts auch ein Risiko dass ein sehr schneller Tastendruck (Beginn und Ende während einem Sleep) nicht vom Programm erkannt wird.
 
Hi

Bratkartoffel hat eigentlich schon vieles gesagt. Dennoch ein paar Anmerkungen:
wenn ich überlege, dass ich High-End-Games spielen kann und die gerade mal 15-20% brauchen?).
Spiele machen i.d.R. einfach eine 100%-Auslastung auf der GPU (ausser mit Frameratelocks wie V-sync. Oder schlechter Programmierung. Das erklärt auch, warum alte Spiele wie Quake heutzutage jenseits der 1000fps laufen).
Ich weiß aber auch nicht wie ich das hinsichtlich der Performance hinbekomme, deswegen habe ich hier mal einen Quellcode und hoffe, dass ihr mir einen Tipp geben könnt, wo mein Problem liegt und wie ich das in den Griff bekomme:
Die Performance deines Programms ist ziemlich perfekt: Du hast kaum Downtimes, nur sehr wenige Tastendrücke werden verzögert erkannt. Dummerweise kann der Computer durch die Auslastung gleichzeitig nix anderes mehr machen.
Bei GetAsyncKeyState(), welches den Status (und nicht den Druck) liest, kommt es auf die Pollrate nicht wirklich an, daher kann man durchaus das von BK vorgeschlagene sleep_for nutzen. Allerdings: std::this_thread::yield() ist für kleinere Latenzen besser geeignet (sleep_for nutzt intern auch yield, ist aber klobiger).

deswegen möchte ich auch einen thread damit dann beide funktionen zeitgleich laufen können
Das ist nicht nötig. Threads sind overhead. Alleine die Context Switches sind wahrscheinlich teurer als deine Funktionen.
Die eine 1 Mikrosekunde (wenn überhaupt) zwischen zwei solchen Calls ist vernachlässigbar.

Und ja: Ich möchte es benutzen um in Minecraft zu bescheißen - man muss da ewig lange mit der Maus klicken. Shame on me! Mir fiel einfach sonst kein sinnvoller Zweck für ein Programm ein um zu lernen und n Taschenrechner fand ich langweilig *g*
Für diesen speziellen Task würde ich aber einen Schalter (on-off) statt eines Triggers (on->sleep->off) verwenden.
sleep(60000) ist auch nicht optimal, da sehr ungenau. Hier wären wirklich sleep_for() oder sleep_until() besser.

Aber es kann doch nicht richtig sein, dass so ein Mini-Programm so eine hohe CPU-Auslastung hat? Was genau mach ich denn da falsch?
Als Faustregel: Sobald ein Programm Endlosloops hat, lastet es einen Kern voll aus, sofern der Loop nicht gebremst wird (durch sleeps oder andere Hardwarezugriffe (Festplatte, Netzwerk, etc.))

Oder anders ausgedrückt: Die CPU-Auslastung ist proportional zu der Anzahl der ausgeführten Instruktionen. Ohne Loops kriegst du es heutzutage nur selten hin, so viele Instruktionen auszuführen. (Bei 10^6 Instruktionen pro Sekunde (moderne CPUs sind schneller) und einer Durchschnittlichen Instruktionsgrösse von 4 Bytes wären das immerhin 4MB an .exe oder .dll, das du haben müsstest, um 1s lang eine 100% Auslastung eines Kerns zu erreichen).


Gruss
cwriter
 
So einfach also... Vielen Dank :D
Ich hab nun in jede while-Schleife ans Ende ein "Sleep(100)" gesetzt. Die Auslastung ist damit direkt auf 1% gesunken.

Gibt es da eigentlich noch andere Möglichkeiten als eine while-Schleife? Also ich kenne das von Javascript, da gibt es Events, so kann man z.B regeln dass eine Funktion nur dann aufgerufen wird wenn XY passiert (also ohne ständige while-Schleife)?
 
Stattdessen gibt es Funktionen, die beim Aufruf prozessorschonend auf "Ereignisse" vom Betriebssystem warten 8es gibt eine Reihen von verschiedenen, Tastendrücke sind eins davon).
Gibt es da eigentlich noch andere Möglichkeiten als eine while-Schleife? Also ich kenne das von Javascript, da gibt es Events, so kann man z.B regeln dass eine Funktion nur dann aufgerufen wird wenn XY passiert (also ohne ständige while-Schleife)?
Für Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx

Ich hab nun in jede while-Schleife ans Ende ein "Sleep(100)" gesetzt.
Microsoft ist da recht nett und behält alte Funktionen. Allerdings ist Sleep() keine Standardfunktion und entsprechend gibt es die auch nicht auf anderen Systemen (oder sie sind anders implementiert, bspw. warten sie dann Sekunden und nicht Millisekunden). Geht natürlich auch so, allerdings sollte man sich früh angewöhnen, wann immer möglich den Standard zu verwenden.

Gruss
cwriter
 
Super, vielen Dank für eure ausführlichen und hilfreichen Antworten. :)
Hab leider erst jetzt alle lesen können.


Das werde ich definitiv mal probieren.

Microsoft ist da recht nett und behält alte Funktionen. Allerdings ist Sleep() keine Standardfunktion und entsprechend gibt es die auch nicht auf anderen Systemen (oder sie sind anders implementiert, bspw. warten sie dann Sekunden und nicht Millisekunden). Geht natürlich auch so, allerdings sollte man sich früh angewöhnen, wann immer möglich den Standard zu verwenden.

Ja, das habe ich schon öfter gelesen. Ich werde mich schnellstmöglich nach besseren alternativen umsehen. :D

Das ist nicht nötig. Threads sind overhead. Alleine die Context Switches sind wahrscheinlich teurer als deine Funktionen.
Die eine 1 Mikrosekunde (wenn überhaupt) zwischen zwei solchen Calls ist vernachlässigbar.

Das verstehe ich nicht so ganz. Wenn ich nun keine threads verwende dann wird doch jede Funktion nacheinander ausgeführt. Wenn ich jetzt also z.B diesen 60-Sekunden Mausklick habe, dann wird doch die nächste Funktion erst ausgeführt wenn der Mausklick (bzw. die Funktion) beendet wurde? Also würden die nicht "gleichzeitig" laufen können oder versteh ich das falsch?
 
Das verstehe ich nicht so ganz. Wenn ich nun keine threads verwende dann wird doch jede Funktion nacheinander ausgeführt.
Das stimmt. Was lässt dich denken, dass Threads parallel sind?
Threads gibt es auch auf 1-Kern-Prozessoren. Dort laufen die einzelnen Programme nacheinander.
Selbst auf Mehrkernprozessoren ist nicht garantiert, dass Threads auf unterschiedlichen Cores laufen müssen (tatsächlich gibt es Probleme mit Cache-Locality, wenn Threading genutzt wird). In aller Regel werden sie durchaus parallel ausgeführt, aber es gibt im C++ Standard keine Garantie dafür.

Wenn ich jetzt also z.B diesen 60-Sekunden Mausklick habe, dann wird doch die nächste Funktion erst ausgeführt wenn der Mausklick (bzw. die Funktion) beendet wurde? Also würden die nicht "gleichzeitig" laufen können oder versteh ich das falsch?
Ok, gehen wir es durch:
Was passiert beim Sleep()?
-> Das Betriebssystem wird den Thread nicht mehr aufrufen (oder zumindest weniger oft), bis die Zeit um ist. Der Thread liegt also quasi brach.
Eine Alternative wäre sowas:
C++:
void Funktionen::SimMausklicks() {

    //kleine FSM
    enum {
        Released,
        Holding
    } state = Released;

    auto start = std::chrono::system_clock::now();


    while (true) {

        // Simuliere langen Mausklick mit Druck auf Linke Alt-Taste
        if(state == Released)
        {
            if (GetAsyncKeyState(18)) {
                mouse_event(MOUSEEVENTF_LEFTDOWN, NULL, NULL, NULL, NULL);
                start = std::chrono::system_clock::now(); //EDIT: Das hier braucht's natürlich auch...
                state = Holding;
            }
        }
        else if(state == Holding)
        {
            if(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - start).count() >= 60) //Wenn 60s um: Release und state switch
            {
                mouse_event(MOUSEEVENTF_LEFTUP, NULL, NULL, NULL, NULL);
                state = Released;
            }
            //Sonst nicht

        }
        andererTask();
        std::this_thread::yield(); //Oder std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
   
}
Also ungetestet, aber müsste eigentlich so etwa funktionieren.
Im Prinzip überprüft es immer wieder die Zeit, sodass es nicht blockiert. Dann kannst du auch in andererTask() etwas anderes machen. Im selben Thread.

In dem Sinne läuft es genug "gleichzeitig".

Gruss
cwriter
 
Zuletzt bearbeitet:
Hatte bei google gesucht wie ich zwei Funktionen gleichzeitig laufen lassen kann. Als Antwort gab es immer nur den Verweis auf Threads. Deswegen hatte ich angenommen, dass wäre dann so.
Ok, vielleicht muss ich da etwas genauer werden:
Sagen wir, wir haben 2 Threads, A und B, welche Einzelschritte A.1, A.2, ... bzw. B.1, B.2, ... haben.

Dann erlaubt Threading sowas:
A.1
A.2
B.1
A.3
B.2
B.3
B.4
A.4

Grundsätzlich: Die Reihenfolge ist nicht vorgeschrieben. Ob das nun auf einem oder auf mehreren Kernen läuft, ist egal:
Core 0:
A.1
A.2
B.1
A.3
B.2
B.3
B.4
A.4

Oder mit 2 Kernen:
(A.n und B.n laufen jeweils gleichzeitig)
Code:
Core 0:     Core 1:
A.1          
A.2
                 B.1
A.3           
                 B.2
                 B.3
                 B.4
A.4

Oder auch so:

Code:
Core 0:     Core 1:
A.1         
A.2
B.1
A.3           
                 B.2
                 B.3
                 B.4
                 A.4

Es gibt keine Garantie, dass irgendeine Reihenfolge eingehalten wird.
Theoretisch kann auch A komplett, dann B komplett durchlaufen.
Nun scheint es parallel, auch auf einem Kern. Einfach daher, dass es oft zwischen den Tasks hin- und her-wechselt. Es läuft also "gleichzeitig".
Das heisst aber nicht, dass 2 Codeabschnitte gleichzeitig ausgeführt werden müssen, eigentlich geht das ja gar nicht.
C:
std::atomic<int> contest = 1;

//Thread 1:
contest = 1;

//Thread 2:
contest = -1;
std::atomic garantiert, dass Schreibvorgänge abgeschlossen werden, d.h. dass es keinen Zustand gibt, wo contest nicht 1 und nicht -1 ist.
Aber gleichzeitig schreiben geht ja nicht, die Bits wären ja immer verschieden. Parallelismus kann hier nicht existieren, daher machen Threads wenig Sinn in diesem Beispiel.

TL;DR:
Threads können sehr cool sein. Aber wenn sie vermeidbar sind, sollte man sie vermeiden. Unter anderem ist die Korrektheit von Parallelen Programmen noch immer eines der grössten Forschungsgebiete und man hat noch keine echte Antwort für die Probleme.

Gruss
cwriter
 

Neue Beiträge

Zurück