f7a985770ba2b3914af169040c1353330ea06cb4
[govpp.git] / examples / perf-bench / perf-bench.go
1 // Copyright (c) 2017 Cisco and/or its affiliates.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Binary simple-client is an example VPP management application that exercises the
16 // govpp API on real-world use-cases.
17 package main
18
19 import (
20         "context"
21         "flag"
22         "fmt"
23         "log"
24         "os"
25         "time"
26
27         "github.com/pkg/profile"
28         "github.com/sirupsen/logrus"
29
30         "git.fd.io/govpp.git/adapter/socketclient"
31         "git.fd.io/govpp.git/adapter/statsclient"
32         "git.fd.io/govpp.git/api"
33         "git.fd.io/govpp.git/binapi/memclnt"
34         "git.fd.io/govpp.git/core"
35 )
36
37 const (
38         defaultSyncRequestCount  = 1000
39         defaultAsyncRequestCount = 10000
40 )
41
42 func main() {
43         // parse optional flags
44         var sync bool
45         var cnt int
46         var sock, prof string
47         flag.BoolVar(&sync, "sync", false, "run synchronous perf test")
48         flag.StringVar(&sock, "api-socket", socketclient.DefaultSocketName, "Path to VPP API socket")
49         flag.String("stats-socket", statsclient.DefaultSocketName, "Path to VPP stats socket")
50         flag.IntVar(&cnt, "count", 0, "count of requests to be sent to VPP")
51         flag.StringVar(&prof, "prof", "", "enable profiling mode [mem, cpu]")
52         flag.Parse()
53
54         if cnt == 0 {
55                 // no specific count defined - use defaults
56                 if sync {
57                         cnt = defaultSyncRequestCount
58                 } else {
59                         cnt = defaultAsyncRequestCount
60                 }
61         }
62
63         switch prof {
64         case "mem":
65                 defer profile.Start(profile.MemProfile, profile.MemProfileRate(1)).Stop()
66         case "cpu":
67                 defer profile.Start(profile.CPUProfile).Stop()
68         case "":
69         default:
70                 fmt.Printf("invalid profiling mode: %q\n", prof)
71                 flag.Usage()
72                 os.Exit(1)
73         }
74
75         a := socketclient.NewVppClient(sock)
76
77         // connect to VPP
78         conn, err := core.Connect(a)
79         if err != nil {
80                 log.Fatalln("Error:", err)
81         }
82         defer conn.Disconnect()
83
84         // create an API channel
85         ch, err := conn.NewAPIChannelBuffered(cnt, cnt)
86         if err != nil {
87                 log.Fatalln("Error:", err)
88         }
89         defer ch.Close()
90
91         ch.SetReplyTimeout(time.Second * 2)
92
93         // log only errors
94         core.SetLogger(&logrus.Logger{Level: logrus.ErrorLevel})
95
96         // run the test & measure the time
97         start := time.Now()
98
99         if sync {
100                 // run synchronous test
101                 syncTest(ch, cnt)
102                 //syncTest2(conn, cnt)
103         } else {
104                 // run asynchronous test
105                 asyncTest(ch, cnt)
106                 //asyncTest2(conn, cnt)
107         }
108
109         elapsed := time.Since(start)
110         fmt.Println("Test took:", elapsed)
111         fmt.Printf("Requests per second: %.0f\n", float64(cnt)/elapsed.Seconds())
112
113         time.Sleep(time.Second)
114 }
115
116 func syncTest(ch api.Channel, cnt int) {
117         fmt.Printf("Running synchronous perf test with %d requests...\n", cnt)
118
119         for i := 0; i < cnt; i++ {
120                 req := &memclnt.ControlPing{}
121                 reply := &memclnt.ControlPingReply{}
122
123                 if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
124                         log.Fatalln("Error in reply:", err)
125                 }
126         }
127 }
128
129 func syncTest2(conn api.Connection, cnt int) {
130         fmt.Printf("Running synchronous perf test with %d requests...\n", cnt)
131
132         stream, err := conn.NewStream(context.Background())
133         if err != nil {
134                 log.Fatalln("Error NewStream:", err)
135         }
136         for i := 0; i < cnt; i++ {
137                 if err := stream.SendMsg(&memclnt.ControlPing{}); err != nil {
138                         log.Fatalln("Error SendMsg:", err)
139                 }
140                 if msg, err := stream.RecvMsg(); err != nil {
141                         log.Fatalln("Error RecvMsg:", err)
142                 } else if _, ok := msg.(*memclnt.ControlPingReply); ok {
143                         // ok
144                 } else {
145                         log.Fatalf("unexpected reply: %v", msg.GetMessageName())
146                 }
147         }
148 }
149
150 func asyncTest(ch api.Channel, cnt int) {
151         fmt.Printf("Running asynchronous perf test with %d requests...\n", cnt)
152
153         ctxChan := make(chan api.RequestCtx, cnt)
154
155         go func() {
156                 for i := 0; i < cnt; i++ {
157                         ctxChan <- ch.SendRequest(&memclnt.ControlPing{})
158                 }
159                 close(ctxChan)
160                 fmt.Printf("Sending asynchronous requests finished\n")
161         }()
162
163         for ctx := range ctxChan {
164                 reply := &memclnt.ControlPingReply{}
165                 if err := ctx.ReceiveReply(reply); err != nil {
166                         log.Fatalln("Error in reply:", err)
167                 }
168         }
169 }
170
171 func asyncTest2(conn api.Connection, cnt int) {
172         fmt.Printf("Running asynchronous perf test with %d requests...\n", cnt)
173
174         ctxChan := make(chan api.Stream, cnt)
175
176         go func() {
177                 for i := 0; i < cnt; i++ {
178                         stream, err := conn.NewStream(context.Background())
179                         if err != nil {
180                                 log.Fatalln("Error NewStream:", err)
181                         }
182                         if err := stream.SendMsg(&memclnt.ControlPing{}); err != nil {
183                                 log.Fatalln("Error SendMsg:", err)
184                         }
185                         ctxChan <- stream
186                 }
187                 close(ctxChan)
188                 fmt.Printf("Sending asynchronous requests finished\n")
189         }()
190
191         for ctx := range ctxChan {
192                 if msg, err := ctx.RecvMsg(); err != nil {
193                         log.Fatalln("Error RecvMsg:", err)
194                 } else if _, ok := msg.(*memclnt.ControlPingReply); ok {
195                         // ok
196                 } else {
197                         log.Fatalf("unexpected reply: %v", msg.GetMessageName())
198                 }
199                 ctx.Close()
200         }
201 }