Skip to content

Commit

Permalink
Add some basic benchmarks to compare to native
Browse files Browse the repository at this point in the history
Currently only benchmarking `Any`, `Select` and `Sort
  • Loading branch information
francoishill authored and clipperhouse committed Jun 11, 2018
1 parent b78fdc9 commit 5e0f4d9
Show file tree
Hide file tree
Showing 3 changed files with 300 additions and 0 deletions.
11 changes: 11 additions & 0 deletions benchmarks/dummy_obj.go
@@ -0,0 +1,11 @@
package benchmarks

// +gen * slice:"Any,Select[*dummyDestinationSelectObject],SortBy"
type dummyObject struct {
Name string
Num int
}

type dummyDestinationSelectObject struct {
Name string
}
70 changes: 70 additions & 0 deletions benchmarks/dummy_obj_test.go
@@ -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})
}
}
219 changes: 219 additions & 0 deletions benchmarks/dummyobject_slice.go
@@ -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)
}
}

0 comments on commit 5e0f4d9

Please sign in to comment.