Go-coopsched - A benchmark and playground for Completely Fair Scheduling in Go

A benchmark and playground for Completely Fair Scheduling in Go

See https://github.com/golang/go/issues/51071. I just wanted to play around with it myself.

Notes about hnes/cpuworker

The issue uses https://github.com/hnes/cpuworker as the testbed. Here are some notes about the gist of it. This repository attempts to use the same test-cases, but simplify the design to allow easier experimentation. It also has self-contained Go benchmarks.

  • Event-intensive tasks emulate goroutines with a lot of I/O wait.
  • The eventRoutineCall subsystem lets the caller emulate I/O wait time.
  • The user decides if they want the new task to be an "event-intensive" or "CPU" task, using eiFlag to Submit3. But this only affects the initial scheduling. Any yielding thereafter uses the computed event-intensity factor (EI-factor).
  • The output of calcEIfactorAndSumbitToRunnableTaskQueue is directly used as priority for event-intensive tasks. The scheduler uses the following task priorities: event-intensive (PQ by EI-factor), new-task, CPU-intensive (based on EI-factor being zero, FIFO).
  • Higher EI-factor means higher priority.
  • Checkpoint is yielding, and the task's priority is decided by the EI-factor.
  • Event-intensivity also determines the maximum time slice given.

Differences to hnes/cpuworker

  • All tasks ride on the same time slot expiration schedule.
  • Event time is renamed "wait time".
  • New tasks have highest priority, instead of sitting between wait-intensive and CPU-intensive.
  • The EI-factor is based on w / (w + r) instead of w / r, to have a bounded value.
  • The factor calculation never resets the accumulated time buckets, thus it's less likely to change priority quickly.
  • There is no explicit P, instead replaced by a numRunning counter.

Benchmark Design

These benchmarks use b.N to run a number of complete tests where 100 tasks per type are created and waited for. Each task performs some 100 operations, yielding in-between. The noYield versions run the same code, but skips calling Yield and Wait (depending on task type.)

As an example, for the sleep task, a total of 100*100*b.N calls to time.Sleep are performed per benchmark run. This is similar to the demo in hnes/cpuworker, except ab would continuously use 100 concurrent requests out of 10M instead of running separate batches.

Initial Results (on a ThinkPad T460s laptop with external power)

The "avg delay overhead" is how long we waited in <-time.After, after discounting the expected delay. Running time and blocking time are total times for all scheduled goroutines. Note that no-yield still blocks at the start of a goroutine, and that can take a long time if the concurrency limit has been reached. "Avg load" is the average size of the queue seen when taking the top task.

See below for full output. The case we care about is "mixed". No-yield should all be equivalent, and never blocks in Yield. The numbers are in the same ballpark, and the accuracy is fair:

BenchmarkFIFO/noYield/mixed-4     	       6	1982894160 ns/op	        27.61 avg-load	   1834925 block-µs/op	    193734 run-µs/op	     12337 wait-overhead-µs/op	         0 wait-µs/op
BenchmarkWaitiness/noYield/mixed-4         6	2000242534 ns/op	        27.60 avg-load	   1886621 block-µs/op	    195722 run-µs/op	     13489 wait-overhead-µs/op	         0 wait-µs/op

FIFO is what Go does, so we wouldn't expect that to be very different from no-yield, except for more overhead. In the yield tests, the channel sleep time is re-accounted as waiting time, instead of running time. The much worse overhead may be due to the large load making the scheduler a bottleneck, as indicated by the greater blocking time:

BenchmarkFIFO/yield/mixed-4       	       8	1289674838 ns/op	        17.97 avg-load	   2126736 block-µs/op	    102034 run-µs/op	   1140682 wait-overhead-µs/op	    129450 wait-µs/op
BenchmarkFIFO/noYield/mixed-4     	       6	1982894160 ns/op	        27.61 avg-load	   1834925 block-µs/op	    193734 run-µs/op	     12337 wait-overhead-µs/op	         0 wait-µs/op

Finally, Waitiess should give a lower overhead than FIFO, and that should be due to lower blocking time. Note that delay overhead only looks at the channel-intensive goroutines, not the CPU-intensive ones, while both types yield to the scheduler. It does improve a factor of 5x for wait-overhead, and around 2x for blocking time:

BenchmarkFIFO/yield/mixed-4       	       8	1289674838 ns/op	        17.97 avg-load	   2126736 block-µs/op	    102034 run-µs/op	   1140682 wait-overhead-µs/op	    129450 wait-µs/op
BenchmarkWaitiness/yield/mixed-4  	       8	1310149936 ns/op	        38.20 avg-load	   1379195 block-µs/op	    102958 run-µs/op	    204797 wait-overhead-µs/op	    124973 wait-µs/op

Conclusions

It took a couple of iterations (see Git history) to get this to do what https://github.com/hnes/cpuworker does in terms of test cases.

The load is high enough for a scheduling algorithm to have possible impact.

Go Scheduler Notes

  • schedule (runtime/proc.go)
    • findRunnableGCWorker
    • globrunqget
      • A non-local gQueue, which is unbounded.
      • This call returns a g, but also fills runq.
    • runqget
      • A local circular buffer of 255/256 entries.
    • findrunnable
      • runqget
      • globrunqget
      • netpoll
        • Internal gs for network polling.
      • stealWork
        • Takes work from another thread/CPU.
        • stealOrder.start
        • The only order is randomOrder, which picks a P at pseudo-random.
        • checkTimers
          • runtimer
        • runqget
        • runqsteal
          • Takes half of the other ps runq.
    • globrunqget checked again.
  • Go preemption timeslice är 10 ms.
  • schedEnabled seems only used for GC pausing.
  • casgstatus sets runnableTime if tracking is enabled.

Output from Initial Benchmarking

Sixth version.

$ go test -bench . -benchtime 10s ./
goos: linux
goarch: amd64
pkg: github.com/tommie/go-coopsched
cpu: Intel(R) Core(TM) i7-6600U CPU @ 2.60GHz
BenchmarkFIFO/yield/run-4         	      13	 912782670 ns/op	        25.52 avg-load	    780148 block-µs/op	     90906 run-µs/op	         0 wait-overhead-µs/op	         0 wait-µs/op
BenchmarkFIFO/yield/wait-4        	      67	 170363049 ns/op	         6.02 avg-load	     36457 block-µs/op	        94 run-µs/op	     62958 wait-overhead-µs/op	    127231 wait-µs/op
BenchmarkFIFO/yield/mixed-4       	       8	1289674838 ns/op	        17.97 avg-load	   2126736 block-µs/op	    102034 run-µs/op	   1140682 wait-overhead-µs/op	    129450 wait-µs/op
BenchmarkFIFO/noYield/run-4       	      12	 956969815 ns/op	        12.72 avg-load	    420550 block-µs/op	     92803 run-µs/op	         0 wait-overhead-µs/op	         0 wait-µs/op
BenchmarkFIFO/noYield/wait-4      	       8	1254513704 ns/op	        13.04 avg-load	    564536 block-µs/op	    125391 run-µs/op	     25351 wait-overhead-µs/op	         0 wait-µs/op
BenchmarkFIFO/noYield/mixed-4     	       6	1982894160 ns/op	        27.61 avg-load	   1834925 block-µs/op	    193734 run-µs/op	     12337 wait-overhead-µs/op	         0 wait-µs/op
BenchmarkWaitiness/yield/run-4    	      12	 915748782 ns/op	        25.59 avg-load	    783788 block-µs/op	     91213 run-µs/op	         0 wait-overhead-µs/op	         0 wait-µs/op
BenchmarkWaitiness/yield/wait-4   	      48	 214082004 ns/op	         5.31 avg-load	     32040 block-µs/op	        93 run-µs/op	     58974 wait-overhead-µs/op	    127550 wait-µs/op
BenchmarkWaitiness/yield/mixed-4  	       8	1310149936 ns/op	        38.20 avg-load	   1379195 block-µs/op	    102958 run-µs/op	    204797 wait-overhead-µs/op	    124973 wait-µs/op
BenchmarkWaitiness/noYield/run-4  	      12	 937030950 ns/op	        12.71 avg-load	    414003 block-µs/op	     91611 run-µs/op	         0 wait-overhead-µs/op	         0 wait-µs/op
BenchmarkWaitiness/noYield/wait-4 	       8	1264407792 ns/op	        13.05 avg-load	    569199 block-µs/op	    126408 run-µs/op	     26367 wait-overhead-µs/op	         0 wait-µs/op
BenchmarkWaitiness/noYield/mixed-4         6	2000242534 ns/op	        27.60 avg-load	   1886621 block-µs/op	    195722 run-µs/op	     13489 wait-overhead-µs/op	         0 wait-µs/op
PASS
ok  	github.com/tommie/go-coopsched	154.875s
Similar Resources

Every 10 minutes, memory, cpu and storage usage is checked and if they over 80%, sending alert via email.

linux-alert Every 10 minutes, memory, cpu and storage usage is checked and if they over 80%, sending alert via email. Usage Create .env file from .env

Feb 6, 2022

Some tests and examples with goroutines and channels

goroutine-playground Some tests and examples with goroutines and channels simpleAsyncCalls Runs functions in background and doesn't wait for results a

Feb 9, 2022

Golog is a logger which support tracing and other custom behaviors out of the box. Blazing fast and simple to use.

GOLOG Golog is an opinionated Go logger with simple APIs and configurable behavior. Why another logger? Golog is designed to address mainly two issues

Oct 2, 2022

Pragmatic and minimalistic module for collecting and sending traces from Go code 💪🏽

Pragmatic and minimalistic module for collecting and sending traces from Go code 💪🏽

tracing-go Pragmatic and minimalistic module for collecting and exporting trace data from the Go code. prometheus/client_golang but for Traces NOTE: T

Jan 6, 2023

Simple and blazing fast lockfree logging library for golang

Simple and blazing fast lockfree logging library for golang

glg is simple golang logging library Requirement Go 1.11 Installation go get github.com/kpango/glg Example package main import ( "net/http" "time"

Nov 28, 2022

a golang log lib supports level and multi handlers

go-log a golang log lib supports level and multi handlers Use import "github.com/siddontang/go-log/log" //log with different level log.Info("hello wo

Dec 29, 2022

The Simplest and worst logging library ever written

gologger A Simple Easy to use go logger library. Displays Colored log into console in any unix or windows platform. You can even store your logs in fi

Sep 26, 2022

Simple, configurable and scalable Structured Logging for Go.

log Log is a simple, highly configurable, Structured Logging library Why another logging library? There's allot of great stuff out there, but also tho

Sep 26, 2022

An golang log lib, supports tracking and level, wrap by standard log lib

Logex An golang log lib, supports tracing and level, wrap by standard log lib How To Get shell go get gopkg.in/logex.v1 source code import "gopkg.in/

Nov 27, 2022
Go playground for Conway's famous Game of Life
Go playground for Conway's famous Game of Life

Conway's Game of Life Playground Go playground for Conway's famous Game of Life Example commands ./conways-playground -seed=copper -init=0 -delay=100

Dec 16, 2021
Benchmark comparison with go-redis
Benchmark comparison with go-redis

Rueidis Benchmark Benchmark comparison with go-redis v8.11.4 Rueidis has higher

Mar 24, 2022
The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.

The open-source platform for monitoring and observability. Grafana allows you to query, visualize, alert on and understand your metrics no matter wher

Jan 3, 2023
Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.
Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.

Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.

Nov 10, 2022
Simple and configurable Logging in Go, with level, formatters and writers

go-log Logging package similar to log4j for the Golang. Support dynamic log level Support customized formatter TextFormatter JSONFormatter Support mul

Sep 26, 2022
A Go (golang) package providing high-performance asynchronous logging, message filtering by severity and category, and multiple message targets.

ozzo-log Other languages 简体中文 Русский Description ozzo-log is a Go package providing enhanced logging support for Go programs. It has the following fe

Dec 17, 2022
Library and program to parse and forward HAProxy logs

haminer Library and program to parse and forward HAProxy logs. Supported forwarder, Influxdb Requirements Go for building from source code git for dow

Aug 17, 2022
Cloudinsight Agent is a system tool that monitors system processes and services, and sends information back to your Cloudinsight account.

Cloudinsight Agent 中文版 README Cloudinsight Agent is written in Go for collecting metrics from the system it's running on, or from other services, and

Nov 3, 2022
Distributed simple and robust release management and monitoring system.
Distributed simple and robust release management and monitoring system.

Agente Distributed simple and robust release management and monitoring system. **This project on going work. Road map Core system First worker agent M

Nov 17, 2022
List files and their creation, modification and access time on android

andfind List files and their access, modification and creation date on a Android

Jan 5, 2022