Count bits
Alan A. A. Donovan and Brian W. Kernighan, The Go Programming Language. Addison-Wesley, 2016, Chapter 2, Program Structure
See also Go: program structure
1
2
3
4
5
6
7
8
9
package popcount
var pc [256]byte
func init() {
for i := range pc {
pc[i] = pc[i/2] + byte(i & 1)
}
}
Original version
Version given in Section 2.7: Scope.
1
2
3
4
5
6
7
8
9
10
func PopCount1(x uint64) int {
return int(pc[byte(x >> (0 * 8))] +
pc[byte(x >> (1 * 8))] +
pc[byte(x >> (2 * 8))] +
pc[byte(x >> (3 * 8))] +
pc[byte(x >> (4 * 8))] +
pc[byte(x >> (5 * 8))] +
pc[byte(x >> (6 * 8))] +
pc[byte(x >> (7 * 8))])
}
Exercise 2.3
Rewrite
PopCountto use a loop instead of a single expression. Compare the performance of the two versions. (Section 11.4 shows how to compare the performance of different implementations systematically.)
1
2
3
4
5
6
7
8
func PopCount2(x uint64) int {
var i uint8
var result byte
for i = 0; i < 8; i++ {
result += pc[byte(x >> (i * 8))]
}
return int(result)
}
Exercise 2.4
Write a version of
PopCountthat counts bits by shifting its argument through 64 bit positions, testing the rightmost bit each time. Compare its performance to the table-lookup version.
1
2
3
4
5
6
7
8
func PopCount3(x uint64) int {
var result uint64 = (x & 1)
for i := 0; i < 64; i++ {
x >>= 1
result += (x & 1)
}
return int(result)
}
Exercise 2.5
The expression
x & (x - 1)clears the rightmost non-zero bit ofx. Write a version ofPopCountthat counts bits by using this fact, and assess its performance.
1
2
3
4
5
6
7
8
func PopCount4(x uint64) int {
count := 0
for x > 0 {
x &= (x - 1)
count++
}
return count
}
Benchmarking (test file)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package popcount
import "testing"
var max32 uint64 = 4294967295
func BenchmarkPopCount1(b *testing.B) {
for i := 0; i < b.N; i++ {
PopCount1(max32)
}
}
func BenchmarkPopCount2(b *testing.B) {
for i := 0; i < b.N; i++ {
PopCount2(max32)
}
}
func BenchmarkPopCount3(b *testing.B) {
for i := 0; i < b.N; i++ {
PopCount3(max32)
}
}
func BenchmarkPopCount4(b *testing.B) {
for i := 0; i < b.N; i++ {
PopCount4(max32)
}
}
Output:
1
2
3
4
5
6
7
8
9
$ go test -bench=.
goos: darwin
goarch: amd64
BenchmarkPopCount1-4 2000000000 0.43 ns/op
BenchmarkPopCount2-4 50000000 31.2 ns/op
BenchmarkPopCount3-4 20000000 88.9 ns/op
BenchmarkPopCount4-4 50000000 34.0 ns/op
PASS
ok _/Users/blennon/sync/desk/scratch/scratch-go/temp 6.133s