Introduzione

Quando si tratta di confrontare le prestazioni del puro instradamento di web server "vuoti", il riferimento standard è web-frameworks.

Ci siamo chiesti come variassero questi risultati con una comunicazione REST HTTP elementare con un minimo di calcolo a carico del server, non una semplice richiesta con risposta vuota.

Testa o croce

Abbiamo quindi ipotizzato di realizzare un endpoint elementarare del tipo

Diagramma-non-titolato

  • il client invia via POST la propria scommessa (valore booleano di bet, pari a testa o croce)
  • il server estrae un booleano casuale e restituisce { win: true } nel caso coincida con la scommessa ricevuta, false altrimenti

aa_run

I framework scelti

I linguaggi di programmazione sono moltissimi e, per filtrare fortemente, abbiamo deciso di utilizzare un criterio per limitarsi ai linguaggi molto maturi e diffusi: l'intersezione tra quelli

  • supportati dall'SDK di AWS (C++, Go, Java, JavaScript/Node.js, .NET, PHP, Python, Ruby)
  • che noi frequentiamo tra i suddetti (Go, JavaScript/Node.js, PHP, Python, Ruby)

Per ciascun linguaggio, abbiamo scelto un rappresentante, non necessariamente il più veloce perché ci interessava una variazione di prestazioni e non un valore assoluto e quindi spesso i criteri son stati anche di semplicità, documentazione e diffusione, oltre alla conoscenza pregressa.

  • PHP => Mark è il primo dei quasi 200 framework: basato su workerman, usa un server autonomo (non ha bisogno di NGINX o Apache) e "forka" diversi processi;
  • Go => Gin (in web-frameworks, il framework Go più veloce è Fiber);
  • JavaScript => Fastify (escludendo quelli nativi per WebSocket, il primo è 0http);
  • Python => FastAPI (il primo dell'elenco era Falcon);
  • Ruby => Rack, essendo la base sulla quale, in Ruby, sono basati molto famosi framewor, mentre il più veloce, pur basato su Rack, è Agoo,

Risultati

Abbiamo provato su AWS EC2 con una istanza c5a.xlarge, eseguendo chiamate locali con wrk, lo stesso client HTTP di benchmarking usato da web-frameworks, lanciando

wrk -t12 -c400 -d120s -s post.lua http://0.0.0.0:3000/coin

dove lo script Lua è

wrk.method = "POST"
wrk.body = '{"bet": true}'
wrk.headers["Content-Type"] = "application/json"

Ecco i risultati in richieste al secondo servite nel caso "vuoto" di web-frameworks e nel caso della moneta. Abbiamo poi normalizzato le prestazioni con uno Score rispetto a Rack.

r/s void r/s coin Score void Score coin
Rack (Ruby) 19318 8869 1,00 1,00
FastAPI (Python) 26078 2997 1,35 0,34
Fastify (Node.js) 69627 20161 3,60 2,27
Gin (Go) 99998 49093 5,18 5,54
Mark (PHP) 167687 141205 8,68 15,92

La tabella può essere resa più evidente nel grafico seguente (diagramma rosso per la moneta, blu per il caso vuoto).

Schermata-2020-10-14-alle-09.40.49

Conclusioni

Rispetto al rappresentante di Ruby, i rappresentanti di Python e Node.js sembrano peggiorare le proprie prestazioni: Go e quel particolare PHP si comportano molto bene a fronte di una maggiore complessità da gestire, pur essendo comunque un esercizio e non un vero applicativo. È inoltre interessante notare come, pur essendo Mark primo, dei 10 migliori framework ben 6 siano in Go.

Il codice del client e dei server è disponibile su GitHub nel repository rest-coin-benchmark.