Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel

Im PHP-Ökosystem gibt es derzeit zwei Konnektoren für die Arbeit mit dem Tarantool-Server – dies ist die offizielle PECL-Erweiterung tarantool/tarantool-php, geschrieben in C, und tarantool-php/client, geschrieben in PHP. Ich bin der Autor des letzteren.

In diesem Artikel möchte ich die Ergebnisse der Leistungstests beider Bibliotheken teilen und zeigen, wie Sie mit minimalen Änderungen am Code eine Leistungssteigerung um 3–5 erreichen können (zu synthetischen Tests!).

Was werden wir testen?

Wir werden die oben genannten testen synchron Konnektoren, die asynchron, parallel und asynchron parallel laufen. 🙂 Auch den Code der Anschlüsse selbst möchten wir nicht anfassen. Derzeit sind mehrere Erweiterungen verfügbar, um das zu erreichen, was Sie wollen:

  • Wolle ― ein leistungsstarkes asynchrones Framework für PHP. Wird von Internetgiganten wie Alibaba und Baidu verwendet. Seit Version 4.1.0 ist eine magische Methode erschienen SwooleRuntime::enableCoroutine(), mit dem Sie „synchrone PHP-Netzwerkbibliotheken mit einer Codezeile in asynchrone konvertieren können“.
  • Async war bis vor Kurzem eine vielversprechende Erweiterung für asynchrones Arbeiten in PHP. Warum bis vor kurzem? Leider hat der Autor aus einem mir unbekannten Grund das Repository gelöscht und das zukünftige Schicksal des Projekts ist unklar. Ich muss es benutzen ein von Gabeln. Wie bei Swoole können Sie mit dieser Erweiterung ganz einfach mit einem Handgriff die Asynchronität aktivieren, indem Sie die Standardimplementierung von TCP- und TLS-Streams durch ihre asynchronen Versionen ersetzen. Dies geschieht über die Option „async.tcp = 1«.
  • Parallel ― eine ziemlich neue Erweiterung des bekannten Joe Watkins, Autor von Bibliotheken wie phpdbg, apcu, pthreads, pcov, uopz. Die Erweiterung stellt eine API für Multithreading in PHP bereit und ist als Ersatz für pthreads positioniert. Eine wesentliche Einschränkung der Bibliothek besteht darin, dass sie nur mit der ZTS-Version (Zend Thread Safe) von PHP funktioniert.

Wie werden wir testen?

Starten wir eine Tarantool-Instanz mit deaktivierter Write-Ahead-Protokollierung (wal_mode = keine) und erhöhter Netzwerkpuffer (Readahead = 1 * 1024 * 1024). Die erste Option macht die Arbeit mit der Festplatte überflüssig, die zweite ermöglicht es, mehr Anfragen aus dem Betriebssystempuffer zu lesen und dadurch die Anzahl der Systemaufrufe zu minimieren.

Bei Benchmarks, die mit Daten arbeiten (Einfügen, Löschen, Lesen usw.), wird vor dem Start des Benchmarks ein Memtx-Bereich (neu) erstellt, in dem die primären Indexwerte durch einen Generator geordneter Ganzzahlwerte erstellt werden ​(Reihenfolge).
Die Space-DDL sieht so aus:

space = box.schema.space.create(config.space_name, {id = config.space_id, temporary = true})
space:create_index('primary', {type = 'tree', parts = {1, 'unsigned'}, sequence = true})
space:format({{name = 'id', type = 'unsigned'}, {name = 'name', type = 'string', is_nullable = false}})

Bei Bedarf wird der Platz vor der Ausführung des Benchmarks mit 10,000 Tupeln des Formulars gefüllt

{id, "tuplе_<id>"}

Der Zugriff auf Tupel erfolgt über einen zufälligen Schlüsselwert.

Der Benchmark selbst ist eine einzelne Anfrage an den Server, die 10,000 Mal (Umdrehungen) ausgeführt wird, die wiederum in Iterationen ausgeführt werden. Die Iterationen werden wiederholt, bis alle Zeitabweichungen zwischen 5 Iterationen innerhalb eines akzeptablen Fehlers von 3 %* liegen. Anschließend wird das Durchschnittsergebnis ermittelt. Zwischen den Iterationen gibt es eine Pause von 1 Sekunde, um eine Drosselung des Prozessors zu verhindern. Luas Garbage Collector wird vor jeder Iteration deaktiviert und muss nach Abschluss der Iteration neu gestartet werden. Der PHP-Prozess wird nur mit den für den Benchmark erforderlichen Erweiterungen gestartet, wobei die Ausgabepufferung aktiviert und der Garbage Collector deaktiviert ist.

* Die Anzahl der Umdrehungen, Iterationen und Fehlerschwelle können in den Benchmark-Einstellungen geändert werden.

Test Umgebung

Die unten veröffentlichten Ergebnisse wurden auf einem MacBookPro (2015) mit dem Betriebssystem Fedora 30 (Kernelversion 5.3.8-200.fc30.x86_64) erstellt. Tarantool wurde im Docker mit dem Parameter „gestartet“--network host".

Paketversionen:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, Build a872fc2f86
PHP: 7.3.11 (cli) (erstellt: 22. Okt. 2019 08:11:04)
tarantool/client: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ Patch für 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-swoole: 4.4.12
ext-parallel: 1.1.3

* Leider funktioniert der offizielle Connector nicht mit PHP-Version > 7.2. Um die Erweiterung unter PHP 7.3 zu kompilieren und auszuführen, musste ich verwenden Patch.

Ergebnisse

Synchronmodus

Das Tarantool-Protokoll verwendet ein Binärformat MessagePack um Nachrichten zu serialisieren. Im PECL-Connector ist die Serialisierung tief in den Tiefen der Bibliothek verborgen und beeinflusst den Codierungsprozess vom Userland-Code nicht möglich. Ein reiner PHP-Connector hingegen bietet die Möglichkeit, den Codierungsprozess anzupassen, indem er den Standard-Encoder erweitert oder eine eigene Implementierung verwendet. Es sind zwei Encoder standardmäßig verfügbar, einer davon basiert auf msgpack/msgpack-php (offizielle MessagePack PECL-Erweiterung), die andere ist aktiviert rybakit/msgpack (in reinem PHP).

Bevor wir Konnektoren vergleichen, messen wir die Leistung der MessagePack-Encoder für den PHP-Konnektor und verwenden in weiteren Tests denjenigen, der das beste Ergebnis zeigt:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Obwohl die PHP-Version (Pure) der PECL-Erweiterung in der Geschwindigkeit unterlegen ist, würde ich in realen Projekten dennoch den Einsatz empfehlen rybakit/msgpack, da in der offiziellen MessagePack-Erweiterung die Formatspezifikation nur teilweise implementiert ist (z. B. gibt es keine Unterstützung für benutzerdefinierte Datentypen, ohne die Sie Decimal nicht verwenden können – einen neuen Datentyp, der in Tarantool 2.3 eingeführt wurde) und eine hat Anzahl anderer проблем (einschließlich Kompatibilitätsproblemen mit PHP 7.4). Nun, im Allgemeinen sieht das Projekt aufgegeben aus.

Lassen Sie uns also die Leistung von Konnektoren im synchronen Modus messen:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Wie aus der Grafik ersichtlich ist, zeigt der PECL-Connector (Tarantool) eine bessere Leistung im Vergleich zum PHP-Connector (Client). Dies ist jedoch nicht verwunderlich, da Letzteres nicht nur in einer langsameren Sprache implementiert ist, sondern auch mehr Arbeit leistet: Bei jedem Aufruf wird ein neues Objekt erstellt PREISANFRAGE (Request) и Antwort (im Fall von Select - auch Eigenschaften, und im Fall von Update/Upsert ― Einkauf & Prozesse), separate Einheiten Sichere, Packer и Handler Sie erhöhen auch den Overhead. Offensichtlich hat Flexibilität ihren Preis. Im Allgemeinen zeigt der PHP-Interpreter jedoch eine gute Leistung, obwohl es einen Unterschied gibt, ist dieser unbedeutend und wird möglicherweise noch geringer sein, wenn das Vorladen in PHP 7.4 verwendet wird, ganz zu schweigen von JIT in PHP 8.

Lass uns weitermachen. Tarantool 2.0 hat Unterstützung für SQL hinzugefügt. Versuchen wir, die Vorgänge „Auswählen“, „Einfügen“, „Aktualisieren“ und „Löschen“ mithilfe des SQL-Protokolls auszuführen und die Ergebnisse mit den noSQL-Äquivalenten (binär) zu vergleichen:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Die SQL-Ergebnisse sind nicht sehr beeindruckend (ich möchte Sie daran erinnern, dass wir noch den synchronen Modus testen). Allerdings würde ich mich darüber nicht im Voraus aufregen; die SQL-Unterstützung befindet sich noch in der aktiven Entwicklung (die Unterstützung wurde beispielsweise erst vor relativ kurzer Zeit hinzugefügt). vorbereitete Aussagen) und, der Liste nach zu urteilen Probleme, wird die SQL-Engine in Zukunft einer Reihe von Optimierungen unterzogen.

Asynchron

Sehen wir uns nun an, wie uns die Async-Erweiterung dabei helfen kann, die oben genannten Ergebnisse zu verbessern. Um asynchrone Programme zu schreiben, stellt die Erweiterung eine auf Coroutinen basierende API bereit, die wir verwenden werden. Wir finden empirisch heraus, dass die optimale Anzahl von Coroutinen für unsere Umgebung 25 beträgt:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
„Verteilen“ Sie 10,000 Operationen auf 25 Coroutinen und sehen Sie, was passiert:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Die Anzahl der Operationen pro Sekunde stieg um mehr als das Dreifache tarantool-php/client!

Leider startete der PECL-Connector nicht mit ext-async.

Was ist mit SQL?

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Wie Sie sehen können, lag der Unterschied zwischen dem Binärprotokoll und SQL im asynchronen Modus innerhalb der Fehlergrenze.

Wolle

Wieder finden wir die optimale Anzahl an Coroutinen heraus, dieses Mal für Swoole:
Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Hören wir bei 25 auf. Wiederholen wir den gleichen Trick wie bei der Async-Erweiterung – verteilen Sie 10,000 Operationen auf 25 Coroutinen. Darüber hinaus werden wir einen weiteren Test hinzufügen, bei dem wir die gesamte Arbeit in zwei Prozesse aufteilen (d. h. jeder Prozess führt 2 Operationen in 5,000 Coroutinen aus). Prozesse werden erstellt mit SwooleProzess.

Ergebnisse:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Swole zeigt im Vergleich zu Async ein etwas schlechteres Ergebnis, wenn es in einem Prozess ausgeführt wird, aber bei zwei Prozessen ändert sich das Bild dramatisch (die Zahl 2 wurde nicht zufällig gewählt; auf meinem Rechner waren es zwei Prozesse, die das beste Ergebnis zeigten).

Übrigens verfügt die Async-Erweiterung auch über eine API zum Arbeiten mit Prozessen, aber dort habe ich keinen Unterschied zum Ausführen von Benchmarks in einem oder mehreren Prozessen bemerkt (möglicherweise habe ich irgendwo einen Fehler gemacht).

SQL vs. Binärprotokoll:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Wie bei Async entfällt im asynchronen Modus der Unterschied zwischen Binär- und SQL-Operationen.

Parallel

Da es bei der Parallel-Erweiterung nicht um Coroutinen, sondern um Threads geht, messen wir die optimale Anzahl paralleler Threads:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Auf meiner Maschine ist es 16. Lassen Sie uns Connector-Benchmarks für 16 parallele Threads durchführen:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Wie Sie sehen, ist das Ergebnis sogar besser als bei asynchronen Erweiterungen (Swoole, das auf 2 Prozessen ausgeführt wird, nicht mitgerechnet). Beachten Sie, dass für den PECL-Connector die Vorgänge Update und Upsert leer sind. Das liegt daran, dass diese Vorgänge mit einem Fehler fehlschlugen – ich weiß nicht, ob es an ext-parallel, ext-tarantool oder an beiden lag.

Vergleichen wir nun die SQL-Leistung:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel
Beachten Sie die Ähnlichkeit mit dem Diagramm für synchron laufende Konnektoren?

Alle zusammen

Und schließlich fassen wir alle Ergebnisse in einer Grafik zusammen, um das Gesamtbild für die getesteten Erweiterungen zu sehen. Fügen wir dem Diagramm nur einen neuen Test hinzu, den wir noch nicht durchgeführt haben: Lassen Sie uns Async-Coroutinen parallel mit Parallel* ausführen. Die Idee, die oben genannten Erweiterungen zu integrieren, besteht bereits diskutiert Autoren, aber es wurde kein Konsens erzielt, Sie müssen es selbst tun.

* Es war nicht möglich, Swoole-Coroutinen mit Parallel zu starten; es scheint, dass diese Erweiterungen nicht kompatibel sind.

Also die Endergebnisse:

Beschleunigung von PHP-Konnektoren für Tarantool mit Async, Swoole und Parallel

Statt einer Schlussfolgerung

Meiner Meinung nach waren die Ergebnisse durchaus ansehnlich, und aus irgendeinem Grund bin ich mir sicher, dass dies nicht die Grenze ist! Ob Sie dies in einem realen Projekt allein für sich selbst entscheiden müssen, ich kann nur sagen, dass es für mich ein interessantes Experiment war, mit dem Sie beurteilen können, wie viel Sie mit minimalem Aufwand aus einem synchronen TCP-Connector „herausquetschen“ können. Wenn Sie Ideen zur Verbesserung von Benchmarks haben, berücksichtige ich gerne Ihre Pull-Anfrage. Der gesamte Code mit Startanweisungen und Ergebnissen wird separat veröffentlicht Lagerstätten.

Source: habr.com

Kommentar hinzufügen