Skip to content

Commit

Permalink
content: update zero-alloc-call-sched
Browse files Browse the repository at this point in the history
  • Loading branch information
changkun committed Jan 25, 2021
1 parent 4c21d4a commit 4070d20
Show file tree
Hide file tree
Showing 15 changed files with 953 additions and 7 deletions.
68 changes: 68 additions & 0 deletions content/assets/zero-alloc-call-sched/app-naive/window.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) 2021 The golang.design Initiative Authors.
// All rights reserved.
//
// The code below is produced by Changkun Ou <[email protected]>.

package app

import (
mainthread "x/mainthread-opt2"
"x/thread"

"github.com/go-gl/glfw/v3.3/glfw"
)

// Init initializes an app environment.
func Init() (err error) {
mainthread.Call(func() { err = glfw.Init() })
return
}

// Terminate terminates the entire application.
func Terminate() {
mainthread.Call(glfw.Terminate)
}

// Win is a window.
type Win struct {
win *glfw.Window
th *thread.Thread
}

// NewWindow constructs a new graphical window.
func NewWindow() (*Win, error) {
var (
w = &Win{}
err error
)
mainthread.Call(func() {
w.win, err = glfw.CreateWindow(640, 480, "golang.design/research", nil, nil)
if err != nil {
return
}
})
if err != nil {
return nil, err
}

w.win.MakeContextCurrent()
return w, nil
}

// Run runs the given window and blocks until it is destroied.
func (w *Win) Run() {
for !w.win.ShouldClose() {
mainthread.Call(func() {
w.win.SwapBuffers()
// This function must be called from the main thread.
glfw.WaitEventsTimeout(1.0 / 30)
})
}
// This function must be called from the mainthread.
mainthread.Call(w.win.Destroy)
}

// Stop stops and closes the given window.
func (w *Win) Stop() {
w.win.SetShouldClose(true)
}
3 changes: 3 additions & 0 deletions content/assets/zero-alloc-call-sched/app/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ func NewWindow() (*Win, error) {
return
}
})
if err != nil {
return nil, err
}

// This function can be called from any thread.
w.th.Call(w.win.MakeContextCurrent)
Expand Down
79 changes: 79 additions & 0 deletions content/assets/zero-alloc-call-sched/cmd/app1/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) 2021 The golang.design Initiative Authors.
// All rights reserved.
//
// The code below is produced by Changkun Ou <[email protected]>.

package main

import (
"flag"
"fmt"
"os"
"runtime/trace"
"time"
app "x/app-naive"
mainthread "x/mainthread-opt2"
)

func main() {
mainthread.Init(fn)
}

func fn() {
d := parseArgs()

err := app.Init()
if err != nil {
panic(err)
}
defer app.Terminate()
w, err := app.NewWindow()
if err != nil {
panic(err)
}

done := make(chan struct{}, 2)
go func() {
f, _ := os.Create(*traceF)
defer f.Close()
trace.Start(f)
defer trace.Stop()
time.Sleep(d)
w.Stop()
}()
go func() {
w.Run()
done <- struct{}{}
}()
<-done
}

var (
run *bool
traceF *string
traceT *string
)

func parseArgs() time.Duration {
run = flag.Bool("run", false, "start test")
traceF = flag.String("trace", "trace.out", "trace file, default: trace.out")
traceT = flag.String("d", "2s", "trace duration, default: 10s")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, `usage: go run main.go -run [-trace FILENAME -d DURATION]
options:
`)
flag.PrintDefaults()
}
flag.Parse()
if !*run {
flag.Usage()
os.Exit(2)
}

d, err := time.ParseDuration(*traceT)
if err != nil {
flag.Usage()
os.Exit(2)
}
return d
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
goos: darwin
goarch: arm64
pkg: x/mainthread-opt1
BenchmarkDirectCall-8 1000000000 0.9476 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9479 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9567 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9528 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9468 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9480 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9538 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9506 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9607 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9489 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 2752809 439.6 ns/op 24 B/op 1 allocs/op
BenchmarkMainThreadCall-8 2750701 438.7 ns/op 24 B/op 1 allocs/op
BenchmarkMainThreadCall-8 2725908 440.3 ns/op 24 B/op 1 allocs/op
BenchmarkMainThreadCall-8 2723350 439.4 ns/op 24 B/op 1 allocs/op
BenchmarkMainThreadCall-8 2754843 439.0 ns/op 24 B/op 1 allocs/op
BenchmarkMainThreadCall-8 2733372 446.4 ns/op 24 B/op 1 allocs/op
BenchmarkMainThreadCall-8 2738767 440.6 ns/op 24 B/op 1 allocs/op
BenchmarkMainThreadCall-8 2741732 439.8 ns/op 24 B/op 1 allocs/op
BenchmarkMainThreadCall-8 2748228 439.6 ns/op 24 B/op 1 allocs/op
BenchmarkMainThreadCall-8 2717796 439.7 ns/op 24 B/op 1 allocs/op
PASS
ok x/mainthread-opt1 28.462s
57 changes: 57 additions & 0 deletions content/assets/zero-alloc-call-sched/mainthread-opt1/mainthread.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) 2021 The golang.design Initiative Authors.
// All rights reserved.
//
// The code below is produced by Changkun Ou <[email protected]>.

package mainthread

import (
"runtime"
"sync"
)

func init() {
runtime.LockOSThread()
}

var (
funcQ = make(chan func(), runtime.GOMAXPROCS(0))
donePool = sync.Pool{New: func() interface{} {
return make(chan struct{})
}}
)

// Init initializes the functionality of running arbitrary subsequent
// functions be called on the main system thread.
//
// Init must be called in the main.main function.
func Init(main func()) {
done := donePool.Get().(chan struct{})
defer donePool.Put(done)

go func() {
main()
done <- struct{}{}
}()

for {
select {
case f := <-funcQ:
f()
case <-done:
return
}
}
}

// Call calls f on the main thread and blocks until f finishes.
func Call(f func()) {
done := donePool.Get().(chan struct{})
defer donePool.Put(done)

funcQ <- func() {
f()
done <- struct{}{}
}
<-done
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2021 The golang.design Initiative Authors.
// All rights reserved.
//
// The code below is produced by Changkun Ou <[email protected]>.

package mainthread_test

import (
"testing"
mainthread "x/mainthread-opt1"
)

var f = func() {}

func BenchmarkDirectCall(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
f()
}
}

func BenchmarkMainThreadCall(b *testing.B) {
mainthread.Init(func() {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
mainthread.Call(f)
}
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
goos: darwin
goarch: arm64
pkg: x/mainthread-opt2
BenchmarkDirectCall-8 1000000000 0.9449 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9478 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9559 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9471 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9576 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9493 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9468 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9581 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9493 ns/op 0 B/op 0 allocs/op
BenchmarkDirectCall-8 1000000000 0.9480 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 3299989 364.9 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 3277143 365.3 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 3275292 371.2 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 3293971 366.1 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 3299977 364.0 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 3024205 367.1 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 3294015 375.2 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 3288907 364.9 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 3285415 366.0 ns/op 0 B/op 0 allocs/op
BenchmarkMainThreadCall-8 3081945 368.7 ns/op 0 B/op 0 allocs/op
PASS
ok x/mainthread-opt2 26.493s
60 changes: 60 additions & 0 deletions content/assets/zero-alloc-call-sched/mainthread-opt2/mainthread.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) 2021 The golang.design Initiative Authors.
// All rights reserved.
//
// The code below is produced by Changkun Ou <[email protected]>.

package mainthread

import (
"runtime"
"sync"
)

func init() {
runtime.LockOSThread()
}

var (
funcQ = make(chan funcdata, runtime.GOMAXPROCS(0))
donePool = sync.Pool{New: func() interface{} {
return make(chan struct{})
}}
)

type funcdata struct {
fn func()
done chan struct{}
}

// Init initializes the functionality of running arbitrary subsequent
// functions be called on the main system thread.
//
// Init must be called in the main.main function.
func Init(main func()) {
done := donePool.Get().(chan struct{})
defer donePool.Put(done)

go func() {
main()
done <- struct{}{}
}()

for {
select {
case fdata := <-funcQ:
fdata.fn()
fdata.done <- struct{}{}
case <-done:
return
}
}
}

// Call calls f on the main thread and blocks until f finishes.
func Call(f func()) {
done := donePool.Get().(chan struct{})
defer donePool.Put(done)

funcQ <- funcdata{fn: f, done: done}
<-done
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2021 The golang.design Initiative Authors.
// All rights reserved.
//
// The code below is produced by Changkun Ou <[email protected]>.

package mainthread_test

import (
"testing"
mainthread "x/mainthread-opt2"
)

var f = func() {}

func BenchmarkDirectCall(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
f()
}
}

func BenchmarkMainThreadCall(b *testing.B) {
mainthread.Init(func() {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
mainthread.Call(f)
}
})
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/assets/zero-alloc-call-sched/opt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 4070d20

Please sign in to comment.