1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http;
17
18 import io.netty.microbench.util.AbstractMicrobenchmark;
19 import io.netty.util.internal.MathUtil;
20 import io.netty.util.internal.PlatformDependent;
21 import io.netty.util.internal.StringUtil;
22 import org.openjdk.jmh.annotations.Benchmark;
23 import org.openjdk.jmh.annotations.CompilerControl;
24 import org.openjdk.jmh.annotations.CompilerControl.Mode;
25 import org.openjdk.jmh.annotations.Measurement;
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
33 import java.util.Arrays;
34 import java.util.Random;
35 import java.util.concurrent.TimeUnit;
36
37 @State(Scope.Benchmark)
38 @Warmup(iterations = 5, time = 1)
39 @Measurement(iterations = 5, time = 1)
40 @OutputTimeUnit(TimeUnit.MICROSECONDS)
41 public class DecodeHexBenchmark extends AbstractMicrobenchmark {
42
43 @Param({
44
45 "135aBa9BBCEA030b947d79fCcaf48Bde",
46
47 "4DDeA5gDD1C6fE567E1b6gf0C40FEcDg",
48 })
49 private String hex;
50
51
52 @Param({ "2048" })
53 private int inputs;
54 private char[][] hexDigits;
55 private static final long SEED = 1578675524L;
56 private long next;
57
58 @Setup
59 public void init() {
60 final char[] hexCh = hex.toCharArray();
61 next = 0;
62 inputs = MathUtil.findNextPositivePowerOfTwo(inputs);
63 hexDigits = new char[inputs][];
64 hexDigits[0] = hexCh;
65 if (inputs > 1) {
66 final Random rnd = new Random(SEED);
67 for (int i = 1; i < inputs; i++) {
68 hexDigits[i] = shuffle(Arrays.copyOf(hexCh, hexCh.length), rnd);
69 }
70 }
71 }
72
73
74 private static char[] shuffle(char[] chars, Random rnd) {
75 int index;
76 char tmp;
77 for (int i = chars.length - 1; i > 0; i--) {
78 index = rnd.nextInt(i + 1);
79 tmp = chars[index];
80 chars[index] = chars[i];
81 chars[i] = tmp;
82 }
83 return chars;
84 }
85
86 private int nextHexDigits() {
87 final int idx = (int) (next & (inputs - 1));
88 next++;
89 return idx;
90 }
91
92 @Benchmark
93 @CompilerControl(Mode.DONT_INLINE)
94 public long hexDigits() {
95 long v = 0;
96 final char[] hexDigits = this.hexDigits[nextHexDigits()];
97 for (int i = 0, size = hexDigits.length; i < size; i++) {
98 v += StringUtil.decodeHexNibble(hexDigits[i]);
99 }
100 return v;
101 }
102
103 @Benchmark
104 @CompilerControl(Mode.DONT_INLINE)
105 public long hexDigitsWithChecks() {
106 long v = 0;
107 final char[] hexDigits = this.hexDigits[nextHexDigits()];
108 for (int i = 0, size = hexDigits.length; i < size; i++) {
109 v += decodeHexNibbleWithCheck(hexDigits[i]);
110 }
111 return v;
112 }
113
114 @Benchmark
115 @CompilerControl(Mode.DONT_INLINE)
116 public long hexDigitsOriginal() {
117 long v = 0;
118 final char[] hexDigits = this.hexDigits[nextHexDigits()];
119 for (int i = 0, size = hexDigits.length; i < size; i++) {
120 v += decodeHexNibble(hexDigits[i]);
121 }
122 return v;
123 }
124
125 private static int decodeHexNibble(final char c) {
126 if (c >= '0' && c <= '9') {
127 return c - '0';
128 }
129 if (c >= 'A' && c <= 'F') {
130 return c - ('A' - 0xA);
131 }
132 if (c >= 'a' && c <= 'f') {
133 return c - ('a' - 0xA);
134 }
135 return -1;
136 }
137
138 private static final byte[] HEX2B;
139
140 static {
141 HEX2B = new byte['f' + 1];
142 Arrays.fill(HEX2B, (byte) -1);
143 HEX2B['0'] = 0;
144 HEX2B['1'] = 1;
145 HEX2B['2'] = 2;
146 HEX2B['3'] = 3;
147 HEX2B['4'] = 4;
148 HEX2B['5'] = 5;
149 HEX2B['6'] = 6;
150 HEX2B['7'] = 7;
151 HEX2B['8'] = 8;
152 HEX2B['9'] = 9;
153 HEX2B['A'] = 10;
154 HEX2B['B'] = 11;
155 HEX2B['C'] = 12;
156 HEX2B['D'] = 13;
157 HEX2B['E'] = 14;
158 HEX2B['F'] = 15;
159 HEX2B['a'] = 10;
160 HEX2B['b'] = 11;
161 HEX2B['c'] = 12;
162 HEX2B['d'] = 13;
163 HEX2B['e'] = 14;
164 HEX2B['f'] = 15;
165 }
166
167 private static int decodeHexNibbleWithCheck(final char c) {
168 if (c >= HEX2B.length) {
169 return -1;
170 }
171 if (PlatformDependent.hasUnsafe()) {
172 return PlatformDependent.getByte(HEX2B, c);
173 }
174 return HEX2B[c];
175 }
176
177 }