Nagle Buffer flushen (WinSock)



  • Wie kann man unter Windows "am besten" den Buffer für Nagle's Algorithm flushen, also das sofortige Senden aller eventuell noch gepufferten Daten erzwingen?

    Bzw. wie geht es überhaupt?

    Ich hab da nämlich eine Applikation, bei der es fatal wäre, den Nagle's Algorithm ganz zu deaktivieren. Allerdings möchte ich bei jedem Flusswechsel (genauer: vor jedem recv() welches auf ein send() folgt) sicherstellen, dass die Daten sofort gesendet werden, da es sonst zu unnötigen Verzögerungen kommt.



  • Wenn's ungefähr vorhersehbar ist, wieviel Bytes im TCP-Puffer drauf warten, gesendet zu werden, könntest du bis zur Max-TCP-Content-Size mit Müll-Daten auffüllen. Das Gegenüber muss natürlich damit umgehen können.
    Kannst du denn halbwegs messen, wann/ob eine Art flushen greifen würde?



  • hustbaer schrieb:

    Ich hab da nämlich eine Applikation, bei der es fatal wäre, den Nagle's Algorithm ganz zu deaktivieren. Allerdings möchte ich bei jedem Flusswechsel (genauer: vor jedem recv() welches auf ein send() folgt) sicherstellen, dass die Daten sofort gesendet werden, da es sonst zu unnötigen Verzögerungen kommt.

    schalte nagle ab, pack die daten manuell in einen puffer und dann ruf send() auf. dann müsste er immer die datenmenge raushauen, die du bestimmst.
    oder du benutzt UDP, dann kannste das paketorientierte ping-pong selbst steuern.
    🙂



  • Also das Protokoll steht schon (ist seit 9 Jahren in verwendung, viele Clients draussen die auch nicht alle aktualisiert werden können), von daher ist nix mit TCP oder Dummy-Daten senden. Mal ganz davon abgesehen dass Dummy-Daten senden das Netz unnötigerweise zumüllen würde.

    schalte nagle ab, pack die daten manuell in einen puffer und dann ruf send() auf. dann müsste er immer die datenmenge raushauen, die du bestimmst.

    Ich hatte gehofft, dass sich das irgendwie vermeiden lässt.
    Die Version die ich mir überlegt hätte wäre...
    a) maximal ~1400 Byte puffern, wenn 1400 voll sind senden, und den Rest puffern
    b) sobald recv() aufgerufen wird, vorher alles senden was noch gepuffert ist
    Das würde dann vermutlich nur minimale Änderungen an den ganzen Programmen erfordern, die die betreffende Socket-Klasse verwenden.
    Ist mir allerdings fast etwas zu heikel.

    Und Nagle ganz abschalten, ohne selbst zu puffern, ist auch nicht ideal. Da werden z.T. Daten in 4 Byte Stücken bzw. manchmal sogar Byteweise an send() übergeben. Mit ausgeschaltetem Nagle würde das vermutlich den Overhead enorm erhöhen.

    Ideal wäre für mich eben eine Variante, wo ich das automatische send-coalescing beibehalten könnte, nur an bestimmten Punkten gezielt ein "jetzt_senden();" aufrufen könnte.

    Ich hab mal probiert dieses "jetzt_senden" als "nagle aus, dummy-null-byte send(), nagle wieder ein" zu implementieren. Scheint minimal was zu bringen, aber lange nicht soviel wie wenn ich den Nagle ganz abdrehe. Hm. Doof.

    EDIT: "nagle aus, dummy-null-byte send(), nagle wieder ein" bringt doch nix 😞



  • hustbaer schrieb:

    Und Nagle ganz abschalten, ohne selbst zu puffern, ist auch nicht ideal. Da werden z.T. Daten in 4 Byte Stücken bzw. manchmal sogar Byteweise an send() übergeben. Mit ausgeschaltetem Nagle würde das vermutlich den Overhead enorm erhöhen.

    deswegen ja: nagle aus, selbst puffern und client-code niemals 'send' direkt aufrufen lassen. aber schau dir mal die WSAxxx-funktionen an. vielleicht gibts ja schon irgendwas fertiges zum unverzögerten senden, oder so.
    🙂



  • WSAXxx hab ich geguckt, aber nix gefunden. Aber dankt für den Tip.
    Hab auch noch probiert SO_SNDBUF kurz aus und wieder einzuschalten. Bringt auch nix.

    Es funktioniert, wenn man noch ein send() macht, während SO_SNDBUF = 0 bzw. TCP_NODELAY = 1. Ein Null-Byte-send() reicht leider nicht. Finde das irgendwie doof. Hätte mir erwartet, dass sofort bei setsockopt() alle Daten in der Queue an den NIC übergeben werden, bzw. spätestens bei einem "dummy send" (Wink mit dem Zaunpfahl quasi), aber ist leider nicht der Fall 😞

    Selber Puffern ist mir wie gesagt etwas zu heikel, das würde einiges an Arbeit (vor allem Testen!) nach sich ziehen, und dafür hab' ich im Moment leider keine Zeit. (Wäre die Schnittstelle der Socket-Klasse "sauber", wäre das kein grosses Ding, aber die ist leider schon sehr alt, und relativ "unsauber".)

    Aber... einige MSDN Artikel lesen sich so, als ob Windows zusätzlich zum Nagle's noch einen "eigenen" send-coalescing Algorithmus hätte. Einen der nur ganz kurz puffert, und nicht gleich bis ein ACK eingetroffen ist. Überprüft habe ich es nicht, aber ich denke ich riskier's einfach, und dreh den Nagle's ganz aus, OHNE selbst Daten zu puffern 🙂

    Mal sehen - wenn es unsere Inet-Anbindung zu arg zumüllt, muss ich es eben wieder rausnehmen, bzw. kann dann immer noch zusätzliche Pufferung einbauen. Wenigstens Serverseitig.

    Und für die Neugierigen: Mit Nagle's an dauert eine Datenübertragung bei einem der Programme (hab nur eins getestet, aber sind alle recht ähnlich was den Datenflow angeht) ~6,5 Sekunden. Mit Nagle's aus ca. 600ms. 🙂 Im lokalen Netz wohlgemerkt -- wenn dann mal echte Trip-Zeiten dazukommen wird der Gewinn prozentuell sicherlich geringer ausfallen (absolut sollte inetwa gleich bleiben).



  • Uaaaaaah!
    Gut dass ich nen Test mit Wireshark gemacht hab.

    Im loklen Netz is jetzt zwar alles toll schnell, aber dafür gehen ~4x soviel Daten übers Netz 😞

    (Und das obwohl ich bei dem Test nur auf der Server-Seite Nagle's ausgeschaltet habe, beim Client aber nicht)

    Doch nix mit (effektivem) send-coalescing ohne Nagle's unter Windows 😞

    Bleibt mir wohl doch nix übrig als selbst Puffern, wenn ich das beheben will...
    Hmpf!



  • wenn du den socket mit WSASocket erzeugst, funktionieren eineige file-funktionen
    wie WriteFile oder ReadFile. möglicherweise auch die gesuchte FlushFileBuffers
    funktion 😉



  • Ist das wirklich abhängig davon, ob man den Socket mit WSASocket erzeugt???
    Wäre ja schön doof.
    Werd es aber auf jeden Fall probieren...

    p.S.: FlushFileBuffers hab' ich nämlich schon probiert, und hat nix gebracht. In der Doku zu FlushFileBuffers steht auch leider nix vonwegen Sockets, nur Pipes.


Anmelden zum Antworten