Skip to content

Commit dce2def

Browse files
committed
add basic benchmark package (most useful stuff still missing)
1 parent 39c8466 commit dce2def

File tree

15 files changed

+668
-108
lines changed

15 files changed

+668
-108
lines changed

bench/bench.go

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package bench
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"io/ioutil"
8+
"math/rand"
9+
10+
"github.com/sahib/brig/client"
11+
"github.com/sahib/brig/client/clienttest"
12+
"github.com/sahib/brig/repo/hints"
13+
"github.com/sahib/brig/server"
14+
"github.com/sahib/brig/util/testutil"
15+
)
16+
17+
type Bench interface {
18+
SupportHints() bool
19+
SetHint(hint hints.Hint) error
20+
Process(r io.Reader) error
21+
Close() error
22+
}
23+
24+
//////////
25+
26+
type NullBench struct{}
27+
28+
func NewNullBench() NullBench {
29+
return NullBench{}
30+
}
31+
32+
func (n NullBench) SupportHints() bool { return false }
33+
func (n NullBench) SetHint(hint hints.Hint) error { return nil }
34+
35+
func (n NullBench) Process(r io.Reader) error {
36+
// NOTE: Use DumbCopy, since io.Copy would use the
37+
// ReadFrom of ioutil.Discard. This is lightning fast.
38+
// We want to measure actual time to copy in memory.
39+
_, err := testutil.DumbCopy(ioutil.Discard, r, false, false)
40+
return err
41+
}
42+
43+
func (n NullBench) Close() error { return nil }
44+
45+
//////////
46+
47+
// TODO: Make backend configurable we can also test with ipfs.
48+
49+
type ServerStageBench struct {
50+
daemon *server.Server
51+
client *client.Client
52+
}
53+
54+
func NewServerStageBench() (*ServerStageBench, error) {
55+
srv, err := clienttest.StartDaemon("ali", "mock")
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
ctl, err := client.Dial(context.Background(), srv.DaemonURL())
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
return &ServerStageBench{
66+
daemon: srv,
67+
client: ctl,
68+
}, nil
69+
}
70+
71+
func (s *ServerStageBench) SupportHints() bool { return true }
72+
73+
func (s *ServerStageBench) SetHint(hint hints.Hint) error {
74+
c := string(hint.CompressionAlgo)
75+
e := string(hint.EncryptionAlgo)
76+
return s.client.HintSet("/", &c, &e)
77+
}
78+
79+
func (s *ServerStageBench) Process(r io.Reader) error {
80+
path := fmt.Sprintf("/path_%d", rand.Int31())
81+
return s.client.StageFromReader(path, r)
82+
}
83+
84+
func (s *ServerStageBench) Close() error {
85+
s.daemon.Close()
86+
s.client.Close()
87+
return nil
88+
}
89+
90+
// TODO: brig-stage-ipfs
91+
// TODO: ipfs add
92+
// TODO: fuse-write
93+
94+
func BenchByName(name string) (Bench, error) {
95+
switch name {
96+
case "null":
97+
return NewNullBench(), nil
98+
case "brig-stage":
99+
return NewServerStageBench()
100+
default:
101+
return nil, fmt.Errorf("no such bench: %s", name)
102+
}
103+
}

bench/inputs.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package bench
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
8+
"github.com/sahib/brig/util/testutil"
9+
)
10+
11+
type Input interface {
12+
Reader() (io.Reader, error)
13+
Close() error
14+
}
15+
16+
//////////
17+
18+
type NullInput struct {
19+
buf []byte
20+
}
21+
22+
func NewNullInput(size uint64, isRandom bool) *NullInput {
23+
var buf []byte
24+
25+
if isRandom {
26+
buf = testutil.CreateRandomDummyBuf(int64(size), 23)
27+
} else {
28+
buf = testutil.CreateDummyBuf(int64(size))
29+
}
30+
31+
return &NullInput{buf: buf}
32+
}
33+
34+
func (ni *NullInput) Reader() (io.Reader, error) {
35+
return bytes.NewReader(ni.buf), nil
36+
}
37+
38+
func (ni *NullInput) Close() error {
39+
return nil
40+
}
41+
42+
//////////
43+
44+
func InputByName(name string, size uint64, isRandom bool) (Input, error) {
45+
switch name {
46+
case "null":
47+
return NewNullInput(size, isRandom), nil
48+
default:
49+
return nil, fmt.Errorf("no such input: %s", name)
50+
}
51+
}

bench/runner.go

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package bench
2+
3+
import (
4+
"sort"
5+
"time"
6+
7+
"github.com/sahib/brig/repo/hints"
8+
)
9+
10+
type Config struct {
11+
InputName string `json:"input_name"`
12+
BenchName string `json:"bench_name"`
13+
Size uint64 `json:"size"`
14+
Random bool `json:"random"`
15+
Encryption string
16+
Compression string
17+
}
18+
19+
type Result struct {
20+
Config Config `json:"config"`
21+
Encryption string `json:"encryption"`
22+
Compression string `json:"compression"`
23+
Took time.Duration `json:"took"`
24+
}
25+
26+
func withTiming(fn func() error) (time.Duration, error) {
27+
start := time.Now()
28+
if err := fn(); err != nil {
29+
return time.Since(start), err
30+
}
31+
32+
return time.Since(start), nil
33+
}
34+
35+
// buildHints handles wildcards for compression and/or encryption.
36+
// If no wildcards are specified, we just take what is set in `cfg`.
37+
func buildHints(cfg Config) []hints.Hint {
38+
encIsWildcard := cfg.Encryption == "*"
39+
zipIsWildcard := cfg.Compression == "*"
40+
41+
if encIsWildcard && zipIsWildcard {
42+
return hints.AllPossibleHints()
43+
}
44+
45+
if encIsWildcard {
46+
hs := []hints.Hint{}
47+
for _, encAlgo := range hints.ValidEncryptionHints() {
48+
hs = append(hs, hints.Hint{
49+
CompressionAlgo: hints.CompressionHint(cfg.Compression),
50+
EncryptionAlgo: hints.EncryptionHint(encAlgo),
51+
})
52+
}
53+
54+
return hs
55+
}
56+
57+
if zipIsWildcard {
58+
hs := []hints.Hint{}
59+
for _, zipAlgo := range hints.ValidCompressionHints() {
60+
hs = append(hs, hints.Hint{
61+
CompressionAlgo: hints.CompressionHint(zipAlgo),
62+
EncryptionAlgo: hints.EncryptionHint(cfg.Encryption),
63+
})
64+
}
65+
66+
return hs
67+
}
68+
69+
return []hints.Hint{{
70+
CompressionAlgo: hints.CompressionHint(cfg.Compression),
71+
EncryptionAlgo: hints.EncryptionHint(cfg.Encryption),
72+
}}
73+
}
74+
75+
func sortHints(hs []hints.Hint) []hints.Hint {
76+
sort.Slice(hs, func(i, j int) bool {
77+
return hs[i].Less(hs[j])
78+
})
79+
80+
// sorts in-place, but also return for ease of use.
81+
return hs
82+
}
83+
84+
func benchmarkSingle(cfg Config, fn func(result Result)) error {
85+
in, err := InputByName(cfg.InputName, cfg.Size, cfg.Random)
86+
if err != nil {
87+
return err
88+
}
89+
90+
defer in.Close()
91+
92+
out, err := BenchByName(cfg.BenchName)
93+
if err != nil {
94+
return err
95+
}
96+
97+
defer out.Close()
98+
99+
for _, hint := range sortHints(buildHints(cfg)) {
100+
supportsHints := out.SupportHints()
101+
if supportsHints {
102+
if err := out.SetHint(hint); err != nil {
103+
return err
104+
}
105+
} else {
106+
// Indicate in output that nothing was encrypted or compressed.
107+
hint.CompressionAlgo = hints.CompressionNone
108+
hint.EncryptionAlgo = hint.EncryptionAlgo
109+
}
110+
111+
r, err := in.Reader()
112+
if err != nil {
113+
return err
114+
}
115+
116+
took, err := withTiming(func() error {
117+
return out.Process(r)
118+
})
119+
120+
if err != nil {
121+
return err
122+
}
123+
124+
fn(Result{
125+
Encryption: string(hint.EncryptionAlgo),
126+
Compression: string(hint.CompressionAlgo),
127+
Config: cfg,
128+
Took: took,
129+
})
130+
131+
if !supportsHints {
132+
// If there are no hints there is no point.
133+
// of repeating the benchmark several times.
134+
break
135+
}
136+
}
137+
138+
return nil
139+
}
140+
141+
func Benchmark(cfgs []Config, fn func(result Result)) error {
142+
for _, cfg := range cfgs {
143+
if err := benchmarkSingle(cfg, fn); err != nil {
144+
return err
145+
}
146+
}
147+
148+
return nil
149+
}

0 commit comments

Comments
 (0)