-
Notifications
You must be signed in to change notification settings - Fork 17.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
syscall: use posix_spawn (or vfork) for ForkExec when possible #5838
Comments
Some related discussion: https://groups.google.com/d/topic/golang-dev/66rHnYuMaeM/discussion |
Thanks for the info. I am pasting the implementation of the posix-spawn gem in case it's a helpful reference. Looks like it forces to use vfork on linux: https://github.com/rtomayko/posix-spawn/blob/master/ext/posix-spawn.c#L399-L404 https://github.com/rtomayko/posix-spawn/blob/master/ext/posix-spawn.c#L418 |
the problem of MADV_DONTFORK heap is that it's difficult for us to make sure ForkExec code doesn't use heap at all. i think posix_spawn is the way to go (vfork is removed in modern POSIX standard, so it should be avoided when possible). Labels changed: added priority-later, performance, removed priority-triage. Status changed to Accepted. |
I see this issue on a regular basis for machines that still have free physical memory (certainly enough for the process I would like to invoke), but are being called by a Go process with a very large virtual memory footprint using Go daemons with large virtual memory footprints needing to invoke small command-line utilities is a common use case. It would be nice to get this assigned to the 1.5 release. |
I took a short look. On GNU/Linux posix_spawn is a C library function, not a system call. vfork is a special case of clone: you pass the CLONE_VFORK flag. This means that a program that cares can already use vfork on GNU/Linux, by setting the Cloneflags field in os/exec.Cmd.SysProcAttr or os.ProcAttr.Sys. So while this would be nice to fix I don't see a pressing need. To fix we need to edit syscall/exec_linux.go to pass CLONE_VFORK when that is safe. It is pretty clearly safe if the only things the child needs to do after the fork is fiddle with file descriptors and call exec. If that is the case, as can be determined by looking at the sys fields, then we could add CLONE_VFORK to the clone flags. If somebody wants to try that out, that would be nice. |
@ianlancetaylor I see this issue on a regular basis, I will give you suggestion a try- Thanks! |
I'm facing with a similar problem. I'm running a go app that allocates about 14GB of VM and can't spawn a simple 'ps' command despite having at leat 300 MB system RAM still available. It would be really great if this issue would be fixed in 1.5 |
Hmm, I gave this a quick try a few days ago, but gave up for now. I failed to determine why the child hangs after the clone syscall. And if the child hangs, the parent won't continue either in the CLONE_VFORK case. I only activated CLONE_VFORK, if everything in syscall.SysProcAttr was set to it's zero value. But even such simple cases are not so simple it seems. if someone want's to work on this with me, just ping me here. |
Did you pass CLONE_VM as well as CLONE_VFORK? I think that without CLONE_VM the parent won't be able to see when the child has exec'ed. Though I don't know why the child would hang. |
@ianlancetaylor yes, I passed both. But I guess the systems needs to be in a single thread mode for this to work, which Go doesn't seem to do at the moment. http://ewontfix.com/7/ has more info on this, if someone wants to continue here (e.g. my future self). |
@ianlancetaylor Edit: I even tried adding runtime.GOMAXPROCS(runtime.NumCPU()) and it still works. |
@tarndt CLONE_VM is not passed in your example. CLONE_VFORK without CLONE_VM will be If you add this, the go program calling execve hangs. Which is exactly what I have seen in my tests. My current plan is to use madvise(...,MADV_DONTFORK) with the heap, but I haven't figured out yet how to do the file descriptor juggling in a safe manner without affecting the parent process and only using stack. |
@tarndt If you use CLONE_VFORK without CLONE_VM, is that really any faster? If it is faster, and it works, then I suppose we could use it. |
There is one reason to not use vfork. It's when the child needs to dup a See This is an edge case, but still worth considering when switching to vfork. |
@neelance I feel super stupid. My first test was incorrect, but Unmodified Go 1.7
Unmodified Go 1.7, application code adds CLONE_VFORK
Go 1.7 with this patch applied: neelance@b7edfba
I'm surprised though that memory is still copied despite the the child_stack is set to zero. |
As far as I understand the manpage of
|
@neelance That makes sense to me. AFAICT the third option (your patch) is this most feasible and performant. The first option seems to be what's in master and suffers from a large amount of copying. CLONE_VFORK avoids some of the copies, but not much apparently. The second option seems to me (as a novice to the internals) much more difficult to get right given the nature of Go and its management of memory. Is that accurate? |
Yes, I also think that the second option is harder to implement. |
I work on an app that makes heavy use of subprocesses to manipulate iptables and ipsets (since that's their only supported API). After observing poor performance when my process is using a lot of RAM, I found this issue. I tried adding FWIW, the previous version of our app was written in Python and we observed a dramatic improvement when we switched from Python's default fork/exec strategy to using |
Measuring in an app that's under load with work going on in other threads, I see |
@fasaxc Yes, only using Would you mind applying the whole patch neelance@f207709 to your GOROOT, then do |
With "improve" I specifically mean the latency on high ram usage. You are right that in a low-RAM situation it may lower the throughput. Please check if it is still 50x when using the full patch. |
That patch makes a dramatic improvement. I'm measuring 99%ile latency of 1ms vs 60ms before and a drop from 100% CPU to 20% CPU usage. |
Yey, I'm happy to hear that. Any downsides that you see? What about the low-memory situation? |
@neelance It seems to improve latency at small VSS size too (~40MB): 800us 99th %ile vs 2600us |
For more details: golang/go#5838.
Cool. So there are no reasons for not bringing this upstream. I'll create a CL today or tomorrow. |
CL https://golang.org/cl/37439 mentions this issue. |
For more details: golang/go#5838.
@neelance nice work! |
gitlab blogged about this patch giving a 30x improvement to the p99 latency of the git service they developed :) https://about.gitlab.com/2018/01/23/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x/ |
The text was updated successfully, but these errors were encountered: