View Javadoc
1   /*
2    * Copyright 2023 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.codec.http;
17  
18  import io.netty.microbench.util.AbstractMicrobenchmark;
19  import org.openjdk.jmh.annotations.Benchmark;
20  import org.openjdk.jmh.annotations.BenchmarkMode;
21  import org.openjdk.jmh.annotations.Level;
22  import org.openjdk.jmh.annotations.Measurement;
23  import org.openjdk.jmh.annotations.Mode;
24  import org.openjdk.jmh.annotations.OperationsPerInvocation;
25  import org.openjdk.jmh.annotations.OutputTimeUnit;
26  import org.openjdk.jmh.annotations.Setup;
27  import org.openjdk.jmh.annotations.Warmup;
28  import org.openjdk.jmh.infra.BenchmarkParams;
29  import org.openjdk.jmh.infra.Blackhole;
30  import org.openjdk.jmh.profile.LinuxPerfNormProfiler;
31  import org.openjdk.jmh.profile.ProfilerException;
32  import org.openjdk.jmh.profile.ProfilerFactory;
33  import org.openjdk.jmh.runner.options.ChainedOptionsBuilder;
34  import org.openjdk.jmh.runner.options.ProfilerConfig;
35  
36  import java.text.DecimalFormat;
37  import java.util.SplittableRandom;
38  import java.util.concurrent.TimeUnit;
39  
40  @BenchmarkMode(Mode.Throughput)
41  @Warmup(iterations = 10, time = 1)
42  @Measurement(iterations = 10, time = 1)
43  @OutputTimeUnit(TimeUnit.MICROSECONDS)
44  public class HttpStatusValueOfBenchmark extends AbstractMicrobenchmark {
45      private static final SplittableRandom random = new SplittableRandom();
46      private static final DecimalFormat df = new DecimalFormat("##.##%");
47      private static final int[] data_1300 = new int[1300];
48      private static final int[] data_2600 = new int[2600];
49      private static final int[] data_5300 = new int[5300];
50      private static final int[] data_11000 = new int[11000];
51      private static final int[] data_23000 = new int[23000];
52      private static final boolean ENABLE_POLLUTE = false;
53  
54      @Setup(Level.Invocation)
55      public void setup(Blackhole bh, BenchmarkParams benchmarkParams) {
56          switch (benchmarkParams.getOpsPerInvocation()) {
57              case 1300 :
58                  polluteBranchIfEnabled(bh, data_1300);
59                  fillBenchMarkData(data_1300);
60                  break;
61              case 2600 :
62                  polluteBranchIfEnabled(bh, data_2600);
63                  fillBenchMarkData(data_2600);
64                  break;
65              case 5300 :
66                  polluteBranchIfEnabled(bh, data_5300);
67                  fillBenchMarkData(data_5300);
68                  break;
69              case 11000 :
70                  polluteBranchIfEnabled(bh, data_11000);
71                  fillBenchMarkData(data_11000);
72                  break;
73              case 23000 :
74                  polluteBranchIfEnabled(bh, data_23000);
75                  fillBenchMarkData(data_23000);
76                  break;
77          }
78      }
79  
80      @Benchmark
81      @OperationsPerInvocation(1300)
82      public void valueOf_1300(Blackhole bh) {
83          for (int code : data_1300) {
84              bh.consume(HttpStatusClass.valueOf(code));
85          }
86      }
87  
88      @Benchmark
89      @OperationsPerInvocation(2600)
90      public void valueOf_2600(Blackhole bh) {
91          for (int code : data_2600) {
92              bh.consume(HttpStatusClass.valueOf(code));
93          }
94      }
95  
96      @Benchmark
97      @OperationsPerInvocation(5300)
98      public void valueOf_5300(Blackhole bh) {
99          for (int code : data_5300) {
100             bh.consume(HttpStatusClass.valueOf(code));
101         }
102     }
103 
104     @Benchmark
105     @OperationsPerInvocation(11000)
106     public void valueOf_11000(Blackhole bh) {
107         for (int code : data_11000) {
108             bh.consume(HttpStatusClass.valueOf(code));
109         }
110     }
111 
112     @Benchmark
113     @OperationsPerInvocation(23000)
114     public void valueOf_23000(Blackhole bh) {
115         for (int code : data_23000) {
116             bh.consume(HttpStatusClass.valueOf(code));
117         }
118     }
119 
120     public HttpStatusValueOfBenchmark() {
121         // disable assertion
122         super(true);
123     }
124 
125     private static void polluteBranchIfEnabled(Blackhole bh, int[] polluteData) {
126         if (ENABLE_POLLUTE) {
127             fillPolluteData(polluteData);
128             for (int code : polluteData) {
129                 bh.consume(HttpStatusClass.valueOf(code));
130             }
131         }
132     }
133 
134     private static void fillBenchMarkData(int[] benchMarkData) {
135         double c1x = 0, c2x = 0, c3x = 0, c4x = 0, c5x = 0, c6x = 0;
136         for (int i = 0; i < benchMarkData.length;) {
137             // [0, 100)
138             int code = random.nextInt(0, 100);
139             // 38%
140             if (code < 38) {
141                 benchMarkData[i++] = random.nextInt(100, 200);
142                 ++c1x;
143                 continue;
144             }
145             // 30%
146             if (code < 68) {
147                 benchMarkData[i++] = random.nextInt(200, 300);
148                 ++c2x;
149                 continue;
150             }
151             // 15%
152             if (code < 83) {
153                 benchMarkData[i++] = random.nextInt(300, 400);
154                 ++c3x;
155                 continue;
156             }
157             // 10%
158             if (code < 93) {
159                 benchMarkData[i++] = random.nextInt(400, 500);
160                 ++c4x;
161                 continue;
162             }
163             // 5%
164             if (code < 98) {
165                 benchMarkData[i++] = random.nextInt(500, 600);
166                 ++c5x;
167                 continue;
168             }
169             // 2%
170             benchMarkData[i++] = random.nextInt(-50, 50);
171             ++c6x;
172         }
173 //        printCodePercentage("fillBenchMarkData", benchMarkData.length, c1x, c2x, c3x, c4x, c5x, c6x);
174     }
175 
176     private static void fillPolluteData(int[] polluteData) {
177         double c1x = 0, c2x = 0, c3x = 0, c4x = 0, c5x = 0, c6x = 0;
178         for (int i = 0; i < polluteData.length;) {
179             // [0, 96)
180             int code = random.nextInt(0, 96);
181             // (100/6) %
182             if (code < 16) {
183                 polluteData[i++] = random.nextInt(100, 200);
184                 ++c1x;
185                 continue;
186             }
187             // (100/6) %
188             if (code < 32) {
189                 polluteData[i++] = random.nextInt(200, 300);
190                 ++c2x;
191                 continue;
192             }
193             // (100/6) %
194             if (code < 48) {
195                 polluteData[i++] = random.nextInt(300, 400);
196                 ++c3x;
197                 continue;
198             }
199             // (100/6) %
200             if (code < 64) {
201                 polluteData[i++] = random.nextInt(400, 500);
202                 ++c4x;
203                 continue;
204             }
205             // (100/6) %
206             if (code < 80) {
207                 polluteData[i++] = random.nextInt(500, 600);
208                 ++c5x;
209                 continue;
210             }
211             // (100/6) %
212             polluteData[i++] = random.nextInt(-50, 50);
213             ++c6x;
214         }
215 //        printCodePercentage("fillPolluteData", polluteData.length, c1x, c2x, c3x, c4x, c5x, c6x);
216     }
217 
218     @Override
219     protected ChainedOptionsBuilder newOptionsBuilder() throws Exception {
220         Class<LinuxPerfNormProfiler> profilerClass = LinuxPerfNormProfiler.class;
221         try {
222             ProfilerFactory.getProfilerOrException(new ProfilerConfig(profilerClass.getCanonicalName()));
223         } catch (ProfilerException t) {
224             // Fall back to default.
225             return super.newOptionsBuilder();
226         }
227         return super.newOptionsBuilder().addProfiler(profilerClass);
228     }
229 
230     private static void printCodePercentage(String desc, int length, double c1x, double c2x, double c3x, double c4x,
231                                             double c5x, double c6x) {
232         System.out.println("\n" + desc + "===>"
233                 + "INFORMATIONAL:" + df.format(c1x / length)
234                 + ", SUCCESS:" + df.format(c2x / length)
235                 + ", REDIRECTION:" + df.format(c3x / length)
236                 + ", CLIENT_ERROR:" + df.format(c4x / length)
237                 + ", SERVER_ERROR:" + df.format(c5x / length)
238                 + ", UNKNOWN:" + df.format(c6x / length)
239         );
240     }
241 }