3 ms·
I did a simple benchmark to test how fast forking is: https://gist.github.com/hadrianw/ad07a76f338f09565703ae6ea35993c8 https://gist.github.com/hadrianw/ad07a76
by hawski 4y ago
I did a simple benchmark to test how fast forking is: https://gist.github.com/hadrianw/ad07a76f338f09565703ae6ea35993c8 https://gist.github.com/hadrianw/ad07a76f338f09565703ae6ea35...
It tested in a loop fork/exec and vfork/exec for a dynamic binary and for a static binary with musl.
On an Intel Core i5 6600k:
regular:
fork 13179 ms / 100000 = 131 µs, 7587 per sec
vfork 14423 ms / 100000 = 144 µs, 6933 per sec
static:
fork 3173 ms / 100000 = 31 µs, 31515 per sec
vfork 3833 ms / 100000 = 38 µs, 26089 per sec
- thehappypm 4y agoWow, I read this as ms at first, microseconds is really phenomenal
- _flux 4y agoYeah, I don't think the speed performance of FCGI vs CGI is really about spawning in 2023: the benefit lies in the fact that the app can keep state and resources between requests. In particular, it can maintain a database connection all the time (with a retry logic for the case when it gets disconnected). However, perhaps 15+ years ago I converted an application to an Apache module from a cgi-bin binary and the performance benefit was immense in an embedded Linux device. We certainly were not talking even about tens requests per second in the CGI version.
- 10000truths 4y ago> the benefit lies in the fact that the app can keep state and resources between requests. You can share state between processes using temporary files/shared memory. Like CGI itself, it doesn’t scale as well, but it’s not even a blip on resource usage unless you’re dealing with thousands of requests per second. > In particular, it can maintain a database connection all the time (with a retry logic for the case when it gets disconnected). You can run a daemon that performs the connection pooling for you, and have each request connect to the daemon instead.
- Kwpolska 4y agoTemporary files/shared memory won’t solve the cost of starting up a Python interpreter or a JVM or whatever and importing all your code with its dependencies. As for the connection pooling daemon, you still need to connect to it. It might be faster than talking to the database directly on a different server, but no new connection still beats new local connection.
- 10000truths 4y ago> Temporary files/shared memory won’t solve the cost of starting up a Python interpreter or a JVM or whatever and importing all your code with its dependencies. This is true, CGI is sensitive to startup latency. It can be addressed with pre-forking, but doing so bears the consequence of increased memory usage at idle workloads. > As for the connection pooling daemon, you still need to connect to it. It might be faster than talking to the database directly on a different server, but no new connection still beats new local connection. It’s a nothingburger. A connection pool’s bottleneck is in the TCP connection to the database. A UNIX socket has orders of magnitude less latency (a couple μs) and supports orders of magnitude more throughput (a few GBs/sec). Compared to spawning new processes running interpreters, it’s noise.
- naniwaduni 4y agoSome context: in 2017, I found by informal testing that I could run (just on a dev laptop) about about 180 lua no-op programs per second, 140 perl5, 35 py2, 30 py3, 15 node, 12 ruby; /usr/bin/true, as a benchmark, a bit over 500 Hz. Doesn't necessarily extrapolate back another 20 years, but perhaps sheds some light on how "slow" CGI would've been.
- 10000truths 4y agoI would also try benchmarking the new io_uring_spawn interface, which is said to be significantly faster than the traditional fork+exec way of spawning processes. https://lwn.net/Articles/908268/ https://lwn.net/Articles/908268/