The Go team will release version 1.14 in February. Compared to the previous version of the upgrade, The performance of Go1.14 has been significantly improved, and many new features have been added. Let’s take a look at the surprises of Go1.14.

1. Performance improvement

Here are a few of the performance improvements that Go1.14 has made.

1.1 Defer performance is “exceptional”

How awesome is abnormal? We can take a look at it using a simple benchmark. Use the following example (defer_test-go):

package main

import (
	"testing"
)

type channel chan int

func NoDefer() {
	ch1 := make(channel, 10)
	close(ch1)
}

func Defer() {
	ch2 := make(channel, 10)
	defer close(ch2)
}

func BenchmarkNoDefer(b *testing.B) {
	for i := 0; i < b.N; i++ {
		NoDefer()
	}
}

func BenchmarkDefer(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Defer()
	}
}
Copy the code

We tested Go1.13 and Go1.14 versions respectively. As for the management switch of multiple versions of Go, we recommend you to use GVM, which is very convenient. To start with the Go1.13 version, just command: GVM use Go1.13; Run: go test-bench =. -v

goos: darwin goarch: amd64 pkg: Github.com/GuoZhaoran/myWebSites/data/goProject/defer BenchmarkNoDefer - 4 15759076 74.5 ns/op BenchmarkDefer - 4, 11046517 102 ns/op PASS ok github.com/GuoZhaoran/myWebSites/data/goProject/defer 3.526 sCopy the code

As you can see, the performance overhead of Go1.13’s call to defer to close the channel is still quite high, with the OP almost 30ns worse off. Switch to go1.14: GVM use go1.14; Go test-bench =.-v

goos: darwin goarch: amd64 pkg: Github.com/GuoZhaoran/myWebSites/data/goProject/defer BenchmarkNoDefer BenchmarkNoDefer - 4 13094874 80.3 ns/op BenchmarkDefer BenchmarkDefer - 4 13227424 ns/op PASS ok github.com/GuoZhaoran/myWebSites/data/goProject/defer 2.328 80.4 sCopy the code

Version Go1.14 uses defer to close a channel with almost zero overhead!

The official response to this improvement is that Go1.14 improves the performance of most uses of defer, with almost zero overhead! Defer can already be used in performance-critical scenarios.

As for defer, some optimizations have been made in Go1.13, and most uses of defer have improved performance by 30% compared to Go1.12. The improvements to Go1.14 are even more exciting! As for the principle and details of Go1.14’s optimization of defer, I haven’t collected any reference materials yet. I believe that there will be some sort out soon, so you can pay attention to it. As for the design principle of Go defer and what improvements Go1.13 makes to defer, I recommend the following articles:

  • Understand more about defer
  • Take a closer look at the implementation mechanism of Defer
  • How has Go1.13 defer improved performance

1.2 Goroutine supports asynchronous preemption

As the performance of the Go language scheduler gets better and better over version iterations, let’s look at the G-M-P model used by a scheduler. First, some concepts:

  • G (Goroutine) : Goroutine, created by the keyword go
  • M (Machine) : Called Machine in Go, it can be understood as a worker thread
  • P: Processor P is the intermediate layer between thread M and the Goroutine (not the CPU).

M must hold P to execute code in G. P has its own local runqueue, runq, which consists of runable G. The following diagram shows the relationship between thread M, processor P, and goroutine.

The working principle of the Go scheduler is processor P from local queue, in turn, the choice of goroutine on M thread scheduling, maintain the G P may be uneven, the scheduler for this maintains a global G queue, when P after perform the local G task, will try to obtain from the global queue G task run (lock), When neither the P local queue nor the global queue has a runnable task, an attempt is made to steal G from other P to run on the local queue (task theft).

In Go1.1, the scheduler does not support preemptive scheduling and can only rely on Goroutine to actively allocate CPU resources, which has very serious scheduling problems:

  • A single Goroutine can always run on thread without switching to another goroutine, causing starvation
  • Garbage collection pauses the entire program (stop-the-world (STW), which can take several minutes without preemption, causing the entire program to fail to work

In Go1.12, the compiler inserts a function at a specific time and triggers preemption through function call as an entry, thus realizing cooperative preemption scheduling. But there are some edge cases with this scheduling approach that requires active coordination of function calls, such as the following example:

package main

import (
	"runtime"
	"time"
)

func main() {
	runtime.GOMAXPROCS(1)
	
	go func() {
		for {
		}
	}()
	
	time.Sleep(time.Millisecond)
	println("OK")}Copy the code

A goroutine is created and suspended. The main Goroutine calls sleep first, and the only P goes to the goroutine created by the for loop, and the main Goroutine is never scheduled again. In other words, before Go1.14, this code would never have printed OK, because cooperative preemptive scheduling would not have preempted a Goroutine that did not voluntarily relinquish execution and did not participate in any function calls.

Go1.14 solves the above problems by implementing true preemptive scheduling based on signals. When Go1.14 is started, the SIGURG signal handler runtime.doSigPreempt is registered in the runtime.sighandler function. When the stack scan of garbage collection is triggered, the function is called to suspend goroutine and send a signal to M. Causes the current Goroutine to go to sleep and continue to execute other Goroutines.

The implementation mechanism of the Go language scheduler is a very deep topic. Here are a few articles that are especially worth exploring:

  • Scheduling system design essentials
  • The Go Scheduler
  • The Original Language of Go by Ou Changkun

1.3 Time. Timer Timer performance is greatly improved

Let’s take a look at the official Benchmark data. The data source

Changes inthe time package benchmarks: Name old time/ OP new time/ OP delta Afterfunc-12 1.57ms ± 1% 0.07ms ± 1% -95.42% (P =0.000 n=10+8) after-12 1.63ms ± 3% Simultaneousafterfun-12 78.3µs ± 3% 73.6µs ± 3% -6.01% (P =0.000 n=9+10 138µs ± 1% 111µs ± 1% -19.57% (P =0.000 n=10+9) startStop-12 28.7µs ± 1% 31.5µs ± 5% +9.64% (P =0.000 n=10+7) reset-12 6.78µs ± 1% 4.24µs ± 7%-37.45% (P =0.000 N =9+10) Sleep-12 183µs ± 1% 125µs ± 1%-31.67% (P =0.000 N =10+9) Ticker-12 5.40ms ± 2% 0.03ms ± 1% -99.43% (P = 0.000N =10+10) sub-12 114ns ± 1% 113ns ± 3% ~ (P = 0.069n =9+10) now-12 37.2ns ± 1% 36.8ns ± 3% ~ (p= 0.287N =8+8) NowUnixNano-12 38.1 NS ± 2% 37.4ns ± 3% -1.87% (p= 0.020n =10+9) Format-12 252ns ± 2% 195ns ± 3%-22.61% (p=0.000 n=9+10) FormatNow-12 234ns ± 1% 177ns ± 2%-24.34% (p=0.000 n=10+10) MarshalJSON-12 320ns ± 2% MarshalText-12 320ns ± 2% 245ns ± 2% -23.30% (p=0.000 n=9+10) Parse-12 206ns ± 2% 208ns ± 4% ~ (P =0.084 N =10+10) ParseDuration-12 89.1ns ± 1% 86.6ns ± 3% -2.78% (P =0.000 n=10+10) hour-12 4.43ns ± 2% 4.46ns ± 1% ~ (p=0.324 N =10+8) Second-12 4.47ns ± 1% 4.40ns ± 3% ~ (P =0.145 N =9+10) year-12 14.6ns ± 1% 14.7ns ± 2% ~ (p = 0.112 n = 9 + 9) Day - 12 to 20.1 ns + 20.2 ns + 1% ~ 3% (p = 0.404 n = 10 + 9)Copy the code

The benchmark results showed that AfterFunc, After, and Ticker in the Go1.14 Time package all saw “significant” performance improvements.

In versions prior to Go1.10, the Go language maintained all timers using a global quadrilateral small top heap. The implementation mechanism looks like this:

Looking at the picture is a bit abstract. Here is a description of the process:

  • G6 calls the function to create a timer, and the system generates a TimerProc that is placed at the head of the local queue. TimerProc is also a G and is called by the system
  • When P is scheduled to execute G of TimerProc, the function is called to give up P, G is executed on M1, the thread sleeps, G6 blocks on channel and is saved to the heap
  • Wake up P and obtain M3 to continue scheduling and executing tasks G1 and G4. After all tasks are executed, P is released and M3 sleeps
  • After TimerProc sleep expires, P is awakened and TimerProc is executed to restore G6 to P’s local queue for execution. TimerProc sleeps with M1 again, waiting to wake up the next time a timer is created
  • P wakes up again, acquires M3, and executes task G6

It may be rough to describe the working principle of Timer, but we can see that the execution of a Timer task has experienced many M/P switches, and such system overhead is very large. In addition, it is necessary to lock the recovery from G to P traversed from the globally unique heap, resulting in poor Timer performance before Go1.10. But in the timing requirements are not particularly harsh scenarios, it is also perfectly competent.

Go1.10 increases the number of timer heaps to 64, and uses the coroutine’s ProcessID % 64 to calculate the corresponding heap saved by the timer, that is, when the number of P is less than 64, each P will only save the timer to 1 heap, so as to avoid the performance loss caused by locking. Multiple PS are distributed in the same heap only if P is set to greater than 64, and locks are still required, although few services set P to greater than 64.

However, as our previous analysis shows, the key to improving the performance of Go timers is to eliminate the overhead of M/P switching frequently when waking up a timer, and Go1.10 does not solve the fundamental problem. Go1.14 did it! Maintain your own timer heap directly on each P, like maintaining your own local queue runq.

I have to say that this design is very nice. First of all, it solves the most important problem, the wake up timer doesn’t have to do a lot of M/P switching, second, it doesn’t have to maintain the TimerProc system coroutine (Go1.14 removes the TimerProc code implementation), and it doesn’t have to worry about locking because of competition. There are more timing of timer scheduling. When P is scheduling G, it can check whether the timer expires once. Like G task, when P has no timer locally, it can try to steal some timer tasks from other P to run.

Go1.14 time.Timer implementation, recommended to everyone B station video, I benefit from a lot: Go time.Timer source analysis

2. Language changes

2.1 Allow embedding of interfaces with overlapping method sets

This is probably the biggest change to Go1.14 at the language level. The following interface definitions were not allowed before Go1.14:

type ReadWriteCloser interface {
	io.ReadCloser
	io.WriteCloser
}
Copy the code

IO.ReadCloser and IO.WriteCloser Close duplicate method Close Go1.14 starts to allow methods with the same signature to be embedded in an interface. Note the same signature. The following code will still not execute in Go1.14 because the Close method defined in MyCloser has a different signature than the Close method defined in IO.

type MyCloser interface {
	Close()
}

type ReadWriteCloser interface {
	io.ReadCloser
	MyCloser
}
Copy the code

Change MyCloser’s Close method signature to:

type MyCloser interface {
	Close() error
}
Copy the code

Then the code can be built in Go1.14! Easily overloading interface definitions.

2.2 Testing package T, B and TB are added with CleanUp method

In parallel tests and subtests, CleanUp(f func()) is very useful and will run F (if multiple registrations are made) on a last-in, first-out basis.

Here’s an example:

func TestSomeing(t *testing.T) {
	t.CleanUp(func() {
		fmt.Println("Cleaning Up!")
	})

	t.Run(t.Name(), func(t *testing.T) {
		
	})
}
Copy the code

You can call T. leanUp or B.leanup after test or benchmark to do some final statistics, very useful!

2.3 Added the new hash/maphash package

This new package provides hash functions on byte sequences. These hash functions are used to implement hash tables or other data structures that need to map arbitrary strings or sequences of bytes to a uniform distribution of integers. These hash functions are collision-resistant, but not cryptographically secure.

2.4 Changes to WebAssembly

For those interested in WebAssembly, Go1.14 makes the following changes to WebAssembly:

  • The Javascript Value referenced by Go can be garbage collected via the js.value object
  • Js. Value values are no longer compared using the == operator; the Equal function must be used
  • Js. Value added IsUndefined, IsNull, IsNaN functions

2.5 Reflect package changes

Reflect sets the PkgPath field in the StructField element, StructOf supports creating structural types using unexported fields.

2.6 Other language changes

There are a number of other language changes in Go1.14, but here are some (not exhaustive) :

Code package changes
crypto/tls SSLv3 support is removed, TLS1.3 is enabled by default, and its version is configured through the config. MaxVersion field rather than through the DEBUG environment variable
strconv The NumError type adds a new UnWrap method that can be used to find the cause of conversion failures. Errors.Is can be used to check whether NumError values are underlying Errors: strconv.ErrRange or strconV. ErrSyntax
runtime Runtime. Goexit is no longer terminated by recursive panic/recover
runtime/pprof The generated profile no longer includes the pseudo PC used for inline markup. The symbolic information for inline functions is encoded in the format expected by the Pprof tool
net/http The new Header method Values can be used to obtain the ownership associated with the normalized Key, and the new Transport field DialTLSContext can be used to specify the optional DAIL function to create A TLS connection with a non-proxy HTTPS request
net/http/httptest The Server field EnableHTTP2 can be used to support HTTP/2 on test Server
mime The default type of.js and.mjs files is Text /javascript, not Application /javascirpt
mime/multipart The new Reader method, NextRawPart, supports fetching the next MIME part without transparently decoding the referenced printable data
signal On Windows, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT will generate a syscall.SIGTERM signal, How to generate syscall.sigint signals like Control-c and Control-break
math The new FMA function does not round x*y calculations when performing floating-point x*y + z calculations (several architectures use dedicated hardware instructions for this calculation to improve performance)
math/bits The new functions Rem, Rem32, Rem64 support calculating the remainder even when the quotient overflows
go/build The Context type has a new field Dir that sets the build working directory
unicode Unicode packages and support across the system have been upgraded from unicode 1.0 to unicode 12.0, adding 554 new characters, including four scripts and 61 new emojis

3. Tool changes

As for the improvement of tools in Go1.14, I would like to talk about go mod and Go Test. Go officials definitely hope developers to use the official package management tools. Go1.14 has improved many functions, if you have other functional requirements for Go mod in business development, you can issue to the official.

The go Mod has the following major improvements:

  • Incompatiable versions: If the latest version of a module contains the go.mod file, go Get will not upgrade to the incompatible major version of the module unless specifically requested or already requested. The Go List also ignores incompatible versions of this module when obtained directly from version control, but may include them if reported by an agent.
  • Go.mod file maintenance: Go commands other than Go mod Tidy no longer remove the require directive, which specifies the indirectly dependent version that has been implied by the other dependencies of the main module. Go commands other than Go mod Tidy no longer edit the go.mod file if the changes are cosmetic.
  • Module download: In Module mode, the GO command supports SVN repositories, and the go command now includes summaries of plain text error messages from Module agents and other HTTP servers. If the error message is a valid UTF-8 and contains graphic characters and Spaces, only the error message is displayed.

The go test changes are minor:

  • Go test-v now streams t.log output instead of output at the end of all test data.

4. 4, Tardiness

Go. dev was announced by the Go team in a Golang-Nuts email on November 14, 2019. We used Go.dev for the first time and found that it provided documentation for Godoc.org and was much more user-friendly. Godoc.org also announced that it will redirect to Go. dev, as you can see, the go official team will make Go. dev a priority of ecological construction.

Pkg.go. dev is a companion site to Go.org, with information on select use cases and other resources. It offers go documentation like GoDoc.org, but it understands modules better and provides information about previous versions of packages, detects and displays licenses, and has a better search algorithm.

Recommended use!

5. Future outlook

Let’s talk about generics first! Go has been criticized by many developers for its lack of generics. Language designers need to make trade-offs between programming efficiency, compilation speed, and execution speed. The introduction of generics will definitely affect compilation speed and execution speed, and also increase the complexity of the compiler, so the community is very cautious about considering generics. The Go team didn’t think it was urgent to include generics, but more important to improve the runtime mechanism, including schedulers, garbage collectors, and so on. But developers are increasingly vocal, and Go officials have promised generics in 2.0. By the end of 2020, Go may be coming with generics, so stay tuned! Why’s THE Design? There are no generics in Go.

Let’s talk about error handling in Go. Try proposal is supported by many people, but also opposed by many people. You can pay attention to issue #32825. Conclusion: Go has abandoned the proposal! These ideas have not been fully developed, especially when considering the implementation costs of changing the language, so enumerations and immutable types have not been considered by the Go language team recently.

Go1.14 also has some planned but unfinished work. Go1.14 attempts to optimize the Page allocator to significantly reduce lock contention when GOMAXPROCS values are high. This change has a great impact, which can significantly improve Go parallelism and further improve the performance of timer. However, due to the complexity of implementation, there are some problems that can not be solved, so we have to delay until THE completion of Go1.15.

As we look to the future of Go, officials are sure to make the scheduler, runtime, and garbage collection better and better. The toolchain will continue to enrich and adjust the corresponding functions to provide convenience for developers. At the same time, Go will continue to improve its ecosystem, with more and more toolkits and community mature applications. Let’s look forward to it!