The State of Go

Where we are in February 2016

Francesc Campoy

Gopher at Google

Time flies

Go 1.4 is one year old (happy birthday!)

Go 1.5 is already 6 months old!

Go 1.6 to be released sometime in February.

Go 1.6 Candidate Release 1 was released on January 28th

Notes

The slides are available on talks.golang.org/2016/state-of-go.slide

Most of the code examples won't run except locally and using Go 1.6.

The playground still runs Go 1.5.

Agenda

Changes since Go 1.5:

Changes to the language

Changes to the language

None.

This is a feature.

Changes to the standard library

Changes to the standard library

A couple of new things, in order of excitement.

Also more speed and fewer bugs.

net/http

If:

you're using HTTP/2!

HTTP/2

At a high level, HTTP/2:

text/template

Imagine that given a slice of strings:

[]string{"one", "two", "three"}

We want to write a template that will generate:

<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>

What template would you write?

text/template: first try

Naturally, I write this:

// +build OMIT

package main

import (
	"html/template"
	"log"
	"os"
)

var tmpl = template.Must(template.New("tmpl").Parse(`
<ul>
{{range .}}
    <li>{{.}}</li>
{{end}}
</ul>
`))

func main() {
	err := tmpl.Execute(os.Stdout, []string{"one", "two", "three"})
	if err != nil {
		log.Fatal(err)
	}
}

But unfortunately it's not exactly what I want!

text/template: let's fix it

We need to be careful with the line breaks.

// +build OMIT

package main

import (
	"html/template"
	"log"
	"os"
)

var tmpl = template.Must(template.New("tmpl").Parse(`
<ul>
{{range .}}<li>{{.}}</li>
{{end}}</ul>
`))

func main() {
	err := tmpl.Execute(os.Stdout, []string{"one", "two", "three"})
	if err != nil {
		log.Fatal(err)
	}
}

This works now, but ... I don't really like my code!

text/template: meet {{- and -}}

Go 1.6 brings two new delimiters:

Similar to {{ and }}, but all white space on the - side will be trimmed.

The template:

{{23 -}}
<
{{- 45}}

generates:

23<45

text/template: back to our problem

We can now have:

// +build OMIT

package main

import (
	"html/template"
	"log"
	"os"
)

var tmpl = template.Must(template.New("tmpl").Parse(`
<ul>
{{range . -}}
    <li>{{.}}</li>
{{end -}}
</ul>
`))

func main() {
	err := tmpl.Execute(os.Stdout, []string{"one", "two", "three"})
	if err != nil {
		log.Fatal(err)
	}
}

text/template: the block action

Go 1.6 brings also a new action named block.

Let's see what it is useful for.

Factoring out repetition in templates

Both <ul> share the same structure.

{{define "presentation"}}
    Authors:
    <ul>
    {{range .Authors}}
        <li>{{.}}</li>
    {{end}}
    </ul>

    Topics:
    <ul>
    {{range .Topics}}
        <li>{{.}}</li>
    {{end}}
    </ul>
{{end}}

Templates can be used to avoid repetition.

Factoring out repetition with templates (cont.)

We can define a new template:

{{define "list"}}
    <ul>
    {{range .}}
        <li>{{.}}</li>
    {{end}}
    </ul>
{{end}}    

And use it where needed:

{{define "presentation"}}
    Authors:
    {{template "list" .Authors}}

    Topics:
    {{template "list" .Topics}}
{{end}}

Factoring out repetition with templates (cont.)

We can parse that template and execute it.

// +build OMIT

package main

import (
	"html/template"
	"log"
	"os"
)

const tmplText = `
{{define "list"}}
	<ul>
	{{range .}}
		<li>{{.}}</li>
	{{end}}
	</ul>
{{end}}	

{{define "presentation"}}
	Authors:
	{{template "list" .Authors}}

	Topics:
	{{template "list" .Topics}}
{{end}}
`

type Presentation struct {
	Authors []string
	Topics  []string
}

func main() {
    p := Presentation{
        Authors: []string{"one", "two", "three"},
        Topics:  []string{"go", "templates"},
    }

    tmpl := template.Must(template.New("presentation").Parse(tmplText))

    err := tmpl.Execute(os.Stdout, p)
    if err != nil {
        log.Fatal(err)
    }
}

Template redefinition

We made also our template easier to reuse, as we can redefine list.

// +build OMIT

package main

import (
	"html/template"
	"log"
	"os"
	"strings"
)

const tmplText = `
{{define "list"}}
	<ul>
	{{range .}}
		<li>{{.}}</li>
	{{end}}
	</ul>
{{end}}	

{{define "presentation"}}
	Authors:
	{{template "list" .Authors}}

	Topics:
	{{template "list" .Topics}}
{{end}}
`

type Presentation struct {
	Authors []string
	Topics  []string
}

func main() {
    p := Presentation{
        Authors: []string{"one", "two", "three"},
        Topics:  []string{"go", "templates"},
    }

    tmpl := template.Must(template.New("presentation").Parse(tmplText))

    tmpl = tmpl.Funcs(template.FuncMap{"join": strings.Join})
    tmpl = template.Must(tmpl.Parse(`{{define "list"}} {{join . " | "}} {{ end}}`))

    err := tmpl.Execute(os.Stdout, p)
    if err != nil {
        log.Fatal(err)
    }
}

Meet the block action

The block action defines and executes a template in place.

{{define "presentation"}}
    Authors:
    {{block "list" .Authors}}
    <ul>
    {{- range .}}
        <li>{{.}}</li>
    {{- end}}
    </ul>
    {{end}}

    Topics:
    {{template "list" .Topics}}
{{end}}

That template defined by block can be:

Why do we need it?

It is more compact when

We can make the following template more compact with block.

{{define "content" .}}
    <h1>{{.Heading}}<h1>
    <p>{{.Content}}</p>
{{end}}

{{define "page"}}
    <title>{{.Title}}</title>
    <body>
    {{template "content" .}}
    </body>
{{end}}

Why do we need it? (cont.)

We can make the following template more compact with block.

{{define "page"}}
    <title>{{.Title}}</title>
    <body>
    {{block "content" .}}
        <h1>{{.Heading}}<h1>
        <p>{{.Content}}</p>
    {{end}}
    </body>
{{end}}

And still easily redefine content.

sort.Sort is faster

Sort sorts your data by calling Less, Swap, and Len.

We reduced the number of comparisons and swaps by about 10%.

Sort []int with Go 1.5

BenchmarkSort_1-4       20000000              67.2 ns/op
BenchmarkSort_10-4      10000000               227 ns/op
BenchmarkSort_100-4       500000              3863 ns/op
BenchmarkSort_1000-4       30000             52189 ns/op

Sort []int with Go 1.6

BenchmarkSort_1-4       20000000              64.7 ns/op
BenchmarkSort_10-4      10000000               137 ns/op
BenchmarkSort_100-4       500000              2849 ns/op
BenchmarkSort_1000-4       30000             46949 ns/op

sort.Sort is faster - plot

Sort order and sort.Stable

Reminder: sort.Sort is not a stable sort.

// +build OMIT

package main

import (
	"fmt"
	"sort"
	"strings"
)

type byLength []string

func (b byLength) Len() int           { return len(b) }
func (b byLength) Less(i, j int) bool { return len(b[i]) < len(b[j]) }
func (b byLength) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }

func main() {
    values := []string{"ball", "hell", "one", "joke", "fool", "moon", "two"}
    sort.Sort(byLength(values))
    fmt.Println(strings.Join(values, "\n"))
}

Use sort.Stable:

// +build OMIT

package main

import (
	"fmt"
	"sort"
	"strings"
)

type byLength []string

func (b byLength) Len() int           { return len(b) }
func (b byLength) Less(i, j int) bool { return len(b[i]) < len(b[j]) }
func (b byLength) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }

func main() {
    values := []string{"ball", "hell", "one", "joke", "fool", "moon", "two"}
    sort.Stable(byLength(values))
    fmt.Println(strings.Join(values, "\n"))
}

Minor changes

too many to discuss: find them here

Let's just discuss one

time.Parse is smarter!

// +build OMIT

package main

import (
	"fmt"
	"time"
)

func main() {
    days := []string{"2015 Feb 29", "2016 Feb 29", "2017 Feb 29"}

    fmt.Println("Are these days valid?")
    for _, day := range days {
        _, err := time.Parse("2006 Jan 2", day)
        fmt.Printf("%v -> %v\n", day, err == nil)
    }
}

Changes to the runtime

Detection of concurrent map accesses

Detection of unsafe concurrent access to maps.

// +build OMIT

package main

import (
	"fmt"
	"sync"
)

func main() {
    const workers = 100 // what if we have 1, 2, 25?

    var wg sync.WaitGroup
    wg.Add(workers)
    m := map[int]int{}
    for i := 1; i <= workers; i++ {
        go func(i int) {
            for j := 0; j < i; j++ {
                m[i]++
            }
            wg.Done()
        }(i)
    }
    wg.Wait()
	fmt.Println(m)
}

Outputs:

fatal error: concurrent map read and map write
fatal error: concurrent map writes

Does it make it slower?

No!

Let's benchmark it - with a correct solution.

func count(n int) {
    var wg sync.WaitGroup
    wg.Add(n)
    m := map[int]int{}
    var mu sync.Mutex
    for i := 1; i <= n; i++ {
        go func(i int) {
            for j := 0; j < i; j++ {
                mu.Lock()
                m[i]++
                mu.Unlock()
            }
            wg.Done()
        }(i)
    }
    wg.Wait()
}

Benchmark results

Go 1.4 - GOMAXPROCS = 4

BenchmarkCount_1         1000000              1862 ns/op
BenchmarkCount_10         100000             21214 ns/op
BenchmarkCount_100          1000           1602507 ns/op
BenchmarkCount_1000           10         141712948 ns/op

Go 1.5 - GOMAXPROCS = 4

BenchmarkCount_1-4       2000000               867 ns/op
BenchmarkCount_10-4       200000              6909 ns/op
BenchmarkCount_100-4        1000           1025092 ns/op
BenchmarkCount_1000-4         20          94093719 ns/op

Go 1.6 - GOMAXPROCS = 4

BenchmarkCount_1-4       2000000               750 ns/op
BenchmarkCount_10-4       200000              6582 ns/op
BenchmarkCount_100-4        2000           1113790 ns/op
BenchmarkCount_1000-4         20          87998054 ns/op

Benchmark results plot

Benchmark results

Go 1.4 - GOMAXPROCS = 1

BenchmarkCount_1         100000               1370 ns/op
BenchmarkCount_10         20000               8622 ns/op
BenchmarkCount_100          500             362725 ns/op
BenchmarkCount_1000          50           31378803 ns/op

Go 1.5 - GOMAXPROCS = 1

BenchmarkCount_1-4      2000000                776 ns/op
BenchmarkCount_10-4      200000               6288 ns/op
BenchmarkCount_100-4       3000             345037 ns/op
BenchmarkCount_1000-4        50           31751625 ns/op

Go 1.6 - GOMAXPROCS = 1

BenchmarkCount_1-4       2000000               767 ns/op
BenchmarkCount_10-4       200000              6041 ns/op
BenchmarkCount_100-4        5000            329328 ns/op
BenchmarkCount_1000-4         50          30176034 ns/op

Benchmark results plot

Garbage Collector in Go 1.5

At GopherCon 2015 Rick Hudson gave a presentation about the Go 1.5 low latency collector

Garbage Collector in Go 1.6

At QCon SF in November Rick Hudson gave an updated presentation which showed this comparison of Go 1.5 to the upcoming Go 1.6

Yes, that is gigabytes on the X axis

Garbage Collector on tip

Right now it's even better!

People loved it with Go 1.5

original tweet

People love it even more with Go 1.6

original tweet

Oops

original tweet and video

New ports

Experimental ports to Linux on 64-bit MIPS (linux/mips64 and linux/mips64le).

Experimental port to Android on 32-bit x86 (android/386).

On Linux on little-endian 64-bit PowerPC (linux/ppc64le), Go 1.6 now supports cgo with external linking and is roughly feature complete.

On NaCl, Go 1.5 required SDK version pepper-41. Go 1.6 adds support for later SDK versions.

Changes to the tooling

The cgo tool

Go is garbage collected, can C and Go share memory?

In short:

In more detail:

This is checked by the runtime at execution.

You could disable the checks, but you probably shouldn't.

Sharing pointers between Go and C

package main

/*
int fn(void* arg) { return arg == 0; }
*/
import "C"
import "unsafe"

type T struct{ a, b int }
type X struct{ t *T }

func main() {
    t := T{a: 1, b: 2}
    C.fn(unsafe.Pointer(&t)) // correct

    x := X{t: &t}
    C.fn(unsafe.Pointer(&x)) // incorrect
}

Outputs:

panic: runtime error: cgo argument has Go pointer to Go pointer

The go tool

GO15VENDOREXPERIMENT is now enabled by default.

How does it work?

/home/user/gocode/
    src/
        server-one/
            main.go            (import "github.com/gorilla/mux")
        server-two/
            main.go            (import "github.com/gorilla/mux")
            vendor/
                github.com/
                    gorilla/
                        mux/
                            ...

server-one uses the mux package in $GOPATH/src/github.com/gorilla/mux.

server-two uses the mux package in vendor.

Vendoring demo

go doc

Go 1.5 added the possibility of searching by name

go doc math Pi

Go 1.6 defines priority of packages with import paths with less elements.

Non vendored packages appear first.

go vet

Go vet warns if the code prints a function instead of its result.

package main

import "fmt"

func foo() string { return "bar" }

func main() {
    fmt.Printf("%v", foo)
}

go vet output:

main.go:8: arg foo in Println call is a function value, not a function call

The warning can be removed using %p in the format string.

The community

The community

Code of Conduct announced on November 24th 2015

Go meetups:

Women Who Go - 7 chapters already!

Conferences:

Go 1.6 release party, February 17th

Go 1.6 ships soon!

Go meetups are organising to hold a release party on the 17th of February.

Join the party!!!

Thank you

Francesc Campoy

Gopher at Google

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)