Repository navigation
cmd/compile: slices.BinarySearchFunc is not inlined, making it slower than sort.Search #81887
Copy link
Copy link
Closed
Labels
FixPendingIssues that have a fix which has not yet been reviewed or submitted.Issues that have a fix which has not yet been reviewed or submitted.Performancecompiler/runtimeIssues related to the Go compiler and/or runtime.Issues related to the Go compiler and/or runtime.
Milestone
Description
The generic slices.BinarySearchFunc function added in Go 1.21 is significantly slower than sort.Search in some cases, such as the program below. The difference appears to be due to inlining of the Search call, permitting the calls to the lambda to be made static; by contrast BinarySearchFunc, which has a slightly more complex comparison expression (that appears twice), seems to exceed the inliner budget.
goos: darwin
goarch: arm64
pkg: binsearch
cpu: Apple M1 Pro
│ sort.Search │ slices.BinarySearchFunc │
│ sec/op │ sec/op vs base │
Search/C-8 18.56n ± 15% 24.57n ± 3% +32.41% (p=0.002 n=6)
Search/WriteString-8 20.30n ± 5% 25.48n ± 2% +25.54% (p=0.002 n=6)
Search/p.c-8 19.06n ± 1% 24.71n ± 1% +29.64% (p=0.002 n=6)
Search/q.grow-8 19.43n ± 21% 23.11n ± 1% ~ (p=0.065 n=6)
Search/missing-8 18.91n ± 1% 26.51n ± 3% +40.19% (p=0.002 n=6)
geomean 19.24n 24.85n +29.15%
package binsearch
import (
"fmt"
"slices"
"sort"
"strings"
"testing"
)
type entry struct {
id string
val *int
}
var list = func() []entry {
var list []entry
for _, id := range []string{
"A", "B", "C", "D", "Grow", "Len", "Read", "Reset",
"String", "Write", "WriteString", "p.a", "p.b", "p.c", "p.d", "q.grow",
} {
list = append(list, entry{id, new(int)})
}
if !slices.IsSortedFunc(list, func(x, y entry) int { return strings.Compare(x.id, y.id) }) {
panic("unsorted")
}
return list
}()
//go:noinline
func lookupSort(list []entry, key string) *int {
i := sort.Search(len(list), func(i int) bool {
return strings.Compare(list[i].id, key) >= 0
})
if i < len(list) && list[i].id == key {
return list[i].val
}
return nil
}
//go:noinline
func lookupSlices(list []entry, key string) *int {
i, found := slices.BinarySearchFunc(list, key, func(e entry, key string) int {
return strings.Compare(e.id, key)
})
if found {
return list[i].val
}
return nil
}
var keys = []string{"C", "WriteString", "p.c", "q.grow", "missing"}
func TestAgree(t *testing.T) {
for _, e := range list {
if lookupSort(list, e.id) != e.val || lookupSlices(list, e.id) != e.val {
t.Errorf("lookup(%q) failed", e.id)
}
}
if lookupSort(list, "missing") != nil || lookupSlices(list, "missing") != nil {
t.Errorf("lookup(missing) succeeded")
}
}
var sink *int
func BenchmarkSortSearch(b *testing.B) {
for _, key := range keys {
b.Run(fmt.Sprint(key), func(b *testing.B) {
for b.Loop() {
sink = lookupSort(list, key)
}
})
}
}
func BenchmarkSlicesBinarySearchFunc(b *testing.B) {
for _, key := range keys {
b.Run(fmt.Sprint(key), func(b *testing.B) {
for b.Loop() {
sink = lookupSlices(list, key)
}
})
}
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
FixPendingIssues that have a fix which has not yet been reviewed or submitted.Issues that have a fix which has not yet been reviewed or submitted.Performancecompiler/runtimeIssues related to the Go compiler and/or runtime.Issues related to the Go compiler and/or runtime.
Type
Projects
- StatusShow more project fieldsDone
Activity
gabyhelp commentedon Sep 30, 2026
Related Issues
Related Code Changes
Related Discussions
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
gopherbot commentedon Oct 1, 2026
Change https://go.dev/cl/842885 mentions this issue:
slices: make BinarySearchFunc inlinablegopherbot commentedon Oct 1, 2026
Change https://go.dev/cl/842886 mentions this issue:
slices: make BinarySearchFunc inlinableslices: make BinarySearchFunc inlinable