1 /*
2  * Copyright 2014 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.benchmarks.netty;
18 
19 import java.util.concurrent.atomic.AtomicBoolean;
20 import java.util.concurrent.atomic.AtomicLong;
21 import org.openjdk.jmh.annotations.AuxCounters;
22 import org.openjdk.jmh.annotations.Benchmark;
23 import org.openjdk.jmh.annotations.Fork;
24 import org.openjdk.jmh.annotations.Level;
25 import org.openjdk.jmh.annotations.Param;
26 import org.openjdk.jmh.annotations.Scope;
27 import org.openjdk.jmh.annotations.Setup;
28 import org.openjdk.jmh.annotations.State;
29 import org.openjdk.jmh.annotations.TearDown;
30 
31 /**
32  * Benchmark using configuration intended to allow maximum QPS for unary calls.
33  */
34 @State(Scope.Benchmark)
35 @Fork(1)
36 public class UnaryCallQpsBenchmark extends AbstractBenchmark {
37 
38   @Param({"1", "2", "4", "8"})
39   public int channelCount = 4;
40 
41   @Param({"10", "100", "1000"})
42   public int maxConcurrentStreams = 100;
43 
44   private static AtomicLong callCounter;
45   private AtomicBoolean completed;
46 
47   /**
48    * Use an AuxCounter so we can measure that calls as they occur without consuming CPU
49    * in the benchmark method.
50    */
51   @AuxCounters
52   @State(Scope.Thread)
53   public static class AdditionalCounters {
54 
55     @Setup(Level.Iteration)
clean()56     public void clean() {
57       callCounter.set(0);
58     }
59 
callsPerSecond()60     public long callsPerSecond() {
61       return callCounter.get();
62     }
63   }
64 
65   /**
66    * Setup with direct executors, small payloads and a large flow control window.
67    */
68   @Setup(Level.Trial)
setup()69   public void setup() throws Exception {
70     super.setup(ExecutorType.DIRECT,
71         ExecutorType.DIRECT,
72         MessageSize.SMALL,
73         MessageSize.SMALL,
74         FlowWindowSize.LARGE,
75         ChannelType.NIO,
76         maxConcurrentStreams,
77         channelCount);
78     callCounter = new AtomicLong();
79     completed = new AtomicBoolean();
80     startUnaryCalls(maxConcurrentStreams, callCounter, completed, 1);
81   }
82 
83   /**
84    * Stop the running calls then stop the server and client channels.
85    */
86   @Override
87   @TearDown(Level.Trial)
teardown()88   public void teardown() throws Exception {
89     completed.set(true);
90     Thread.sleep(5000);
91     super.teardown();
92   }
93 
94   /**
95    * Measure throughput of unary calls. The calls are already running, we just observe a counter
96    * of received responses.
97    */
98   @Benchmark
unary(AdditionalCounters counters)99   public void unary(AdditionalCounters counters) throws Exception {
100     // No need to do anything, just sleep here.
101     Thread.sleep(1001);
102   }
103 
104   /**
105    * Useful for triggering a subset of the benchmark in a profiler.
106    */
main(String[] argv)107   public static void main(String[] argv) throws Exception {
108     UnaryCallQpsBenchmark bench = new UnaryCallQpsBenchmark();
109     bench.setup();
110     Thread.sleep(30000);
111     bench.teardown();
112     System.exit(0);
113   }
114 }
115