Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions integration/benchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# FSC Benchmarks

This package contains useful micro benchmarks for the FSC runtime.

## Benchmarking

### Background material

There are many useful articles about go benchmarks available online. Here just a few good starting points:
- https://gobyexample.com/testing-and-benchmarking
- https://blog.cloudflare.com/go-dont-collect-my-garbage/
- https://mcclain.sh/posts/go-benchmarking/

### Run them all

We can run all benchmarks in this package as follows:

```bash
go test -bench=. -benchmem -count=10 -timeout=20m -cpu=1,2,4,8,16 -run=^$ ./... > plots/benchmark_gc_100.txt
```

#### Garbage Collection

Some code, in particular, allocation-intensive operations may benefit from tweaking the garbage collector settings.
There is a highly recommended read about go's GC https://go.dev/doc/gc-guide.

```bash
# Default
GOGC=100

# No garbage collection, use this setting only for testing! :)
GOGC=off
```

If we want to study the impact of different GC settings we can run the following, for example:

```bash
GOGC=100 go test -bench=. -benchmem -count=10 -timeout=20m -cpu=1,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,48,64 -run=^$ ./... > plots/benchmark_gc_100.txt
GOGC=off go test -bench=. -benchmem -count=10 -timeout=20m -cpu=1,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,48,64 -run=^$ ./... > plots/benchmark_gc_100.txt
GOGC=8000 go test -bench=. -benchmem -count=10 -timeout=20m -cpu=1,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,48,64 -run=^$ ./... > plots/benchmark_gc_8000.txt
```


## Plotting

The `plot/` directory contains a python script to visualize the benchmark results.

### Install

```bash
python3 -m venv env
source env/bin/activate
pip3 install -r requirements.txt
```

### Plot

Run the python script and provide the input file and the output file as arguments.

```bash
python3 plot.py benchmark_gc_off.txt benchmark_gc_off.pdf
```

This will generate the graph as pdf (`result_<timestamp>.pdf`).

### Example

Let's run the `ECDSASignView` benchmark as an example.
We turn garbage collection off using with `GOGC=off`, set the number of benchmark iteration with `-count=10`, and set the number of workers with `-cpu=1,2,4,8,16`.
We save the results in `benchmark_gc_off.txt`, which we use later to plot our graphs.

Once the benchmark is finished, we use `plot/plot.py` to create the result graphs as `pdf`.

```bash
GOGC=off go test -bench='ECDSASignView' -benchmem -count=10 -cpu=1,2,4,8,16 -run=^$ ./... > plots/benchmark_gc_off.txt
cd plots; python3 plot.py benchmark_gc_off.txt benchmark_gc_off.pdf
```

Happy benchmarking!
15 changes: 15 additions & 0 deletions integration/benchmark/benchmark.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
Copyright IBM Corp All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package benchmark

import (
"testing"
)

func ReportTPS(b *testing.B) {
b.ReportMetric(float64(b.N)/b.Elapsed().Seconds(), "TPS")
}
153 changes: 153 additions & 0 deletions integration/benchmark/grpc/grpc_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
Copyright IBM Corp All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package test

import (
"encoding/json"
"testing"
"time"

"github.com/hyperledger-labs/fabric-smart-client/integration/benchmark"
benchviews "github.com/hyperledger-labs/fabric-smart-client/integration/benchmark/views"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/grpc"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/metrics/disabled"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/view/grpc/server"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/view/grpc/server/protos"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace/noop"
)

func BenchmarkGRPC(b *testing.B) {
srvEndpoint := setupServer(b)

// we share a single connection among all client goroutines
cli, closeF := setupClient(b, srvEndpoint)
defer closeF()

b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
resp, err := cli.CallViewWithContext(b.Context(), "fid", nil)
require.NoError(b, err)
require.NotNil(b, resp)
}
})
benchmark.ReportTPS(b)
}

func setupServer(tb testing.TB) string {
tb.Helper()

mDefaultIdentity := view.Identity("server identity")
mSigner := &benchmark.MockSigner{
SerializeFunc: func() ([]byte, error) {
return mDefaultIdentity.Bytes(), nil
},
SignFunc: func(bytes []byte) ([]byte, error) {
return bytes, nil
},
}
mIdentityProvider := &benchmark.MockIdentityProvider{DefaultSigner: mDefaultIdentity}
mSigService := &benchmark.MockSignerProvider{DefaultSigner: mSigner}

// marshaller
tm, err := server.NewResponseMarshaler(mIdentityProvider, mSigService)
require.NoError(tb, err)
require.NotNil(tb, tm)

// setup server
grpcSrv, err := grpc.NewGRPCServer("localhost:0", grpc.ServerConfig{
ConnectionTimeout: 0,
SecOpts: grpc.SecureOptions{
Certificate: certPEM,
Key: keyPEM,
UseTLS: true,
},
KaOpts: grpc.KeepaliveOptions{},
Logger: nil,
HealthCheckEnabled: false,
})

require.NoError(tb, err)
require.NotNil(tb, grpcSrv)

srv, err := server.NewViewServiceServer(tm, &server.YesPolicyChecker{}, server.NewMetrics(&disabled.Provider{}), noop.NewTracerProvider())
require.NoError(tb, err)
require.NotNil(tb, srv)

parms := &benchviews.CPUParams{N: 200000}
input, _ := json.Marshal(parms)
factory := &benchviews.CPUViewFactory{}
v, _ := factory.NewView(input)

// our view manager
vm := &benchmark.MockViewManager{Constructor: func() view.View {
return v
}}

// register view manager wit grpc impl
server.InstallViewHandler(vm, srv, noop.NewTracerProvider())

// register grpc impl with grpc server
protos.RegisterViewServiceServer(grpcSrv.Server(), srv)

// start the actual grpc server
go func() {
_ = grpcSrv.Start()
}()
tb.Cleanup(grpcSrv.Stop)

return grpcSrv.Address()
}

func setupClient(tb testing.TB, srvEndpoint string) (*benchmark.ViewClient, func()) {
tb.Helper()

mDefaultIdentity := view.Identity("client identity")
mSigner := &benchmark.MockSigner{
SerializeFunc: func() ([]byte, error) {
return mDefaultIdentity.Bytes(), nil
},
SignFunc: func(bytes []byte) ([]byte, error) {
return bytes, nil
}}

signerIdentity, err := mSigner.Serialize()
require.NoError(tb, err)

grpcClient, err := grpc.NewGRPCClient(grpc.ClientConfig{
SecOpts: grpc.SecureOptions{
ServerRootCAs: [][]byte{certPEM},
UseTLS: true,
},
KaOpts: grpc.KeepaliveOptions{},
Timeout: 5 * time.Second,
AsyncConnect: false,
})
require.NoError(tb, err)
require.NotNil(tb, grpcClient)

conn, err := grpcClient.NewConnection(srvEndpoint)
require.NoError(tb, err)
require.NotNil(tb, conn)

tlsCert := grpcClient.Certificate()
tlsCertHash, err := grpc.GetTLSCertHash(&tlsCert)
require.NoError(tb, err)

cli := &benchmark.ViewClient{
SignF: mSigner.Sign,
Creator: signerIdentity,
TLSCertHash: tlsCertHash,
Client: protos.NewViewServiceClient(conn),
}

return cli, func() {
assert.NoError(tb, conn.Close())
}
}
Loading
Loading