Skip to content

cmd/gc: optimize switch statements with string literal cases #10000

@jonhoo

Description

@jonhoo

Consider the following benchmark:

package main

import (
    "fmt"
    "testing"
)

var words = []string{"super", "califragi", "listic", "expiali", "docius"}
var wlen = len(words)

func BenchmarkSwitch(b *testing.B) {
    m := 0
    for i := 0; i < b.N; i++ {
        switch words[i%wlen] {
        case "super":
            m++
        case "califragi":
            m++
        case "listic":
            m++
        case "expiali":
            m++
        case "docius":
            m++
        }
    }
    fmt.Println(m)
}

func BenchmarkIf(b *testing.B) {
    m := 0
    for i := 0; i < b.N; i++ {
        w := words[i%wlen]
        if w == "super" {
            m++
        } else if w == "califragi" {
            m++
        } else if w == "listic" {
            m++
        } else if w == "expiali" {
            m++
        } else if w == "docius" {
            m++
        }
    }
    fmt.Println(m)
}

On my machine, this gives

$ go test -bench . -benchtime 30s
BenchmarkSwitch
2000000000          29.2 ns/op
BenchmarkIf
2000000000          22.9 ns/op
ok      x   109.375s

Despite the switch statement having more opportunity for optimization, it is consistently slower than the if variant. After a brief discussion on golang-nuts, it was pointed out that Go will move to a binary search for switch statements with more than three constant cases. Firstly, this threshold seems too low, as runtime.eqstring (because of length checks) is often an order of magnitude faster than runtime.cmpstring. Second, other optimizations can probably also be implemented here by the compiler. @josharian mentions a few in the golang-nuts thread:

I think that there can be significant improvement here, e.g. using diverge indices and lengths for the binary search followed by a final call to eqstring.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions