From 403fd2c075f73caf5f478ad4a42d7d2fc6c3100b Mon Sep 17 00:00:00 2001 From: Matthew Rothenberg Date: Tue, 6 Oct 2020 18:06:01 -0400 Subject: [PATCH] sort: inline search convenience wrappers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SearchInts, SearchFloat64s, and SearchStrings functions are convenience wrappers around the generic Search function, which takes a function parameter to determine truthfulness. However, since this passed function is utilized within a for loop, it cannot currently be inlined by the Go compiler, resulting in some degree of performance overhead (see #15561). This trivial commit manually inlines the Search function itself to these convenience wrappers, avoiding the function call overhead in the hot loop. It replaces 1 line of copy-pasted code with 10 lines of copy-pasted code, however it has a roughly 2x beneficial effect on performance as seen below: $ benchstat before.txt after.txt name old time/op new time/op delta SearchWrappers-16 84.7ns ± 1% 43.8ns ± 2% -48.34% (p=0.000 n=19+19) In the future, generics may enable similar perf gains while avoiding the minimal copypasta, but for now this small change can improve standard library performance and give the existing search "convenience wrappers" performance benefits as well as convenience. Updates #15561 --- src/sort/search.go | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/sort/search.go b/src/sort/search.go index fcff0f9491bd38..c5288890448369 100644 --- a/src/sort/search.go +++ b/src/sort/search.go @@ -81,7 +81,16 @@ func Search(n int, f func(int) bool) int { // The slice must be sorted in ascending order. // func SearchInts(a []int, x int) int { - return Search(len(a), func(i int) bool { return a[i] >= x }) + i, j := 0, len(a) + for i < j { + h := int(uint(i+j) >> 1) // avoid overflow when computing h + if a[h] < x { + i = h + 1 + } else { + j = h + } + } + return i } // SearchFloat64s searches for x in a sorted slice of float64s and returns the index @@ -90,7 +99,16 @@ func SearchInts(a []int, x int) int { // The slice must be sorted in ascending order. // func SearchFloat64s(a []float64, x float64) int { - return Search(len(a), func(i int) bool { return a[i] >= x }) + i, j := 0, len(a) + for i < j { + h := int(uint(i+j) >> 1) // avoid overflow when computing h + if a[h] < x { + i = h + 1 + } else { + j = h + } + } + return i } // SearchStrings searches for x in a sorted slice of strings and returns the index @@ -99,7 +117,16 @@ func SearchFloat64s(a []float64, x float64) int { // The slice must be sorted in ascending order. // func SearchStrings(a []string, x string) int { - return Search(len(a), func(i int) bool { return a[i] >= x }) + i, j := 0, len(a) + for i < j { + h := int(uint(i+j) >> 1) // avoid overflow when computing h + if a[h] < x { + i = h + 1 + } else { + j = h + } + } + return i } // Search returns the result of applying SearchInts to the receiver and x.