Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Being multi-process is the "waste". Each process has its own heap, its own copy of the AST and bytecode, etc. Even with preforking and copy-on-write, the overhead introduced by processes is still significantly bigger than running many threads within 1 process.

Of course, this doesn't have to be a problem for everyone. Whether threads are advantageous depends a lot on the workload.



I think a little extra memory in exchange for a whole lot of safety and code simplicity is a fair tradeoff. If you don't have enough memory to spawn a few hundred subprocesses I'd suggest you tackle that first.


The first step of scaling an app is not to make sure you have a server with at least 43GB of RAM.

If we say a typical Rails process is 150MB, then consider several hundred of them...

150MB * 300 = 43GB

So, while your point is valid, in that there is value in safety and code simplicity, we're not talking just "a little" memory. Consider your use case, that's all.


Your point is about economics, not scaling per se. A program that requires lots of memory may be wasteful, but it could still very well scale (in terms of its ability to service requests under increasing load).

Nevertheless, the memory requirements of a multiprocess server are not a linear function of the footprint of the master process. fork(2) on modern Unices (including Linux, since, like forever) has copy-on-write semantics for a child's memory usage. So when you fork a new process, the memory overhead is relatively small (only the page tables are copied, not the heap, and never the program text or shared libraries).

Only when a child process modifies or allocates a new heap page or exec()s another program will it incur additional memory overhead. So the additional memory required by a child Unicorn process (assuming the parent preloads the application server code) won't usually be anywhere near 150MB (and if there's a memory leak it's trivial to free it up by killing the child after it services a request).


Thank you. "Threads are always better than processes, lololol" really gets under my skin. There are pros and cons to each approach.


What simplicity/safety does using processes buy you relative to a good threaded runtime (read: JVM/Haskell/Go)?


Building a good threaded runtime is difficult in the first instance, and even then, shared memory requires special handling (mutexes).

There has been plenty of ink spilled on the subject, but here's a good read: http://msdn.microsoft.com/en-us/magazine/cc163744.aspx


Building a JVM is harder than building an RVM. I was asking from the perspective of user code. Shared memory requires mutexes in both threaded and forked code - this is not an advantage of multiple processes. In fact, multiprocess shared memory is generally harder to work with than threaded shared memory.


We're talking about Ruby here, though. MRI threading has concurrency limitations, so processes are better here IMO.

I tend to think out of the box for multiprocess shared memory - Kyoto Cabinet and Redis can be helpful here, to name a few. Even SysV shared memory is pretty easy to use.


Can you quantify how much memory you're saving per worker?


It varies from app to app and what the underlying hardware is. On "real" hardware, meaning not virtual CPUs and virtual threads, I've seen one Puma worker replace 24 Unicorns.

Most cloud (god I hate that word) providers will be giving you vCPUs, in those cases usually one Puma will replace 4-8 Unicorn workers.

Your mileage may vary based on whether your app is more IO or CPU bound. Most Rails apps are incredibly IO bound, though.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: