Ich mache einige Benchmarks mit einem optimierten Java NIO-Selektor unter Linux-Loopback (127.0.0.1).
Mein Test ist sehr einfach:
Für die Rundreisezeit bekomme ich folgende Ergebnisse:
Aber der Haken ist, dass ich 1 Million Nachrichten spinne.
Wenn ich nur 10 Nachrichten spinne, bekomme ich sehr unterschiedliche Ergebnisse:
Korrigiere mich, wenn ich falsch liege, aber ich vermute, dass sobald wir den NIO-Selektor drehen, die Antwortzeiten optimal werden. Wenn wir jedoch Nachrichten mit einem ausreichend großen Intervall zwischen ihnen senden, zahlen wir den Preis für das Aufwecken des Selektors.
Wenn ich mit dem Senden einer einzelnen Nachricht herumspiele, bekomme ich verschiedene Zeiten zwischen 150 und 250 Mikros.
Also meine Fragen für die Gemeinschaft sind:
1 - Ist meine Mindestzeit von 13 Mikros mit durchschnittlich 19 Mikros für diesen Round-Trip-Paket-Test optimal? Es sieht so aus, als ob ich ZeroMQ bei weitem prügele, damit ich hier etwas verpasse. Aus diesem Benchmark sieht es so aus, als ob ZeroMQ eine 49 Mikro-Zeit (99% Perzentil) auf einem Standard-Kernel hat = & gt; Ссылка
2 - Kann ich etwas tun, um die Reaktionszeit des Selektors zu verbessern, wenn ich eine einzelne oder sehr wenige Nachrichten spinne? 150 Mikros sieht nicht gut aus. Oder sollte ich davon ausgehen, dass der Selektor in einer Prod-Umgebung nicht ganz stimmt?
Indem ich beschäftigt bin, mich um selectNow () zu drehen, kann ich bessere Ergebnisse erzielen. Das Senden von wenigen Paketen ist immer noch schlimmer als das Senden vieler Pakete, aber ich denke, dass ich jetzt die Selektor-Leistungsgrenze erreiche. Meine Ergebnisse:
Schlussfolgerungen
Es sieht also so aus, als ob die physikalische Barriere für die UDP-Paketumleitung im Durchschnitt 10 Mikrosekunden beträgt, obwohl ich einige Pakete erhalten habe, die den Trip in 8 Mikros (Min-Zeit) machen.
Mit viel Spinnerei (danke Peter) konnte ich im Durchschnitt von 200 Mikros auf durchschnittlich 65 Mikros für ein einzelnes Paket gehen.
Nicht sicher, warum ZeroMQ fünfmal so langsam ist . ( Bearbeiten: Vielleicht, weil ich dies auf dem gleichen Rechner über Loopback teste und ZeroMQ zwei verschiedene Rechner benutzt?)
Sie sehen oft, dass Fälle, in denen ein Thread aufwacht, sehr teuer sein können, nicht nur weil der Thread Zeit braucht, um aufwachen, aber der Thread läuft 2-5x langsamer für einige zehn Mikrosekunden als die Caches und
Die Art, wie ich das in der Vergangenheit vermieden habe, ist beschäftigt zu warten. Leider erstellt SelectNow bei jedem Aufruf eine neue Sammlung, auch wenn es sich um eine leere Sammlung handelt. Dies erzeugt so viel Müll, dass es nicht wert ist, verwendet zu werden.
Um dies zu umgehen, muss man auf nicht blockierende Sockets warten. Dies lässt sich nicht besonders gut skalieren, bietet Ihnen jedoch die geringste Latenzzeit, da der Thread nicht aufwachen muss und der Code, den Sie danach ausführen, eher im Cache vorhanden ist. Wenn Sie auch Thread-Affinität verwenden, kann es Ihre Threads-Störung reduzieren.
Ich würde auch vorschlagen, dass Sie versuchen, Ihre Code-Sperre weniger und weniger überflüssig zu machen. Wenn Sie dies tun, können Sie einen Prozess in Java haben, der 90% der Zeit eine Antwort auf ein eingehendes Paket unter 100 Mikrosekunden sendet. Dies würde es Ihnen ermöglichen, jedes Paket bei 100 Mb zu verarbeiten, wenn sie ankommen (bis zu 145 Mikrosekunden Abstand aufgrund von Bandbreiteneinschränkungen). Für eine 1 Gb-Verbindung können Sie ziemlich nahe kommen.
Wenn Sie eine schnelle Interprozesskommunikation auf derselben Box in Java wünschen, könnten Sie etwas wie Ссылка in Betracht ziehen Gemeinsamer Speicher zum Übergeben von Nachrichten mit Round-Trip-Latenzzeiten (was mit Sockets schwieriger zu tun ist) von weniger als 200 Nanosekunden . Es behält auch die Daten bei und ist nützlich, wenn Sie nur eine schnelle Möglichkeit haben möchten, eine Journaldatei zu erstellen.
Wenn Sie den Selektor richtig einstellen, können Sie die Kommunikation zwischen Sockets in Java in weniger als 2 Mikrosekunden durchführen. Hier sind meine One-Way-Ergebnisse für ein 256-Byte-UDP-Paket:
%Vor%Ich spreche mehr über Java NIO und das Reaktormuster in meinem Artikel Inter-Socket-Kommunikation mit weniger als 2 Mikrosekunden Latenz .