I've been investigating the performance of Plumber and the underlying httpuv
package, and I noticed a curious pattern. I was wondering if anyone could shed some light on why this might be happening.
I have a toy Plumber server, which looks like this:
library(plumber)
srv <- plumber$new()
srv$handle("GET", "/", function(req, res) {
list(hello = "world")
})
srv$run(port = 8080)
If I benchmark the response time using wrk
(which is a widely-used tool), I get estimates along the lines of ~2ms on this particular machine:
$ Rscript plumber-bench.R &
$ wrk -c 1 -d 10 -t 1 --latency http://localhost:8080
Running 10s test @ http://localhost:8081
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.71ms 1.81ms 46.63ms 99.38%
Req/Sec 593.63 47.26 656.00 77.00%
Latency Distribution
50% 1.54ms
75% 1.67ms
90% 1.83ms
99% 3.10ms
5910 requests in 10.00s, 859.95KB read
Requests/sec: 590.80
Transfer/sec: 85.97KB
In contrast, I also have a toy httpuv
server which looks like this:
library(methods)
library(httpuv)
httpuv::runServer(
"0.0.0.0", 8080,
list(call = function(req) {
list(
status = 200L,
headers = list(
'Content-Type' = 'text/html'
),
body = "{\"hello\":\"world\"}"
)
})
)
If I try the same kind of benchmark, I get something bizarrely different: httpuv
seems to have a minimum latency of about 40ms:
$ Rscript httpuv-bench.R &
$ wrk -c 1 -d 10 -t 1 --latency http://localhost:8080
Running 10s test @ http://localhost:8080
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 43.83ms 2.88ms 48.10ms 98.68%
Req/Sec 22.80 4.51 30.00 72.00%
Latency Distribution
50% 44.00ms
75% 44.01ms
90% 44.04ms
99% 44.09ms
228 requests in 10.00s, 18.10KB read
Requests/sec: 22.80
Transfer/sec: 1.81KB
This seems very odd to me. From my reading of the source code, plumber
is essentially calling runServer()
in exactly the same way as this toy program, but it manages to get much better performance. Does anyone have a clue as to how this is accomplished? Or is my testing just producing some kind of artifact in the case of httpuv
?
For reference: I'm using the latest httpuv
(1.4.5) and the latest plumber
(0.4.6).