Skip to content
Open
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
58 changes: 54 additions & 4 deletions examples/perf-bench/perf-bench.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ func main() {
var sync bool
var cnt int
var sock, prof string
var testV2, debugOn bool
var testRPC, testV2, debugOn bool
flag.BoolVar(&sync, "sync", false, "run synchronous perf test")
flag.StringVar(&sock, "api-socket", socketclient.DefaultSocketName, "Path to VPP API socket")
flag.String("stats-socket", statsclient.DefaultSocketName, "Path to VPP stats socket")
flag.IntVar(&cnt, "count", 0, "count of requests to be sent to VPP")
flag.StringVar(&prof, "prof", "", "enable profiling mode [mem, cpu]")
flag.BoolVar(&testV2, "v2", false, "Use test function v2")
flag.BoolVar(&testRPC, "rpc", false, "Use test function rpc")
flag.BoolVar(&debugOn, "debug", false, "Enable debug mode")
flag.Parse()

Expand Down Expand Up @@ -85,7 +86,7 @@ func main() {
defer conn.Disconnect()

// create an API channel
ch, err := conn.NewAPIChannelBuffered(cnt, cnt)
ch, err := conn.NewAPIChannel()
if err != nil {
log.Fatalln("Error:", err)
}
Expand All @@ -101,7 +102,9 @@ func main() {
// run the test & measure the time
start := time.Now()

if testV2 {
if testRPC {
rpcTest(conn, cnt)
} else if testV2 {
if sync {
syncTest2(conn, cnt)
} else {
Expand All @@ -122,6 +125,46 @@ func main() {
time.Sleep(time.Second)
}

func rpcTest(conn api.Connection, cnt int) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function syncTest2 is optimized way to use stream. It does basically the same thing ControlPing RPC call does, but reused the same stream which makes it more performant.

Copy link
Member

@ondrej-fabry ondrej-fabry Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend reading Sending API messages from our GoVPP User Guide and RPC services and Stream API for more in-depth documentation of how it works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are using RPC services in our private application and can't change the whole code base to stream APIs. I have benchmarked them and got different results from stream APIs and RPC services API.
Thanks for the reading list. I have already read them.
The point here is to improve the overall performance of stream APIs and RPC service APIs.

fmt.Printf("Running RPC perf test with %d requests...\n", cnt)

for i := 0; i < cnt; i++ {
c := memclnt.NewServiceClient(conn)

_, err := c.ControlPing(context.Background(), &memclnt.ControlPing{})
if err != nil {
log.Fatalln("Error in reply:", err)
}
}
}

func rpcTestAsync(conn api.Connection, cnt int) {
fmt.Printf("Running async RPC perf test with %d requests...\n", cnt)

ctxChan := make(chan memclnt.RPCService, cnt)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong. The memclnt.RPCService is a an interface of RPC service client that is needed to be created only once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore rpcTestAsync function. It should have been deleted. you can check this function has never been called.

var index int

c := memclnt.NewServiceClient(conn)
go func() {

for i := 0; i < cnt; i++ {
ctxChan <- c.ControlPing(context.Background(), &memclnt.ControlPing{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't even compile. The ControlPing returns two values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please Ignore it....

}
close(ctxChan)
fmt.Printf("Sending asynchronous requests finished\n")
}()

index = 0
for ctx := range ctxChan {
err := ctx.error
if err != nil {
log.Fatalln("Error in reply:", err)
}
index++
}
fmt.Printf("Sending %d Receiving %d\n", cnt, index)
}

func syncTest(ch api.Channel, cnt int) {
fmt.Printf("Running synchronous perf test with %d requests...\n", cnt)

Expand Down Expand Up @@ -160,7 +203,7 @@ func asyncTest(ch api.Channel, cnt int) {
fmt.Printf("Running asynchronous perf test with %d requests...\n", cnt)

ctxChan := make(chan api.RequestCtx, cnt)

var index int
go func() {
for i := 0; i < cnt; i++ {
ctxChan <- ch.SendRequest(&memclnt.ControlPing{})
Expand All @@ -169,18 +212,22 @@ func asyncTest(ch api.Channel, cnt int) {
fmt.Printf("Sending asynchronous requests finished\n")
}()

index = 0
for ctx := range ctxChan {
reply := &memclnt.ControlPingReply{}
if err := ctx.ReceiveReply(reply); err != nil {
log.Fatalln("Error in reply:", err)
}
index++
}
fmt.Printf("Sending %d Receiving %d\n", cnt, index)
}

func asyncTest2(conn api.Connection, cnt int) {
fmt.Printf("Running asynchronous perf test with %d requests...\n", cnt)

ctxChan := make(chan api.Stream, cnt)
var index int

go func() {
for i := 0; i < cnt; i++ {
Expand All @@ -197,6 +244,7 @@ func asyncTest2(conn api.Connection, cnt int) {
fmt.Printf("Sending asynchronous requests finished\n")
}()

index = 0
for ctx := range ctxChan {
if msg, err := ctx.RecvMsg(); err != nil {
log.Fatalln("Error RecvMsg:", err)
Expand All @@ -208,5 +256,7 @@ func asyncTest2(conn api.Connection, cnt int) {
if err := ctx.Close(); err != nil {
log.Fatalf("Stream.Close error: %v", err)
}
index++
}
fmt.Printf("Sending %d Receiving %d\n", cnt, index)
}