Toon posts:

[C++] Vreemde verdeling CPU tijd

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik heb de afgelopen paar dagen zitten spelen met overlapped I/O in combinatie met de Winsock API. Dit zou de performance ten goede moeten komen volgens verscheidene sites. De moeite waard om te proberen dus :D.

Ik heb een kleine applicatie gebakken die gebruik maakt van het overlapped systeem van Windows. Bij het testen (met 25 openstaande verbindingen) gaat 99-100% van de CPU tijd naar de server applicatie. Mijn inziens zou Windows andere zaken moeten gaan schedulen. Zaken zoals DUMeter.

Ik heb met Intel VTune even een run gedaan om te zien in welke modules de meeste tijd besteed wordt. Hier is een lijstje van de modules waar de meeste tijd wordt gespendeerd:

16.78% - TCPIP.SYS
16.06% - NTOSKRNL.EXE
12.57% - HAL.DLL
10.82% - BCM4SBXP.SYS [= De driver voor mijn netwerkkaart]
4.78% - NDIS.SYS
4.51% - AFD.SYS
3.05% - WIN32K.SYS

Dit levert mij in totaal al een percentage op van 68.58%. Ik neem toch aan dat Windows bij gebruik van overlapped I/O niet busy gaat idlen maar rustig op een event gaat zitten wachten.

Ik kan me echter niet voorstellen dat 25 threads, die elk lezen en sturen op hun eigen socket, voor een dermate belasting zorgen. Het gedrag van elk van de threads is als volgt: bij het ontvangen van een pakket van 4096 bytes stuurt hij een pakket van 4096 bytes terug.

Als iemand een verklaring heeft voor dit gedrag of een idee hoe ik dit ongewenste gedrag aan zou kunnen pakken dan hoor ik het graag >:)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 13-02 18:09
Heb je toch nog ergens busy loops zitten misschien? Deze kun je vinden door op verdachte plaasten een sleep te 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.


Verwijderd

Topicstarter
Er zitten geen busy loops meer in de applicatie. Ik ben de applicatie meerdere malen doorgelopen. Daarnaast bestaat de applicatie enkel uit basiscode om dit te kunnen testen.

Verder zou de CPU tijd dan niet in de Windows kernel kruipen maar in mijn eigen .EXE. (Wat volgens Intel V-Tune niet het geval is... :D)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 13-02 18:09
Verwijderd schreef op dinsdag 26 september 2006 @ 16:40:
Er zitten geen busy loops meer in de applicatie. Ik ben de applicatie meerdere malen doorgelopen. Daarnaast bestaat de applicatie enkel uit basiscode om dit te kunnen testen.

Verder zou de CPU tijd dan niet in de Windows kernel kruipen maar in mijn eigen .EXE. (Wat volgens Intel V-Tune niet het geval is... :D)
Nah, als jij kernel functies aanroept ( bijvoorbeeld blocking io iod ) in die lus gaat de tijd naar de kernel niet naar jouw app.

Kan het misschien zijn dat het gebuik van die threads voor meer belasting zorgt dan nodig is? ( taskswitching etc )

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.


Verwijderd

Topicstarter
farlane schreef op woensdag 27 september 2006 @ 08:49:
Nah, als jij kernel functies aanroept ( bijvoorbeeld blocking io iod ) in die lus gaat de tijd naar de kernel niet naar jouw app.
Daar heb je gelijk in, echter wordt er geen gebruik gemaakt van blocking I/O :P.

Verwijderd

Het ligt er natuurlijk ook aan in welk tempo die 25 threads data ontvangen en versturen.
Zonder code is het moeilijk op je vraag een zinnig antwoord te geven. Er zijn een hoop dingen die verkeerd kunnen gaan bij asynchrone I/O operaties. Waarom gebruik je trouwens threads? Is het niet handiger om gebruik te maken van I/O completion ports en een thread pool?

Verwijderd

Oh, en nog iets... je zal waarschijnlijk 'wachten' op het resultaat van je I/O operatie. Je proces valt dan wel in slaap, maar de tijd van het wachten wordt door sommige profilers gewoon toegekend aan de tijd gespendeerd in de wait call....

Verwijderd

Topicstarter
Verwijderd schreef op woensdag 27 september 2006 @ 10:36:
Het ligt er natuurlijk ook aan in welk tempo die 25 threads data ontvangen en versturen.
Zonder code is het moeilijk op je vraag een zinnig antwoord te geven. Er zijn een hoop dingen die verkeerd kunnen gaan bij asynchrone I/O operaties. Waarom gebruik je trouwens threads? Is het niet handiger om gebruik te maken van I/O completion ports en een thread pool?
Het tempo ligt vrij hoog. Er worden blokken van 4K verstuurd. In totaal wordt er zo 8Megabyte up en down verstuurd. Dat schiet dus wel lekker op :).

De keuze voor threads is gemaakt omdat deze applicatie zowel op linux als windows zal moeten gaan draaien. Er is natuurlijk altijd nog de mogelijk om gebruik te gaan maken van IO Completion ports maar de voorbeelden hiervan presteren bar slecht (althans op de PC's waar ik het op geprobeerd heb, maar mag toch hopen dat die techniek vrij universeel zou moeten zijn).

Het liefst een socket system wat zowel op windows als linux goed presteerd met zo weinig mogelijk conditionele code.

PS: Was in de TS niet vermeld maar de threads komen uit een threadpool. De threadpool is getest met 2, 4, 8, 16, 25, 50, 100 en 200 threads. In alle gevallen blijft de CPU verdeling vrijwel gelijk. Er wordt veel tijd gespendeerd in de windows kernel en gerelateerde lagere subsystemen.
Verwijderd schreef op woensdag 27 september 2006 @ 10:38:
Oh, en nog iets... je zal waarschijnlijk 'wachten' op het resultaat van je I/O operatie. Je proces valt dan wel in slaap, maar de tijd van het wachten wordt door sommige profilers gewoon toegekend aan de tijd gespendeerd in de wait call....
Er wordt inderdaad gewacht m.b.v. WaitForMultipleObjects. Er zijn namelijk meerdere events waar de thread op moeten kunnen reageren. Op het moment dat mijn proces slaapt behoort Windows toch een ander proces op te pakken? Nu gaat letterlijk 99-100% van de CPU tijd naar mijn applicatie terwijl deze grotendeels licht te slapen :P. Beetje zonde van de tijd :+.

[ Voor 19% gewijzigd door Verwijderd op 27-09-2006 10:55 . Reden: MB i.p.v. Mbit ;) ]


Verwijderd

Topicstarter
Hierbij de code van de readSocket methode. Wellicht dat dat meer duidelijkheid verschaft

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
bool SocketUtils::readSocket(Socket *pSocket, char *buffer, int bufferSize)
{
    WSABUF wsabuf;
    WSAOVERLAPPED over;
    DWORD dwRecv, dwFlags, dwRet;
    bool fPending;
    int nRet;

    // Clear the receiving buffer
    memset(buffer, 0, bufferSize);

        // Prepare the winsock buffer and event handle
    wsabuf.buf  = buffer;
    wsabuf.len  = bufferSize;
    over.hEvent = pSocket->getOverlappedEvent();

    // Attempt to send the buffer
    dwFlags = 0;
    fPending = false;
    nRet = WSARecv(pSocket->getSocketHandle(), &wsabuf, 1, &dwRecv, &dwFlags, &over, NULL);
    if (nRet != 0)
    {
        int retval = WSAGetLastError();

                // Weggelaten: code om te kijken of de verbinding gesloten is

        // Did an error occur while scheduling the I/O transfer
        else if (retval != WSA_IO_PENDING)
        {
            if(pSocket->isConnected() == true)
            {
                MessageBox(NULL, "WSARecv() did not return IO_PENDING", "WSARecv failed... :-(", MB_OK);
                return false;
            }
        }
        else {
            fPending = true;
        }
    }

    // If the I/O isn't finished...
    if (fPending)
    {
        // Wait for the request to complete or the exit event 
        dwRet = WaitForSingleObject(over.hEvent, INFINITE);

        if (dwRet != 0)
            return false;

        // Get the overlapped result
        if (!WSAGetOverlappedResult(pSocket->getSocketHandle(), &over, &dwRecv, FALSE, &dwFlags))
        {
            MessageBox(NULL, "Could not get the overlapped result", "Failed :-(", MB_OK);
            return false;
        }
    }

    return true;
}
Pagina: 1