1818import io .netty .microbench .util .AbstractMicrobenchmark ;
1919import io .netty .util .internal .PlatformDependent ;
2020import io .netty .util .internal .StringUtil ;
21+ import org .jctools .util .Pow2 ;
2122import org .openjdk .jmh .annotations .Benchmark ;
23+ import org .openjdk .jmh .annotations .CompilerControl ;
24+ import org .openjdk .jmh .annotations .CompilerControl .Mode ;
2225import org .openjdk .jmh .annotations .Measurement ;
2326import org .openjdk .jmh .annotations .OutputTimeUnit ;
2427import org .openjdk .jmh .annotations .Param ;
2831import org .openjdk .jmh .annotations .Warmup ;
2932
3033import java .util .Arrays ;
34+ import java .util .Random ;
3135import 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