WriteFile zu langsam?
-
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 abgebrochen2. 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. ]
-
Das ist eine gute Frage. Weil ich bis jetzt nicht über dieses Beispiel gestolpert bin. Kannst mir ja mal sagen, wie der Artikel heißt?
Aber wenn ich mir das Beispiel so ansehe, erkenne ich einen gravierenden Nachteil. Meine Funktion PutBufInRingbuf wird erst aufgerufen, wenn 800 Zeichen eingetroffen sind. Was passiert aber, wenn nur 100 kommen? Hängt dann der Thread nicht die ganze zeit in der Schleife fest? (Oh oh, die Systemauslastung???)
Aber vielleicht gibt es ja noch eine andere Mglk.?
Grüße
Franky[ Dieser Beitrag wurde am 22.10.2002 um 16:45 Uhr von Franky editiert. ]
-
Nanana, alles kann dir das System nun doch nicht abnehmen :).
D.h. idealerweise setzt du ein Timeout oder definierst dir ein Übertragungsprotokoll. Also entweder es kam eine gewisse Anzahl Zeichen, oder du hast eine gewisse Zeit in der Abfrageschleife verbracht, oder dein im Übertragungsprotokoll definierter Übertragungsende - String wurde gesendet........
-
Was passiert aber, wenn nur 100 kommen? Hängt dann der Thread nicht die ganze zeit in der Schleife fest? (Oh oh, die Systemauslastung???)
Ach Franky, übersezte doch mal den Befehl WaitCommEvent !! Für mich bedeutet das: "Warte bis nächstes Zeichen"
Was wiederum bedeutet, dass der Thread NICHT in der Schleife, sondern in WaitCommEvent 'hängt'! Und diese Funktion braucht FAST GAR KEINE Prozessorzeit!
-
Also wenn ich das richtig verstanden habe, handelt es sich bei dem Aufruf von WaitCommEvent um eine overlapped Operation. D.h., sie kehrt sofort zurück und wartet nicht solange, bis ein Zeichen ankommt. Ich müsste dann mit WaitForSingleObject darauf warten, bis die Funktion beendet ist, also ein zeichen empfangen wurde. Und dies kann ich in dem Code nicht finden. Hat das evtl. etwas mit dem Event zu tun -> CreateEvent mit AutoReset deklariert???
Oder habe ich da jetzt was falsch verstanden?
Wie heißt denn nun der Artikel mir dem Beispiel?
Grüße
Franky[ Dieser Beitrag wurde am 23.10.2002 um 10:36 Uhr von Franky editiert. ]
-
Also erstmal steht in der MSDN:
If hFile was opened with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, WaitCommEvent is performed as an overlapped operation. In this case, the OVERLAPPED structure must contain a handle to a manual-reset event object (created by using the CreateEvent function).
Ok, bei mir fehlt WaitForSingleObject, wäre aber kein Problem, das einzubringen. Mal ne andere Frage: Wofür brauchst Du überhaupt die Overlapped-Struktur? Willst Du wirklich asynchron auf die Schnittstelle zugreifen?