Skip to content

Commit

Permalink
Revert "popcnt -> bits.OnesCount64"
Browse files Browse the repository at this point in the history
This reverts commit 12c3e01.
  • Loading branch information
maciej committed Aug 25, 2017
1 parent 9ea82ed commit a5f78b2
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 24 deletions.
25 changes: 16 additions & 9 deletions popcnt.go
Original file line number Diff line number Diff line change
@@ -1,45 +1,52 @@
package roaring

import "math/bits"

// bit population count, uses Go 1.9 math/bits library
// bit population count, take from
// https://code.google.com/p/go/issues/detail?id=4988#c11
// credit: https://code.google.com/u/arnehormann/
// credit: https://play.golang.org/p/U7SogJ7psJ
// credit: http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
func popcount(x uint64) uint64 {
return uint64(bits.OnesCount64(x))
x -= (x >> 1) & 0x5555555555555555
x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
x += x >> 4
x &= 0x0f0f0f0f0f0f0f0f
x *= 0x0101010101010101
return x >> 56
}

func popcntSlice(s []uint64) uint64 {
func popcntSliceGo(s []uint64) uint64 {
cnt := uint64(0)
for _, x := range s {
cnt += popcount(x)
}
return cnt
}

func popcntMaskSlice(s, m []uint64) uint64 {
func popcntMaskSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
for i := range s {
cnt += popcount(s[i] &^ m[i])
}
return cnt
}

func popcntAndSlice(s, m []uint64) uint64 {
func popcntAndSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
for i := range s {
cnt += popcount(s[i] & m[i])
}
return cnt
}

func popcntOrSlice(s, m []uint64) uint64 {
func popcntOrSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
for i := range s {
cnt += popcount(s[i] | m[i])
}
return cnt
}

func popcntXorSlice(s, m []uint64) uint64 {
func popcntXorSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
for i := range s {
cnt += popcount(s[i] ^ m[i])
Expand Down
103 changes: 103 additions & 0 deletions popcnt_amd64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// +build amd64,!appengine

TEXT ·hasAsm(SB),4,$0-1
MOVQ $1, AX
CPUID
SHRQ $23, CX
ANDQ $1, CX
MOVB CX, ret+0(FP)
RET

#define POPCNTQ_DX_DX BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0xd2

TEXT ·popcntSliceAsm(SB),4,$0-32
XORQ AX, AX
MOVQ s+0(FP), SI
MOVQ s_len+8(FP), CX
TESTQ CX, CX
JZ popcntSliceEnd
popcntSliceLoop:
BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0x16 // POPCNTQ (SI), DX
ADDQ DX, AX
ADDQ $8, SI
LOOP popcntSliceLoop
popcntSliceEnd:
MOVQ AX, ret+24(FP)
RET

TEXT ·popcntMaskSliceAsm(SB),4,$0-56
XORQ AX, AX
MOVQ s+0(FP), SI
MOVQ s_len+8(FP), CX
TESTQ CX, CX
JZ popcntMaskSliceEnd
MOVQ m+24(FP), DI
popcntMaskSliceLoop:
MOVQ (DI), DX
NOTQ DX
ANDQ (SI), DX
POPCNTQ_DX_DX
ADDQ DX, AX
ADDQ $8, SI
ADDQ $8, DI
LOOP popcntMaskSliceLoop
popcntMaskSliceEnd:
MOVQ AX, ret+48(FP)
RET

TEXT ·popcntAndSliceAsm(SB),4,$0-56
XORQ AX, AX
MOVQ s+0(FP), SI
MOVQ s_len+8(FP), CX
TESTQ CX, CX
JZ popcntAndSliceEnd
MOVQ m+24(FP), DI
popcntAndSliceLoop:
MOVQ (DI), DX
ANDQ (SI), DX
POPCNTQ_DX_DX
ADDQ DX, AX
ADDQ $8, SI
ADDQ $8, DI
LOOP popcntAndSliceLoop
popcntAndSliceEnd:
MOVQ AX, ret+48(FP)
RET

TEXT ·popcntOrSliceAsm(SB),4,$0-56
XORQ AX, AX
MOVQ s+0(FP), SI
MOVQ s_len+8(FP), CX
TESTQ CX, CX
JZ popcntOrSliceEnd
MOVQ m+24(FP), DI
popcntOrSliceLoop:
MOVQ (DI), DX
ORQ (SI), DX
POPCNTQ_DX_DX
ADDQ DX, AX
ADDQ $8, SI
ADDQ $8, DI
LOOP popcntOrSliceLoop
popcntOrSliceEnd:
MOVQ AX, ret+48(FP)
RET

TEXT ·popcntXorSliceAsm(SB),4,$0-56
XORQ AX, AX
MOVQ s+0(FP), SI
MOVQ s_len+8(FP), CX
TESTQ CX, CX
JZ popcntXorSliceEnd
MOVQ m+24(FP), DI
popcntXorSliceLoop:
MOVQ (DI), DX
XORQ (SI), DX
POPCNTQ_DX_DX
ADDQ DX, AX
ADDQ $8, SI
ADDQ $8, DI
LOOP popcntXorSliceLoop
popcntXorSliceEnd:
MOVQ AX, ret+48(FP)
RET
67 changes: 67 additions & 0 deletions popcnt_asm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// +build amd64,!appengine

package roaring

// *** the following functions are defined in popcnt_amd64.s

//go:noescape

func hasAsm() bool

// useAsm is a flag used to select the GO or ASM implementation of the popcnt function
var useAsm = hasAsm()

//go:noescape

func popcntSliceAsm(s []uint64) uint64

//go:noescape

func popcntMaskSliceAsm(s, m []uint64) uint64

//go:noescape

func popcntAndSliceAsm(s, m []uint64) uint64

//go:noescape

func popcntOrSliceAsm(s, m []uint64) uint64

//go:noescape

func popcntXorSliceAsm(s, m []uint64) uint64

func popcntSlice(s []uint64) uint64 {
if useAsm {
return popcntSliceAsm(s)
}
return popcntSliceGo(s)
}

func popcntMaskSlice(s, m []uint64) uint64 {
if useAsm {
return popcntMaskSliceAsm(s, m)
}
return popcntMaskSliceGo(s, m)
}

func popcntAndSlice(s, m []uint64) uint64 {
if useAsm {
return popcntAndSliceAsm(s, m)
}
return popcntAndSliceGo(s, m)
}

func popcntOrSlice(s, m []uint64) uint64 {
if useAsm {
return popcntOrSliceAsm(s, m)
}
return popcntOrSliceGo(s, m)
}

func popcntXorSlice(s, m []uint64) uint64 {
if useAsm {
return popcntXorSliceAsm(s, m)
}
return popcntXorSliceGo(s, m)
}
23 changes: 23 additions & 0 deletions popcnt_generic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// +build !amd64 appengine

package roaring

func popcntSlice(s []uint64) uint64 {
return popcntSliceGo(s)
}

func popcntMaskSlice(s, m []uint64) uint64 {
return popcntMaskSliceGo(s, m)
}

func popcntAndSlice(s, m []uint64) uint64 {
return popcntAndSliceGo(s, m)
}

func popcntOrSlice(s, m []uint64) uint64 {
return popcntOrSliceGo(s, m)
}

func popcntXorSlice(s, m []uint64) uint64 {
return popcntXorSliceGo(s, m)
}
50 changes: 35 additions & 15 deletions popcnt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,69 @@ import (

func TestPopcntSlice(t *testing.T) {
s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
resGo := popcntSliceGo(s)
resAsm := popcntSliceAsm(s)
if resGo != resAsm {
t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm)
}
res := popcntSlice(s)
expected := uint64(27)
if res != expected {
t.Errorf("Got %d, expected %d", res, expected)
if res != resGo {
t.Errorf("The implementations are different")
}
}

func TestPopcntMaskSlice(t *testing.T) {
s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71}
resGo := popcntMaskSliceGo(s, m)
resAsm := popcntMaskSliceAsm(s, m)
if resGo != resAsm {
t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm)
}
res := popcntMaskSlice(s, m)
expected := uint64(9)
if res != expected {
t.Errorf("Got %d, expected %d", res, expected)
if res != resGo {
t.Errorf("The implementations are different")
}
}

func TestPopcntAndSlice(t *testing.T) {
s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71}
resGo := popcntAndSliceGo(s, m)
resAsm := popcntAndSliceAsm(s, m)
if resGo != resAsm {
t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm)
}
res := popcntAndSlice(s, m)
expected := uint64(18)
if res != expected {
t.Errorf("Got %d, expected %d", res, expected)
if res != resGo {
t.Errorf("The implementations are different")
}
}

func TestPopcntOrSlice(t *testing.T) {
s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71}
resGo := popcntOrSliceGo(s, m)
resAsm := popcntOrSliceAsm(s, m)
if resGo != resAsm {
t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm)
}
res := popcntOrSlice(s, m)
expected := uint64(50)
if res != expected {
t.Errorf("Got %d, expected %d", res, expected)
if res != resGo {
t.Errorf("The implementations are different")
}
}

func TestPopcntXorSlice(t *testing.T) {
s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71}
resGo := popcntXorSliceGo(s, m)
resAsm := popcntXorSliceAsm(s, m)
if resGo != resAsm {
t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm)
}
res := popcntXorSlice(s, m)
expected := uint64(32)
if res != expected {
t.Errorf("Got %d, expected %d", res, expected)
if res != resGo {
t.Errorf("The implementations are different")
}
}

0 comments on commit a5f78b2

Please sign in to comment.