-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbatch_bench_test.go
128 lines (109 loc) · 2.55 KB
/
batch_bench_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package batch_test
import (
"fmt"
"math"
"math/rand"
"testing"
"github.com/veggiemonk/batch"
)
func BenchmarkBatchSlice(b *testing.B) {
for size := 10; size < 1000000; size *= 10 {
array := genArrayInt(size)
batchNo := len(array) / 11
res := make([][]int, 0, batchNo)
b.Run(fmt.Sprint("size=", size), func(b *testing.B) {
for i := 0; i < b.N; i++ {
res = batch.Slice(array, batchNo)
}
b.ReportAllocs()
_ = res
})
}
}
var result [][]int
func BenchmarkSliceCompare(b *testing.B) {
impls := []struct {
name string
fun func([]int, int) [][]int
}{
{
name: "Trick",
fun: sliceTrick[int],
},
{
name: "Batch",
fun: batch.Slice[int],
},
{
name: "Simple",
fun: simple[int],
},
}
for _, impl := range impls {
for k := 1; k <= len(prime); k++ {
size := int(math.Pow(10, float64(k)))
array := genArrayRandomInt(size)
batchNo := prime[k-1]
res := make([][]int, 0, batchNo)
b.Run(fmt.Sprintf("%s/%d", impl.name, size), func(b *testing.B) {
b.ReportMetric(float64(batchNo), "#batches")
b.ReportAllocs()
for i := 0; i < b.N; i++ {
res = impl.fun(array, batchNo)
}
result = res
})
}
}
_ = result
}
var prime = []int{5, 7, 11, 53, 97, 997}
func genArrayRandomInt(n int) []int {
r := rand.New(rand.NewSource(13378232375))
a := make([]int, 0, n)
for i := 1; i <= n; i++ {
a = append(a, r.Int())
}
return a
}
// sliceTrick is not the same output as Slice but has amazing performance.
// Taken from the Go Wiki in the SliceTricks page
func sliceTrick[T any](actions []T, batchSize int) [][]T {
if len(actions) == 0 || batchSize < 1 {
return [][]T{}
}
batches := make([][]T, 0, (len(actions)+batchSize-1)/batchSize)
for batchSize < len(actions) {
actions, batches = actions[batchSize:], append(batches, actions[0:batchSize:batchSize])
}
batches = append(batches, actions)
return batches
}
// simple is a naive implementation without pre-allocating the resulting slice of slices
func simple[T any](a []T, b int) [][]T {
var batches [][]T
l := len(a)
for i := 0; i < b; i++ {
low := i * l / b
upp := ((i + 1) * l) / b
batches = append(batches, a[low:upp])
}
return batches
}
// Shard breaks the given slice into n slices of (almost) the same size.
func Shard[E any](s []E, n int) func(func(int, []E)) {
return func(yield func(int, []E)) {
var (
size, remainder = len(s) / n, len(s) % n
start, end int
)
for i := 0; i < n; i++ {
start, end = end, end+size
if remainder > 0 {
remainder--
end++
}
yield(i, s[start:end])
}
}
}