From 61729694efc6015b9bba76c311fa2de5678f90fd Mon Sep 17 00:00:00 2001 From: Hanchin Hsieh Date: Sun, 14 Jul 2024 19:06:24 +0800 Subject: [PATCH] feat(bindings/go): add benchmark. (#4893) Signed-off-by: Hanchin Hsieh --- bindings/go/README.md | 90 ++++++++++++++++++++++++++- bindings/go/benchmark_test.go | 114 ++++++++++++++++++++++++++++++++++ bindings/go/opendal_test.go | 35 +++++++---- 3 files changed, 226 insertions(+), 13 deletions(-) create mode 100644 bindings/go/benchmark_test.go diff --git a/bindings/go/README.md b/bindings/go/README.md index 12871e8aacf1..11cdbf3e31d0 100644 --- a/bindings/go/README.md +++ b/bindings/go/README.md @@ -20,7 +20,7 @@ import ( "os" "github.com/yuchanns/opendal-go-services/memory" - "github.com/apache/opendal/bindings/go" + opendal "github.com/apache/opendal/bindings/go" ) func main() { @@ -89,7 +89,11 @@ func main() { ## Run Tests +### Behavior Tests + ```bash +# Test a specific backend +export OPENDAL_TEST=memory # Run all tests CGO_ENABLE=0 go test -v -run TestBehavior # Run specific test @@ -98,6 +102,90 @@ CGO_ENABLE=0 go test -v -run TestBehavior/Write CGO_ENABLE=0 GOMAXPROCS=1 go test -v -run TestBehavior ``` +### Benchmark + +```bash +# Benchmark a specific backend +export OPENDAL_TEST=memory + +go test -bench . +``` + +
+ + A benchmark between [purego+libffi](https://github.com/apache/opendal/commit/bf15cecd5e3be6ecaa7056b5594589c9f4d85673) vs [CGO](https://github.com/apache/opendal/commit/9ef494d6df2e9a13c4e5b9b03bcb36ec30c0a7c0) + + +**purego+libffi** (as `new.txt`) +``` +goos: linux +goarch: arm64 +pkg: github.com/apache/opendal/bindings/go +BenchmarkWrite4KiB-10 1000000 2844 ns/op +BenchmarkWrite256KiB-10 163346 10092 ns/op +BenchmarkWrite4MiB-10 12900 99161 ns/op +BenchmarkWrite16MiB-10 1785 658210 ns/op +BenchmarkRead4KiB-10 194529 6387 ns/op +BenchmarkRead256KiB-10 14228 82704 ns/op +BenchmarkRead4MiB-10 981 1227872 ns/op +BenchmarkRead16MiB-10 328 3617185 ns/op +PASS +ok +``` + +**CGO** (as `old.txt`) +``` +go test -bench=. -tags dynamic . +goos: linux +goarch: arm64 +pkg: opendal.apache.org/go +BenchmarkWrite4KiB-10 241981 4240 ns/op +BenchmarkWrite256KiB-10 126464 10105 ns/op +BenchmarkWrite4MiB-10 13443 89578 ns/op +BenchmarkWrite16MiB-10 1737 646155 ns/op +BenchmarkRead4KiB-10 53535 20939 ns/op +BenchmarkRead256KiB-10 9008 132738 ns/op +BenchmarkRead4MiB-10 576 1846683 ns/op +BenchmarkRead16MiB-10 230 6305322 ns/op +PASS +ok +``` + +**Diff** with [benchstat](https://pkg.go.dev/golang.org/x/perf/cmd/benchstat) +``` +benchstat old.txt new.txt +goos: linux +goarch: arm64 +pkg: github.com/apache/opendal/bindings/go + │ new.txt │ + │ sec/op │ +Write4KiB-10 2.844µ ± ∞ ¹ +Write256KiB-10 10.09µ ± ∞ ¹ +Write4MiB-10 99.16µ ± ∞ ¹ +Write16MiB-10 658.2µ ± ∞ ¹ +Read4KiB-10 6.387µ ± ∞ ¹ +Read256KiB-10 82.70µ ± ∞ ¹ +Read4MiB-10 1.228m ± ∞ ¹ +Read16MiB-10 3.617m ± ∞ ¹ +geomean 90.23µ +¹ need >= 6 samples for confidence interval at level 0.95 + +pkg: opendal.apache.org/go + │ old.txt │ + │ sec/op │ +Write4KiB-10 4.240µ ± ∞ ¹ +Write256KiB-10 10.11µ ± ∞ ¹ +Write4MiB-10 89.58µ ± ∞ ¹ +Write16MiB-10 646.2µ ± ∞ ¹ +Read4KiB-10 20.94µ ± ∞ ¹ +Read256KiB-10 132.7µ ± ∞ ¹ +Read4MiB-10 1.847m ± ∞ ¹ +Read16MiB-10 6.305m ± ∞ ¹ +geomean 129.7µ +¹ need >= 6 samples for confidence interval at level 0.95 +``` +
+ ## Capabilities - [x] OperatorInfo diff --git a/bindings/go/benchmark_test.go b/bindings/go/benchmark_test.go new file mode 100644 index 000000000000..f38dabc5af32 --- /dev/null +++ b/bindings/go/benchmark_test.go @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 opendal_test + +import ( + "testing" + + "github.com/google/uuid" +) + +type Size uint64 + +const ( + _ = iota + KiB = 1 << (10 * iota) + MiB +) + +func fromKibibytes(kib uint64) Size { + return Size(kib * KiB) +} + +func fromMebibytes(mib uint64) Size { + return Size(mib * MiB) +} + +func (s Size) Bytes() uint64 { + return uint64(s) +} + +func runBenchmarkWrite(b *testing.B, size Size) { + path := uuid.NewString() + + data := genFixedBytes(uint(size.Bytes())) + + b.ResetTimer() + + for n := 0; n < b.N; n++ { + err := op.Write(path, data) + if err != nil { + b.Fatalf("%s", err) + } + } +} + +func BenchmarkWrite4KiB(b *testing.B) { + runBenchmarkWrite(b, fromKibibytes(4)) +} + +func BenchmarkWrite256KiB(b *testing.B) { + runBenchmarkWrite(b, fromKibibytes(256)) +} + +func BenchmarkWrite4MiB(b *testing.B) { + runBenchmarkWrite(b, fromMebibytes(4)) +} + +func BenchmarkWrite16MiB(b *testing.B) { + runBenchmarkWrite(b, fromMebibytes(16)) +} + +func runBenchmarkRead(b *testing.B, size Size) { + path := uuid.NewString() + + data := genFixedBytes(uint(size.Bytes())) + + err := op.Write(path, data) + + if err != nil { + b.Fatalf("%s", err) + } + + b.ResetTimer() + + for n := 0; n < b.N; n++ { + _, err := op.Read(path) + if err != nil { + b.Fatalf("%s", err) + } + } +} + +func BenchmarkRead4KiB(b *testing.B) { + runBenchmarkRead(b, fromKibibytes(4)) +} + +func BenchmarkRead256KiB(b *testing.B) { + runBenchmarkRead(b, fromKibibytes(256)) +} + +func BenchmarkRead4MiB(b *testing.B) { + runBenchmarkRead(b, fromMebibytes(4)) +} + +func BenchmarkRead16MiB(b *testing.B) { + runBenchmarkRead(b, fromMebibytes(16)) +} diff --git a/bindings/go/opendal_test.go b/bindings/go/opendal_test.go index 6ffcb6743890..bf6e5fcbaab2 100644 --- a/bindings/go/opendal_test.go +++ b/bindings/go/opendal_test.go @@ -30,7 +30,7 @@ import ( "sync" "testing" - "github.com/apache/opendal/bindings/go" + opendal "github.com/apache/opendal/bindings/go" "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/yuchanns/opendal-go-services/aliyun_drive" @@ -43,14 +43,27 @@ var schemes = []opendal.Scheme{ memory.Scheme, } +var op *opendal.Operator + +func TestMain(m *testing.M) { + var ( + closeFunc func() + err error + ) + op, closeFunc, err = newOperator() + if err != nil { + panic(err) + } + defer closeFunc() + + m.Run() +} + type behaviorTest = func(assert *require.Assertions, op *opendal.Operator, fixture *fixture) func TestBehavior(t *testing.T) { assert := require.New(t) - op, closeFunc, err := newOperator() - assert.Nil(err) - cap := op.Info().GetFullCapability() var tests []behaviorTest @@ -68,11 +81,6 @@ func TestBehavior(t *testing.T) { t.Cleanup(func() { fixture.Cleanup(assert) - op.Close() - - if closeFunc != nil { - closeFunc() - } }) for i := range tests { @@ -104,9 +112,6 @@ func newOperator() (op *opendal.Operator, closeFunc func(), err error) { if err != nil { return } - closeFunc = func() { - os.Remove(s.Path()) - } scheme = s break } @@ -136,6 +141,12 @@ func newOperator() (op *opendal.Operator, closeFunc func(), err error) { err = fmt.Errorf("create operator must succeed: %s", err) } + closeFunc = func() { + op.Close() + + os.Remove(scheme.Path()) + } + return }