View Javadoc
1   /*
2    * Copyright 2024 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.http2;
17  
18  import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName;
19  import io.netty.microbench.util.AbstractMicrobenchmark;
20  import io.netty.util.AsciiString;
21  import org.openjdk.jmh.annotations.Benchmark;
22  import org.openjdk.jmh.annotations.BenchmarkMode;
23  import org.openjdk.jmh.annotations.CompilerControl;
24  import org.openjdk.jmh.annotations.Measurement;
25  import org.openjdk.jmh.annotations.Mode;
26  import org.openjdk.jmh.annotations.OutputTimeUnit;
27  import org.openjdk.jmh.annotations.Param;
28  import org.openjdk.jmh.annotations.Scope;
29  import org.openjdk.jmh.annotations.Setup;
30  import org.openjdk.jmh.annotations.State;
31  import org.openjdk.jmh.annotations.Warmup;
32  import org.openjdk.jmh.infra.Blackhole;
33  
34  import java.util.concurrent.TimeUnit;
35  
36  
37  @State(Scope.Benchmark)
38  @Warmup(iterations = 10, time = 200, timeUnit = TimeUnit.MILLISECONDS)
39  @Measurement(iterations = 10, time = 200, timeUnit = TimeUnit.MILLISECONDS)
40  @BenchmarkMode(Mode.AverageTime)
41  @OutputTimeUnit(TimeUnit.NANOSECONDS)
42  public class Http2PseudoHeadersLookupBenchmark extends AbstractMicrobenchmark {
43  
44      @Param({ "true", "false" })
45      public boolean same;
46  
47      private AsciiString[] asciiStrings;
48      private String[] strings;
49  
50      private DefaultHttp2Headers headers;
51      @Setup
52      public void init() {
53          // this benchmark is assuming a good happy path:
54          // 1. ascii strings have hashCode cached
55          // 2. String doesn't have AsciiString::hashCode cached -> cannot be compared directly with AsciiStrings!
56          // 3. the call-sites are never observing the 2 types together
57          PseudoHeaderName[] pseudoHeaderNames = PseudoHeaderName.values();
58          asciiStrings = new AsciiString[pseudoHeaderNames.length];
59          strings = new String[pseudoHeaderNames.length];
60          for (int i = 0; i < pseudoHeaderNames.length; i++) {
61              PseudoHeaderName pseudoHeaderName = pseudoHeaderNames[i];
62              asciiStrings[i] = same? pseudoHeaderName.value() : new AsciiString(pseudoHeaderName.value().array(), true);
63              byte[] chars = asciiStrings[i].array();
64              strings[i] = same? pseudoHeaderName.value().toString() : new String(chars, 0, 0, chars.length);
65              // force hashCode caching
66              asciiStrings[i].hashCode();
67              pseudoHeaderName.hashCode();
68              pseudoHeaderName.toString().hashCode();
69              strings[i].hashCode();
70          }
71          this.headers = new DefaultHttp2Headers();
72      }
73  
74      @Benchmark
75      @CompilerControl(CompilerControl.Mode.DONT_INLINE)
76      public void getAsciiStringPseudoHeader(Blackhole bh) {
77          for (AsciiString asciiString : asciiStrings) {
78              bh.consume(PseudoHeaderName.getPseudoHeader(asciiString));
79          }
80      }
81  
82      @Benchmark
83      @CompilerControl(CompilerControl.Mode.DONT_INLINE)
84      public void getStringPseudoHeader(Blackhole bh) {
85          for (String string : strings) {
86              bh.consume(PseudoHeaderName.getPseudoHeader(string));
87          }
88      }
89  
90      @Benchmark
91      @CompilerControl(CompilerControl.Mode.DONT_INLINE)
92      public void addAsciiStringInHttp2Headers(Blackhole bh) {
93          DefaultHttp2Headers headers = this.headers;
94          for (AsciiString asciiString : asciiStrings) {
95              boolean hasPrefix = PseudoHeaderName.hasPseudoHeaderFormat(asciiString);
96              if (hasPrefix) {
97                  boolean isPseudoHeader = PseudoHeaderName.isPseudoHeader(asciiString);
98                  if (isPseudoHeader) {
99                      bh.consume(headers.add(asciiString, "0"));
100                     headers.clear();
101                 }
102             }
103         }
104     }
105 
106     @Benchmark
107     @CompilerControl(CompilerControl.Mode.DONT_INLINE)
108     public void addStringInHttp2Headers(Blackhole bh) {
109         DefaultHttp2Headers headers = this.headers;
110         for (String asciiString : strings) {
111             boolean hasPrefix = PseudoHeaderName.hasPseudoHeaderFormat(asciiString);
112             if (hasPrefix) {
113                 boolean isPseudoHeader = PseudoHeaderName.isPseudoHeader(asciiString);
114                 if (isPseudoHeader) {
115                     bh.consume(headers.add(asciiString, "0"));
116                     headers.clear();
117                 }
118             }
119         }
120     }
121 }