8000 Switch from m3db/stackmurmur3 to a customized implementation of murmur3 by vdarulis · Pull Request #2 · m3db/bloom · GitHub
[go: up one dir, main page]

Skip to content
8000
Merged
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ branches:
- travis

go:
- 1.8
- 1.9
- 1.13
- 1.14
- tip

matrix:
Expand Down
73 changes: 17 additions & 56 deletions bloom.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,46 +33,11 @@ Similarly, to test if "Love" is in bloom:
package bloom

import (
"hash"
"math"

"github.com/m3db/bitset"
stackmurmur3 "github.com/m3db/stackmurmur3"

"github.com/spaolacci/murmur3"
)

// Hash128 the golang stdlib does not have a Hash128 definition.
type Hash128 interface {
hash.Hash
Sum128() (uint64, uint64)
}

var entropy = []byte{1}[:]

func bloomFilterHashes(hash Hash128, data []byte) [4]uint64 {
hash.Reset()
hash.Write(data)
h1, h2 := hash.Sum128()
hash.Write(entropy) // Add entropy
h3, h4 := hash.Sum128()
return [4]uint64{h1, h2, h3, h4}
}

// concurrentBloomFilterHashes provides the same hashes as bloomFilterHashes
// but allows the caller to not have to provide a resuseable hash, this is
// useful in highly concurrrent call paths that would otherwise have to take
// a ref to a hash from a shared pool (involving a write lock or channel) or
// allocate a new hash per operation (expensive for the GC).
func concurrentBloomFilterHashes(data []byte) [4]uint64 {
var hash stackmurmur3.Digest128
hash = hash.Write(data)
h1, h2 := hash.Sum128()
hash = hash.Write(entropy) // Add entropy
h3, h4 := hash.Sum128()
return [4]uint64{h1, h2, h3, h4}
}

func bloomFilterLocation(h [4]uint64, i, m uint64) uint {
v := h[i%2] + i*h[2+(((i+(i%2))%4)/2)]
return uint(v % m)
Expand All @@ -84,10 +49,9 @@ func bloomFilterLocation(h [4]uint64, i, m uint64) uint {
// desired. This is due to a cached hash digest being used to avoid
// allocation each read/write.
type BloomFilter struct {
m uint64
k uint64
set *bitset.BitSet
hash Hash128
m uint64
k uint64
set *bitset.BitSet
}

// NewBloomFilter creates a new bloom filter that can represent
Expand All @@ -100,14 +64,13 @@ func NewBloomFilter(m uint, k uint) *BloomFilter {
k = 1
}
return &BloomFilter{
m: uint64(m),
k: uint64(k),
set: bitset.NewBitSet(m),
hash: murmur3.New128(),
m: uint64(m),
k: uint64(k),
set: bitset.NewBitSet(m),
}
}

// BloomFilterEstimate estimates m and k, based on:
// EstimateFalsePositiveRate estimates m and k, based on:
// https://stackoverflow.com/a/22467497
func EstimateFalsePositiveRate(n uint, p float64) (m uint, k uint) {
floatM := (float64(-1) * float64(n) * math.Log(p)) / (math.Pow(math.Log(2), 2))
Expand All @@ -118,15 +81,15 @@ func EstimateFalsePositiveRate(n uint, p float64) (m uint, k uint) {

// Add value to the set.
func (b *BloomFilter) Add(value []byte) {
h := bloomFilterHashes(b.hash, value)
h := sum128WithEntropy(value)
for i := uint64(0); i < b.k; i++ {
b.set.Set(bloomFilterLocation(h, i, b.m))
}
}

// Test if value is in the set.
func (b *BloomFilter) Test(value []byte) bool {
h := bloomFilterHashes(b.hash, value)
h := sum128WithEntropy(value)
for i := uint64(0); i < b.k; i++ {
if !b.set.Test(bloomFilterLocation(h, i, b.m)) {
return false
Expand Down Expand Up @@ -156,27 +119,25 @@ func (b *BloomFilter) BitSet() *bitset.BitSet {
// desired. This is due to a cached hash digest being used to avoid
// allocation each read/write.
type ReadOnlyBloomFilter struct {
m uint64
k uint64
set *bitset.ReadOnlyBitSet
hash Hash128
m uint64
k uint64
set *bitset.ReadOnlyBitSet
}

// NewReadOnlyBloomFilter returns a new read only bloom filter backed
// by a byte slice, this means it can be used with a mmap'd bytes ref.
// It is not concurrent read or write safe.
func NewReadOnlyBloomFilter(m, k uint, data []byte) *ReadOnlyBloomFilter {
return &ReadOnlyBloomFilter{
m: uint64(m),
k: uint64(k),
set: bitset.NewReadOnlyBitSet(data),
hash: murmur3.New128(),
m: uint64(m),
k: uint64(k),
set: bitset.NewReadOnlyBitSet(data),
}
}

// Test if value is in the set.
func (b *ReadOnlyBloomFilter) Test(value []byte) bool {
h := bloomFilterHashes(b.hash, value)
h := sum128WithEntropy(value)
for i := uint64(0); i < b.k; i++ {
if !b.set.Test(bloomFilterLocation(h, i, b.m)) {
return false
Expand Down Expand Up @@ -224,7 +185,7 @@ func NewConcurrentReadOnlyBloomFilter(

// Test if value is in the set.
func (b *ConcurrentReadOnlyBloomFilter) Test(value []byte) bool {
h := concurrentBloomFilterHashes(value)
h := sum128WithEntropy(value)
for i := uint64(0); i < b.k; i++ {
if !b.set.Test(bloomFilterLocation(h, i, b.m)) {
return false
Expand Down
14 changes: 5 additions & 9 deletions bloom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (
"sync"
"testing"

"github.com/spaolacci/murmur3"

"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -44,7 +42,7 @@ func TestConcurrent(t *testing.T) {
for i := 0; i < try; i++ {
n1b := ro.Test(n1)
if !n1b {
err1 = fmt.Errorf("%v should be in", n1)
err1 = fmt.Errorf("%v should be in", string(n1))
break
}
}
Expand All @@ -56,7 +54,7 @@ func TestConcurrent(t *testing.T) {
for i := 0; i < try; i++ {
n2b := ro.Test(n2)
if !n2b {
err2 = fmt.Errorf("%v should be in", n2)
err2 = fmt.Errorf("%v should be in", string(n2))
break
}
}
Expand All @@ -66,10 +64,10 @@ func TestConcurrent(t *testing.T) {
wg.Wait()

if err1 != nil {
t.Fatal(err1)
t.Fatalf("bloom test 1 failed: %v", err1)
}
if err2 != nil {
t.Fatal(err2)
t.Fatalf("bloom test 2 failed: %v", err2)
}
}

Expand Down Expand Up @@ -221,10 +219,8 @@ func chiTestBloom(m, k, rounds uint, elements [][]byte) (succeeds bool) {
results := make([]uint, m)
chi := make([]float64, m)

hash := murmur3.New128()
for _, data := range elements {
hash.Reset()
h := bloomFilterHashes(hash, data)
h := sum128WithEntropy(data)
for i := uint64(0); i < f.k; i++ {
results[bloomFilterLocation(h, i, f.m)]++
}
Expand Down
17 changes: 11 additions & 6 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ testImport:
subpackages:
- assert
- require
- package: github.com/twmb/murmur3
version: ^1.1.4
- package: github.com/leanovate/gopter
version: ^0.2.8
Loading
0