Optimalisatie van de TCP/IP-stack

Het internet zit niet verlegen om tips en esoterische registeringrepen die beloven je aan een optimale internetverbinding te helpen. In dit artikel gaan we na welke factoren meespelen en in hoeverre die zich inderdaad laten optimaliseren om zo sneller dataverkeer mogelijk maken. We focussen ons op Windows Vista en hoger.

Wanneer je data downloadt van een server (via routers) met afgeknepen bandbreedte, dan halen optimalisaties aan clientzijde weinig uit om die gegevens sneller tot bij jou te krijgen. Anderzijds zijn er scenario’s denkbaar waarbij de data op de ene pc merkbaar sneller toekomen dan op de andere, ook al hangen beide clients achter dezelfde router. Naast andere hardware (zoals de netwerkadapters) en software (denk aan een lokale proxy of firewall) ligt een mogelijke verklaring in een verschillende uitvoering en configuratie van de TCP/IP-stack. Deze stack is de softwarematige implementatie van een reeks communicatieprotocollen waarvan het internet zich bedient. Gezien TCP (Transmission Control Protocol) en IP (Internet Protocol) de twee meest prominente protocollen zijn om machines op het internet met elkaar te verbinden, begrijp je meteen vanwaar de naam TCP/IP-stack komt.

Om de huidige status van de TCP/IP-stack op je Windows-computer te weten te komen, ga je als administrator naar de opdrachtprompt en voer je daar het commando netsh interface tcp show global uit. Je krijgt nu een overzicht van de – instelbare – globale TCP-parameters. Nu mag je nog de commando’s kennen waarmee je aan deze parameters sleutelt (ze komen trouwens uitgebreid aan bod in dit artikel), je moet uiteraard goed weten waar ze precies voor staan. Immers, verkeerde instellingen kunnen je met een gebrekkige internetverbinding opzadelen.

Heb je helemaal geen zin om je in deze materie te verdiepen (en dit artikel geheel door te nemen), dan kan je je eventueel van een semi-automatische tuningtool als SG TCP Optimizer bedienen. Let er wel op dat je de recentste versie (3.0.8?) downloadt, gezien oudere versies Windows Vista of hoger niet ondersteunen. In principe kan je je er al met de volgende handelingen vanaf maken. Voer het (portable) programma als administrator uit en zorg dat Current staat aangestipt op het tabblad General Settings: je krijgt nu een overzicht van de huidige instellingen van je TCP/IP-stack, deels vergelijkbaar met wat het netsh-commando je te zien gaf. Stel vervolgens de schuifknop in op de maximaal beschikbare verbindingssnelheid – eventueel ga je dat via een online snelheidstest na, zoals deze -, kies de netwerkadapter waarlangs de internetverbinding loopt of plaats desnoods een vinkje bij Modify All Network Adapters, stip de optie Optimal aan en druk op de knop Apply changes. Je krijgt nu een detailoverzicht van alle instellingen die de tool zal aanpassen zodra je met OK bevestigt. Je doet er absoluut goed aan een vinkje te plaatsen bij Backup voor je dat doet! Het programma maakt dan een back-up van de huidige (register)instellingen en slaat die informatie in een spg-bestand op (in de map van het programma). Via File, Restore backed up settings kan je de wijzigingen dan nog ongedaan maken. Mogelijk moet je het systeem wel even herstarten om alle aanpassingen effect te laten sorteren.

Wijzelf zijn echter geen fan van zo’n zwarte doos-aanpak – en jij, als lezer van PCM, ongetwijfeld evenmin. We weten dus graag waarom een tool als SG TCP Optimizer denkt bepaalde parameters van de TCP/IP-stack te moeten aanpassen. Daar gaat het vervolg van dit artikel over.

Alles begint eigenlijk bij het gegevensstroombeheer (flow control) zoals dat in een aantal protocollen zit ingebouwd. Zo staat met name het TCP-protocol bekend als een relatief betrouwbaar protocol precies omwille van enkele foutdetectiemechanismen. Om deze processen goed te kunnen volgen is een netwerksniffer annex protocolanalyzer als het gratis Wireshark eigenlijk onmisbaar (www.wireshark.org). Heb je bijvoorbeeld data van een http-sessie gecapteerd, dan zal je aan het begin van die sessie een driedelige TCP-handshake tussen client en (web)server herkennen, waarbij achtereenvolgens de volgende vlaggen in de TCP-header worden gezet: [Syn] (client ? server), [Syn, Ack] (server ? client), [Ack] (client ? server). Hierbij worden enkele communicatieparameters doorgegeven en afgesproken tussen beide. Zo maakt de client aan de server duidelijk wat de initiële maximale grootte van zijn ontvangstbuffer (receive window oftewel RWIN) is. De server interpreteert dat als het maximum aantal bytes dat hij richting client mag versturen voor de client de ontvangst van die data bevestigt (via een Ack, wat staat voor acknowledge). Tijdens de communicatie kan de client de grootte van die buffer echter tijdelijk verhogen of verlagen, afhankelijk van hoe vlot die alle gegevens kan verwerkt krijgen. Het probleem is dat het veld voor de venstergrootte slechts 16 bits groot is - dus maximaal 65535 bytes kan aangeven -, wat voor hedendaagse omgevingen met hoge bandbreedtes niet langer volstaat. Daarom is er in een uitbreiding voorzien met een extra optie: TCP window scaling. Indien zowel zender als ontvanger te kennen geven dat ze met WS overweg kunnen, dan zal de window scaling-waarde als een vergrotingsfactor voor de opgegeven venstergrootte fungeren: een WS-waarde van 3 bijvoorbeeld betekent een vermenigvuldigingsfactor van 23 (8 dus). Dit proces is goed te volgen als je in Wireshark een TCP-pakketje opent en in het detailpaneel Options, Window scale openvouwt. Overigens is het wel zo dat pas met de latere Windows-versies, vanaf Vista zeg maar, (en recente versies van andere besturingssystemen) hiervoor optimale waarden kunnen worden berekend. In Windows heet deze functie overigens auto-aanpassing (auto tuning): zie ook verder.

In de TCP-header merk je tevens de velden volgnummer en bevestigingsnummer op. Die zorgen ervoor dat pakketjes die onderweg zijn verloren geraakt, alsnog terecht komen bij de ontvanger. Enigszins vereenvoudigd komt het hierop neer. Het volgnummer (sequence number) houdt bij hoeveel data er al zijn verstuurd. Dit wordt dan door de ‘tegenpartij’ als een bevestigingsnummer (acknowledgement number) bijgehouden: het tot dan toe correct aantal binnengekomen bytes. In Wireshark laat dit proces zich beter volgen via het menu Statistics, Flow Graph, waarbij je TCP flow selecteert. Op deze pagina wordt deze werkwijze mooi toegelicht.

Blijft zo’n bevestiging uit, dan weet de zender dat er iets fout is gelopen en verstuurt die nogmaals alle nog niet eerder bevestigde data; hoe groter de venstergrootte was ingesteld, hoe meer data er dan opnieuw moet worden verstuurd, wat bij frequente fouten een zware impact op de snelheid kan hebben. Nu is dat probleem deels wel aangepakt met zogenoemde selective acks (SACKS). De ontvanger maakt aan de zender duidelijk welke delen van het venster al effectief zijn binnengekomen. Dat kan door slim gebruik te maken van de TCP-opties: op basis van hier opgenomen begin- en eindwaarden weet de zender precies welke data ontvangen zijn. De zender kan vervolgens op eigen initiatief alleen de effectief ontbrekende data versturen, zodat er geen retransmissie van het complete venster is vereist. Of SACK tijdens de communicatie zal worden ingezet, is trouwens ook het voorwerp van onderhandeling tijdens de al eerder vermelde handshake. Bij Wireshark lees je in dit geval SACK_PERM=1 af (SACK Permission).

Een andere interessant mechanisme om vertraging tegen te gaan is de ‘fast retransmit’. Deze functie zorgt ervoor dat de zender niet noodzakelijk hoeft te wachten op de normale time-out (op basis van de round-trip time) vooraleer een pakketje opnieuw te versturen. Dat werkt als volgt. Stel, bij de ontvanger komt een pakket binnen met volgnummer 1. De ontvanger verstuurt dan een ACK en verhoogt daarbij het volgnummer met 1 (dat wordt dus 2). Hierdoor geeft die te kennen aan de zender dat die het daaropvolgende pakketje verwacht. Komt echter eerst pakketje 3 binnen, dan zal de ontvanger geen ACK met nummer 3, maar nog altijd met nummer 2 versturen. Dat doet hij zolang pakketje 2 niet is binnengekomen. We spreken van een duplicate ACK ofte DupACK’s (want tweemaal met hetzelfde volgnummer). In de meeste gevallen zal de zender na 3 DupACK’s dan automatisch pakketje 2 opnieuw versturen, ook al is de normale time-out nog niet verstreken: vandaar de naam ‘fast retransmit’.

Met deze, weliswaar beknopte, achtergrondinformatie, zijn we alvast beter gewapend om de typische parameters van de TCP/IP-stack te begrijpen en desgevallend bij te sturen. We focussen ons op de parameters zoals je die via het netsh-commando en op het tabblad General Settings van SG TCP Optimizer te zien kan krijgen.

Samen met Invoegtoepassing congestiecontroleprovider is het Niveau auto-aanpassing ontvangstvenster een van de belangrijkste, instelbare parameters onder Windows Vista en hoger.

We hebben het eerder in dit artikel al over de auto-aanpassing van Windows gehad: een ingebouwde functie die Windows toelaat voor elke connectie zelf de optimale RWIN-waarde te berekenen. Dat gebeurt voornamelijk op grond van de verbindingssnelheid, de vastgestelde latentie (round-trip time) en de snelheid waarmee applicaties hun buffer leegmaken. Deze functie werkt in de meeste gevallen goed en doorgaans zijn er weinig redenen om die niet op de standaardwaarde (normal) in te stellen. Dat kan je doen met het commando netsh int tcp set global autotuninglevel=normal. Dat levert gewoonlijk een kleine RWIN-waarde bij het begin van de sessie op, maar met de mogelijkheid om die stelselmatig te laten toenemen. Het kan echter gebeuren de instelling ‘normal’ problematisch is voor sommige, oudere NAT-routers. Ook bij langdurige connecties (zoals bij torrent-software) kan deze instelling problemen geven: de RWIN-waarde kan namelijk zeer groot worden en teveel systeembronnen (geheugen) verbruiken. Bij gebrekkige of trage verbindingen ben je daarom soms beter af met een andere, meer restrictieve instelling. Je vervangt ‘normal’ dan door ‘restricted’ (wat de RWIN-waarde minder snel zal doen toenemen) of desnoods zelfs ‘highlyrestricted’. Desnoods schakel je deze functie zelfs helemaal uit met de parameter ‘disabled’. Dit laatste heeft wel een ingrijpende bijwerking: ook de WS-functie valt dan uit, zodat RWIN effectief tot een magere 16 bits-waarde beperkt blijft (zie ook boven)!

Wat vervelend is nog dat Windows blijkbaar koppig vasthoudt aan zijn eigen visie en je handmatige aanpassingen durft te negeren. In dit geval krijg je bij het oproepen van de globale parameters iets te lezen als “De bovenstaande instelling voor autotuninglevel is tot stand gekomen doordat de methodiek voor vensterschaalbaarheid […]” (zie ook afbeelding). Om dat te voorkomen dien je eerst het commando netsh int tcp set heuristics disabled uit te voeren en pas daarna het gewenste niveau van de auto-aanpassing in te stellen.

Typisch worden bij het begin van een TCP-connectie de datapakketjes langzaam verstuurd en geleidelijk aan wordt de snelheid opgedreven tot de RWIN-grenswaarde is bereikt of tot er pakketverlies optreedt. Bij de snelle breedbandverbindingen van tegenwoordig wordt de venstergrootte bij de zender echter vaak niet snel genoeg opgevoerd om ten volle de beschikbare bandbreedte te gebruiken. Er is echter een recenter algoritme dat die grootte in zo’n geval sneller doet toenemen: CTCP, wat staat voor Compound TCP. Dit hele procedé staat uitvoerig beschreven op deze pagina. In Windows Vista, 7 en 8 is dat algoritme echter standaard uitgeschakeld, terwijl het in bepaalde gevallen – en met name wanneer je over een royale breedbandverbinding beschikt of in een gigabit ethernet-omgeving opereert – tot betere verzendprestaties kan leiden. Je schakelt CTCP als volgt in: netsh int tcp set global congestionprovider=ctcp  (met none zet je die weer op non-actief).

RSS (receive-side scaling, ‘status schaling aan ontvangstzijde’) zorgt ervoor dat de ontvangen datapakketjes door verschillende processorkernen tegelijk kunnen worden verwerkt – in Windows XP kon dat slechts door één kern gebeuren. Aan welke kern een datapakketje wordt toevertrouwd, hangt af van de hash-waarde die op basis van enkele velden in elk pakketje wordt berekend. Deze hash-waarden dragen er zorg voor dat alle pakketjes die bij een bepaalde TCP-connectie horen aan een en dezelfde processorkern worden toegewezen, in dezelfde volgorde als waarin de pakketjes zijn ontvangen. Je laat RSS beter ingeschakeld (via netsh int tcp set global rss=enabled) wanneer je beschikt over een moderne processor (met minstens 2 kernen) en een netwerkadapter die met RSS overweg kan – wat voor de meeste moderne adapters het geval is. Dat kan je overigens wel uitvissen door in het Windows Apparaatbeheer het eigenschappenvenster van je netwerkkaart te openen en naar het tabblad Geavanceerd te gaan.

TCP Chimney-offload stelt Windows in staat alle noodzakelijke TCP-verwerkingen en –berekeningen over te laten aan de netwerkadapter, zodat er CPU-tijd vrijkomt voor andere taken. Veel voordeel echter hoef je daar doorgaans niet van te verwachten, gezien dergelijke berekeningen een moderne processor nauwelijks extra belasten – een mogelijke uitzondering als een druk bezochte webserver niet te na gesproken. Je kan echter gerust met deze instelling experimenteren (via netsh int tcp set global chimney=enabled; om de beslissing aan Windows zelf over te laten stel je deze parameter in op default). Controleer echter ook of de TCP-checksum-offload wel is ingeschakeld in het (geavanceerde) eigenschappenvenster van je netwerkkaart.

NetDMA oftewel TCPA (TCP acceleration) is er eigenlijk op berekend het CPU-gebruik te beperken door DMA (direct memory access) in te zetten bij het transfereren van de data uit de buffers van de netwerkkaart(en) naar het werkgeheugen van de applicatie. Direct Cache Access (DCA) doet iets gelijkaardigs door data rechtstreeks in de CPU-cache af te leveren. Voorwaarde is wel dat je hardware (netwerkkaart, chipset en CPU) deze functie ondersteunt.  Veel snelheidsvoordeel hoef je van deze functies echter niet te verwachten. Je kan ze als volgt activeren: netsh int tcp set global netdma=enabled en netsh int tcp set global dca=enabled.

ECN staat voor Explicit Congestion Notification en is specifiek bedoeld voor routers. ECN moet meer bepaald voorkomen dat er netwerkcongestie optreedt doordat de buffers van de router overvol geraken. Een router die met ECN overweg kan, zal een bit in de TCP/IP-header zetten om de zender te attenderen op dreigende congestie. Als het goed is, zal de zender daarop reageren net alsof de router datapakketjes zou hebben ‘gedropt’: de zender zal de datatransfer tijdelijk vertragen zodat er geen pakketjes verloren gaan. Nagenoeg alle moderne routers kunnen daarmee overweg, maar in de praktijk blijkt ECN nauwelijks voordeel op te leveren. Je kan deze functie desgewenst activeren via netsh int tcp set global ecncapability=enabled.

In moderne besturingssystemen als Windows Vista en hoger hoef je in de meeste gevallen voor je TCP/IP-verkeer geen spectaculaire snelheidswinsten te verwachten door aan de TCP/IP-stack te sleutelen. (Ook) Windows is intussen slim genoeg om de bijhorende parameters voor de meest gebruikelijke scenario’s zelf optimaal in te stellen. Toch kan het in bepaalde omstandigheden – zoals bij gebruik van specifieke netwerktoestellen of bij snelle breedbandverbindingen -  aangewezen zijn die parameters te controleren en desgevallend aan te passen. Als je goed weet waar je mee bezig bent, is de optie Custom van het programma SG TCP Optimizer een comfortabele en veilige manier om hiermee te experimenteren. Veilig, zolang je maar dat vinkje bij Backup laat staan.

Deel dit artikel
Voeg toe aan favorieten