Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add some basic benchmarks to compare to native
Currently only benchmarking `Any`, `Select` and `Sort
- Loading branch information
1 parent
b78fdc9
commit 5e0f4d9
Showing
3 changed files
with
300 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package benchmarks | ||
|
||
// +gen * slice:"Any,Select[*dummyDestinationSelectObject],SortBy" | ||
type dummyObject struct { | ||
Name string | ||
Num int | ||
} | ||
|
||
type dummyDestinationSelectObject struct { | ||
Name string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package benchmarks | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
var ( | ||
dummyObjects dummyObjectSlice | ||
globalBoolResult bool | ||
globalSliceResult []*dummyDestinationSelectObject | ||
globalSliceResul2 []*dummyObject | ||
) | ||
|
||
//Any | ||
func BenchmarkDummyObjAny_Generics(b *testing.B) { | ||
for n := 0; n < b.N; n++ { | ||
globalBoolResult = dummyObjects.Any(func(d *dummyObject) bool { return d.Num > 10000 }) | ||
} | ||
} | ||
|
||
func BenchmarkDummyObjAny_NativeLoop(b *testing.B) { | ||
for n := 0; n < b.N; n++ { | ||
any := false | ||
for _, d := range dummyObjects { | ||
if d.Num > 10000 { | ||
any = true | ||
break | ||
} | ||
} | ||
globalBoolResult = any | ||
} | ||
} | ||
|
||
//Select | ||
func BenchmarkDummyObjSelect_Generics(b *testing.B) { | ||
for n := 0; n < b.N; n++ { | ||
globalSliceResult = dummyObjects.SelectDummyDestinationSelectObject(func(d *dummyObject) *dummyDestinationSelectObject { return &dummyDestinationSelectObject{d.Name} }) | ||
globalBoolResult = len(globalSliceResult) == len(dummyObjects) | ||
} | ||
} | ||
|
||
func BenchmarkDummyObjSelect_NativeLoop(b *testing.B) { | ||
for n := 0; n < b.N; n++ { | ||
globalSliceResult = []*dummyDestinationSelectObject{} | ||
for _, d := range dummyObjects { | ||
globalSliceResult = append(globalSliceResult, &dummyDestinationSelectObject{d.Name}) | ||
} | ||
globalBoolResult = len(globalSliceResult) == len(dummyObjects) | ||
} | ||
} | ||
|
||
//SortBy | ||
func BenchmarkDummyObjSortBy_Generics(b *testing.B) { | ||
for n := 0; n < b.N; n++ { | ||
globalSliceResul2 = dummyObjects.SortBy(func(a *dummyObject, b *dummyObject) (isLess bool) { return a.Num%3 == 0 }) | ||
} | ||
} | ||
|
||
/*TODO: | ||
func BenchmarkDummyObjSortBy_NativeLoop(b *testing.B) { | ||
}*/ | ||
|
||
func init() { | ||
dummyObjects = dummyObjectSlice([]*dummyObject{}) | ||
for i := 0; i < 10000; i++ { | ||
dummyObjects = append(dummyObjects, &dummyObject{fmt.Sprintf("Name %d", i), i}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
// Generated by: gen | ||
// TypeWriter: slice | ||
// Directive: +gen on *dummyObject | ||
|
||
package benchmarks | ||
|
||
// Sort implementation is a modification of http://golang.org/pkg/sort/#Sort | ||
// Copyright 2009 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found at http://golang.org/LICENSE. | ||
|
||
// dummyObjectSlice is a slice of type *dummyObject. Use it where you would use []*dummyObject. | ||
type dummyObjectSlice []*dummyObject | ||
|
||
// Any verifies that one or more elements of dummyObjectSlice return true for the passed func. See: http://clipperhouse.github.io/gen/#Any | ||
func (rcv dummyObjectSlice) Any(fn func(*dummyObject) bool) bool { | ||
for _, v := range rcv { | ||
if fn(v) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
// SelectDummyDestinationSelectObject projects a slice of *dummyDestinationSelectObject from dummyObjectSlice, typically called a map in other frameworks. See: http://clipperhouse.github.io/gen/#Select | ||
func (rcv dummyObjectSlice) SelectDummyDestinationSelectObject(fn func(*dummyObject) *dummyDestinationSelectObject) (result []*dummyDestinationSelectObject) { | ||
for _, v := range rcv { | ||
result = append(result, fn(v)) | ||
} | ||
return | ||
} | ||
|
||
// SortBy returns a new ordered dummyObjectSlice, determined by a func defining ‘less’. See: http://clipperhouse.github.io/gen/#SortBy | ||
func (rcv dummyObjectSlice) SortBy(less func(*dummyObject, *dummyObject) bool) dummyObjectSlice { | ||
result := make(dummyObjectSlice, len(rcv)) | ||
copy(result, rcv) | ||
// Switch to heapsort if depth of 2*ceil(lg(n+1)) is reached. | ||
n := len(result) | ||
maxDepth := 0 | ||
for i := n; i > 0; i >>= 1 { | ||
maxDepth++ | ||
} | ||
maxDepth *= 2 | ||
quickSortdummyObjectSlice(result, less, 0, n, maxDepth) | ||
return result | ||
} | ||
|
||
// Sort implementation based on http://golang.org/pkg/sort/#Sort, see top of this file | ||
|
||
func swapdummyObjectSlice(rcv dummyObjectSlice, a, b int) { | ||
rcv[a], rcv[b] = rcv[b], rcv[a] | ||
} | ||
|
||
// Insertion sort | ||
func insertionSortdummyObjectSlice(rcv dummyObjectSlice, less func(*dummyObject, *dummyObject) bool, a, b int) { | ||
for i := a + 1; i < b; i++ { | ||
for j := i; j > a && less(rcv[j], rcv[j-1]); j-- { | ||
swapdummyObjectSlice(rcv, j, j-1) | ||
} | ||
} | ||
} | ||
|
||
// siftDown implements the heap property on rcv[lo, hi). | ||
// first is an offset into the array where the root of the heap lies. | ||
func siftDowndummyObjectSlice(rcv dummyObjectSlice, less func(*dummyObject, *dummyObject) bool, lo, hi, first int) { | ||
root := lo | ||
for { | ||
child := 2*root + 1 | ||
if child >= hi { | ||
break | ||
} | ||
if child+1 < hi && less(rcv[first+child], rcv[first+child+1]) { | ||
child++ | ||
} | ||
if !less(rcv[first+root], rcv[first+child]) { | ||
return | ||
} | ||
swapdummyObjectSlice(rcv, first+root, first+child) | ||
root = child | ||
} | ||
} | ||
|
||
func heapSortdummyObjectSlice(rcv dummyObjectSlice, less func(*dummyObject, *dummyObject) bool, a, b int) { | ||
first := a | ||
lo := 0 | ||
hi := b - a | ||
|
||
// Build heap with greatest element at top. | ||
for i := (hi - 1) / 2; i >= 0; i-- { | ||
siftDowndummyObjectSlice(rcv, less, i, hi, first) | ||
} | ||
|
||
// Pop elements, largest first, into end of rcv. | ||
for i := hi - 1; i >= 0; i-- { | ||
swapdummyObjectSlice(rcv, first, first+i) | ||
siftDowndummyObjectSlice(rcv, less, lo, i, first) | ||
} | ||
} | ||
|
||
// Quicksort, following Bentley and McIlroy, | ||
// Engineering a Sort Function, SP&E November 1993. | ||
|
||
// medianOfThree moves the median of the three values rcv[a], rcv[b], rcv[c] into rcv[a]. | ||
func medianOfThreedummyObjectSlice(rcv dummyObjectSlice, less func(*dummyObject, *dummyObject) bool, a, b, c int) { | ||
m0 := b | ||
m1 := a | ||
m2 := c | ||
// bubble sort on 3 elements | ||
if less(rcv[m1], rcv[m0]) { | ||
swapdummyObjectSlice(rcv, m1, m0) | ||
} | ||
if less(rcv[m2], rcv[m1]) { | ||
swapdummyObjectSlice(rcv, m2, m1) | ||
} | ||
if less(rcv[m1], rcv[m0]) { | ||
swapdummyObjectSlice(rcv, m1, m0) | ||
} | ||
// now rcv[m0] <= rcv[m1] <= rcv[m2] | ||
} | ||
|
||
func swapRangedummyObjectSlice(rcv dummyObjectSlice, a, b, n int) { | ||
for i := 0; i < n; i++ { | ||
swapdummyObjectSlice(rcv, a+i, b+i) | ||
} | ||
} | ||
|
||
func doPivotdummyObjectSlice(rcv dummyObjectSlice, less func(*dummyObject, *dummyObject) bool, lo, hi int) (midlo, midhi int) { | ||
m := lo + (hi-lo)/2 // Written like this to avoid integer overflow. | ||
if hi-lo > 40 { | ||
// Tukey's Ninther, median of three medians of three. | ||
s := (hi - lo) / 8 | ||
medianOfThreedummyObjectSlice(rcv, less, lo, lo+s, lo+2*s) | ||
medianOfThreedummyObjectSlice(rcv, less, m, m-s, m+s) | ||
medianOfThreedummyObjectSlice(rcv, less, hi-1, hi-1-s, hi-1-2*s) | ||
} | ||
medianOfThreedummyObjectSlice(rcv, less, lo, m, hi-1) | ||
|
||
// Invariants are: | ||
// rcv[lo] = pivot (set up by ChoosePivot) | ||
// rcv[lo <= i < a] = pivot | ||
// rcv[a <= i < b] < pivot | ||
// rcv[b <= i < c] is unexamined | ||
// rcv[c <= i < d] > pivot | ||
// rcv[d <= i < hi] = pivot | ||
// | ||
// Once b meets c, can swap the "= pivot" sections | ||
// into the middle of the slice. | ||
pivot := lo | ||
a, b, c, d := lo+1, lo+1, hi, hi | ||
for { | ||
for b < c { | ||
if less(rcv[b], rcv[pivot]) { // rcv[b] < pivot | ||
b++ | ||
} else if !less(rcv[pivot], rcv[b]) { // rcv[b] = pivot | ||
swapdummyObjectSlice(rcv, a, b) | ||
a++ | ||
b++ | ||
} else { | ||
break | ||
} | ||
} | ||
for b < c { | ||
if less(rcv[pivot], rcv[c-1]) { // rcv[c-1] > pivot | ||
c-- | ||
} else if !less(rcv[c-1], rcv[pivot]) { // rcv[c-1] = pivot | ||
swapdummyObjectSlice(rcv, c-1, d-1) | ||
c-- | ||
d-- | ||
} else { | ||
break | ||
} | ||
} | ||
if b >= c { | ||
break | ||
} | ||
// rcv[b] > pivot; rcv[c-1] < pivot | ||
swapdummyObjectSlice(rcv, b, c-1) | ||
b++ | ||
c-- | ||
} | ||
|
||
min := func(a, b int) int { | ||
if a < b { | ||
return a | ||
} | ||
return b | ||
} | ||
|
||
n := min(b-a, a-lo) | ||
swapRangedummyObjectSlice(rcv, lo, b-n, n) | ||
|
||
n = min(hi-d, d-c) | ||
swapRangedummyObjectSlice(rcv, c, hi-n, n) | ||
|
||
return lo + b - a, hi - (d - c) | ||
} | ||
|
||
func quickSortdummyObjectSlice(rcv dummyObjectSlice, less func(*dummyObject, *dummyObject) bool, a, b, maxDepth int) { | ||
for b-a > 7 { | ||
if maxDepth == 0 { | ||
heapSortdummyObjectSlice(rcv, less, a, b) | ||
return | ||
} | ||
maxDepth-- | ||
mlo, mhi := doPivotdummyObjectSlice(rcv, less, a, b) | ||
// Avoiding recursion on the larger subproblem guarantees | ||
// a stack depth of at most lg(b-a). | ||
if mlo-a < b-mhi { | ||
quickSortdummyObjectSlice(rcv, less, a, mlo, maxDepth) | ||
a = mhi // i.e., quickSortdummyObjectSlice(rcv, mhi, b) | ||
} else { | ||
quickSortdummyObjectSlice(rcv, less, mhi, b, maxDepth) | ||
b = mlo // i.e., quickSortdummyObjectSlice(rcv, a, mlo) | ||
} | ||
} | ||
if b-a > 1 { | ||
insertionSortdummyObjectSlice(rcv, less, a, b) | ||
} | ||
} |