1
+ package scala .collection
2
+
3
+ import org .openjdk .jmh .annotations ._
4
+ import org .openjdk .jmh .infra ._
5
+ import org .openjdk .jmh .runner .IterationType
6
+ import benchmark ._
7
+ import java .util .concurrent .TimeUnit
8
+
9
+ @ BenchmarkMode (Array (Mode .AverageTime ))
10
+ @ Fork (2 )
11
+ @ Threads (1 )
12
+ @ Warmup (iterations = 10 )
13
+ @ Measurement (iterations = 10 )
14
+ @ OutputTimeUnit (TimeUnit .NANOSECONDS )
15
+ @ State (Scope .Benchmark )
16
+ class BitManipulationBenchmark {
17
+ val powersOfTwo = Array (1 , 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 , 512 , 1024 , 2048 , 4096 , 8192 , 16384 , 32768 , 65536 , 131072 , 262144 , 524288 , 1048576 , 2097152 , 4194304 , 8388608 , 16777216 , 33554432 , 67108864 , 134217728 , 268435456 , 536870912 , 1073741824 )
18
+
19
+ // ////////////////////////////////////////////
20
+
21
+ @ Benchmark def withIntegerBitCount (bh : Blackhole ) {
22
+ for (v <- powersOfTwo) {
23
+ val leadingZeros = withIntegerBitCount(v)
24
+ // assert (leadingZeros == withLoop(v), s"$leadingZeros != ${withLoop(v)} ($v)")
25
+ bh.consume(leadingZeros)
26
+ }
27
+ }
28
+
29
+ private def withIntegerBitCount (v : Int ) = Integer .SIZE - Integer .bitCount(v - 1 )
30
+
31
+ // ////////////////////////////////////////////
32
+
33
+ @ Benchmark def withIntegerNumberOfLeadingZeros (bh : Blackhole ) {
34
+ for (v <- powersOfTwo) {
35
+ val leadingZeros = withIntegerNumberOfLeadingZeros(v)
36
+ // assert (leadingZeros == withLoop(v), s"$leadingZeros != ${withLoop(v)} ($v)")
37
+ bh.consume(leadingZeros)
38
+ }
39
+ }
40
+
41
+ private def withIntegerNumberOfLeadingZeros (v : Int ) = Integer .numberOfLeadingZeros(v - 1 )
42
+
43
+ // ////////////////////////////////////////////
44
+
45
+ @ Benchmark def withLoop (bh : Blackhole ) {
46
+ for (v <- powersOfTwo) {
47
+ val leadingZeros = withLoop(v)
48
+ bh.consume(leadingZeros)
49
+ }
50
+ }
51
+
52
+ private def withLoop (v : Int ): Int = {
53
+ var r = Integer .SIZE
54
+ var copy = v >> 1
55
+ while (copy != 0 ) {
56
+ r -= 1
57
+ copy = copy >> 1
58
+ }
59
+ r
60
+ }
61
+
62
+ // ////////////////////////////////////////////
63
+
64
+ @ Benchmark def withMatch (bh : Blackhole ) {
65
+ for (v <- powersOfTwo) {
66
+ val leadingZeros = withMatch(v)
67
+ // assert (leadingZeros == withLoop(v), s"$leadingZeros != ${withLoop(v)} ($v)")
68
+ bh.consume(leadingZeros)
69
+ }
70
+ }
71
+
72
+ private def withMatch (i : Int ) = i match {
73
+ case 1 => 32
74
+ case 2 => 31
75
+ case 4 => 30
76
+ case 8 => 29
77
+ case 16 => 28
78
+ case 32 => 27
79
+ case 64 => 26
80
+ case 128 => 25
81
+ case 256 => 24
82
+ case 512 => 23
83
+ case 1024 => 22
84
+ case 2048 => 21
85
+ case 4096 => 20
86
+ case 8192 => 19
87
+ case 16384 => 18
88
+ case 32768 => 17
89
+ case 65536 => 16
90
+ case 131072 => 15
91
+ case 262144 => 14
92
+ case 524288 => 13
93
+ case 1048576 => 12
94
+ case 2097152 => 11
95
+ case 4194304 => 10
96
+ case 8388608 => 9
97
+ case 16777216 => 8
98
+ case 33554432 => 7
99
+ case 67108864 => 6
100
+ case 134217728 => 5
101
+ case 268435456 => 4
102
+ case 536870912 => 3
103
+ case 1073741824 => 2
104
+ }
105
+
106
+
107
+ // ////////////////////////////////////////////
108
+
109
+ @ Benchmark def with2DeBruijn (bh : Blackhole ) {
110
+ for (v <- powersOfTwo) {
111
+ val leadingZeros = with2DeBruijn(v)
112
+ // assert (leadingZeros == withLoop(v), s"$leadingZeros != ${withLoop(v)} ($v)")
113
+ bh.consume(leadingZeros)
114
+ }
115
+ }
116
+
117
+ // https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn
118
+ private val multiplyDeBruijnBitPosition2 = Array (32 , 31 , 4 , 30 , 3 , 18 , 8 , 29 , 2 , 10 , 12 , 17 , 7 , 15 , 28 , 24 , 1 , 5 , 19 , 9 , 11 , 13 , 16 , 25 , 6 , 20 , 14 , 26 , 21 , 27 , 22 , 23 )
119
+
120
+ private def with2DeBruijn (v : Int ) = multiplyDeBruijnBitPosition2((v * 0x077CB531 ) >>> 27 )
121
+
122
+
123
+ // ////////////////////////////////////////////
124
+
125
+ @ Benchmark def withBinSearch (bh : Blackhole ) {
126
+ for (v <- powersOfTwo) {
127
+ val leadingZeros = withBinSearch(v)
128
+ // assert (leadingZeros == withLoop(v), s"$leadingZeros != ${withLoop(v)} ($v)")
129
+ bh.consume(leadingZeros)
130
+ }
131
+ }
132
+
133
+ private def withBinSearch (v : Int ) =
134
+ if (v < 65536 ) if (v < 256 ) if (v < 16 ) if (v < 4 ) if (v == 1 ) 32 else 31
135
+ else if (v == 4 ) 30 else 29
136
+ else if (v < 64 ) if (v == 16 ) 28 else 27
137
+ else if (v == 64 ) 26 else 25
138
+ else if (v < 4096 ) if (v < 1024 ) if (v == 256 ) 24 else 23
139
+ else if (v == 1024 ) 22 else 21
140
+ else if (v < 16384 ) if (v == 4096 ) 20 else 19
141
+ else if (v == 16384 ) 18 else 17
142
+ else if (v < 16777216 ) if (v < 1048576 ) if (v < 262144 ) if (v == 65536 ) 16 else 15
143
+ else if (v == 262144 ) 14 else 13
144
+ else if (v < 4194304 ) if (v == 1048576 ) 12 else 11
145
+ else if (v == 4194304 ) 10 else 9
146
+ else if (v < 268435456 ) if (v < 67108864 ) if (v == 16777216 ) 8 else 7
147
+ else if (v == 67108864 ) 6 else 5
148
+ else if (v < 1073741824 ) if (v == 268435456 ) 4 else 3
149
+ else if (v == 1073741824 ) 2 else 1
150
+
151
+ // ////////////////////////////////////////////
152
+
153
+ @ Benchmark def withSumBinSearch (bh : Blackhole ) {
154
+ for (v <- powersOfTwo) {
155
+ val leadingZeros = withSumBinSearch(v)
156
+ // assert(leadingZeros == withLoop(v), s"$leadingZeros != ${withLoop(v)} ($v)")
157
+ bh.consume(leadingZeros)
158
+ }
159
+ }
160
+
161
+ private def withSumBinSearch (v : Int ): Int = {
162
+ var exponent = Integer .SIZE
163
+ var remaining = v
164
+ if (remaining >= 65536 ) { remaining >>>= 16 ; exponent = 16 }
165
+ if (remaining >= 256 ) { remaining >>>= 8 ; exponent -= 8 }
166
+ if (remaining >= 16 ) { remaining >>>= 4 ; exponent -= 4 }
167
+ if (remaining >= 4 ) { remaining >>>= 2 ; exponent -= 2 }
168
+ if (remaining >= 2 ) exponent - 1 else exponent
169
+ }
170
+ }
0 commit comments