Being simple to use and rather complete (support ssl, chaml: an equivalent of php, but in OCaml and compiled, status and statistics, authentication, cookies, …).
Being fast: our latencies and number of requests per seconds are very good, thanks to using linux epoll, eventfd, OCaml’s effects and domains, … The first page of the documentation shows some graphics, but here is a small
comparison of latencies for a small 1kb file:
These were obtained with vegeta at 1000 requests/s. Simple_httpd offers much more stable latencies under charge than nginx or apache.
If you want your own measurments, you need to setup nginx/php on ports 7080 and 7443, an apache/php on port 80 and 443. Then, you can run [./bench.sh] from the [tests] folder of the source tree. I would be happy to have measurments for a big server with more than 20 cores.
Currently only linux is supported.
Help, comments, bug reports, … would be greatly appreciated, as this is alpha release, it is time for you to propose change in the design of the library.
My website https://raffalli.eu and therefore simple_httpd documentation are powered by simple_httpd (do we name this bootstrap ?
For the number of requests per seconds with wrk, we are at 139675 for tiny+moonpool and 167819 for simple_httpd.
Tiny is a bit better for the worst case. Simple_httpd is better in average and for all quantile. I think I am missing a preemptive scheduler that I could get if there where some way to perform an effect periodically in OCaml.
Moonpool is a very good idea actually to get a preemptive scheduler with domains !
I should also say that simple_httpd routing is now looking at the Host field, address and port, not only the path and method, It has logging by type and level (not just on/off) and maybe a few other extra feature that do not come to my mind now but have an impact for an hello request.
On the side of memory, VSZ=1032908Ko RSS=27492Ko for simple_https and VSZ=999288Ko RSS=401804Ko for tiny All this is very reasonnable but threads use much more resident memory.
Yes simple_httpd use edge trigerred epoll and eventfd. This means we do not have to change the configuration of the socket in the epoll list (it is set up when we accept the connection) and mutex are dealt with using epoll too thanks to eventfd. So all scheduling is done by epoll_wait (inclusing our support for sleep, using the timeout).
That’s pretty cool, thank you. I think it’s good that both options exist
Tiny_httpd was never designed to be super high performance anyway, but
it’s nice that with a thread pool it’s at least decent. Memory usage is
not as good indeed (I also think OCaml 5.0 has some GC issues in
general, it’ll improve).
I think two things are missing from your results:
what machine you used
the CPU usage in both cases (I suspect it’s higher for tiny_httpd)
In fact as Tiny_hhtpd is simple, it is very efficient (even if mono thread, unless you use some extra machinery like moonpool). This is why I was using it and started working on it!
And now that simple_httpd is in production on my server I feel relived not to have php running on my server!
My machine for the test is a poor ACER Ryzen 5 3500U laptop with 6 cores, client and server are on the same machine so I don’t measure the network performance and CPU usage is at the maximum with wrk and mixup clients and servers, so I am not sure it is very significative.
We are both using OCaml 5.0, so the memory difference is not from there, but rather from the number of threads. You default configuration has 20, mine has 6 (1 to accept, 5 to serve) on a 6 core machines. I did play a bit with the thread parameter and it is not changing things significatively, I kept the default for both software as it seems to be good. I copied your t1.ml test to have the same number of routes in both case.
Hi Christophe, interesting that you mention this now. For various reasons OCaml’s effect implementation does not support this (yet?). The problem is more technical than conceptual though.
I tried various ways, in particular with signals, but never got something working, especially that I want a period around 1ms. This is the only thing missing to have lightweight preemptive threads from effect. If you know a way to do it, even using some C code (I already have some for epoll and eventfd), I would try it.
But I should say while I would probably get a better worst case for simple_httpd (but I am already much better for the worst case that nginx of apache) I would probably loose for the mean or the 50% quantile. But it would be worth trying has it could be a good option to resist DDOS attack exploiting the cooperative aspect of the scheduler.
What have you tried to do exactly? I assumed that you tried to perform an effect from a signal handler, in which case you had noticed that it simply does not work (exception Effect.Unhandled).
(The deep technical reason is that OCaml’s effect handlers cannot capture C stack frames, only OCaml stack frames.)
Being able to perform an effect from any kind of asynchronous callback would require a major effort in changing the way those are currently implemented in the OCaml runtime, so, no, not any time soon. But this would be possible modulo some caveats and applications of such asynchronous effects have been investigated here: [2307.13795] Higher-Order Asynchronous Effects. It is nice to know that there is concrete interest in this idea.
The timing aspect is a separate concern. Running callbacks predictably after a certain amount of allocations seems to work well (see my work on memprof-limits) and could be used to interrupt threads that run for too long in order to resist DOS attacks.