Skip to content

Commit d943d11

Browse files
authored
DecodeHexBenchmark is too branch-predictor friendly (#9942)
Motivation: DecodeHexBenchmark needs to be less branch-predictor friendly to mimic the "real" behaviour while decoding Modifications: DecodeHexBenchmark uses a larger sets of inputs, picking them at random on each iteration and the benchmarked method is made !inlineable Result: DecodeHexBenchmark is more trusty while showing the performance difference between different decoding methods
1 parent d0bcf44 commit d943d11

File tree

1 file changed

+47
-5
lines changed

1 file changed

+47
-5
lines changed

microbench/src/main/java/io/netty/handler/codec/http/DecodeHexBenchmark.java

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
import io.netty.microbench.util.AbstractMicrobenchmark;
1919
import io.netty.util.internal.PlatformDependent;
2020
import io.netty.util.internal.StringUtil;
21+
import org.jctools.util.Pow2;
2122
import org.openjdk.jmh.annotations.Benchmark;
23+
import org.openjdk.jmh.annotations.CompilerControl;
24+
import org.openjdk.jmh.annotations.CompilerControl.Mode;
2225
import org.openjdk.jmh.annotations.Measurement;
2326
import org.openjdk.jmh.annotations.OutputTimeUnit;
2427
import org.openjdk.jmh.annotations.Param;
@@ -28,6 +31,7 @@
2831
import org.openjdk.jmh.annotations.Warmup;
2932

3033
import java.util.Arrays;
34+
import java.util.Random;
3135
import java.util.concurrent.TimeUnit;
3236

3337
@State(Scope.Benchmark)
@@ -43,37 +47,75 @@ public class DecodeHexBenchmark extends AbstractMicrobenchmark {
4347
"4DDeA5gDD1C6fE567E1b6gf0C40FEcDg",
4448
})
4549
private String hex;
46-
private char[] hexDigits;
50+
// Needs to specify a high number of inputs to allow the current strategy
51+
// on nextHexDigits to produce enough branch-misses
52+
@Param({ "2048" })
53+
private int inputs;
54+
private char[][] hexDigits;
55+
private static final long SEED = 1578675524L;
56+
private long next;
4757

4858
@Setup
4959
public void init() {
50-
hexDigits = hex.toCharArray();
60+
final char[] hexCh = hex.toCharArray();
61+
next = 0;
62+
inputs = Pow2.roundToPowerOfTwo(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+
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
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;
5190
}
5291

5392
@Benchmark
93+
@CompilerControl(Mode.DONT_INLINE)
5494
public long hexDigits() {
5595
long v = 0;
56-
final char[] hexDigits = this.hexDigits;
96+
final char[] hexDigits = this.hexDigits[nextHexDigits()];
5797
for (int i = 0, size = hexDigits.length; i < size; i++) {
5898
v += StringUtil.decodeHexNibble(hexDigits[i]);
5999
}
60100
return v;
61101
}
62102

63103
@Benchmark
104+
@CompilerControl(Mode.DONT_INLINE)
64105
public long hexDigitsWithChecks() {
65106
long v = 0;
66-
final char[] hexDigits = this.hexDigits;
107+
final char[] hexDigits = this.hexDigits[nextHexDigits()];
67108
for (int i = 0, size = hexDigits.length; i < size; i++) {
68109
v += decodeHexNibbleWithCheck(hexDigits[i]);
69110
}
70111
return v;
71112
}
72113

73114
@Benchmark
115+
@CompilerControl(Mode.DONT_INLINE)
74116
public long hexDigitsOriginal() {
75117
long v = 0;
76-
final char[] hexDigits = this.hexDigits;
118+
final char[] hexDigits = this.hexDigits[nextHexDigits()];
77119
for (int i = 0, size = hexDigits.length; i < size; i++) {
78120
v += decodeHexNibble(hexDigits[i]);
79121
}

0 commit comments

Comments
 (0)