Toon posts:

[C] Socket timers

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik ben bezig met het maken van een eigen webserver. Daarbij heb ik het volgende probeem: ik test de snelheid van mijn server met de tool 'ab' die met Apache wordt meegeleverd. Als ik deze test met keep-alive connecties is de snelheid van mijn webserver bedroevend slecht. 'Beter coden' zou je zeggen, maar het gekke is dat de load van de computer waar ik op test (draait zowel ab als de webserver) bijna 0 is. Wat blijkt: select() duurt relatief lang voordat hij terugkeert met de melding 'er is nieuwe data'. Een setsockopt() met TCP_NODELAY scheelt al een boel, maar nog steeds is 'keep-alive' trager dan 'close' connections. Bij close connections is de load hoog (compu is duidelijk aan het stressen), met keep-alive is ie uit z'n neus aan het vreten. Hoe krijg ik die @#!* sockets zover dat ze gewoon opschieten ipv wachten met sturen/ontvangen van data?

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Geen select() probleem, maar een code-probleem denk ik. select() is net zoals de meeste dingen gebonden aan de interrupts frequentie, en die is 100 Hz, maar onder normale omstandigheden heb je daar geen last van. Zodra je een behoorlijk aantal connecties (lees : een paar honderd) moet afhandelen is select() beduidend trager als alternatieven zoals poll(). Verder doen veel threads de performance ook geen goed, evenals veel locking en disk-io.

Verwijderd

Topicstarter
Mijn code bevat geen sleeps tijdens de afhandeling van een request. Hoe moet ik dan verklaren dat de load op 0 staat??

Ik heb op verschillende plekken in mijn code timers geplaatst om de snelheid te meten. De traagheid zit em echt in de select().

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Heb je de timeout bij select te groot staan misschien ?

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.


  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Verwijderd schreef op 07 september 2004 @ 22:58:
Ik heb op verschillende plekken in mijn code timers geplaatst om de snelheid te meten. De traagheid zit em echt in de select().
Wat is traag overigens, en hoe meet je ? Ik heb behoorlijk wat daemons met select() in elkaar gezet, en jouw problemen zijn mij onbekend. De meeste / alle problemen met select / poll zijn te wijten aan problemen met de code.

Mocht je een kernel > 2.4.20 < 2.4.27 gebruiken op een SMP machine : Dan loop je mogelijk tegen een kernel bug aan.

Verwijderd

Keep-Alive is gewoon een dure TCP optie als je een server met hoge troughput en veel cients wilt schijven (volgens deze page 15% overhead bij 51 clients). Als je dus een test-suite gebruikt die honderden of zelfs duizenden clients simuleert is je probleem verklaarbaar.

(Daarnaast is het Nagle-algoritme uitzetten (met TCP_NODELAY) ook niet altijd bevorderlijk voor de troughput van een server met een groot aantal clients.)

Verwijderd

Topicstarter
@farlane: timeout staat op 1 sec. Is dat te groot / klein?

@igmar: wat is traag: ongeveer 10x langzamer dan zonder keep-alive. En mijn code ligt het niet: wat in een keep-alive gebeurd is dat buffers ge-free()-ed worden en wat variabelen op default worden gezet. Dat is alles. Daardoor kan ie niet 10x langzamer worden. Hoe ik meet: met timer-checks in de code die tijdverschillen printf()-en: de select() is duidelijk de bottleneck. In een keep-alive is de eerste request (soms de eerste twee) snel, daarna wordt ie trager. Ik gebruik geen SMP.

@mietje: we hebben het hier over HTTP-keepalive, niet over TCP-keepalive. En ik gebruik TCP_NODELAY alleen bij minder dan een bepaald aantal connecties.

[ Voor 6% gewijzigd door Verwijderd op 09-09-2004 20:45 ]


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Verwijderd schreef op 09 september 2004 @ 20:21:
@farlane: timeout staat op 1 sec. Is dat te groot / klein?
Kwestie van uitproberen of het uberhaupt invloed heeft. Ik kan me voorstellen dat veel 'korte' waits een ander gedrag geven dan minder lange waits.

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
Offtopic: kwam er net via logfiles achter dat mijn webserver nu gebruikt wordt door een of ander IT bedrijfje :) Ben ik even apetrots

/Offtopic.

Verwijderd

Verwijderd schreef op 09 september 2004 @ 20:21:
@mietje: we hebben het hier over HTTP-keepalive, niet over TCP-keepalive. En ik gebruik TCP_NODELAY alleen bij minder dan een bepaald aantal connecties.
Mja, dat nam ik eerst ook aan, maar dan is het wel tamelijk vreemd dat select() trager reageert, want volgens mij is HTTP Keep-Alive niet geïmplementeerd in de kernel maar in je webserver. Dat zou dus al bijna moeten betekenen dat ab trager reageert met keep-alive (cq. dat je server iets doet waardoor ab "van streek raakt" en trager wordt).

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Verwijderd schreef op 09 september 2004 @ 20:21:
@farlane: timeout staat op 1 sec. Is dat te groot / klein?

@igmar: wat is traag: ongeveer 10x langzamer dan zonder keep-alive.
TCP keep-alive heeft totaal geen invloed op select().
En mijn code ligt het niet: wat in een keep-alive gebeurd is dat buffers ge-free()-ed worden en wat variabelen op default worden gezet. Dat is alles. Daardoor kan ie niet 10x langzamer worden. Hoe ik meet: met timer-checks in de code die tijdverschillen printf()-en: de select() is duidelijk de bottleneck. In een keep-alive is de eerste request (soms de eerste twee) snel, daarna wordt ie trager. Ik gebruik geen SMP.
Mjah.. Ik zie nog steeds niks concreets, hebben we het over seconden, milliseconden ? Ik heb een squid server die soms 120 concurrent connecties afhandeld, en dat gebeurd ook met select().
@mietje: we hebben het hier over HTTP-keepalive, niet over TCP-keepalive. En ik gebruik TCP_NODELAY alleen bij minder dan een bepaald aantal connecties.
[/quote]

Wat heeft dat met select() te maken ? Ik vrees dat je het probleem toch in je eigen code moet zoeken.

  • rollebol
  • Registratie: Mei 2000
  • Laatst online: 22-08-2025
Gebruik eens poll in plaats van select. Juist ook voor schaalbaarheidsproblemen.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Example timings (Pentium 100):
1021 file descriptors (3-1023), check for activity on 1014-1023


Using a select(2) loop like this:
memcpy (&i_fds, &input_fds, sizeof i_fds);
memcpy (&o_fds, &output_fds, sizeof i_fds);
memcpy (&e_fds, &exception_fds, sizeof i_fds);
nready = select (max_fd + 1, &i_fds, &o_fds, &e_fds, &tv);
takes 362 microseconds.


Using a poll(2) loop like this:
nready = poll (pollfd_array + start_index, num_to_poll, 0);
takes 93 microseconds.


Now, if we change the test to do checks on descriptors 924-1023:
select(2) takes 1274 microseconds
poll(2) takes 1023 microseconds.


And now if we check descriptors 24-1023:
select(2) takes 3897 microseconds
poll(2) takes 4221 microseconds.


So we can see that poll(2) has a distinct advantage with modest
numbers of high-valued fds. It starts to loose when the number of fds
is increased, since it has to push more bits around than select(2).
The point of my poll2(2) proposal is that less bits are pushed, both
in kernel space as well as user space, compared with poll(2). poll(2)
maintains the advantage of poll(2) when watching modest numbers of
high-valued fds, but also reduces the cost when watching large numbers
of fds with only a few returning active status.
Ik denk dus niet dat het met poll vs select te maken heeft.

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
igmar schreef op 10 september 2004 @ 09:46:
TCP keep-alive heeft totaal geen invloed op select().
Ik heb dat ook nooit beweerd. Ik had het over de timeout van select.

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.


  • RetepV
  • Registratie: Juli 2001
  • Laatst online: 27-10-2025

RetepV

ALLES valt te repareren

Is de snelheid van select() niet afhankelijk van de prioriteit waarmee je programma draait? Want volgens mij kost de select() je in ieder geval één task-switch. De delay is dan de tijd tussen dat je proces pre-empted wordt en weer de processor toegewezen krijgt. Net als WaitForSingleObject() en Sleep. Als jouw proces dus op een hogere prioriteit loopt, zal hij sneller weer de processor toegewezen krijgen (zou je zeggen).

Een Sleep(0) duurt geen 0 seconden. Maar het zorgt er wel voor dat je task released wordt en de scheduler eerst alle andere tasks hun tijd gaat geven voordat hij weer terug komt bij jou proces. Een Sleep(0) duurt altijd langer dan 0 milliseconden.

Overigens: als je echt snel socket connecties wil afhandelen moet je eens kijken naar I/O Completion Ports (check bijvoorbeeld GetQueuedCompletionStatus).

[ Voor 8% gewijzigd door RetepV op 10-09-2004 14:38 ]

Macbook Pro


Verwijderd

RetepV schreef op 10 september 2004 @ 14:37:
Is de snelheid van select() niet afhankelijk van de prioriteit waarmee je programma draait? Want volgens mij kost de select() je in ieder geval één task-switch. De delay is dan de tijd tussen dat je proces pre-empted wordt en weer de processor toegewezen krijgt. Net als WaitForSingleObject() en Sleep. Als jouw proces dus op een hogere prioriteit loopt, zal hij sneller weer de processor toegewezen krijgen (zou je zeggen).
Dat verklaart niet waarom zijn server 10x zo traag wordt bij het zetten van een bep. optie die in principe weinig rekencapaciteit en bandbreedte kost.
Een Sleep(0) duurt geen 0 seconden. Maar het zorgt er wel voor dat je task released wordt en de scheduler eerst alle andere tasks hun tijd gaat geven voordat hij weer terug komt bij jou proces. Een Sleep(0) duurt altijd langer dan 0 milliseconden.
Er zijn compilers die een sleep(0) "weg optimaliseren" tot een NOP, als je wilt dat een proces taskswitched (zonder te blocken) dan gebruik je sched_yield() (onder POSIX dan, maar ik neem aan dat dit niet over Windows gaat).

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Zoijar schreef op 10 september 2004 @ 10:14:
Ik denk dus niet dat het met poll vs select te maken heeft.
ivm dit probleem heeft men epoll() uitgevonden. Daar krijg je gewoon een set met alles wat aan de criteria voldoet, dus je hoeft niet meer fd's te testen. Het vereist helaas een patch voor 2.4, maar zit wel standaard in 2.6.

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Verwijderd schreef op 10 september 2004 @ 16:35:
Dat verklaart niet waarom zijn server 10x zo traag wordt bij het zetten van een bep. optie die in principe weinig rekencapaciteit en bandbreedte kost.
bugs ? select() is in linux vanaf 2.4 volledig gelijk aan poll(). Verder is mij onduidelijk wat een 'bepaalde optie is', aangezien men het zowel over TCP, HTTP als select opties heeft inmiddels.

Verwijderd

igmar schreef op 11 september 2004 @ 13:34:
bugs ? select() is in linux vanaf 2.4 volledig gelijk aan poll(). Verder is mij onduidelijk wat een 'bepaalde optie is', aangezien men het zowel over TCP, HTTP als select opties heeft inmiddels.
Lees het ff in de context igmar, ik ben het namelijk vrijwel geheel met je eens; de TS geeft aan dat het over HTTP Keep-Alive gaat, dus de enige (voor mij bendenkbare) manier waarop select() trager kan worden is als de client aan de de andere kant van de connection trager wordt.

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Verwijderd schreef op 11 september 2004 @ 16:44:
Lees het ff in de context igmar, ik ben het namelijk vrijwel geheel met je eens; de TS geeft aan dat het over HTTP Keep-Alive gaat, dus de enige (voor mij bendenkbare) manier waarop select() trager kan worden is als de client aan de de andere kant van de connection trager wordt.
In dat geval is het geen select() bug, maar een bug in de code. TCP keepalives hebben overigens net zo min een invloed op select().

Verwijderd

Topicstarter
igmar schreef op 12 september 2004 @ 13:20:
In dat geval is het geen select() bug, maar een bug in de code. TCP keepalives hebben overigens net zo min een invloed op select().
Een vriend van me heeft met een of andere tool van Apple (shark) m'n webserver doorgemeten. 99% van de runtime gaat zitten in de select(). Heb select() vervangen door poll(), geen verschil. Ik heb echt geen idee wat er nu mis gaat.

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Verwijderd schreef op 13 september 2004 @ 07:48:
Een vriend van me heeft met een of andere tool van Apple (shark) m'n webserver doorgemeten. 99% van de runtime gaat zitten in de select(). Heb select() vervangen door poll(), geen verschil. Ik heb echt geen idee wat er nu mis gaat.
Hoe wordt dat gemeten ? Als de tool in kwestie de tijd ook meet die select() in sleep doorbrengt kan dat wel eens kloppen. Verder is select() in de kernel een wrapper voor poll(), dus dat zal inderdaad weinig schelen. select() is overigens O(n), waarbij n de het nummer van fd's is die je als parameter doorgeeft. Die moet je dus zo klein mogelijk houden.

Profilen kun je overigens zelf ook doen met gprof, of met een valgrind skin.

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Heb je al geprobeerd of het met een kleinere select timeout beter gaat ?

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

Verwijderd schreef op 13 september 2004 @ 07:48:
Een vriend van me heeft met een of andere tool van Apple (shark) m'n webserver doorgemeten. 99% van de runtime gaat zitten in de select(). Heb select() vervangen door poll(), geen verschil. Ik heb echt geen idee wat er nu mis gaat.
Even een logische opbouw van wat ik denk dat er aan de hand is:
  1. Zowel select() als poll() moeten wachten op data die de clients over de lijn sturen.
  2. Als je dus een identieke test uitvoert (met evenveel clients ed.) met en zonder Keep-Alive dan moet je vergelijkbare wachttijden voor select() cq. poll() uitkrijgen.
  3. Als die wachttijden niet gelijk zijn, dan betekent dat dat select() cq. poll() langer moet wachten op data van de clients.
  4. Aangezien de enige verandering in de test de Keep-Alive optie is, moet die vertraging in de clients worden veroorzaakt door die Keep-Alive.
  5. Daar je mag aannemen dat de Keep-Alive optie in de clients goed geïmplementeerd is, mag je aannemen dat de fout in de Keep-Alive code van je server zit; een fout die de clients "van streek" maakt en ze trager laat worden.
Ergo, debug de Keep-Alive code van je server.

  • Sjaaky
  • Registratie: Oktober 2000
  • Laatst online: 22-04 07:04
Als ik ab draai met keep alive en het aantal concurrent connections opschroef komt de performance steeds meer bij de zonder keep alive test. Dus het ligt dan echt aan die ene thread die die ene (of meer, zo diep heb ik niet in je source gekeken) socket afhandeld.
Heb je de test al uitgeprobeerd met een grote hoeveelheid GET of for that matter POST data? (is mij niet gelukt, kon geen php niet aan de praat krijgen om post data te lezen 8)7 en had geen zin om een perl cgi te proberen ).
Als je geen keep alive hebt past elke request makkelijk in 1 packetje, die krijg je dus altijd helemaal in 1x binnen. Bij keep alive en bij grote requests wordt de hele loop om je select heen (en de select zelf) belangrijk. Als je wel snel bent met grote requests en traag met keep alive, tja dan kan het niet aan de select liggen.

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Verwijderd schreef op 13 september 2004 @ 07:48:
Een vriend van me heeft met een of andere tool van Apple (shark) m'n webserver doorgemeten. 99% van de runtime gaat zitten in de select(). Heb select() vervangen door poll(), geen verschil. Ik heb echt geen idee wat er nu mis gaat.
Heb je de input van select al gecontroleerd?
Misschien vergeet je bepaalde fds aan select te geven waardoor select pas returned als andere fds getriggered worden.
En wat doet select als je geen timeout meegeeft?

Verwijderd

Topicstarter
@igmar: geen idee, ik zag die tool toen voor het eerst.
"Verder is select() in de kernel een wrapper voor poll(),...": Niet altijd. MacOSX implementeert poll() m.b.v. select(), net omgekeerd dus :)

@farlane: ja. ipv x maal 1 seconde wachten, wacht ik nu gewoon x seconde. Geen verschil.

@mietje: "II Als je dus een identieke test uitvoert (met evenveel clients ed.) met en zonder Keep-Alive dan moet je vergelijkbare wachttijden voor select() cq. poll() uitkrijgen.": En daar gaat het dus al mis......
"Aangezien de enige verandering in de test de Keep-Alive optie is, moet die vertraging in de clients worden veroorzaakt door die Keep-Alive.": en die is bijna niks. Zoals je in connection_handler() (regel 827 in hiawatha.c) kunt zien is het enige dat ie tussen 2 requests doet een reset_session() (staat in session.c) die alleen wat pointers free()-ed. Lijkt me niet dat dat de vertraging kan opleveren.

Ik ga nog eens wat meer tests uitvoeren.....

Verwijderd

Topicstarter
OlafvdSpek: select invoer is oke.

Ander interessant puntje: ik heb als test de select()/poll() eruit gesloopt en laat Hiawatha dus direct naar de recv() doorgaan. Wat blijkt, nu zit de vertraging em in de recv()......

Verwijderd

Verwijderd schreef op 14 september 2004 @ 18:18:
@mietje: "II Als je dus een identieke test uitvoert (met evenveel clients ed.) met en zonder Keep-Alive dan moet je vergelijkbare wachttijden voor select() cq. poll() uitkrijgen.": En daar gaat het dus al mis......
"Aangezien de enige verandering in de test de Keep-Alive optie is, moet die vertraging in de clients worden veroorzaakt door die Keep-Alive.": en die is bijna niks. Zoals je in connection_handler() (regel 827 in hiawatha.c) kunt zien is het enige dat ie tussen 2 requests doet een reset_session() (staat in session.c) die alleen wat pointers free()-ed. Lijkt me niet dat dat de vertraging kan opleveren.
Ik snap dat je Keep-Alive code licht is, wat ik vermoed is dat je Keep-Alive code het RFC2616 protocol niet goed implementeert. Als jouw code volgens het protocol nog iets naar de clients zou moeten sturen (bv. een 100 Continue status) maar dat niet doet (of te laat doet, of niet in de juiste volgorde), kan het best zijn dat die clients daarop gaan zitten wachten, terwijl je server op dataverkeer van de clients wacht; met als gevolg dat de select()/poll() tijden op je server de pan uitrijzen.

Verwijderd

Topicstarter
Verwijderd schreef op 14 september 2004 @ 18:36:
Ik snap dat je Keep-Alive code licht is, wat ik vermoed is dat je Keep-Alive code het RFC2616 protocol niet goed implementeert. Als jouw code volgens het protocol nog iets naar de clients zou moeten sturen (bv. een 100 Continue status) maar dat niet doet (of te laat doet, of niet in de juiste volgorde), kan het best zijn dat die clients daarop gaan zitten wachten, terwijl je server op dataverkeer van de clients wacht; met als gevolg dat de select()/poll() tijden op je server de pan uitrijzen.
Ik heb hier een RFC2616 als bijbel naast me liggen. Die is goed geimplementeert (1xx codes mogen btw niet gestuurd worden door een server).
Daarbij, ik heb zelf een stresstest tool geschreven en ook die is langzamer bij een keep-alive connectie (wacht dus niet op wat voor een extra tussen bericht dan ook).

Ik heb het gevoel dat het toch iets te maken heeft met een socket instelling/optie....

[ Voor 5% gewijzigd door Verwijderd op 14-09-2004 18:56 ]


Verwijderd

Verwijderd schreef op 14 september 2004 @ 18:54:
Ik heb hier een RFC2616 als bijbel naast me liggen. Die is goed geimplementeert (1xx codes mogen btw niet gestuurd worden door een server).
Klopt niet, 1xx codes mogen niet gestuurd worden naar een HTTP/1.0 client maar moeten gestuurd worden naar een HTTP/1.1 client:
10.1 Informational 1xx

This class of status code indicates a provisional response,
consisting only of the Status-Line and optional headers, and is
terminated by an empty line. There are no required headers for this
class of status code. Since HTTP/1.0 did not define any 1xx status
codes, servers MUST NOT send a 1xx response to an HTTP/1.0 client

except under experimental conditions.

A client MUST be prepared to accept one or more 1xx status responses
prior to a regular response, even if the client does not expect a 100
(Continue) status message.
Unexpected 1xx status responses MAY be
ignored by a user agent.

[...]

10.1.1 100 Continue

The client SHOULD continue with its request. This interim response is
used to inform the client that the initial part of the request has
been received and has not yet been rejected by the server.
The client
SHOULD continue by sending the remainder of the request or, if the
request has already been completed, ignore this response. The server
MUST send a final response after the request has been completed.
See
section 8.2.3 for detailed discussion of the use and handling of this
status code.
Maw. staat hier duidelijk dat de 1xx codes gestuurd dienen te worden door de HTTP/1.1 server (en geaccepteerd door de HTTP/1.1 client).

Verwijderd

Topicstarter
Oh ja, dat was het :) Maar iig, de 100 codes zijn niet het probleem/oplossing. Het is echt een socket probleem....
Pagina: 1