Skip to content

Commit 9e6b79a

Browse files
neelanceianlancetaylor
authored andcommitted
syscall: use CLONE_VFORK and CLONE_VM
This greatly improves the latency of starting a child process when the Go process is using a lot of memory. Even though the kernel uses copy-on-write, preparation for that can take up to several 100ms under certain conditions. All other goroutines are suspended while starting a subprocess so this latency directly affects total throughput. With CLONE_VM the child process shares the same memory with the parent process. On its own this would lead to conflicting use of the same memory, so CLONE_VFORK is used to suspend the parent process until the child releases the memory when switching to to the new program binary via the exec syscall. When the parent process continues to run, one has to consider the changes to memory that the child process did, namely the return address of the syscall function needs to be restored from a register. A simple benchmark has shown a difference in latency of 16ms vs. 0.5ms at 10GB memory usage. However, much higher latencies of several 100ms have been observed in real world scenarios. For more information see comments on #5838. Fixes #5838 Change-Id: I6377d7bd8dcd00c85ca0c52b6683e70ce2174ba6 Reviewed-on: https://go-review.googlesource.com/37439 Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 0a0186f commit 9e6b79a

10 files changed

+58
-2
lines changed

src/syscall/asm_linux_amd64.s

+23
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,29 @@ ok2:
111111
MOVQ $0, err+72(FP)
112112
RET
113113

114+
// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr)
115+
TEXT ·rawVforkSyscall(SB),NOSPLIT,$0-32
116+
MOVQ a1+8(FP), DI
117+
MOVQ $0, SI
118+
MOVQ $0, DX
119+
MOVQ $0, R10
120+
MOVQ $0, R8
121+
MOVQ $0, R9
122+
MOVQ trap+0(FP), AX // syscall entry
123+
POPQ R12 // preserve return address
124+
SYSCALL
125+
PUSHQ R12
126+
CMPQ AX, $0xfffffffffffff001
127+
JLS ok2
128+
MOVQ $-1, r1+16(FP)
129+
NEGQ AX
130+
MOVQ AX, err+24(FP)
131+
RET
132+
ok2:
133+
MOVQ AX, r1+16(FP)
134+
MOVQ $0, err+24(FP)
135+
RET
136+
114137
// func gettimeofday(tv *Timeval) (err uintptr)
115138
TEXT ·gettimeofday(SB),NOSPLIT,$0-16
116139
MOVQ tv+0(FP), DI

src/syscall/exec_linux.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,12 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
9595
// About to call fork.
9696
// No more allocation or calls of non-assembly functions.
9797
runtime_BeforeFork()
98-
if runtime.GOARCH == "s390x" {
98+
switch {
99+
case runtime.GOARCH == "amd64" && sys.Cloneflags&CLONE_NEWUSER == 0:
100+
r1, err1 = rawVforkSyscall(SYS_CLONE, uintptr(SIGCHLD|CLONE_VFORK|CLONE_VM)|sys.Cloneflags)
101+
case runtime.GOARCH == "s390x":
99102
r1, _, err1 = RawSyscall6(SYS_CLONE, 0, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0)
100-
} else {
103+
default:
101104
r1, _, err1 = RawSyscall6(SYS_CLONE, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0, 0)
102105
}
103106
if err1 != 0 {

src/syscall/syscall_linux_386.go

+4
Original file line numberDiff line numberDiff line change
@@ -375,3 +375,7 @@ func (msghdr *Msghdr) SetControllen(length int) {
375375
func (cmsg *Cmsghdr) SetLen(length int) {
376376
cmsg.Len = uint32(length)
377377
}
378+
379+
func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) {
380+
panic("not implemented")
381+
}

src/syscall/syscall_linux_amd64.go

+2
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,5 @@ func (msghdr *Msghdr) SetControllen(length int) {
134134
func (cmsg *Cmsghdr) SetLen(length int) {
135135
cmsg.Len = uint64(length)
136136
}
137+
138+
func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno)

src/syscall/syscall_linux_arm.go

+4
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,7 @@ func (msghdr *Msghdr) SetControllen(length int) {
215215
func (cmsg *Cmsghdr) SetLen(length int) {
216216
cmsg.Len = uint32(length)
217217
}
218+
219+
func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) {
220+
panic("not implemented")
221+
}

src/syscall/syscall_linux_arm64.go

+4
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,7 @@ const (
139139
SYS_EPOLL_CREATE = 1042
140140
SYS_EPOLL_WAIT = 1069
141141
)
142+
143+
func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) {
144+
panic("not implemented")
145+
}

src/syscall/syscall_linux_mips64x.go

+4
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,7 @@ func (msghdr *Msghdr) SetControllen(length int) {
198198
func (cmsg *Cmsghdr) SetLen(length int) {
199199
cmsg.Len = uint64(length)
200200
}
201+
202+
func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) {
203+
panic("not implemented")
204+
}

src/syscall/syscall_linux_mipsx.go

+4
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,7 @@ func (msghdr *Msghdr) SetControllen(length int) {
218218
func (cmsg *Cmsghdr) SetLen(length int) {
219219
cmsg.Len = uint32(length)
220220
}
221+
222+
func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) {
223+
panic("not implemented")
224+
}

src/syscall/syscall_linux_ppc64x.go

+4
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,7 @@ func (msghdr *Msghdr) SetControllen(length int) {
115115
func (cmsg *Cmsghdr) SetLen(length int) {
116116
cmsg.Len = uint64(length)
117117
}
118+
119+
func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) {
120+
panic("not implemented")
121+
}

src/syscall/syscall_linux_s390x.go

+4
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,7 @@ func (msghdr *Msghdr) SetControllen(length int) {
286286
func (cmsg *Cmsghdr) SetLen(length int) {
287287
cmsg.Len = uint64(length)
288288
}
289+
290+
func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) {
291+
panic("not implemented")
292+
}

0 commit comments

Comments
 (0)