URL: https://linuxfr.org/users/n_e/journaux/le-taptempo-du-web-mais-plus-rapide Title: Le TapTempo du web, mais plus rapide Authors: n_e Date: 2022年06月19日T16:48:21+02:00 Tags: taptempo Score: 15 spacefox a lancé dans ce [journal](https://linuxfr.org/users/spacefox/journaux/java-presque-9-000-requetes-par-seconde-avec-8-mo-de-ram) un "concours" d'implémentation d'un programme dans divers langages. Le but est d'écrire un serveur HTTP qui retourne une redirection vers une page aléatoire : `https://avatar.spacefox.fr/Renard-$random.png`. Pas mal d'implémentations ont déjà été proposées, je vais essayer à mon tour en essayer 4 : - Deno, pour découvrir - Node.js, pour avoir une référence par rapport à Deno - nginx (ngx_http_lua_module) et Varnish, car ce sont deux outils qu'on utiliserait dans la vraie vie pour implémenter le programme. # Outil de benchmark Pour commencer, au lieu d'utiliser `ab`, je vais utiliser `wrk`. En effet, je n'arrive pas à obtenir plus de 19 000 req/s avec `ab` sur ma machine, alors que, avec wrk (spoiler alert), on mesure jusqu'à 250 000 req/s. # Méthodologie J'ai fait les tests sur ma machine, un desktop sous ArchLinux, avec un core i5-8400 (6 cœurs), avec `wrk -d10s -t4 -c 100`. J'ai essayé d'obtenir des résultats représentatifs, mais sans y passer trop de temps et sans chercher une rigueur excessive. Notamment, `wrk` et le programme sont exécutés sur la même machine, ce qui peut fausser les résultats. Également, je n'ai pas fait spécialement attention aux versions et aux optimisations, si ce n'est d'avoir des versions récentes et de ne pas avoir des désoptimisations flagrantes (compilation en mode debug, logs sur stdout, parallélisme insuffisant...). # Résultats ## Java Pour avoir une baseline, l'original de spacefox : ``` Requests/sec: 20 267.84 Transfer/sec: 2.75MB ``` Pas de surprise, on est sur le même ordre de grandeur que spacefox (17000 avec ab sur sa machine perso). ## rust + hyper Maintenant la version rust/hyper de abriotde (qui est parallélisée) : ``` Requests/sec: 232 214.97 Transfer/sec: 31.34MB ``` Sans surprise, c'est beaucoup plus rapide. Rust et hyper sont très rapides, et devraient être la borne haute de ce qu'on peut atteindre sans optimisations complexes. ## Deno ``` Requests/sec: 42 960.47 Transfer/sec: 6.23MB ``` Là c'est une surprise pour moi. En général Java est un peu plus rapide que Node/Deno, mais sur ce benchmark Deno est 2x plus rapide. Après je connais mal Java, et c'est possible que j'ai raté des flags ad hoc. A noter que le JavaScript est exécuté sur un thread, comme pour la version Java. ## node.js ``` Requests/sec: 33 400.03 Transfer/sec: 6.18MB ``` Un peu plus lent que deno. En effet, deno est censé être le successeur de node.js, et il a des optimisations qui n'existent pas sur node.js. ## Varnish Varnish est un caching reverse proxy, qui peut être programmé dans un langage spécifique, le VCL, qui, à l'exécution, est transformé en C puis compilé. Il est donc rapide: ``` Requests/sec: 159 889.33 Transfer/sec: 28.16MB ``` ## nginx + ngx_http_lua_module ``` Requests/sec: 243841.01 Transfer/sec: 83.70MB ``` nginx est réputé pour être rapide, mais, avec le module lua, je m'attendais à ce qu'il soit plus lent que l'implémentation rust. En fait il est systématiquement plus rapide, et ce malgré le double de données transféré (il répond une petite page html, alors que les autres implémentations renvoient un body vide). # Conclusion nginx est à la fois le plus rapide et le plus simple à configurer (surtout si on l'utilise déjà pour d'autres services). Coté langages de programmation, il y a de grosses différences de performances, mais tous sont largement assez rapides sauf cas très particuliers, surtout si, pour ceux qui ne sont pas parallèles, on exécute plusieurs instances derrière un reverse proxy. # Code source ## Deno ```js import { serve } from "https://deno.land/std@0.144.0/http/server.ts"; serve((request) => { const url = new URL(request.url); if (url.pathname !== "/") return new Response("nope", { status: 404 }); return new Response(null, { status: 302, headers: { location: `https://avatar.spacefox.fr/Renard-${Math.round( Math.random() * 10 )}.png`, }, }); }); ``` ## Node.js ``` const http = require("node:http"); const server = http.createServer((req, res) => { if (req.path !== "/") res.writeHead(404); res.writeHead(302, { location: `https://avatar.spacefox.fr/Renard-${Math.round( Math.random() * 10 )}.png`, }); res.end(); }); server.listen(8000); ``` ## Varnish ``` vcl 4.0; import std; backend default none; sub vcl_recv { if (req.url != "/") { return(synth(404, "Not Found")); } return(synth(302, "")); } sub vcl_synth { if (resp.status == 302) { set resp.http.location = "https://avatar.spacefox.fr/Renard-" + std.real2integer(std.random(0,10),0) + ".png"; } return(deliver); } ``` ## nginx ``` events { worker_connections 2048; } worker_processes 6; http { access_log /dev/null; server { listen 8080; location / { set_by_lua_block $random { return math.random(1, 10) } return 302 https://avatar.spacefox.fr/Renard-$random.png; } } } ```