Benchmarks für die wichtigsten serverseitigen Swift Frameworks im Vergleich zu Node.js

7. Oktober: Checkout my Follow Up: Benchmarks für Linux (Ubuntu)

Einführung

Kürzlich habe ich in Server-Side Swift gearbeitet, und mir wurde die Frage gestellt:

"Kann Server-Side Swift Node.js schlagen?"

Swift als Hauptsprache für alles, einschließlich des Servers, war faszinierend, seit es als Open-Source-Version auf Linux portiert wurde. Viele von Ihnen sind mit Sicherheit genauso neugierig wie ich. Daher freue ich mich sehr, die Ergebnisse meiner Studie hier zu teilen.

Die wichtigsten serverseitigen Swift-Frameworks

Zum Zeitpunkt des Schreibens sind die wichtigsten Server-Side-Swift-Frameworks (in der Reihenfolge der Sterne auf GitHub aufgeführt):

  • Perfekt 7.956 ️
  • Dampf 5183
  • Kitura ️4.017
  • Zewo ️ 1.186

Organisation dieses Beitrags

Dieses Dokument ist folgendermaßen aufgebaut:

  • Dieses kurze Intro
  • Zusammenfassung der Ergebnisse
  • Methodik
  • Detaillierte Ergebnisse
  • Fazit & Schlussbemerkungen

Zusammenfassung der Ergebnisse

Das Folgende ist eine kurze Zusammenfassung der wichtigsten Benchmarks, und ich werde dies hier sagen:

Unabhängig von den einzelnen Rankings schnitten alle diese Frameworks unglaublich gut ab, Swift schlug Node.js durchweg und es hat viel Spaß gemacht, mit ihnen zu arbeiten.
Dieses Bild wurde am 1. September 2016 mit einer Korrektur aktualisiert

Methodik & Hinweise

Warum sollten Sie Blogs und JSON verwenden?

Am einfachsten ist Blogs mehr als nur "Hallo Welt!", Und JSON ist ein sehr häufiger Anwendungsfall. Gute Benchmarks erfordern eine ähnliche Belastung für jedes Framework, und es muss eine etwas höhere Belastung vorliegen, als wenn zwei Wörter auf den Bildschirm gedruckt werden.

Dinge gleich halten

Eines der Hauptthemen, an dem ich festzuhalten versuchte, war, alle Blogs so ähnlich wie möglich zu halten und dabei den Stil und die Syntax der einzelnen Frameworks weiterzuentwickeln. Viele der Strukturen, wie z. B. die Inhaltserstellung, werden wörtlich wiederholt, sodass für jedes Framework die gleichen Datenmodelle zur Verfügung stehen. Facetten wie das URL-Routing unterscheiden sich jedoch manchmal stark, um mit der Syntax und dem Stil jedes Frameworks übereinzustimmen.

Feine Unterschiede

Es gibt einige subtile Unterschiede zwischen den serverseitigen Swift-Frameworks.

  • Sowohl bei Kitura als auch bei Zewo treten Probleme beim Erstellen auf, wenn die absoluten Dateipfade Leerzeichen enthalten. Xcode hat auch Probleme beim Erstellen von Leerzeichen in absoluten Dateipfaden in jedem Framework.
  • Zewo verwendet den Swift-Snapshot 05–09-a. Dies bedeutet, dass beim Erstellen im Release-Modus Probleme auftreten und der Debug-Modus ausgeführt werden muss. Aus diesem Grund wurden alle Zewo-Tests mit Zewo durchgeführt, das im Debug-Modus (ohne Release-Optimierungen) erstellt und ausgeführt wurde.
  • Die Behandlung statischer Dateien ist ein Diskussionspunkt unter den serverseitigen Swift Frameworks. Vapor und Zewo empfehlen beide, Nginx als Proxy für die Verarbeitung statischer Dateien zu verwenden und dann das dahinter stehende Framework als Rechenleistung für das Backend zu verwenden. Perfect schlägt vor, den integrierten Handler zu verwenden, und ich habe keine Kommentare zu IBM zu ihren eigenen Ansichten zu diesem Thema erhalten. Da es in dieser Studie nicht darum ging, wie gut Frameworks mit etablierten Serveranwendungen wie Nginx funktionieren, wurde die statische Dateiverwaltung nativ für jedes Framework verwendet. Möglicherweise können Sie sowohl in Vapor als auch in Zewo eine bessere Leistung erzielen, wenn Sie dies berücksichtigen. Dies ist auch ein sekundärer Grund, warum ich JSON-Tests durchgeführt habe.
  • [Hinzugefügt 1. September mit aktualisierten Ergebnissen] Zewo ist eine Singlethread-Anwendung. Sie können zusätzliche Leistung erzielen, indem Sie eine Instanz der Anwendung pro verfügbarer CPU ausführen, da sie einem gleichzeitigen Modell und nicht einem Multithread-Modell folgen. Für die Zwecke dieser Studie wurde nur eine Instanz jeder Anwendung ausgeführt.
  • Werkzeugketten. Jedes Framework baut auf einer anderen Entwicklungs-Snapshot-Toolchain von Apple auf. Zum Zeitpunkt der Endprüfung waren / sind sie:
     
    - DEVELOPMENT-SNAPSHOT-2016-08-24-a für Perfect
    - DEVELOPMENT-SNAPSHOT-2016-07–25-a für Vapor & Kitura
    - DEVELOPMENT-SNAPSHOT-2016-05-09-a für Zewo
  • Vapor hat eine spezielle Syntax zum Ausführen von Releases. Wenn Sie einfach die Binärdatei ausführen, erhalten Sie eine zusätzliche Konsolenprotokollierung, die Sie beim Entwickeln und Debuggen unterstützen soll. Das hat etwas Overhead. Um Vapor im Release-Modus auszuführen, müssen Sie hinzufügen
--env = produktion

auf die ausführbare Datei. d.h.

.build / release / App --env = Produktion
  • [Hinzugefügt am 1. September mit aktualisierten Ergebnissen] Wenn Sie mit Zewo arbeiten, können Sie, obwohl Sie in der Toolchain 05-09-a nicht mit swift im Release-Modus arbeiten können, einige Optimierungen für den Release-Modus hinzufügen, indem Sie die folgenden Argumente übergeben:
schneller Build -Xswiftc -O
  • Node.js / Express erstellt keine Debug- / Release-Dateien und unterscheidet auch nicht zwischen diesen
  • Die Verarbeitung statischer Dateien ist in der Standard-Middleware von Vapor enthalten. Wenn Sie keine statischen Dateien verwenden und die Geschwindigkeit optimieren möchten, müssen Sie Folgendes angeben (wie in VaporJSON):
drop.middleware = []

Warum Node.js / Express?

Ich habe beschlossen, eine Variante des Blogs mit dem Express-Framework in Node.js aufzunehmen. Dies ist ein guter Vergleich, da die Syntax der von Server-Side Swift sehr ähnlich ist und weit verbreitet ist. Es hilft, eine gute Ausgangsbasis zu finden, um zu zeigen, wie beeindruckend Swift sein kann.

Entwicklung der Blogs

Irgendwann fing ich an, dies als "Jagd nach dem springenden Ball" zu bezeichnen. Die aktuellen Server-Side-Swift-Frameworks sowie Swift 3 werden derzeit sehr aktiv weiterentwickelt und haben jede Menge Änderungen gegenüber ihren Vorgängerversionen. Dies wurde durch die häufigen Veröffentlichungen aller serverseitigen Swift Frameworks sowie des Swift-Teams von Apple noch verstärkt. Keiner von ihnen war zu diesem Zeitpunkt in seiner Dokumentation besonders vollständig. Daher bin ich den Mitgliedern des Framework-Teams und der Server-Side-Swift-Community im Allgemeinen für ihre Beiträge sehr dankbar. Ich bin auch dankbar für die Hilfe, die mir unzählige Community-Mitglieder und Framework-Teams auf dem Weg gegeben haben. Es war eine Menge Spaß, und ich würde es wieder tun, ohne nachzudenken.

Als Randnotiz, auch wenn in der Lizenz keine Namensnennung erforderlich war, finde ich es schön zu bemerken, dass alle in den Quellen enthaltenen Bilder aus Pixbay stammen und von mir nach dem Zufallsprinzip ausgewählt wurden. Quellen wie diese vereinfachen Demoprojekte erheblich.

Hosting & Umgebung

Um Unterschiede in der Umgebung zu minimieren, habe ich 2012 einen Mac Mini genommen und El Capitan (10.11.6) neu installiert. Danach habe ich Xcode 8 Beta 6 heruntergeladen und installiert und meine Befehlszeilentools auf Xcode 8 eingestellt. Von dort aus habe ich swiftenv installiert, die erforderlichen Snapshots installiert, die Repos geklont und jedes der Blogs sauber erstellt, erneut im Veröffentlichungsmodus, in dem möglich. Ich habe nie mehr als einen Test gleichzeitig ausgeführt, und jeder wurde zwischen den Tests angehalten und neu gestartet. Die Testserverspezifikationen sind:

Für die Entwicklung verwende ich ein 2015 rMBP. Ich habe hier die Build-Time-Tests durchgeführt, da dies meine reale Entwicklungsmaschine ist und dies am sinnvollsten ist. Ich habe wrk verwendet, um die Benchmarks zu erhalten, und dies über eine Thunderbolt-Brücke unter Verwendung eines Thunderbolt-2-Kabels zwischen den Maschinen. Durch die Verwendung einer Thunderbolt-Bridge wird sichergestellt, dass Sie über eine unglaubliche Bandbreite verfügen und nicht die Einschränkungen Ihres Routers, sondern die anstehende Aufgabe als Benchmark betrachten. Es ist auch zuverlässiger, die Blogs auf einem Computer bereitzustellen und eine separate, leistungsstärkere Maschine zum Generieren der Last zu verwenden, um sicherzustellen, dass Sie den Server überlasten können, sodass Sie sicher sein können, dass dies keine Einschränkung darstellt. Dies gibt Ihnen auch eine konsistente Testumgebung, sodass ich sagen kann, dass jedes Blog auf derselben Hardware und unter denselben Bedingungen ausgeführt wurde. Für die Neugierigen sind die Spezifikationen meiner Maschine:

Benchmarking-Hinweise

Für das Benchmarking habe ich mich für einen zehnminütigen Test mit vier Threads mit jeweils 20 Anschlüssen entschieden. Vier Sekunden ist kein Test. Zehn Minuten sind ein angemessener Zeitraum, um genügend Daten zu erhalten, und 20 Verbindungen in vier Threads zu betreiben, ist eine enorme Belastung für die Blogs, aber keine Bruchlast.

Quellcode

Wenn Sie den Quellcode für die Projekte durchsuchen oder eigene Tests durchführen möchten, habe ich den zum Testen verwendeten Code in einem Repository zusammengefasst. Dieses finden Sie unter:

https://github.com/rymcol/Server-Side-Swift-Benchmarking

Detaillierte Ergebnisse

Bauzeit

Ich dachte, es würde Spaß machen, zuerst einen Blick auf die Build-Zeiten zu werfen. Die Build-Zeit kann bei der täglichen Entwicklung eine große Rolle spielen. Obwohl sie wenig mit der Leistung eines Frameworks zu tun hat, dachte ich, es könnte Spaß machen, echte Zahlen zu untersuchen, verglichen mit der Dauer, die sich während des Wartens anfühlte.

Was ist gelaufen?

Für jedes Framework

schneller Build --clean = dist

und dann

Zeit schnell bauen

wurden ausgeführt, gefolgt von einem zweiten Test von

schneller Aufbau - sauber

dann:

Zeit schnell bauen

Dies führt sowohl zu einem vollständigen Build, der das Abrufen von Abhängigkeiten mit SPM umfasst, als auch zu einem regelmäßigen, sauberen Build mit bereits heruntergeladenen Abhängigkeiten.

Wie es gelaufen ist

Dies wurde auf meinem lokalen rMBP 2015 ausgeführt und die Builds wurden alle im Debug-Modus ausgeführt, da dies der normale Prozess bei der Entwicklung von Swift-Software ist.

Build Time-Ergebnisse

Speichernutzung

Das zweite, was ich mir ansah, war der Speicherbedarf jedes Frameworks unter Last.

Was war Run?

1. - Speicherbedarf starten (nur den Prozess angestarrt)
2nd - Peak Memory Usage des Prozesses auf meinem Testserver unter Verwendung von:

wrk -d 1m -t 4 -c 10

3. Wiederholung des zweiten Tests mit:

wrk -d 1m -t 8 -c 100

Wie es lief

Dieser Test wurde auf einem sauberen Mac mini als Testserver ausgeführt. Jedes Framework wurde nach Möglichkeit im Release-Modus erstellt. Es wurde jeweils nur ein Framework über die Befehlszeile ausgeführt und zwischen den Tests neu gestartet. Das einzige andere offene Fenster zum Zeitpunkt des Tests war der Aktivitätsmonitor, mit dem ich die maximale Speichernutzung visualisierte. Beim Ausführen der einzelnen Frameworks habe ich lediglich den höchsten Wert notiert, der im Aktivitätsüberwachungsfenster angezeigt wurde.

Ergebnisse der Speichernutzung

Thread-Nutzung

Das dritte, was ich mir ansah, war die Thread-Nutzung jedes Frameworks unter Last.

Was war Run?

1. - Start-Threads (nur den Prozess angestarrt)
2nd - Peak Thread Usage des Prozesses auf meinem Testserver unter Verwendung von:

wrk -d 1m -t 4 -c 10

Wie es lief

Dieser Test wurde auf einem sauberen Mac mini als Testserver ausgeführt. Jedes Framework wurde nach Möglichkeit im Release-Modus erstellt (mehr dazu im Abschnitt Methodik). Es wurde jeweils nur ein Framework über die Befehlszeile ausgeführt und zwischen den Tests neu gestartet. Das einzige andere offene Fenster zum Zeitpunkt des Tests war der Aktivitätsmonitor, der von mir zur Visualisierung der Thread-Nutzung verwendet wurde. Da jedes Framework ausgeführt wurde, habe ich einfach den höchsten Wert notiert, der im Aktivitätsüberwachungsfenster angezeigt wurde, während der Test ausgeführt wurde.

Ein Hinweis zu diesen Ergebnissen

In dieser Kategorie gibt es kein "Gewinnen". Viele Anwendungen behandeln Threads unterschiedlich, und diese Frameworks sind keine Ausnahme. Zewo ist beispielsweise eine einzelne Thread-Anwendung und verwendet niemals mehr als eine (Bearbeiten: ohne Ihr Zutun, um sie auf jeder CPU in einer gleichzeitigen Konfiguration auszuführen). Perfect hingegen verwendet eine pro verfügbarer CPU. Vapor verwendet eine pro Anschlussmodell. Als solches wurde dieses Diagramm so entworfen, dass die Spitzen in den Gewinden unter Last leicht sichtbar sind, da sie in beiden Diagrammen in derselben Reihenfolge sind.

Thread-Nutzungsergebnisse

Blog-Benchmarks

Der erste Benchmark ist die / blog-Route in jeder Seite, die 5 zufällige Bilder und gefälschte Blog-Posts für jede Anfrage zurückgibt.

Was war Run?

wrk -d 10m -t 4 -c 20 http://169.254.237.101:(PORT)/blog

wurde für jeden blog von meinem rMBP über meine thunderbolt bridge gefahren.

Wie es lief

Wie beim Speichertest wurde jedes Framework nach Möglichkeit im Release-Modus ausgeführt und vor jedem Test angehalten und neu gestartet. Es wurde jeweils nur ein Framework auf dem Server ausgeführt. Alle Aktivitäten wurden während des Tests auf ein Minimum beschränkt, um die Umgebung so ähnlich wie möglich zu halten.

Ergebnisse

Dieses Bild wurde am 1. September 2016 mit einer Korrektur aktualisiertDieses Bild wurde am 1. September 2016 mit einer Korrektur aktualisiert

JSON-Benchmarks

Aufgrund einiger Komplikationen beim Umgang mit statischen Dateien war es fairer, dieselben Tests über eine einfachere Benutzeroberfläche erneut auszuführen. Daher fügte ich Versionen jeder Anwendung hinzu, die einer / json-Route zugeordnet sind, die zehn Zufallszahlen innerhalb von 0 zurückgibt –1000. Sie sind voneinander getrennt, um sicherzustellen, dass die statischen Dateihandler und die Middleware die Ergebnisse nicht beeinträchtigen.

Was war Run?

wrk -d 10m -t 4 -c 20 http://169.254.237.101:(PORT)/json

wurde für jedes JSON-Projekt ausgeführt.

Wie es lief

Wie bei den anderen Tests wurde jedes Framework nach Möglichkeit im Release-Modus ausgeführt und vor jedem Test angehalten und neu gestartet. Es wurde jeweils nur ein Framework auf dem Server ausgeführt. Alle Aktivitäten wurden während des Tests auf ein Minimum beschränkt, um die Umgebung so ähnlich wie möglich zu halten.

Ergebnisse

Dieses Bild wurde am 1. September 2016 mit einer Korrektur aktualisiertDieses Bild wurde am 1. September 2016 mit einer Korrektur aktualisiert

Schlussfolgerungen

Meine Frage wurde mit einem überwältigenden JA beantwortet. Swift ist mehr als in der Lage, die etablierten serverseitigen Frameworks zu übernehmen. Nicht nur das, sondern alle serverseitigen Swift Frameworks zeigten eine unglaublich gute Leistung, und Node.js wurde in jedem Test von mindestens zwei von ihnen geschlagen.

Angesichts der Tatsache, dass Server-Side-Swift unglaublich viel Zeit sparen kann, wenn Sie die Codebasis mit Ihren anderen Swift-Apps, den Funktionssätzen der verschiedenen Server-Side-Swift-Frameworks und den hier erzielten Ergebnissen teilen, ist Server-Side-Swift auf dem neuesten Stand Weg zu einem sehr großen Konkurrenten in der Programmierarena. Ich persönlich programmiere so viel wie möglich in Swift, besonders auf der Serverseite, und ich kann es kaum erwarten, all die unglaublichen Projekte zu sehen, die aus der Community hervorgehen werden!

Machen Sie mit

Wenn Sie sich für Server-Side Swift interessieren, ist jetzt die Zeit gekommen, sich zu engagieren! Es gibt noch viel zu tun an den Frameworks, deren Dokumentation und einigen wirklich coolen Anwendungen als Beispiele, Open oder Closed Source. Sie können mehr über jedes Framework erfahren und sich hier beteiligen:

Perfekt: Website | Github | Schlaff | Gitter
Vapor: Website | Github | Locker
Kitura: Website | Github | Gitter
Zewo: Website | Github | Locker

In Kontakt kommen

Wenn Sie eine Verbindung herstellen möchten, können Sie mich auf Twitter unter @rymcol erreichen.

Angaben, die ich für notwendig hielt: Am 1. September 2016 wurden Änderungen hinzugefügt, um einige Daten zu korrigieren, nachdem Release-Optimierungen für Zewo mit einer Methode vorgenommen wurden, die eine Alternative zu "swift build -c release" darstellt. PerfectlySoft Inc. hat zugestimmt, diese Studie für mich zu finanzieren, um die Leistungsfähigkeit von Swift zu fördern. Ich bin auch in den Github-Teams von Perfect & Vapor, obwohl ich weder Angestellter von Perfect & Vapor bin noch deren Meinung widerspiegelt. Ich habe mein Bestes gegeben, um unparteiisch zu bleiben, da ich mich auf allen vier Plattformen weiterentwickle, und ich war wirklich neugierig auf die Ergebnisse. Der gesamte Code ist für diese Studie öffentlich verfügbar. Probieren Sie ihn aus oder wiederholen Sie einige der Tests und überprüfen Sie ihn selbst!