Skip to content

Commit 22346e6

Browse files
authored
Merge pull request #103 from maciej/go_bits
Use Go 1.9 math/bits package
2 parents a8594f1 + 91c3bed commit 22346e6

16 files changed

+180
-137
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ notifications:
1010
go:
1111
- 1.7
1212
- 1.8
13+
- 1.9
1314
- tip
1415

1516
# Next lines seem to fail horribly

bitmapcontainer.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (bc *bitmapContainer) minimum() uint16 {
5151
for i := 0; i < len(bc.bitmap); i += 1 {
5252
w := bc.bitmap[i]
5353
if w != 0 {
54-
r := countTrailingZerosDeBruijn(w)
54+
r := countTrailingZeros(w)
5555
return uint16(r + i*64)
5656
}
5757
}
@@ -847,12 +847,12 @@ func (bc *bitmapContainer) NextSetBit(i int) int {
847847
w := bc.bitmap[x]
848848
w = w >> uint(i%64)
849849
if w != 0 {
850-
return i + countTrailingZerosDeBruijn(w)
850+
return i + countTrailingZeros(w)
851851
}
852852
x++
853853
for ; x < len(bc.bitmap); x++ {
854854
if bc.bitmap[x] != 0 {
855-
return (x * 64) + countTrailingZerosDeBruijn(bc.bitmap[x])
855+
return (x * 64) + countTrailingZeros(bc.bitmap[x])
856856
}
857857
}
858858
return -1

container_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ func checkContent(c container, s []uint16) bool {
5555
func TestRoaringContainer(t *testing.T) {
5656
Convey("countTrailingZeros", t, func() {
5757
x := uint64(0)
58-
o := countTrailingZerosDeBruijn(x)
58+
o := countTrailingZeros(x)
5959
So(o, ShouldEqual, 64)
6060
x = 1 << 3
61-
o = countTrailingZerosDeBruijn(x)
61+
o = countTrailingZeros(x)
6262
So(o, ShouldEqual, 3)
6363
})
6464
Convey("ArrayShortIterator", t, func() {

ctz.go

Lines changed: 7 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,11 @@
1-
package roaring
2-
3-
// Reuse of portions of go/src/math/big standard lib code
4-
// under this license:
5-
/*
6-
Copyright (c) 2009 The Go Authors. All rights reserved.
7-
8-
Redistribution and use in source and binary forms, with or without
9-
modification, are permitted provided that the following conditions are
10-
met:
11-
12-
* Redistributions of source code must retain the above copyright
13-
notice, this list of conditions and the following disclaimer.
14-
* Redistributions in binary form must reproduce the above
15-
copyright notice, this list of conditions and the following disclaimer
16-
in the documentation and/or other materials provided with the
17-
distribution.
18-
* Neither the name of Google Inc. nor the names of its
19-
contributors may be used to endorse or promote products derived from
20-
this software without specific prior written permission.
21-
22-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23-
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24-
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25-
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26-
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27-
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28-
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29-
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30-
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31-
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33-
*/
1+
// +build go1.9
2+
// "go1.9", from Go version 1.9 onward
3+
// See https://golang.org/pkg/go/build/#hdr-Build_Constraints
344

35-
const deBruijn32 = 0x077CB531
36-
37-
var deBruijn32Lookup = []byte{
38-
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
39-
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9,
40-
}
41-
42-
const deBruijn64 = 0x03f79d71b4ca8b09
5+
package roaring
436

44-
var deBruijn64Lookup = []byte{
45-
0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
46-
62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
47-
63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
48-
54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
49-
}
7+
import "math/bits"
508

51-
// trailingZeroBits returns the number of consecutive least significant zero
52-
// bits of x.
53-
func countTrailingZerosDeBruijn(x uint64) int {
54-
// x & -x leaves only the right-most bit set in the word. Let k be the
55-
// index of that bit. Since only a single bit is set, the value is two
56-
// to the power of k. Multiplying by a power of two is equivalent to
57-
// left shifting, in this case by k bits. The de Bruijn constant is
58-
// such that all six bit, consecutive substrings are distinct.
59-
// Therefore, if we have a left shifted version of this constant we can
60-
// find by how many bits it was shifted by looking at which six bit
61-
// substring ended up at the top of the word.
62-
// (Knuth, volume 4, section 7.3.1)
63-
if x == 0 {
64-
// We have to special case 0; the fomula
65-
// below doesn't work for 0.
66-
return 64
67-
}
68-
return int(deBruijn64Lookup[((x&-x)*(deBruijn64))>>58])
9+
func countTrailingZeros(x uint64) int {
10+
return bits.TrailingZeros64(x)
6911
}

ctz_compat.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// +build !go1.9
2+
3+
package roaring
4+
5+
// Reuse of portions of go/src/math/big standard lib code
6+
// under this license:
7+
/*
8+
Copyright (c) 2009 The Go Authors. All rights reserved.
9+
10+
Redistribution and use in source and binary forms, with or without
11+
modification, are permitted provided that the following conditions are
12+
met:
13+
14+
* Redistributions of source code must retain the above copyright
15+
notice, this list of conditions and the following disclaimer.
16+
* Redistributions in binary form must reproduce the above
17+
copyright notice, this list of conditions and the following disclaimer
18+
in the documentation and/or other materials provided with the
19+
distribution.
20+
* Neither the name of Google Inc. nor the names of its
21+
contributors may be used to endorse or promote products derived from
22+
this software without specific prior written permission.
23+
24+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35+
*/
36+
37+
const deBruijn32 = 0x077CB531
38+
39+
var deBruijn32Lookup = []byte{
40+
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
41+
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9,
42+
}
43+
44+
const deBruijn64 = 0x03f79d71b4ca8b09
45+
46+
var deBruijn64Lookup = []byte{
47+
0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
48+
62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
49+
63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
50+
54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
51+
}
52+
53+
// trailingZeroBits returns the number of consecutive least significant zero
54+
// bits of x.
55+
func countTrailingZeros(x uint64) int {
56+
// x & -x leaves only the right-most bit set in the word. Let k be the
57+
// index of that bit. Since only a single bit is set, the value is two
58+
// to the power of k. Multiplying by a power of two is equivalent to
59+
// left shifting, in this case by k bits. The de Bruijn constant is
60+
// such that all six bit, consecutive substrings are distinct.
61+
// Therefore, if we have a left shifted version of this constant we can
62+
// find by how many bits it was shifted by looking at which six bit
63+
// substring ended up at the top of the word.
64+
// (Knuth, volume 4, section 7.3.1)
65+
if x == 0 {
66+
// We have to special case 0; the fomula
67+
// below doesn't work for 0.
68+
return 64
69+
}
70+
return int(deBruijn64Lookup[((x & -x)*(deBruijn64))>>58])
71+
}

ctz_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ func TestCountTrailingZeros072(t *testing.T) {
1717
So(numberOfTrailingZeros(7<<17), ShouldEqual, 17)
1818
So(numberOfTrailingZeros(255<<33), ShouldEqual, 33)
1919

20-
So(countTrailingZerosDeBruijn(0), ShouldEqual, 64)
21-
So(countTrailingZerosDeBruijn(8), ShouldEqual, 3)
22-
So(countTrailingZerosDeBruijn(7), ShouldEqual, 0)
23-
So(countTrailingZerosDeBruijn(1<<17), ShouldEqual, 17)
24-
So(countTrailingZerosDeBruijn(7<<17), ShouldEqual, 17)
25-
So(countTrailingZerosDeBruijn(255<<33), ShouldEqual, 33)
20+
So(countTrailingZeros(0), ShouldEqual, 64)
21+
So(countTrailingZeros(8), ShouldEqual, 3)
22+
So(countTrailingZeros(7), ShouldEqual, 0)
23+
So(countTrailingZeros(1<<17), ShouldEqual, 17)
24+
So(countTrailingZeros(7<<17), ShouldEqual, 17)
25+
So(countTrailingZeros(255<<33), ShouldEqual, 33)
2626

2727
})
2828
}
@@ -64,7 +64,7 @@ func Benchmark100OrigNumberOfTrailingZeros(b *testing.B) {
6464
}
6565
}
6666

67-
func Benchmark100CountTrailingZerosDeBruijn(b *testing.B) {
67+
func Benchmark100CountTrailingZeros(b *testing.B) {
6868
b.StopTimer()
6969

7070
r := getRandomUint64Set(64)
@@ -74,7 +74,7 @@ func Benchmark100CountTrailingZerosDeBruijn(b *testing.B) {
7474
b.StartTimer()
7575
for i := 0; i < b.N; i++ {
7676
for i := range r {
77-
countTrailingZerosDeBruijn(r[i])
77+
countTrailingZeros(r[i])
7878
}
7979
}
8080
}

popcnt.go

Lines changed: 7 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,11 @@
1-
package roaring
2-
3-
// bit population count, take from
4-
// https://code.google.com/p/go/issues/detail?id=4988#c11
5-
// credit: https://code.google.com/u/arnehormann/
6-
// credit: https://play.golang.org/p/U7SogJ7psJ
7-
// credit: http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
8-
func popcount(x uint64) uint64 {
9-
x -= (x >> 1) & 0x5555555555555555
10-
x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
11-
x += x >> 4
12-
x &= 0x0f0f0f0f0f0f0f0f
13-
x *= 0x0101010101010101
14-
return x >> 56
15-
}
16-
17-
func popcntSliceGo(s []uint64) uint64 {
18-
cnt := uint64(0)
19-
for _, x := range s {
20-
cnt += popcount(x)
21-
}
22-
return cnt
23-
}
1+
// +build go1.9
2+
// "go1.9", from Go version 1.9 onward
3+
// See https://golang.org/pkg/go/build/#hdr-Build_Constraints
244

25-
func popcntMaskSliceGo(s, m []uint64) uint64 {
26-
cnt := uint64(0)
27-
for i := range s {
28-
cnt += popcount(s[i] &^ m[i])
29-
}
30-
return cnt
31-
}
32-
33-
func popcntAndSliceGo(s, m []uint64) uint64 {
34-
cnt := uint64(0)
35-
for i := range s {
36-
cnt += popcount(s[i] & m[i])
37-
}
38-
return cnt
39-
}
5+
package roaring
406

41-
func popcntOrSliceGo(s, m []uint64) uint64 {
42-
cnt := uint64(0)
43-
for i := range s {
44-
cnt += popcount(s[i] | m[i])
45-
}
46-
return cnt
47-
}
7+
import "math/bits"
488

49-
func popcntXorSliceGo(s, m []uint64) uint64 {
50-
cnt := uint64(0)
51-
for i := range s {
52-
cnt += popcount(s[i] ^ m[i])
53-
}
54-
return cnt
9+
func popcount(x uint64) uint64 {
10+
return uint64(bits.OnesCount64(x))
5511
}

popcnt_amd64.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// +build amd64,!appengine
1+
// +build amd64,!appengine,!go1.9
22

33
TEXT ·hasAsm(SB),4,$0-1
44
MOVQ $1, AX

popcnt_asm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// +build amd64,!appengine
1+
// +build amd64,!appengine,!go1.9
22

33
package roaring
44

popcnt_bench_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package roaring
2+
3+
import "testing"
4+
5+
func BenchmarkPopcount(b *testing.B) {
6+
b.StopTimer()
7+
8+
r := getRandomUint64Set(64)
9+
10+
b.ResetTimer()
11+
b.StartTimer()
12+
for i := 0; i < b.N; i++ {
13+
popcntSlice(r)
14+
}
15+
}

0 commit comments

Comments
 (0)