Ab sgcWebSockets 2022.9.0 wurde der Indy-Server-IOCP-IOHandler von Grund auf neu geschrieben und die Leistung verbessert. Der IOCP-IOHandler ist nur im sgcWebSockets-Enterprise-Paket verfügbar.
Mit IOCP vermeidest du das "Ein-Thread-pro-Client"-Problem, bei dem die Leistung stark einbricht, je mehr Verbindungen der Server verarbeitet. IOCP stellt wenige Threads bereit, die viele Clients bedienen. Die Threads sind suspendiert und verbrauchen keine CPU-Zyklen, solange nichts zu tun ist.
Konfiguration
Um IOCP für Indy-Server zu aktivieren, gehe zur Eigenschaft IOHandlerOptions und wähle iohIOCP als IOHandler-Typ.
Server.IOHandlerOptions.IOHandlerType := iohIOCP; Server.IOHandlerOptions.IOCP.IOCPThreads := 0; // the number of IOCP threads will be calculated automatically using the number of processors. Server.IOHandlerOptions.IOCP.WorkOpThreads := 0;
1. IOCPThreads sind die Threads für asynchrone IOCP-Anfragen (überlappte Operationen). Standardwert ist null, was bedeutet, dass die Threadanzahl anhand der Prozessorzahl berechnet wird (außer bei Delphi 7 und 2007, wo der Wert auf 32 gesetzt wird, da die Funktion cpucount dort nicht unterstützt wird).
2. WorkOpThreads muss nur aktiviert werden, wenn Verbindungen immer im selben Thread verarbeitet werden sollen. Bei IOCP werden Anfragen von einem Thread-Pool verarbeitet, und jede Anfrage (für dieselbe Verbindung) kann in unterschiedlichen Threads laufen. Möchtest du jede Verbindung im selben Thread halten, setze in WorkOpThreads die Anzahl der Threads, die diese Anfragen verarbeiten. Das beeinflusst die Server-Leistung; es wird nur empfohlen, einen Wert größer null zu setzen, wenn du diese Funktion wirklich brauchst.
IOCP für Windows-Server wird empfohlen, wenn du tausende Verbindungen verarbeiten musst. Bei höchstens 100 gleichzeitigen Verbindungen kannst du beim Standard-Indy-Thread-Modell bleiben.
Leistungstest
Ein einfacher Test zeigt die Unterschiede zwischen dem Indy-Thread-Modell und IOCP. Der Test verbindet sich per WebSocket-Protokoll zum Server, und CPU-Auslastung sowie Speicherverbrauch werden in folgender Tabelle dargestellt. Die CPU-Auslastung wird gemessen, während der Server im Leerlauf ist.
| Num. Connections | Indy Standard-IOHandler | Indy IOCP-IOHandler |
| 100 | 0% (4.1MB) | 0% (3.9MB) |
| 500 | 0% (17.1MB) | 0% (7.7MB) |
| 1000 | 0% (32.2MB) | 0% (12.4MB) |
| 1500 | 7.3% (46.8MB) | 0% (17.1MB) |
| 2000 | 15.4% (61.6MB) | 0% (21.9MB) |
| 2500 | 51.9% (76.5MB) | 0% (26.5MB) |
| 3000 | 68.8% (91.7MB) | 0% (31.2MB) |
| 3500 | 72.3% (106MB) | 0% (35.9MB) |
Der Indy-Server begann ab mehr als 3000 gleichzeitigen Verbindungen langsamer zu werden — höchstwahrscheinlich wegen des Thread-Context-Switching — und die CPU-Auslastung war im Leerlauf sehr hoch.
Der IOCP-Server verbrauchte im Leerlauf keine CPU, und auch der Speicherverbrauch war geringer.
Der nächste Test misst, wie lange das Verbinden von 3000 Clients dauert. Jeder Client sendet beim Verbinden eine Nachricht, und der Server gibt diese Nachricht zurück.
| Indy Standard-IOHandler | Indy IOCP-IOHandler | |
| 3000 Clients verbinden und Echo-Nachricht | 15,32 Sekunden | 6,89 Sekunden |
Die Leistungstests liefen auf einem Windows Server 2022 mit 16 Kernen und 32 GB RAM.
