mirror of
https://github.com/golang/go
synced 2024-11-19 07:34:44 -07:00
runtime: change osyield to use Windows SwitchToThread
It appears that windows osyield is just 15ms sleep on my computer (see benchmarks below). Replace NtWaitForSingleObject in osyield with SwitchToThread (as suggested by Dmitry). Also add issue #14790 related benchmarks, so we can track perfomance changes in CL 20834 and CL 20835 and beyond. Update #14790 benchmark old ns/op new ns/op delta BenchmarkChanToSyscallPing1ms 1953200 1953000 -0.01% BenchmarkChanToSyscallPing15ms 31562904 31248400 -1.00% BenchmarkSyscallToSyscallPing1ms 5247 4202 -19.92% BenchmarkSyscallToSyscallPing15ms 5260 4374 -16.84% BenchmarkChanToChanPing1ms 474 494 +4.22% BenchmarkChanToChanPing15ms 468 489 +4.49% BenchmarkOsYield1ms 980018 75.5 -99.99% BenchmarkOsYield15ms 15625200 75.8 -100.00% Change-Id: I1b4cc7caca784e2548ee3c846ca07ef152ebedce Reviewed-on: https://go-review.googlesource.com/21294 Run-TryBot: Alex Brainman <alex.brainman@gmail.com> Reviewed-by: Dmitry Vyukov <dvyukov@google.com> Run-TryBot: Dmitry Vyukov <dvyukov@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
02adfa0b3d
commit
1f5b1b2b66
@ -9,6 +9,7 @@ package runtime
|
|||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var TestingWER = &testingWER
|
var TestingWER = &testingWER
|
||||||
|
var OsYield = osyield
|
||||||
|
|
||||||
func NumberOfProcessors() int32 {
|
func NumberOfProcessors() int32 {
|
||||||
var info systeminfo
|
var info systeminfo
|
||||||
|
@ -41,6 +41,7 @@ import (
|
|||||||
//go:cgo_import_dynamic runtime._SetUnhandledExceptionFilter SetUnhandledExceptionFilter%1 "kernel32.dll"
|
//go:cgo_import_dynamic runtime._SetUnhandledExceptionFilter SetUnhandledExceptionFilter%1 "kernel32.dll"
|
||||||
//go:cgo_import_dynamic runtime._SetWaitableTimer SetWaitableTimer%6 "kernel32.dll"
|
//go:cgo_import_dynamic runtime._SetWaitableTimer SetWaitableTimer%6 "kernel32.dll"
|
||||||
//go:cgo_import_dynamic runtime._SuspendThread SuspendThread%1 "kernel32.dll"
|
//go:cgo_import_dynamic runtime._SuspendThread SuspendThread%1 "kernel32.dll"
|
||||||
|
//go:cgo_import_dynamic runtime._SwitchToThread SwitchToThread%0 "kernel32.dll"
|
||||||
//go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc%4 "kernel32.dll"
|
//go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc%4 "kernel32.dll"
|
||||||
//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
|
//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
|
||||||
//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll"
|
//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll"
|
||||||
@ -84,6 +85,7 @@ var (
|
|||||||
_SetUnhandledExceptionFilter,
|
_SetUnhandledExceptionFilter,
|
||||||
_SetWaitableTimer,
|
_SetWaitableTimer,
|
||||||
_SuspendThread,
|
_SuspendThread,
|
||||||
|
_SwitchToThread,
|
||||||
_VirtualAlloc,
|
_VirtualAlloc,
|
||||||
_VirtualFree,
|
_VirtualFree,
|
||||||
_WSAGetOverlappedResult,
|
_WSAGetOverlappedResult,
|
||||||
@ -189,6 +191,8 @@ var useLoadLibraryEx bool
|
|||||||
|
|
||||||
func osinit() {
|
func osinit() {
|
||||||
asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall))
|
asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall))
|
||||||
|
usleep2Addr = unsafe.Pointer(funcPC(usleep2))
|
||||||
|
switchtothreadAddr = unsafe.Pointer(funcPC(switchtothread))
|
||||||
|
|
||||||
setBadSignalMsg()
|
setBadSignalMsg()
|
||||||
|
|
||||||
@ -586,17 +590,22 @@ func stdcall7(fn stdFunction, a0, a1, a2, a3, a4, a5, a6 uintptr) uintptr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// in sys_windows_386.s and sys_windows_amd64.s
|
// in sys_windows_386.s and sys_windows_amd64.s
|
||||||
func usleep1(usec uint32)
|
func onosstack(fn unsafe.Pointer, arg uint32)
|
||||||
|
func usleep2(usec uint32)
|
||||||
|
func switchtothread()
|
||||||
|
|
||||||
|
var usleep2Addr unsafe.Pointer
|
||||||
|
var switchtothreadAddr unsafe.Pointer
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
func osyield() {
|
func osyield() {
|
||||||
usleep1(1)
|
onosstack(switchtothreadAddr, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
func usleep(us uint32) {
|
func usleep(us uint32) {
|
||||||
// Have 1us units; want 100ns units.
|
// Have 1us units; want 100ns units.
|
||||||
usleep1(10 * us)
|
onosstack(usleep2Addr, 10*us)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ctrlhandler1(_type uint32) uint32 {
|
func ctrlhandler1(_type uint32) uint32 {
|
||||||
|
@ -358,10 +358,11 @@ TEXT runtime·setldt(SB),NOSPLIT,$0
|
|||||||
MOVL CX, 0x14(FS)
|
MOVL CX, 0x14(FS)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// Sleep duration is in 100ns units.
|
// onosstack calls fn on OS stack.
|
||||||
TEXT runtime·usleep1(SB),NOSPLIT,$0
|
// func onosstack(fn unsafe.Pointer, arg uint32)
|
||||||
MOVL usec+0(FP), BX
|
TEXT runtime·onosstack(SB),NOSPLIT,$0
|
||||||
MOVL $runtime·usleep2(SB), AX // to hide from 8l
|
MOVL fn+0(FP), AX // to hide from 8l
|
||||||
|
MOVL arg+4(FP), BX
|
||||||
|
|
||||||
// Execute call on m->g0 stack, in case we are not actually
|
// Execute call on m->g0 stack, in case we are not actually
|
||||||
// calling a system call wrapper, like when running under WINE.
|
// calling a system call wrapper, like when running under WINE.
|
||||||
@ -423,6 +424,14 @@ TEXT runtime·usleep2(SB),NOSPLIT,$20
|
|||||||
MOVL BP, SP
|
MOVL BP, SP
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
// Runs on OS stack.
|
||||||
|
TEXT runtime·switchtothread(SB),NOSPLIT,$0
|
||||||
|
MOVL SP, BP
|
||||||
|
MOVL runtime·_SwitchToThread(SB), AX
|
||||||
|
CALL AX
|
||||||
|
MOVL BP, SP
|
||||||
|
RET
|
||||||
|
|
||||||
// func now() (sec int64, nsec int32)
|
// func now() (sec int64, nsec int32)
|
||||||
TEXT time·now(SB),NOSPLIT,$8-12
|
TEXT time·now(SB),NOSPLIT,$8-12
|
||||||
CALL runtime·unixnano(SB)
|
CALL runtime·unixnano(SB)
|
||||||
|
@ -381,10 +381,10 @@ TEXT runtime·settls(SB),NOSPLIT,$0
|
|||||||
MOVQ DI, 0x28(GS)
|
MOVQ DI, 0x28(GS)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// Sleep duration is in 100ns units.
|
// func onosstack(fn unsafe.Pointer, arg uint32)
|
||||||
TEXT runtime·usleep1(SB),NOSPLIT,$0
|
TEXT runtime·onosstack(SB),NOSPLIT,$0
|
||||||
MOVL usec+0(FP), BX
|
MOVQ fn+0(FP), AX // to hide from 6l
|
||||||
MOVQ $runtime·usleep2(SB), AX // to hide from 6l
|
MOVL arg+8(FP), BX
|
||||||
|
|
||||||
// Execute call on m->g0 stack, in case we are not actually
|
// Execute call on m->g0 stack, in case we are not actually
|
||||||
// calling a system call wrapper, like when running under WINE.
|
// calling a system call wrapper, like when running under WINE.
|
||||||
@ -445,6 +445,18 @@ TEXT runtime·usleep2(SB),NOSPLIT,$48
|
|||||||
MOVQ 40(SP), SP
|
MOVQ 40(SP), SP
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
// Runs on OS stack.
|
||||||
|
TEXT runtime·switchtothread(SB),NOSPLIT,$0
|
||||||
|
MOVQ SP, AX
|
||||||
|
ANDQ $~15, SP // alignment as per Windows requirement
|
||||||
|
SUBQ $(48), SP // room for SP and 4 args as per Windows requirement
|
||||||
|
// plus one extra word to keep stack 16 bytes aligned
|
||||||
|
MOVQ AX, 32(SP)
|
||||||
|
MOVQ runtime·_SwitchToThread(SB), AX
|
||||||
|
CALL AX
|
||||||
|
MOVQ 32(SP), SP
|
||||||
|
RET
|
||||||
|
|
||||||
// func now() (sec int64, nsec int32)
|
// func now() (sec int64, nsec int32)
|
||||||
TEXT time·now(SB),NOSPLIT,$8-12
|
TEXT time·now(SB),NOSPLIT,$8-12
|
||||||
CALL runtime·unixnano(SB)
|
CALL runtime·unixnano(SB)
|
||||||
|
@ -864,3 +864,147 @@ func TestLoadLibraryEx(t *testing.T) {
|
|||||||
t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)",
|
t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)",
|
||||||
have, flags)
|
have, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modwinmm = syscall.NewLazyDLL("winmm.dll")
|
||||||
|
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
|
||||||
|
proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod")
|
||||||
|
proctimeEndPeriod = modwinmm.NewProc("timeEndPeriod")
|
||||||
|
|
||||||
|
procCreateEvent = modkernel32.NewProc("CreateEventW")
|
||||||
|
procSetEvent = modkernel32.NewProc("SetEvent")
|
||||||
|
)
|
||||||
|
|
||||||
|
func timeBeginPeriod(period uint32) {
|
||||||
|
syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeEndPeriod(period uint32) {
|
||||||
|
syscall.Syscall(proctimeEndPeriod.Addr(), 1, uintptr(period), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createEvent() (syscall.Handle, error) {
|
||||||
|
r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
|
||||||
|
if r0 == 0 {
|
||||||
|
return 0, syscall.Errno(e0)
|
||||||
|
}
|
||||||
|
return syscall.Handle(r0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setEvent(h syscall.Handle) error {
|
||||||
|
r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0)
|
||||||
|
if r0 == 0 {
|
||||||
|
return syscall.Errno(e0)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchChanToSyscallPing(b *testing.B) {
|
||||||
|
ch := make(chan int)
|
||||||
|
event, err := createEvent()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
syscall.WaitForSingleObject(event, syscall.INFINITE)
|
||||||
|
ch <- 1
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := setEvent(event)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
<-ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkChanToSyscallPing1ms(b *testing.B) {
|
||||||
|
timeBeginPeriod(1)
|
||||||
|
benchChanToSyscallPing(b)
|
||||||
|
timeEndPeriod(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkChanToSyscallPing15ms(b *testing.B) {
|
||||||
|
benchChanToSyscallPing(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchSyscallToSyscallPing(b *testing.B) {
|
||||||
|
event1, err := createEvent()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
event2, err := createEvent()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
syscall.WaitForSingleObject(event1, syscall.INFINITE)
|
||||||
|
err := setEvent(event2)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := setEvent(event1)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
syscall.WaitForSingleObject(event2, syscall.INFINITE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSyscallToSyscallPing1ms(b *testing.B) {
|
||||||
|
timeBeginPeriod(1)
|
||||||
|
benchSyscallToSyscallPing(b)
|
||||||
|
timeEndPeriod(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSyscallToSyscallPing15ms(b *testing.B) {
|
||||||
|
benchSyscallToSyscallPing(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchChanToChanPing(b *testing.B) {
|
||||||
|
ch1 := make(chan int)
|
||||||
|
ch2 := make(chan int)
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
<-ch1
|
||||||
|
ch2 <- 1
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ch1 <- 1
|
||||||
|
<-ch2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkChanToChanPing1ms(b *testing.B) {
|
||||||
|
timeBeginPeriod(1)
|
||||||
|
benchChanToChanPing(b)
|
||||||
|
timeEndPeriod(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkChanToChanPing15ms(b *testing.B) {
|
||||||
|
benchChanToChanPing(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchOsYield(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
runtime.OsYield()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkOsYield1ms(b *testing.B) {
|
||||||
|
timeBeginPeriod(1)
|
||||||
|
benchOsYield(b)
|
||||||
|
timeEndPeriod(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkOsYield15ms(b *testing.B) {
|
||||||
|
benchOsYield(b)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user