Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[Win32/C++] Asynchronous WriteFile

Pagina: 1
Acties:

  • evolution536
  • Registratie: Maart 2009
  • Laatst online: 05-06-2024
Beste Tweakers,

Ik zit met een probleem. Ik ben bezig met het schrijven van een memory scanner. Deze schrijft zijn data naar een file. Gezien de hoeveelheid data, die kan oplopen tot 2 ~ 3 GB, zou ik het schrijven naar de files graag asynchroon willen laten doen. Hierdoor blijft mijn applicatie niet hangen op de punten waar de disk de bottleneck is, en zal de resterende data alsnog naar de files worden geschreven nadat de scan klaar is.

Ik heb een mooie manier gevonden met WriteFile(Ex) en overlapped I/O. Hiermee kan ik de handle en de buffer met data er gewoon in fietsen en het systeem doet de I/O zelf. Voor het design/functionaliteit van mijn applicatie heb ik geen terugkoppeling nodig van: "ik ben klaar met schrijven", dus met WriteFile zat ik prima.

Echter loop ik tegen het probleem aan dat mijn applicatie de gebruikte buffer en handles al heeft vrijgegeven op het moment dat de asynchrone operations nog bezig zijn. hierdoor wordt soms incomplete en verkeerde data naar de files geschreven, wat mij in een volgende fase behoorlijke problemen geeft.

Ik heb gezien dat ik met WriteFileEx een callback kan aangeven die mij aangeeft wanneer de functie klaar is met schrijven, maar hier krijg ik het probleem dat deze functie mij niet aangeeft welke handle het betreft. Dat betekent dat ik op dat moment niet de mogelijkheid heb de handle te closen en de juiste buffer vrij te geven. Let op, dit is namelijk echt een must voor mijn applicatie. Als er een aantal buffers niet op tijd worden vrijgegeven zal de applicatie snel buiten zijn working set treden, wat in principe helemaal niet nodig hoeft te zijn.

Mijn vraag is eigenlijk: Hoe kan ik er voor zorgen dat ik een callback heb, ofwel een push naar mijn applicatie, die mij aan kan geven welke handle/buffer de WriteFile operation betreft? Hiermee los ik het probleem op van het te vroeg vrijgeven van de buffers en handles, want ik kan dan in de callback deze resources vrijgeven.

Alvast bedankt!

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
In plaats van de Overlapped struct direct mee te geven aan WriteFileEx kun je ook een struct als deze meegeven:

C:
1
2
3
4
5
6
7
8
9
10
11
struct OVERLAPPED_WITH_USERDATA
{
    OVERLAPPED overlapped;
    bool myUserData;
    void *myOtherUserData;
};

WriteFileEx(hFile, lpBuffer, dwNumberOfBytesToWrite, (LP_OVERLAPPED)&myOverlappedStruct, cbCallbackFunction);

// in de callback kun je dan zoiets doen:
OVERLAPPED_WITH_USERDATA* userData = (OVERLAPPED_WITH_USERDATA*)lpOverlapped;


Verder zou je naar mijn Async I/O library kunnen kijken.

[ Voor 9% gewijzigd door PrisonerOfPain op 16-08-2013 16:26 ]


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 13:12
Alternatief : Als je een administratie bijhoudt welke OVERLAPPED structures in gebruik zijn, kun je daarbij ook de HANDLE bewaren.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
farlane schreef op vrijdag 16 augustus 2013 @ 15:15:
Alternatief : Als je een administratie bijhoudt welke OVERLAPPED structures in gebruik zijn, kun je daarbij ook de HANDLE bewaren.
Nadeel is dat je dan je access naar die datastructure mag gaan beschermen met critical sections.

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

Op zich kan het zo zijn dat als je een memory scanner schrijft, je bottleneck toch al je disk I/O gaat zijn, gezien je geheugen toch al snel een paar factoren sneller bereikbaar gaat zijn dan je disk.

Je hoort mij niet zeggen dat je dit niet moet doen, maar weet je wel zeker dat het zin heeft asynchroon te werken? Als ik zo tussen jouw regels door lees dan heb je een set "resultaten" die elk in een eigen buffer zitten, en die je daarna wegschrijft.

Wellicht dat het sneller is om 1 buffer te hebben van een redelijke grootte (1MB ofzo), en die gewoon synchroon weg te schrijven elke keer dat die vol is. Minder geheugengebruik in je app (wegens 1 buffer), en gezien je toch al I/O bound was, gaat je app er niet langzamer van worden :)

Maar goed, ik weet niet welke operaties je nog meer uitvoert naast je scan en disk I/O, dus misschien heeft het wel degelijk zin, maar voordat je moeilijk gaat doen, kan je het misschien eerst makkelijk proberen :)

-niks-


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 13:12
PrisonerOfPain schreef op vrijdag 16 augustus 2013 @ 16:26:
Nadeel is dat je dan je access naar die datastructure mag gaan beschermen met critical sections.
Inderdaad een groot nadeel.
MLM schreef op vrijdag 16 augustus 2013 @ 18:07:
Maar goed, ik weet niet welke operaties je nog meer uitvoert naast je scan en disk I/O, dus misschien heeft het wel degelijk zin, maar voordat je moeilijk gaat doen, kan je het misschien eerst makkelijk proberen :)
Voorstander hier.

[ Voor 41% gewijzigd door farlane op 16-08-2013 21:07 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
MLM schreef op vrijdag 16 augustus 2013 @ 18:07:
Wellicht dat het sneller is om 1 buffer te hebben van een redelijke grootte (1MB ofzo), en die gewoon synchroon weg te schrijven elke keer dat die vol is. Minder geheugengebruik in je app (wegens 1 buffer), en gezien je toch al I/O bound was, gaat je app er niet langzamer van worden :)
Als je er mee weg kunt komen moet je dat sowieso doen. Simpeler is beter. Als performance er erg doet kun je met async io wel de latency van je memory reads verbergen natuurlijk. Met een simpel n-buffering scheme kun je het geheugengebruik nog best binnen de perken houden dan.

  • evolution536
  • Registratie: Maart 2009
  • Laatst online: 05-06-2024
PrisonerOfPain, jouw oplossing vind ik mooi. Ik heb echter iets anders gevonden op de MSDN. WriteFileEx gebruikt de hEvent parameter niet. Dat betekent dus dat ik daar mijn gegevens kwijt kan!

Nu heb ik een ander probleem. Ik heb WriteFileEx gebruikt, maar de opgegeven callback wordt nooit aangeroepen. Mijn calling thread is ook niet in een alertable state neem ik aan, maar ik snap eigenlijk niet waarom de callback niet gewoon altijd aangeroepen wordt. Weet iemand hoe ik dit kan oplossen?

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
Er zijn tween manieren om die WriteFileEx af te vangen, eentje is met I/O Completion ports en de andere is met Async Procedure Calls.

Met I/O Completion ports maak je een aantal threads aan die wachten op de completion port, zodra je WriteFile klaar is met schrijven krijg je een event op een van die threads waarop je dan de rest van de I/O kunt afhandelen.

Met APCs werkt het net wat anders, wat er dan gebeurt is dat er een callback pointer in de context van de thread word geplaatst die WriteFile aanriep. Die APCs worden dan uitgevoerd als je thread alertable is (eg, door een WaitForMultipleObjects, SleepEx of aanverwant aan te roepen).

De reden dat je APC niet altijd aangeroepen kan worden is eenvoudig, je kunt niet zomaar je instruction pointer aanpassen en andere code op dezelfde thread gaan uitvoeren. Het makkelijkste wat je kunt doen is bij iedere write een thread aanmaken (! traag !) en daarin dergelijke code zetten:

C:
1
2
WriteFileEx(... params ...);
SleepEx(INFINITE, TRUE);


Maar dat word al snel een zooitje (je zult je threads ook weg moeten gooien), en dat is dan ook waar die hEvent van toepassing zal komen. Als je daar een event in zet kun je ipv die SleepEx een WaitForSingleObject doen die je dan in je callback met SetEvent signaled - waarna je thread uit zichzelf sterft.

Persoonlijk vind ik I/O Completion ports makkelijker omdat je dan in princiepe gewoon werk naar een thread-safe queue verstuurd die je op een andere thread kunt afvangen. Code sample in het Async I/O project van eerder. En meer over Alertable I/O.

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 13:12
evolution536 schreef op vrijdag 16 augustus 2013 @ 21:51:
PrisonerOfPain, jouw oplossing vind ik mooi. Ik heb echter iets anders gevonden op de MSDN. WriteFileEx gebruikt de hEvent parameter niet. Dat betekent dus dat ik daar mijn gegevens kwijt kan!
Ik moet zeggen dat ik dat een hack vind; je geeft een niet voor de hand liggende betekenis aan een niet gebruikte parameter. De oplossing van PoP vind ik een een stuk cleaner.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • evolution536
  • Registratie: Maart 2009
  • Laatst online: 05-06-2024
PrisonerOfPain schreef op vrijdag 16 augustus 2013 @ 22:07:
Maar dat word al snel een zooitje (je zult je threads ook weg moeten gooien), en dat is dan ook waar die hEvent van toepassing zal komen. Als je daar een event in zet kun je ipv die SleepEx een WaitForSingleObject doen die je dan in je callback met SetEvent signaled - waarna je thread uit zichzelf sterft.
Dit snap ik niet helemaal. Als WriteFileEx de hEvent parameter negeert, wat kan ik dan met het event?

Ik denk dat ik de APC queue snap en hoe ik dit kan gebruiken, maar inderdaad is het aanmaken van onnodige threads voor het schrijven en ontvangen van het event behoorlijk expensive. Dit zou ik niet graag doen. Het nadeel is dat de Alertable I/O alleen maar de callback terug krijgt op de thread die de call heeft uitgevoerd.

Zou je me kunnen uitleggen wat je bedoeld met de toepassing van het hEvent in deze situatie?

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
evolution536 schreef op zaterdag 17 augustus 2013 @ 19:01:
[...]


Dit snap ik niet helemaal. Als WriteFileEx de hEvent parameter negeert, wat kan ik dan met het event?

Zou je me kunnen uitleggen wat je bedoeld met de toepassing van het hEvent in deze situatie?
De hEvent wacht je op in je callback. De API zet 'm naar non-signalled als de IO event begint en signaled 'm als 'ie klaar is.

[ Voor 10% gewijzigd door PrisonerOfPain op 17-08-2013 19:12 ]


  • evolution536
  • Registratie: Maart 2009
  • Laatst online: 05-06-2024
PrisonerOfPain schreef op zaterdag 17 augustus 2013 @ 19:10:
De hEvent wacht je op in je callback. De API zet 'm naar non-signalled als de IO event begint en signaled 'm als 'ie klaar is.
Op de MSDN staat dit:
quote: MSDN
Functions such as ReadFile and WriteFile set this handle to the nonsignaled state before they begin an I/O operation. When the operation has completed, the handle is set to the signaled state.
WriteFileEx slaat deze parameter gewoon over lijkt me. WriteFile gebruikt dit event wel, maar volgens mij doet dat precies hetzelfde als de callback parameter van WriteFileEx. :)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 13:12
Een callback functie aanroepen is heel iets anders als een event naar signaled zetten.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • evolution536
  • Registratie: Maart 2009
  • Laatst online: 05-06-2024
Dank jullie wel voor jullie reacties. Ik heb nog geprobeerd de threads aan te maken en daarin vervolgens de callback af te vangen. Dit werkte wel, maar de hoeveelheid write calls is te hoog. Ik krijg geregeld verschillende dwErrorCode's die erop duiden dat het niet kan.

Ik ben een stapje terug gegaan naar synchronous I/O, zodat ik in ieder geval even verder kan met mijn applicatie. Dit is een tikje langzamer voor de UI, maar het werkt prima.

Dank jullie wel voor jullie reacties. Ik heb hier erg veel aan gehad, want ik ben nu aardig op de hoogte van o.a. Alertable I/O, APC en IO Completion ports. Ik zal it op een andere plek in mijn applicatie nog nodig hebben naar alle waarschijnlijkheid, dus het is zeker niet voor niets. :)

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
Als je het alleen maar doet om de UI te ontlasten kun je ook heel het werk gewoon op een andere thread zetten.
Pagina: 1