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
This commit is contained in:
Francesco Nigro 2021-02-05 15:27:35 +01:00 committed by GitHub
parent d0bcf44445
commit d943d11eb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -18,7 +18,10 @@ package io.netty.handler.codec.http;
import io.netty.microbench.util.AbstractMicrobenchmark; import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import org.jctools.util.Pow2;
import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.CompilerControl;
import org.openjdk.jmh.annotations.CompilerControl.Mode;
import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Param;
@ -28,6 +31,7 @@ import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.annotations.Warmup;
import java.util.Arrays; import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@State(Scope.Benchmark) @State(Scope.Benchmark)
@ -43,17 +47,53 @@ public class DecodeHexBenchmark extends AbstractMicrobenchmark {
"4DDeA5gDD1C6fE567E1b6gf0C40FEcDg", "4DDeA5gDD1C6fE567E1b6gf0C40FEcDg",
}) })
private String hex; private String hex;
private char[] hexDigits; // Needs to specify a high number of inputs to allow the current strategy
// on nextHexDigits to produce enough branch-misses
@Param({ "2048" })
private int inputs;
private char[][] hexDigits;
private static final long SEED = 1578675524L;
private long next;
@Setup @Setup
public void init() { public void init() {
hexDigits = hex.toCharArray(); final char[] hexCh = hex.toCharArray();
next = 0;
inputs = Pow2.roundToPowerOfTwo(inputs);
hexDigits = new char[inputs][];
hexDigits[0] = hexCh;
if (inputs > 1) {
final Random rnd = new Random(SEED);
for (int i = 1; i < inputs; i++) {
hexDigits[i] = shuffle(Arrays.copyOf(hexCh, hexCh.length), rnd);
}
}
}
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
private static char[] shuffle(char[] chars, Random rnd) {
int index;
char tmp;
for (int i = chars.length - 1; i > 0; i--) {
index = rnd.nextInt(i + 1);
tmp = chars[index];
chars[index] = chars[i];
chars[i] = tmp;
}
return chars;
}
private int nextHexDigits() {
final int idx = (int) (next & (inputs - 1));
next++;
return idx;
} }
@Benchmark @Benchmark
@CompilerControl(Mode.DONT_INLINE)
public long hexDigits() { public long hexDigits() {
long v = 0; long v = 0;
final char[] hexDigits = this.hexDigits; final char[] hexDigits = this.hexDigits[nextHexDigits()];
for (int i = 0, size = hexDigits.length; i < size; i++) { for (int i = 0, size = hexDigits.length; i < size; i++) {
v += StringUtil.decodeHexNibble(hexDigits[i]); v += StringUtil.decodeHexNibble(hexDigits[i]);
} }
@ -61,9 +101,10 @@ public class DecodeHexBenchmark extends AbstractMicrobenchmark {
} }
@Benchmark @Benchmark
@CompilerControl(Mode.DONT_INLINE)
public long hexDigitsWithChecks() { public long hexDigitsWithChecks() {
long v = 0; long v = 0;
final char[] hexDigits = this.hexDigits; final char[] hexDigits = this.hexDigits[nextHexDigits()];
for (int i = 0, size = hexDigits.length; i < size; i++) { for (int i = 0, size = hexDigits.length; i < size; i++) {
v += decodeHexNibbleWithCheck(hexDigits[i]); v += decodeHexNibbleWithCheck(hexDigits[i]);
} }
@ -71,9 +112,10 @@ public class DecodeHexBenchmark extends AbstractMicrobenchmark {
} }
@Benchmark @Benchmark
@CompilerControl(Mode.DONT_INLINE)
public long hexDigitsOriginal() { public long hexDigitsOriginal() {
long v = 0; long v = 0;
final char[] hexDigits = this.hexDigits; final char[] hexDigits = this.hexDigits[nextHexDigits()];
for (int i = 0, size = hexDigits.length; i < size; i++) { for (int i = 0, size = hexDigits.length; i < size; i++) {
v += decodeHexNibble(hexDigits[i]); v += decodeHexNibble(hexDigits[i]);
} }