5 ms·
CGI as in a command line program lying on the disk, executed on every request and the output sent to browser is dog slow. Convert this one to fcgi, and it will
by habibur 3y ago
CGI as in a command line program lying on the disk, executed on every request and the output sent to browser is dog slow.
Convert this one to fcgi, and it will get as fast as any other solution. In fcgi mode it runs as a service listening to a socket and reads and sends response over it while never terminating.
Even php running on fcgi mode is far faster than the old styled mod_php.
- toast0 3y agoIf you have any reasonable OS and enough ram, the sectors your program actually uses will be in disk cache. Sure, there's maybe some syscalls and vm faults to get it mapped into the process, but it's not dog slow. If you don't have enough ram for it to be in the disk cache, chances are your fcgi daemon is going to get swapped out too. Now if your program insists on taking forever to start up, that's something that a long running daemon can help. I haven't benchmarked, but I really don't see why php in fcgi mode would be much faster than mod_php. I've run both modes and they're both very fast. But then someone uses one of the popular frameworks and hello world takes 20-40ms, so it barely matters how you run php.
- chrismorgan 3y agoEven with warm disk caches, interpreted languages tend to have pretty slow startup times, because they spend a lot of CPU time doing the same stuff over and over again. On my machine, the fastest of a dozen runs of just the interpreter is 66ms for Node.js (`time node -e ''`), and 14ms for CPython (`time python -c pass`). And my measurements a few years ago had overhead of about 1ms per module/file in Node.js, so if your source is spread over 100 files (which is honestly unrealistically low if you use any popular libraries and don’t bundle anything), your typical startup floor is now 200ms of CPU time, apart from disk time, on a good day. And some of those modules probably incur other costs, too. I have no figures on PHP, though if its execution model hasn’t changed from a decade ago most of this probably won’t affect it much because it effectively didn’t even persist modules. But I don’t know.
- toast0 3y agoAs always, don't use languages that suck. On my system in hosting (FreeBSD 13.2-RELEASE, 2x Xeon L5640), time perl -e '' with perl5-5.34.3_3 reports 3ms. On the same system time php -r '' takes 16 ms with php82-8.2.11 and a handful of libraries installed. By my own criteria, php as a cgi sucks; but I did say if your program takes too long to startup, run it as a daemon, which I think includes linking it in like mod_php or mod_perl. On the topic of mod_php vs php_fcgi; with apache mod_php, an empty php file takes about 1.2 ms to serve, as measured by curl from when curl sends the GET request to when the content ends. I don't have a php fcgi server to try, but my recollection was something similar for minimum php output from lighttpd with php_fcgi on newer hardware.
- whartung 3y agoIf you run an actual binary, the OS will not only cache the code pages, it will share them across processes. In the perl case, the actual interpreter is loaded, cached, and shared across the CGI invocations, while the actual script code will be cached from the disk, but each invocation will (likely) get their own copy of the code (i.e. they will duplicate it from cache when they read it in). Finally, of course, the runtime needs to start up again, compile/interpret the code, and, finally, execute it. A binary will have the fastest startup. I'm sure there may be things that could be done. I don't know if perl mmap'd the .pl file as a readonly buffer, if each invocation would share that file (probably), and thus prevent that initial duplication. Don't know if any of the interpreters do this. Odds are this gain is lost in the whole startup of the interpreter anyways. Also, using a binary, by a similar mechanic, lowers overall memory consumption. Have 100 connections all running the binary, they all share the code pages, and thus only "pay" for their individual data use. Share 100 connections with an interpreter, and they each pay their own cost for the entire program file plus any runtime data. Again, not so much a problem today with our ample memory resources, and mmap the source may remedy this, but this actually was an issue back in the day.
- chrismorgan 3y agoIt may not be about any language implementation inferiority, but rather a question of what they’ve optimised for. Node.js is firmly designed for daemon operation, where having a high cost of instantiating a module, something that you only do at startup, is fine, if it means you later get better throughput because you’ve done JIT compilation or whatever. (It may also just be that Node.js is unnecessarily slow at these things because no one’s cared enough. But module loading being slow is reasonably well-known in the ecosystem at large so I doubt it’s particularly that.) PHP might have lower engine startup costs because of its history of CGI usage, and lower code loading costs because of its start-from-scratch-for-each-request model—but both are probably at a later runtime cost. Perl might have lower engine startup costs because it’s tended to be in a similar basket to PHP, plus more general scripting use, where you just want to get through things quickly: faster for one-offs, slower for repeated things. And my understanding of Perl is that it’s somewhat closer to just being an interpreter (which is excellent for latency but terrible for throughput) than are the other languages we’re discussing.
- linuxftw 3y agoYou still have to build the process image. I suspect for anything simple enough to run as a CGI app, the instruction count to build the process image is as long as the instruction count of the CGI app itself.
- toast0 3y agoIt really depends on what you're doing. Resizing images is a pretty common CGI application, and that's going to spend a lot more time on compression than it does on process setup (one hopes). Process separation is a good idea for image processing, as codec processing has high risk.
- PaulHoule 3y agoIt is stored in cache so the system hardly ever goes to the disk. When you run some command on Linux like "grep", most of the time it is running out of cache. A real weakness cgi has is that if your application involves a lot of in-memory configuration that has a terrible impact on startup time even if it has an excellent effect on execution time.
- jjtheblunt 3y ago> It is stored in cache so the system hardly ever goes to the cache. Typo?
- PaulHoule 3y agoThanks for the catch. I fixed it.
- synergy20 3y agohttps://fastcgi-archives.github.io/ https://fastcgi-archives.github.io/ fastcgi c/c++ server development was essentially stopped many years ago, would love to see a c version that I can use for embedded boards, though cgi does the job 90% of the time just fine.
- joosters 3y agoFastCGI was feature complete decades ago, there's little development needed for shiny new features.
- hedora 3y agoApache’s mod_fastcgi’s last commit was 2 weeks ago: https://svn.apache.org/viewvc/httpd/httpd/trunk/ https://svn.apache.org/viewvc/httpd/httpd/trunk/ It’s a fork of what you linked (and was more popular afaik back when fastcgi was state of the art, and apache was the undisputed champion of web servers). These days, nginx has more market share than apache, and its fastcgi module is one of the more recently updated ones in its source tree (5 months vs multiple years): https://github.com/nginx/nginx/tree/master/src/http/modules https://github.com/nginx/nginx/tree/master/src/http/modules If I was going to build an embedded web server, I’d start with nostd rust, probably with though axum + tokio, since thats already memory safe-ish. If I needed fastcgi for some reason (dynamically loadable endpoints, or os-level isolation), there are at least four implementations of fastcgi for it. No idea if any are decent though.
- rascul 3y ago> These days, nginx has more market share than apache Maybe not by much though. https://www.netcraft.com/blog/december-2023-web-server-survey/ https://www.netcraft.com/blog/december-2023-web-server-surve...
- j45 3y agoSeems pretty well established, and started a long time ago, could it be feature complete and not need as many commits?
- thesnide 3y ago
- debugnik 3y ago> In fcgi mode it runs as a service listening to a socket and reads and sends response over it while never terminating. What's the point of using this over serving HTTP directly? The whole point of CGI was providing an easy to implement interface to proper HTTP servers, but FastCGI is such as step up in complexity compared to CGI that you're going to need a library anyway. Maybe FastCGI made sense back when companies tried to convince us we needed their heavy-duty software to handle any HTTP traffic at all, but now HTTP libraries and reverse proxies are dime a dozen.
- wharvle 3y agoHTTP daemons remain pretty nice, and if you use them rather than serving HTTP from your language (... and how many languages are you using? Each HTTP-serving lib is another liability) you get a lot of cool stuff "for free" ("just" config against a battle-hardened system, not code)
- scottlamb 3y agoThe two aren't mutually exclusive. * I agree that you often will want a server such as Caddy/nginx/Apache httpd to be the Internet-facing http server. They give you a lot of nice things like DoS resistance, logging, robust TLS implementations, and dispatch to all the various applications you might be running. * But you need some protocol between that proxy and your application. Like debugnik, I'd prefer to just use HTTP as that protocol rather than something else like FastCGI. I already have to be familiar with HTTP/1.1 in the sense that I both actually know the wire format by heart and know of a huge variety of tools that can directly speak it. So I'd rather just have my application speak HTTP over a Unix or IP socket that my frontend webserver proxies to. "Each HTTP-serving lib is another liability" is at least as true for "each FastCGI-serving lib" given that the FastCGI libs will be obscure (less well-tested and maintained) and (due to poorer familiarity/tooling) harder to debug.
- wharvle 3y ago> So I'd rather just have my application speak HTTP over a Unix or IP socket that my frontend webserver proxies to. "Each HTTP-serving lib is another liability" is at least as true for "each FastCGI-serving lib" given that the FastCGI libs will be obscure (less well-tested and maintained) and (due to poorer familiarity/tooling) harder to debug. True-ish, but complicated a bit because (as far as I can tell) the FastCGI spec is tiny compared to HTTP... even just HTTP 1.1, let alone 2 and (yikes) 3.
- jeffbee 3y agoA few years ago I developed a websockets-based RPC protocol for connecting Go application workers with a higher-performance C++ networking supervisor, to workaround scalability issues in grpc-go. When I got done with it I realized I had reinvented fcgi. It's really a quite OK protocol that is so simple it can't become obsolete.
- sitharus 3y agoHow slow is “dog slow”? And for some purposes “dog slow” is acceptable. I have an internal app that runs as a CGI but is written in a compiled language. It averages 100ms response times, mostly due to database queries. I wouldn’t write a major app using cgi but for small sites and internal services it’s fine
- vidarh 3y agoNo, it's not - at least not inherently -, because unless you're extremely memory constrained your program will be cached in memory. If it's then also prepared properly, e.g statically linked position independent code, you won't even have relocation overhead and the only thing you "pay" is the process creation overhead, which hasn't been a major issue on at least Linux for a couple of decades at least. Whether the difference will matter really depends on your specific runtime. With an interpreter, startup can easily bite you hard, sure. PHP is a bad example for that reason - the moment you need a bunch of extra syscalls on startup, the context switch latency alone will kill performance vs. what you can achieve.