5 ms·
Has anyone an example for wanting to fork? Most use cases seem covered by either threads or fork+exec process spawning. The example given by the author: ma
by hyperman1 8y ago
Has anyone an example for wanting to fork? Most use cases seem covered by either threads or fork+exec process spawning.
The example given by the author:
make it practical to introduce concurrency after development, rather than designing it in from the start
seems risky to me: Even if you're safe from other threads, there is a lot of other unknown state like fd's still inherited. And concurrency can be introduced after development, e.g. by using message passing. So going all the way threaded or re-execing yourself seem both safer to me with no real downsides.
- heavenlyblue 8y agoI’ve found fork useful for Python multiprocessing. To reduce the overall memory usage. But this is an ancient solution to an ancient problem in the first place.
- haimez 8y agoSeems like the GIL is still a very modern problem with ancient solutions not yet realized by python runtimes. What you want is threads, but you can’t use them. Bummer. Other language runtimes don’t have that problem.
- tyingq 8y agoYou could also fork out a pool at start time, before creating any threads.
- int0x80 8y agoThis is the well known and only not broken solution. Im glad ive read it finally
- sjmulder 8y agoI'm open to arguments, but right now I feel that threads are (for many uses cases) a poor man's fork. Like threading, forking gives you concurrency but unlike threading it also gives you isolation. There are plenty of IPC mechanisms available to communicate things back and forth when you need to.
- Const-me 8y agoRight, and forks are poor man's processes. All of them have pros and cons but I think forks are only useful for very exotic use cases. > unlike threading it also gives you isolation. If you want isolation why not launch processes? With forks, that copy-on-write thing can increase total RAM usage by a huge factor, if the parent process writes to RAM a lot. > There are plenty of IPC mechanisms available to communicate things back and forth when you need to. For communicating gigabytes of data back and forth you want shared memory anyway. And even for small messages, with threads IPC is faster (1) they are in the same address space, TLB stays current, context switches are faster (2) In some cases, shared memory enables more efficient user-mode IPC methods like busy waiting and lock-free stuff.
- zzzcpan 8y ago> For communicating gigabytes of data back and forth you want shared memory anyway. Not really. You are better off communicating what to do with the data, rather than sharing the data.
- adwn 8y ago> You are better off communicating what to do with the data, rather than sharing the data. Not if you actually want to distribute the work over several cores. Example: Your data is partitioned into 4 segments A, B, C, and D. In step 1, node X processes A&B and node Y processes C&D; in step 2, node X processes A&D and node Y processes C&B. If A, B, C, and D are large, this is a prime example where shared memory trumps message passing in terms of performance. If you think that's a contrived case: dense matrix multiplication behaves like this.
- pcwalton 8y agoOnly if you don't have a data-parallel workload. Message passing is often a nonstarter for data-parallel workloads. Another way to look at it is that the hardware has a very efficient word-level message-passing interface built-in, via shared memory. Why not take advantage of it?
- nemanjaboric 8y agoOne of the available garbage collector implementations in D (version 1) (and there's a work in progress to port it to D2) is a concurrent garbage collector which performs a mark phase in forked process going over the address space. Also I've seen a project which persists large amounts of data to disk by forking and using a consistent snapshot of the data structures (which the new process will not mutate, so there's no need for locking).
- detaro 8y agoRedis background saving works like that too. https://redis.io/topics/faq#background-saving-fails-with-a-fork-error-under-linux-even-if-i-have-a-lot-of-free-ram https://redis.io/topics/faq#background-saving-fails-with-a-f...
- scottlamb 8y ago> One of the available garbage collector implementations in D (version 1) (and there's a work in progress to port it to D2) is a concurrent garbage collector which performs a mark phase in forked process going over the address space. I'm pretty skeptical of that approach. If there's heavy churn, the copy-on-write behavior means that the mark phase _increases_ memory use, when presumably you're doing GC because you need to _decrease_ memory use. And I assume there's some cost to these VM manipulations in general. Is this implementation competitive with other D garbage collectors in terms of memory usage, CPU time, latency, etc? are D's garbage collectors in general competitive with those of Go and Java?
- Filligree 8y ago> Also I've seen a project which persists large amounts of data to disk by forking and using a consistent snapshot of the data structures (which the new process will not mutate, so there's no need for locking). It isn't an uncommon strategy. For example, the Factorio dedicated server can (optionally) do that if you're running it on non-Windows. The performance is great, but it's marked as experimental, probably because of issues like this article describes.
- blattimwind 8y agoForking from a setup environment n times is much faster than setting said environment up n times (=fork+exec)
- anticensor 8y agoIf you spawn, you can set up different process descriptors. Not all properties need to be inherited in all circumstances.
- tedunangst 8y agoThat doesn't seem like much of a rebuttal for the case where you want to inherit more than just descriptors.
- tedunangst 8y agoIf you have a dependency for every function, you have slow start up times. If you also have a unit test for every function, you have a very slow test cycle. You can mitigate this by forking after startup. Can't use threads because they don't provide isolation between tests.
- cuspycode 8y agoA classic example is daemonizing a process. While still attached to the terminal you first parse command-line options, read config files, and do some sanity checks. Any errors caught at this point cause an error message and exit(1). Otherwise fork+setsid to detach from the session, then fork again to lose the new session leader. Apart from that, the only thing I have used fork for (apart from fork+exec) is doing concurrent blocking I/O while prototyping, before rewriting the code to use non-blocking I/O with no forking.
- adrianmonk 8y agoI used to work on a system that processed lots of batch jobs. The jobs themselves were built to run inside of a software framework because they needed to be customized for different purposes but still had lots of common needs. So we had a process that would start up, initialize the framework, then look for jobs to run. For each job, it would fork, giving the job an initialized framework but also giving jobs some isolation. Android does something similar: "Each app process is forked from an existing process called Zygote. The Zygote process starts when the system boots and loads common framework code and resources (such as activity themes). To start a new app process, the system forks the Zygote process then loads and runs the app's code in the new process. This approach allows most of the RAM pages allocated for framework code and resources to be shared across all app processes." ( https://developer.android.com/topic/performance/memory-overview#SharingRAM https://developer.android.com/topic/performance/memory-overv... )
- xenadu02 8y agofork() is not required for this. On Apple platforms the dyld shared cache performs the same function: all processes share the same copy of libSystem, Foundation, etc.
- nybble41 8y agoMost platforms with dynamically-loaded shared libraries do something similar. However, that only avoids having multiple copies of the library code and read-only data in memory—each process still has to run its initialization code separately, parsing configuration files, setting up global variables, etc., and any data which is written during initialization but read-only for the rest of the program is not shared. The Zygote model allows the system to perform this initialization just once and share the results with any number of child processes. I believe the Apache web server's Prefork MPM module does something similar—fork() allows the server to spin up new child processes in a pre-initialized state, ready to respond to requests. Servers in general tend to be a good fit for this style of execution.
- geofft 8y ago
- cryptonector 8y agohttps://gist.github.com/nicowilliams/a8a07b0fc75df05f684c23c18d7db234 https://gist.github.com/nicowilliams/a8a07b0fc75df05f684c23c...
- foobiekr 8y agoCopy-on-write semantics can be pretty useful. You've built up a large in-memory graph and want to execute a destructive or long running query on the data set in the background (e.g., write it out to media when the main process MUST continue to service requests), you want to generate a snapshot core without exiting, etc. etc. I don't classify these use cases are concurrency-after-development cases. Lots of systems use this to do interesting things.
- panic 8y agoThe American Fuzzy Lop fuzzing tool can use fork to speed up fuzzing by avoiding the overhead of repeatedly initializing processes: https://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html https://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-withou...
- geocar 8y ago> The example given by the author seems risky to me Ages ago I made a DNS server, and near the top of the program was a simple: for(i=0;i<ncpus;++i)if(!fork())break; > Most use cases seem covered by either threads or fork+exec process spawning. Most of the use cases of threads are covered by fork+explicit mmap/vm_remap. A "shared-nothing" will also contain less code, and having no need for lock/unlock keeps a lot of spam invalidations away from the memory controller making it for many tasks faster. > Even if you're safe from other threads, there is a lot of other unknown state like fd's still inherited. The problems you get with threads are bigger. http://geocar.sdf1.org/close.html http://geocar.sdf1.org/close.html
- Negitivefrags 8y agoI run an online game that has has instanced multiplayer. We have a single process on each server that starts up and loads every single game resource. After this is waits for requests from the instance manager to create new instances. As new instances are required, this "spawner" process fork()s to create one. Each process uses less memory because all the loaded resources the game needs to run are shared between all the copies. You also get slightly faster performance because commonly used data still in the CPU cache when switching between processes. If we used a thread per instance, then we wouldn't have crash isolation between the different game instances, which is handy. It's also possible to explicitly mmap a shared memory segment to load resources in that without using fork()s copy on write, but it's just more tricky, and the fork() method is totally robust no matter what code other programmers might write.