WriteFile zu langsam?



  • Hallo,

    ich sende mit WriteFile Zeichen über die serielle Schnittstelle. Dabei ist mir eine zu hohe Systemauslastung aufgefallen. Als Beispiel:

    Baudrate: 115200
    gesendet wird ein Array mit 100 Zeichen
    overlapped IO

    Das Programm macht nichts anderes als diese Zeichen zu senden in einer while Schleife und das in einem separaten Thread. Dabei habe ich eine Systemauslastung von ständig 5-8%

    Mache ich das Array kleiner geht das bis max 50% bei einem Zeichen.

    Ist das normal? Eigentlich kann man doch nicht so viel Systemleistung verschwenden für das versenden von Zeichen? Achso, der Rechner ist ein Athlon mit 700 MHz mit WinXP und die Werte gelesen mit dem TaskManager.

    Das ganze ist sehr wichtig, weil damit später eine zeitkritische Anwendung gesteuert werden soll und eigentlich sollte das Senden und Empfangen nicht so viel Systemleistung verschlingen.

    Bin mal auf Erfahrungen und evtl. Lösungen gespannt.

    Grüße
    Franky



  • Mir z.B. ist noch nicht ganz klar, wo die Schleife ins Spiel kommt. Du versendest die Zeichen doch bestimmt in einem einzigen Aufruf von WriteFile, oder?



  • kann es sein das er jedes zeichen / jeden buchstaben einzeln schickt ??
    wegen der schleife ..



  • Nein, nein.

    Ich übergebe der WriteFile- Funktion ein Array mit 100 Zeichen. Und diese WriteFile- Funktion läuft dann in einer Schleife, um die Systemauslastung zu überprüfen.

    Es werden also kontinuierlich Daten rausgeschoben. Habe mich wohl etwas unglücklich ausgedrückt.

    Grüße
    Franky



  • Dann kommt die Auslastung nicht von WritFile, sondern direkt aus Deinem Thread. Dieser wacht ja schließlich regelmäßig auf, um die nächsten Bytes zu senden. Wenn dieser Thread der einzige zuteilungsfähige ist, bekommt er die volle CPU-Zeit zugewiesen. Wenn Du das Array jetzt kleiner machst, kommst Du wesentlich schneller aus der Wait-Funktion zurück -> die Auslastung steigt. Das beobachtete Verhalten ist also völlig normal und kein Grund zur Sorge.



  • Ich habe jetzt die Threads entfernt und WriteFile wird im Main-File aufgerufen. Das brachte keine Verbesserung. Dann habe ich die ganze Geschichte noch einmal mit non-overlapped I/O versucht und die selben Ergebnisse erhalten.

    @-King-
    Vielleicht kannst Du das mit der CPU Aufteilung für die threads noch einmal näher erläutern. Das habe ich noch nicht so ganz verstanden. Danke.

    Wie bringe ich denn meine WriteFile Funktion dazu, möglichst wenig CPU Zeit zu verbrauchen. Das müsste doch möglich sein, oder?

    Grüße
    Franky



  • Du machst doch in etwa sowas hier:

    OVERLAPPED o;
    // Struktur initialisieren
    while(1)
    {
        WriteFile(100Bytes, &o);
        WaitForSingleObject(o.hEvent, INFINITE);
    }
    

    Wenn die Funktion WaitForSingleObject aufgerufen wird, bekommt der Thread keine Rechenzeit mehr zugewiesen. Ist die IO-Operation dann abgeschlossen, dann wacht der Thread auf und bekommt wieder seine Gelegenheit, Code auszuführen. Irgendwie mußt Du auch wieder an den Schleifenanfang gelangen, nur durch 'drandenken' geht das nicht. Dafür brauchst Du die Rechenzeit. Danach wird wieder WriteFile aufgerufen, gefolgt von WaitForSingleObject. Jetzt schläft der Thread wieder, usw..

    Wenn Du das Array nun kleiner machst, so wacht der Thread entsprechend schneller auf. WriteFile wird zwangsläufig schneller fertig. Dann benötigt der Thread auch mehr Rechenzeit, weil er wesentlich öfter an den Schleifenanfang springen muß.

    Wie bringe ich denn meine WriteFile Funktion dazu, möglichst wenig CPU Zeit zu verbrauchen. Das müsste doch möglich sein, oder?

    Nein, das ist nicht möglich. Wozu auch? Wenn Du das einbremsen willst, dann reduziere die Baudrate.



  • Alles klar. Jetzt habe ich es verstanden. Danke für die ausführliche Erklärung.

    Nur was mir nicht so ganz in den Kopf will: Wasrum es nötig ist, soviel Rechenleistung zu verbrauchen, um ein paar Zeichen an die UART zu schicken.

    Aber da wird man wohl mit den API- Funktionen nicht viel machen können.

    Grüße
    Franky



  • Systemaufrufe kosten. Was passiert, wenn du WriteFile aufrufst, ist eben nicht nur das Schreiben an sich, sondern insbesondere 2 Kontextwechsel vom User- in den Kernelmode und zurück, und einige Sachen mehr.



  • Nur was mir nicht so ganz in den Kopf will: Wasrum es nötig ist, soviel Rechenleistung zu verbrauchen, um ein paar Zeichen an die UART zu schicken.

    Wie ich sehe, hast Du es noch immer nicht kapiert. Es ist nicht das System, was die Rechenleistung verbraucht, sondern Deine Schleife!
    Der Befehl WriteFile wird aufgrund der Datenrate alle 6,9ms aufgerufen. Das System hat also gar keine Zeit, eine Thread-Umschaltung vorzunehmen, da diese länger dauern würde (Stack sichern, Position sichern, Programm umschalten, neue Speicherseite einladen, alten Stack wiederherstellen ...).
    Aber man setzt ja sowieso darauf, dass die Daten kontinuierlich an der Schnittstelle ankommen! Wenn man das nicht unbedingt braucht und dem System auch ein bisschen Zeit 'schenken' will, kann man ja ein 'Sleep(...) einsetzen. Dann klappt das auch mit der Systemauslastung!



  • Also gut, das habe ich verstanden.

    @RenéG
    An welcher Stelle soll denn Sleep() stehen?

    Ich habe dann noch einen anderen versuch gestartet. Und zwar habe ich mir ein ganz großes Array gemacht (ca. 1 Mio Zeichen) und gebe das an WriteFile. Das dauert dann so ungefähr 90 sec, bis alles gesendet wurde. Es wird also nur einmal dieses Array verschickt, ohne irgendwelche Schleifen.
    Während dieser Zeit habe ich eine Systemauslastung von 3-5%. Wie ist denn das nun zu erklären?
    Danke.

    Grüße
    Franky



  • Zu 1.

    while(1)
    {
        WriteFile(100Bytes, &o);
        Sleep(1);  // wo sollte es wohl sonst stehen?
    }
    

    Zu 2. Wie ich schon sagte: DEINE SCHLEIFE VERURSACHT DIE HOHE PROZESSORAUSLASTUNG! Da die Schleife während WriteFile GAR NICHT ausgeführt wird, ist auch die Prozessorauslastung niedrig!



  • Du brauchst deswegen ja nicht so zu schreien. 😃

    Ich meinte mit der Angabe von 3-5 % auch nicht, daß die Prozessorleistung niedrig ist, sondern noch zu hoch, obwohl überhaupt keine Schleife ausgeführt wird. Es wird also WriteFile aufgerufen (1 Mio bytes übergeben). Die Funktion kehrt zurück und WaitForSingleobject wartet, bis diese beendet ist. Während dieser Zeit kommt diese Prozessorauslastung zustande.

    Und was soll Sleep bringen? Damit reduziere ich doch bloß die Baudrate, oder nicht?

    Auch wenn ich nerve, bitte nicht schimpfen. 🙄

    Grüße
    Franky



  • Nunja, überleg Dir doch mal, wie WriteFile funktioniert?
    Nehmen wir an, der Buffer der Schnittstelle beträgt 2048Byte, dann muss eine Schleife laufen, die 500x 2kB in den Buffer schreibt. Weiterhin muss WriteFile ständig auf ein Signal des Buffers warten, wann wieder Zeichen reingeschrieben werden können. Dass das nicht ohne Prozessorzeit ablaufen kann ist doch verständlich, oder?

    Apropos, wieso sollte mit dem Sleep die Baudrate reduziert werden? Die 100Byte von WriteFile werden mit 115200 geschrieben, nicht langsamer! Dass er dann ne Millisekunde warten muss dürfte kaum ins Gewicht fallen!



  • OK, damit gebe ich mich zufrieden...
    was das Schreiben angeht.

    Nun habe ich das Problem, daß ich auch noch Daten lesen muß. Leider weiß ich vorher nicht, wieviele Daten kommen. Deshalb muß ich ReadFile eine Puffer übergeben, der nur ein Zeichen groß ist.

    Damit habe ich dann wieder das problem mit den vielen Schleifen und der sehr hohen prozessorauslastung.

    Hast Du da vielleicht auch einen Tip, wie ich das umgehen kann? Denn beim Lesen habe ich auf diese Art eine Auslastung von über 20%, was definitiv zu viel ist. Dass das von den vielen Schleifenaufrufen kommt, habe ich nun kappiert. Aber wie löse ich denn diesen Fall am besten?
    Danke.

    Grüße
    Franky



  • 2 Möglichkeiten ...
    1. Wenn die Zeichen innerhalb eines bestimmten Zeitabstandes kommen müssen, kannst Du mit CommTimeOuts arbeiten, d.h. wenn nach einer gewissen Zeit kein Zeichen kam, wird abgebrochen

    2. wenn 1. nicht gilt, packe das Lesen in einen eigenen Thread, setze ne Com-Maske und mit WaitCommEvent wartet dann das System, bis wieder nen Zeichen anliegt.

    Beispielcode für beides findet man auch im I-Net, weiss aber net mehr, wo! Vielleicht bei programmersheaven oder so

    [ Dieser Beitrag wurde am 21.10.2002 um 14:35 Uhr von RenéG editiert. ]



  • Nun habe ich das Problem, daß ich auch noch Daten lesen muß. Leider weiß ich vorher nicht, wieviele Daten kommen. Deshalb muß ich ReadFile eine Puffer übergeben, der nur ein Zeichen groß ist.

    Das muß nicht sein. Wenn das Event, das Du an WaitCommEvent übergeben hast, signalisiert ist, ermittelst Du mit ClearCommError, wieviele Zeichen angekommen sind. Dadurch kannst Du durchaus mehrere Zeichen gleichzeitig lesen.

    Damit habe ich dann wieder das problem mit den vielen Schleifen und der sehr hohen prozessorauslastung.

    Was ist denn überhaupt das Problem? Wenn der Thread die Zeit benötigt und diese auch zugesprochen bekommt, so ist das durchaus wünschenswert. Sei doch froh, dann geht Dir wenigstens nichts verloren. Und wenn alles empfangen wurde, legt sich der Thread wieder schlafen und beansprucht nur noch 0% Rechenleistung.



  • Danke schon mal für die tipps.
    Dann habe ich ja erstmal wieder zu tun.

    Melde mich wieder, wenn ich nicht weiter weiß... 😃

    Grüße
    Franky



  • Hi,

    habe jetzt das Problem mit dem Lesen gelöst. Ich habe die Version mi ClearCommError benutzt. Problem dabei war, dass jedesmal, wenn WaitCommEvent ein zeichen erkennt, die ClearCommError- Funktion ja auch nur ein Zeichen meldet. Das bringt dann natürlich nix.

    Ich habe dann bevor ClearCommError aufgerufen wird, einen Sleep eingebaut und kann so abwarten, wieviele Bytes kommen und so gleich einen ganzen Schwung mit ReadFile auslesen. Der Puffer muß dann natürlich groß genug sein, um die max. angefallenen Bytes auffangen zu können.

    char szBuf[800]={0};
    WaitCommEvent(hComm, &dwCommMask, &osReader)
    if (WaitForSingleObject(osReader.hEvent,INFINITE)==WAIT_OBJECT_0) {
    Sleep(50); //wenn genau 50ms -> können max 576 Zeichen einlaufen
    ClearCommError(hComm, &dwDummy, &cs);
    dwBytesRead = cs.cbInQue;

    ReadFile(hComm, &szBuf, dwBytesRead, &dwRead, &osReader);
    PutBufInRingbuf (szBuf, dwRead);
    }

    So funktioniert es jedenfalls. Ob es eine elegante Lösung ist, weiß ich nicht. Es kommen alle bytes so an, wie ich sie gesendet habe. Und wenn ich zwei Programme laufen habe, eins sendet eins empfängt, habe ich eine Gesamtsystemauslastung von 8-10%. Ich denke, damit kann ich leben.

    Danke nochmal an die fleißigen Helfer. Kommantare sind gern gesehen. 😉

    Grüße
    Franky

    [ Dieser Beitrag wurde am 22.10.2002 um 14:44 Uhr von Franky editiert. ]

    [ Dieser Beitrag wurde am 22.10.2002 um 14:45 Uhr von Franky editiert. ]



  • Hm, wieso nimmst Du net das Beispiel aus der MSDN und änderst es, so wie Du es brauchst?

    HANDLE hCom=CreateFile( "COM1", GENERIC_READ|GENERIC_WRITE, 0, NULL,    OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    if (hCom == INVALID_HANDLE_VALUE) 
    {
        // Handle the error. 
    }
    
    // Set the event mask. 
    BOOL fSuccess = SetCommMask(hCom, EV_RXCHAR);
    if (!fSuccess) 
    {
        // Handle the error. 
    }
    
    // Create an event object for use in WaitCommEvent. 
    OVERLAPPED o;
    o.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
    
    char szBuf[800];
    DWORD dwEvtMask, dwRead = 0;
    while( dwRead < sizeof(szBuf))
      if( WaitCommEvent(hCom, &dwEvtMask, &o))
      {
        ClearCommError( hCom, &dwDummy, &cs);
        min = min( sizeof(szBuf)-dwRead, cs.cbInQue);
        ReadFile( hCom, szBuf+dwRead, min, &min, &o);
        dwRead += min;
      }
      else
        break;  // Error reading from COM (maybe cancelled)
    PutBufInRingbuf( szBuf, dwRead);
    

    [ Dieser Beitrag wurde am 22.10.2002 um 16:26 Uhr von RenéG editiert. ]

    [ Dieser Beitrag wurde am 22.10.2002 um 16:29 Uhr von RenéG editiert. ]


Anmelden zum Antworten