diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2019-10-11 14:55:46 +0300 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2019-10-13 13:36:17 +0300 |
commit | 653e6856ea1cfc60cc16733807d23b302dbe4bd5 (patch) | |
tree | c77d48f3ec9a07a47ae5a8d53b2cf7b6c459f66f /resources/page/pages_sort_search.go | |
parent | f4f566edf4bd6a590cf9cdbd5cfc0026ecd93b14 (diff) |
resources/page: Use binary search in Pages.Prev/Next if possible
This is obviously much faster for lager data sets:
```bash
name old time/op new time/op delta
SearchPage/ByWeight-100-4 267ns ± 4% 272ns ± 5% ~ (p=0.457 n=4+4)
SearchPage/ByWeight-5000-4 10.8µs ± 3% 1.2µs ± 2% -88.99% (p=0.029 n=4+4)
SearchPage/ByWeight-10000-4 21.1µs ± 1% 1.4µs ±11% -93.28% (p=0.029 n=4+4)
```
See #4500
Diffstat (limited to 'resources/page/pages_sort_search.go')
-rw-r--r-- | resources/page/pages_sort_search.go | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/resources/page/pages_sort_search.go b/resources/page/pages_sort_search.go new file mode 100644 index 000000000..ff44e42d5 --- /dev/null +++ b/resources/page/pages_sort_search.go @@ -0,0 +1,126 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package page + +import "sort" + +// Used in page binary search, the most common in front. +var pageLessFunctions = []func(p1, p2 Page) bool{ + DefaultPageSort, + lessPageDate, + lessPagePubDate, + lessPageTitle, + lessPageLinkTitle, +} + +func searchPage(p Page, pages Pages) int { + if len(pages) < 1000 { + // For smaller data sets, doing a linear search is faster. + return searchPageLinear(p, pages, 0) + } + + less := isPagesProbablySorted(pages, pageLessFunctions...) + if less == nil { + return searchPageLinear(p, pages, 0) + } + + i := searchPageBinary(p, pages, less) + if i != -1 { + return i + } + + return searchPageLinear(p, pages, 0) +} + +func searchPageLinear(p Page, pages Pages, start int) int { + for i := start; i < len(pages); i++ { + c := pages[i] + if c.Eq(p) { + return i + } + } + return -1 +} + +func searchPageBinary(p Page, pages Pages, less func(p1, p2 Page) bool) int { + n := len(pages) + + f := func(i int) bool { + c := pages[i] + isLess := less(c, p) + return !isLess || c.Eq(p) + } + + i := sort.Search(n, f) + + if i == n { + return -1 + } + + return searchPageLinear(p, pages, i) + +} + +// isProbablySorted tests if the pages slice is probably sorted. +func isPagesProbablySorted(pages Pages, lessFuncs ...func(p1, p2 Page) bool) func(p1, p2 Page) bool { + n := len(pages) + step := 1 + if n > 500 { + step = 50 + } + + is := func(less func(p1, p2 Page) bool) bool { + samples := 0 + + for i := n - 1; i > 0; i = i - step { + if less(pages[i], pages[i-1]) { + return false + } + samples++ + if samples >= 15 { + return true + } + } + return samples > 0 + } + + isReverse := func(less func(p1, p2 Page) bool) bool { + samples := 0 + + for i := 0; i < n-1; i = i + step { + if less(pages[i], pages[i+1]) { + return false + } + samples++ + + if samples > 15 { + return true + } + } + return samples > 0 + } + + for _, less := range lessFuncs { + if is(less) { + return less + } + if isReverse(less) { + return func(p1, p2 Page) bool { + return less(p2, p1) + } + } + } + + return nil +} |