pot: new package for proximity order tree

This commit is contained in:
zelig 2017-02-05 17:04:05 +06:30
parent fa99986143
commit 78ad3775fa
5 changed files with 2091 additions and 0 deletions

289
pot/address.go Normal file
View file

@ -0,0 +1,289 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package pot
import (
"encoding/binary"
"fmt"
"math/rand"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common"
)
var (
zeroAddr = &common.Hash{}
zerosHex = zeroAddr.Hex()[2:]
zerosBin = Address{}.Bin()
)
var (
addrlen = keylen
)
type Address common.Hash
func (a Address) String() string {
return fmt.Sprintf("%x", a[:])
}
func (a *Address) MarshalJSON() (out []byte, err error) {
return []byte(`"` + a.String() + `"`), nil
}
func (a *Address) UnmarshalJSON(value []byte) error {
*a = Address(common.HexToHash(string(value[1 : len(value)-1])))
return nil
}
// the string form of the binary representation of an address (only first 8 bits)
func (a Address) Bin() string {
return ToBin(a[:])
}
func ToBin(a []byte) string {
var bs []string
for _, b := range a {
bs = append(bs, fmt.Sprintf("%08b", b))
}
return strings.Join(bs, "")
}
func (a Address) Bytes() []byte {
return a[:]
}
/*
Proximity(x, y) returns the proximity order of the MSB distance between x and y
The distance metric MSB(x, y) of two equal length byte sequences x an y is the
value of the binary integer cast of the x^y, ie., x and y bitwise xor-ed.
the binary cast is big endian: most significant bit first (=MSB).
Proximity(x, y) is a discrete logarithmic scaling of the MSB distance.
It is defined as the reverse rank of the integer part of the base 2
logarithm of the distance.
It is calculated by counting the number of common leading zeros in the (MSB)
binary representation of the x^y.
(0 farthest, 255 closest, 256 self)
*/
func proximity(one, other Address) (ret int, eq bool) {
return posProximity(one, other, 0)
}
// posProximity(a, b, pos) returns proximity order of b wrt a (symmetric) pretending
// the first pos bits match, checking only bits kzindex >= pos
func posProximity(one, other Address, pos int) (ret int, eq bool) {
for i := pos / 8; i < len(one); i++ {
if one[i] == other[i] {
continue
}
oxo := one[i] ^ other[i]
start := 0
if i == pos/8 {
start = pos % 8
}
for j := start; j < 8; j++ {
if (uint8(oxo)>>uint8(7-j))&0x01 != 0 {
return i*8 + j, false
}
}
}
return len(one) * 8, true
}
// Address.ProxCmp compares the distances a->target and b->target.
// Returns -1 if a is closer to target, 1 if b is closer to target
// and 0 if they are equal.
func (target Address) ProxCmp(a, b Address) int {
for i := range target {
da := a[i] ^ target[i]
db := b[i] ^ target[i]
if da > db {
return 1
} else if da < db {
return -1
}
}
return 0
}
// randomAddressAt(address, prox) generates a random address
// at proximity order prox relative to address
// if prox is negative a random address is generated
func RandomAddressAt(self Address, prox int) (addr Address) {
addr = self
pos := -1
if prox >= 0 {
pos = prox / 8
trans := prox % 8
transbytea := byte(0)
for j := 0; j <= trans; j++ {
transbytea |= 1 << uint8(7-j)
}
flipbyte := byte(1 << uint8(7-trans))
transbyteb := transbytea ^ byte(255)
randbyte := byte(rand.Intn(255))
addr[pos] = ((addr[pos] & transbytea) ^ flipbyte) | randbyte&transbyteb
}
for i := pos + 1; i < len(addr); i++ {
addr[i] = byte(rand.Intn(255))
}
return
}
// KeyRange(a0, a1, proxLimit) returns the address inclusive address
// range that contain addresses closer to one than other
// func KeyRange(one, other Address, proxLimit int) (start, stop Address) {
// prox := proximity(one, other)
// if prox >= proxLimit {
// prox = proxLimit
// }
// start = CommonBitsAddrByte(one, other, byte(0x00), prox)
// stop = CommonBitsAddrByte(one, other, byte(0xff), prox)
// return
// }
func CommonBitsAddrF(self, other Address, f func() byte, p int) (addr Address) {
prox, _ := proximity(self, other)
var pos int
if p <= prox {
prox = p
}
pos = prox / 8
addr = self
trans := byte(prox % 8)
var transbytea byte
if p > prox {
transbytea = byte(0x7f)
} else {
transbytea = byte(0xff)
}
transbytea >>= trans
transbyteb := transbytea ^ byte(0xff)
addrpos := addr[pos]
addrpos &= transbyteb
if p > prox {
addrpos ^= byte(0x80 >> trans)
}
addrpos |= transbytea & f()
addr[pos] = addrpos
for i := pos + 1; i < len(addr); i++ {
addr[i] = f()
}
return
}
func CommonBitsAddr(self, other Address, prox int) (addr Address) {
return CommonBitsAddrF(self, other, func() byte { return byte(rand.Intn(255)) }, prox)
}
func CommonBitsAddrByte(self, other Address, b byte, prox int) (addr Address) {
return CommonBitsAddrF(self, other, func() byte { return b }, prox)
}
// randomAddressAt() generates a random address
func RandomAddress() Address {
return RandomAddressAt(Address{}, -1)
}
// wraps an Address to implement the PotVal interface
type HashAddress struct {
Address
}
func (a *HashAddress) String() string {
return a.Address.Bin()
}
func NewAddressFromString(s string) []byte {
ha := [32]byte{}
t := s + string(zerosBin)[:len(zerosBin)-len(s)]
for i := 0; i < 4; i++ {
n, err := strconv.ParseUint(t[i*64:(i+1)*64], 2, 64)
if err != nil {
panic("wrong format: " + err.Error())
}
binary.BigEndian.PutUint64(ha[i*8:(i+1)*8], uint64(n))
}
return ha[:]
}
func NewHashAddress(s string) *HashAddress {
ha := NewAddressFromString(s)
h := common.Hash{}
copy(h[:], ha)
return &HashAddress{Address(h)}
}
func NewHashAddressFromBytes(b []byte) *HashAddress {
h := common.Hash{}
copy(h[:], b)
return &HashAddress{Address(h)}
}
// PO(addr, pos) return the proximity order of addr wrt to
// the pinned address of the tree
// assuming it is greater than or equal to pos
func (self *HashAddress) PO(val PotVal, pos int) (po int, eq bool) {
return posProximity(self.Address, val.(*HashAddress).Address, pos)
}
type BoolAddress struct {
addr []bool
}
func NewBoolAddress(s string) *BoolAddress {
return NewBoolAddressXOR(s, zerosBin[:len(s)])
}
func NewBoolAddressXOR(s, t string) *BoolAddress {
if len(s) != len(t) {
panic("lengths do not match")
}
addr := make([]bool, len(s))
for i, _ := range addr {
addr[i] = s[i] != t[i]
}
return &BoolAddress{addr}
}
func (self *BoolAddress) String() string {
a := self.addr
s := []byte(zerosBin)[:len(a)]
for i, one := range a {
if one {
s[i] = byte('1')
}
}
return string(s)
}
func (self *BoolAddress) PO(val PotVal, pos int) (po int, eq bool) {
a := self.addr
b := val.(*BoolAddress).addr
for po = pos; po < len(b); po++ {
if a[po] != b[po] {
return po, false
}
}
return po, true
}

141
pot/address_test.go Normal file
View file

@ -0,0 +1,141 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package pot
import (
"math/rand"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
)
func (Address) Generate(rand *rand.Rand, size int) reflect.Value {
var id Address
for i := 0; i < len(id); i++ {
id[i] = byte(uint8(rand.Intn(255)))
}
return reflect.ValueOf(id)
}
func TestCommonBitsAddrF(t *testing.T) {
a := Address(common.HexToHash("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
b := Address(common.HexToHash("0x8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
c := Address(common.HexToHash("0x4123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
d := Address(common.HexToHash("0x0023456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
e := Address(common.HexToHash("0x01A3456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
ab := CommonBitsAddrF(a, b, func() byte { return byte(0x00) }, 10)
expab := Address(common.HexToHash("0x8000000000000000000000000000000000000000000000000000000000000000"))
if ab != expab {
t.Fatalf("%v != %v", ab, expab)
}
ac := CommonBitsAddrF(a, c, func() byte { return byte(0x00) }, 10)
expac := Address(common.HexToHash("0x4000000000000000000000000000000000000000000000000000000000000000"))
if ac != expac {
t.Fatalf("%v != %v", ac, expac)
}
ad := CommonBitsAddrF(a, d, func() byte { return byte(0x00) }, 10)
expad := Address(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"))
if ad != expad {
t.Fatalf("%v != %v", ad, expad)
}
ae := CommonBitsAddrF(a, e, func() byte { return byte(0x00) }, 10)
expae := Address(common.HexToHash("0x0180000000000000000000000000000000000000000000000000000000000000"))
if ae != expae {
t.Fatalf("%v != %v", ae, expae)
}
acf := CommonBitsAddrF(a, c, func() byte { return byte(0xff) }, 10)
expacf := Address(common.HexToHash("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
if acf != expacf {
t.Fatalf("%v != %v", acf, expacf)
}
aeo := CommonBitsAddrF(a, e, func() byte { return byte(0x00) }, 2)
expaeo := Address(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"))
if aeo != expaeo {
t.Fatalf("%v != %v", aeo, expaeo)
}
aep := CommonBitsAddrF(a, e, func() byte { return byte(0xff) }, 2)
expaep := Address(common.HexToHash("0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
if aep != expaep {
t.Fatalf("%v != %v", aep, expaep)
}
}
func TestRandomAddressAt(t *testing.T) {
var a Address
for i := 0; i < 100; i++ {
a = RandomAddress()
prox := rand.Intn(255)
b := RandomAddressAt(a, prox)
p, _ := proximity(a, b)
if p != prox {
t.Fatalf("incorrect address prox(%v, %v) == %v (expected %v)", a, b, p, prox)
}
}
}
const (
maxTestPOs = 1000
testPOkeylen = 9
)
func TestPOs(t *testing.T) {
for i := 0; i < maxTestPOs; i++ {
length := rand.Intn(256) + 1
v0 := RandomAddress().Bin()[:length]
v1 := RandomAddress().Bin()[:length]
a0 := NewBoolAddress(v0)
a1 := NewBoolAddress(v1)
b0 := NewHashAddress(v0)
b1 := NewHashAddress(v1)
pos := rand.Intn(length) + 1
apo, aeq := a0.PO(a1, pos)
bpo, beq := b0.PO(b1, pos)
if bpo == 256 {
bpo = length
}
a0s := a0.String()
if a0s != v0 {
t.Fatalf("incorrect bool address. expected %v, got %v", v0, a0s)
}
a1s := a1.String()
if a1s != v1 {
t.Fatalf("incorrect bool address. expected %v, got %v", v1, a1s)
}
b0s := b0.String()[:length]
if b0s != v0 {
t.Fatalf("incorrect hash address. expected %v, got %v", v0, b0s)
}
b1s := b1.String()[:length]
if b1s != v1 {
t.Fatalf("incorrect hash address. expected %v, got %v", v1, b1s)
}
if apo != bpo {
t.Fatalf("PO does not match for %v X %v (pos: %v): expected %v, got %v", v0, v1, pos, apo, bpo)
}
if aeq != beq {
t.Fatalf("PO equality does not match for %v X %v (pos: %v): expected %v, got %v", v0, v1, pos, aeq, beq)
}
}
}

65
pot/doc.go Normal file
View file

@ -0,0 +1,65 @@
/*
POT: proximity order tree implements a container similar to a binary tree.
Value types implement the PoVal interface which provides the PO (proximity order)
comparison operator.
Each fork in the trie is itself a value. Values of the subtree contained under
a node all share the same order when compared to other elements in the tree.
Example of proximity order is the length of the common prefix over bitvectors.
(which is equivalent to the order of magnitude of the XOR distance over integers).
The package provides two implementations of PoVal,
* BoolAddress: an arbitrary length boolean vector
* HashAddress: a bitvector based address derived from 256 bit hash (common.Hash)
If the address space if limited, equality is defined as the maximum proximity order.
Arbitrary value types can extend these base types or define their own PO method.
The container offers applicative (funcional) style methods on PO trees:
* adding/removing en element
* swap (value based add/remove)
* merging two PO trees (union)
as well as iterator accessors that respect proximity order
When synchronicity of membership if not 100% requirement (e.g. used as a database
of network connections), applicative structures have the advantage that nodes
are immutable therefore manipulation does not need locking allowing for parallel retrievals.
For the use case where the entire container is supposed to allow changes by parallel routines, instance methods with write locks and access methods with readlock are provided. The latter only locks while reading the root node.
Pot
* retrieval, insertion and deletion by key involves log(n) pointer lookups
* for any item retrieval (defined as common prefix on the binary key)
* provide syncronous iterators respecting proximity ordering wrt any item
* provide asyncronous iterator (for parallel execution of operations) over n items
* allows cheap iteration over ranges
* asymmetric parallellised merge (union)
Note:
* as is, union only makes sense for set representations since which of two values with equal keys survives is random
* intersection is not implemented
* simple get accessor is not implemented (but derivable from EachNeighbour)
Pinned value on the node implies no need to copy keys of the item type.
Note that
* the same set of values allows for a large number of alternative
POT representations.
* values on the top are accessed faster than lower ones and the steps needed to retrieve items has a logarithmic distribution.
As a consequence on can organise the tree so that items that need faster access are torwards the top.
In particular for any subset where popularity has a power distriution that is independent of
proximity order (content addressed storage of chunks), it is in principle possible to create a pot where the steps needed to access an item is inversely proportional to its popularity.
Such organisation is not implemented as yet.
TODO:
* swap
* get
* overwrite-style merge
* intersection
* access frequency based optimisations
*/
package pot

881
pot/pot.go Normal file
View file

@ -0,0 +1,881 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package pot
import (
"fmt"
"sync"
)
const (
keylen = 256
maxkeylen = 256
)
// Pot is the root node type, allows locked non-applicative manipulation
type Pot struct {
lock sync.RWMutex
*pot
}
// pot is the node type (same for root, branching node and leaf)
type pot struct {
pin PotVal
bins []*pot
size int
po int
}
// PotVal is the interface the generic container item should implement
type PotVal interface {
PO(PotVal, int) (po int, eq bool)
String() string
}
// Pot constructor. Requires value of type PotVal to pin
// and po to point to a span in the PotVal key
// The pinned item counts towards the size
func NewPot(v PotVal, po int) *Pot {
var size int
if v != nil {
size++
}
return &Pot{
pot: &pot{
pin: v,
po: po,
size: size,
},
}
}
// Pin() returns the pinned element (key) of the Pot
func (t *Pot) Pin() PotVal {
return t.pin
}
// Size() returns the number of values in the Pot
func (t *Pot) Size() int {
t.lock.RLock()
defer t.lock.RUnlock()
return t.size
}
// Add(v) inserts v into the Pot and
// returns the proximity order of v and a boolean
// indicating if the item was found
// Add locks the Pot while using applicative add on its pot
func (t *Pot) Add(val PotVal) (po int, found bool) {
t.lock.Lock()
defer t.lock.Unlock()
t.pot, po, found = add(t.pot, val)
return po, found
}
// Add(t, v) returns a new Pot that contains all the elements of t
// plus the value v, using the applicative add
// the second return value is the proximity order of the inserted element
// the third is boolean indicating if the item was found
// it only readlocks the Pot while reading its pot
func Add(t *Pot, val PotVal) (*Pot, int, bool) {
t.lock.RLock()
n := t.pot
t.lock.RUnlock()
r, po, found := add(n, val)
return &Pot{pot: r}, po, found
}
func add(t *pot, val PotVal) (*pot, int, bool) {
var r *pot
if t == nil || t.pin == nil {
r = &pot{
pin: val,
size: t.size + 1,
po: t.po,
bins: t.bins,
}
return r, 0, false
}
po, found := t.pin.PO(val, t.po)
if found {
r = &pot{
pin: val,
size: t.size,
po: t.po,
bins: t.bins,
}
return r, po, true
}
var p *pot
var i, j int
size := t.size
for i < len(t.bins) {
n := t.bins[i]
if n.po == po {
p, _, found = add(n, val)
if !found {
size++
}
j++
break
}
if n.po > po {
break
}
i++
j++
}
if p == nil {
size++
p = &pot{
pin: val,
size: 1,
po: po,
}
}
bins := append([]*pot{}, t.bins[:i]...)
bins = append(bins, p)
bins = append(bins, t.bins[j:]...)
r = &pot{
pin: t.pin,
size: size,
po: t.po,
bins: bins,
}
return r, po, found
}
// T.Re move(v) deletes v from the Pot and returns
// the proximity order of v and a boolean value indicating
// if the value was found
// Remove locks Pot while using applicative remove on its pot
func (t *Pot) Remove(val PotVal) (po int, found bool) {
t.lock.Lock()
defer t.lock.Unlock()
t.pot, po, found = remove(t.pot, val)
return po, found
}
// Remove(t, v) returns a new Pot that contains all the elements of t
// minus the value v, using the applicative remove
// the second return value is the proximity order of the inserted element
// the third is boolean indicating if the item was found
// it only readlocks the Pot while reading its pot
func Remove(t *Pot, v PotVal) (*Pot, int, bool) {
t.lock.RLock()
n := t.pot
t.lock.RUnlock()
r, po, found := remove(n, v)
return &Pot{pot: r}, po, found
}
func remove(t *pot, val PotVal) (r *pot, po int, found bool) {
size := t.size
po, found = t.pin.PO(val, t.po)
if found {
size--
if size == 0 {
r = &pot{
po: t.po,
}
return r, po, true
}
i := len(t.bins) - 1
last := t.bins[i]
r = &pot{
pin: last.pin,
bins: append(t.bins[:i], last.bins...),
size: size,
po: t.po,
}
return r, t.po, true
}
var p *pot
var i, j int
for i < len(t.bins) {
n := t.bins[i]
if n.po == po {
p, po, found = remove(n, val)
if found {
size--
}
j++
break
}
if n.po > po {
return t, po, false
}
i++
j++
}
bins := t.bins[:i]
if p != nil && p.pin != nil {
bins = append(bins, p)
}
bins = append(bins, t.bins[j:]...)
r = &pot{
pin: val,
size: size,
po: t.po,
bins: bins,
}
return r, po, found
}
// Swap(k, f) looks up the item at k
// and applies the function f to the value v at k or nil if the item is not found
// if f returns nil, the element is removed
// if f returns v' <> v then v' is inserted into the Pot
// if v' == v the pot is not changed
// it panics if v'.PO(k, 0) says v and k are not equal
func (t *Pot) Swap(val PotVal, f func(v PotVal) PotVal) (po int, found bool, change bool) {
t.lock.Lock()
defer t.lock.Unlock()
var t0 *pot
t0, po, found, change = swap(t.pot, val, f)
if change {
t.pot = t0
}
return po, found, change
}
func swap(t *pot, k PotVal, f func(v PotVal) PotVal) (r *pot, po int, found bool, change bool) {
var val PotVal
if t == nil || t.pin == nil {
val = f(nil)
if val == nil {
return t, t.po, false, false
}
if _, eq := val.PO(k, t.po); !eq {
panic("value key mismatch")
}
r = &pot{
pin: val,
size: t.size + 1,
po: t.po,
bins: t.bins,
}
return r, t.po, false, true
}
size := t.size
if k == nil {
panic("k is nil")
}
po, found = k.PO(t.pin, t.po)
if found {
val = f(t.pin)
if val == nil {
size--
if size == 0 {
r = &pot{
po: t.po,
}
return r, po, true, true
}
i := len(t.bins) - 1
last := t.bins[i]
r = &pot{
pin: last.pin,
bins: append(t.bins[:i], last.bins...),
size: size,
po: t.po,
}
return r, t.po, true, true
// remove element
} else if val == t.pin {
return nil, po, true, false
} else { // add element
r = &pot{
pin: val,
size: t.size,
po: t.po,
bins: t.bins,
}
return r, po, true, true
}
}
var p *pot
var i, j int
for i < len(t.bins) {
n := t.bins[i]
if n.po == po {
p, po, found, change = swap(n, k, f)
if !change {
return nil, po, found, false
}
size += p.size - n.size
j++
break
}
if n.po > po {
break
}
i++
j++
}
if p == nil {
val := f(nil)
if val == nil {
return nil, po, false, false
}
size++
p = &pot{
pin: val,
size: 1,
po: po,
}
}
bins := append([]*pot{}, t.bins[:i]...)
if p.pin != nil {
bins = append(bins, p)
}
bins = append(bins, t.bins[j:]...)
r = &pot{
pin: t.pin,
size: size,
po: t.po,
bins: bins,
}
return r, po, found, true
}
// t0.Merge(t1) changes t0 to contain all the elements of t1
// it locks t0, but only readlocks t1 while taking its pot
// uses applicative union
func (t *Pot) Merge(t1 *Pot) (c int) {
t.lock.Lock()
defer t.lock.Unlock()
t1.lock.RLock()
n1 := t1.pot
t1.lock.RUnlock()
t.pot, c = union(t.pot, n1)
return c
}
// Union(t0, t1) return the union of t0 and t1
// it only readlocks the Pot-s to read their pots and
// calculates the union using the applicative union
// the second return value is the number of common elements
func Union(t0, t1 *Pot) (*Pot, int) {
t0.lock.RLock()
n0 := t0.pot
t0.lock.RUnlock()
t1.lock.RLock()
n1 := t1.pot
t1.lock.RUnlock()
p, c := union(n0, n1)
return &Pot{
pot: p,
}, c
}
func union(t0, t1 *pot) (*pot, int) {
if t0 == nil || t0.size == 0 {
return t1, 0
}
if t1 == nil || t1.size == 0 {
return t0, 0
}
var pin PotVal
var bins []*pot
var mis []int
wg := &sync.WaitGroup{}
pin0 := t0.pin
pin1 := t1.pin
bins0 := t0.bins
bins1 := t1.bins
var i0, i1 int
var common int
po, eq := pin0.PO(pin1, 0)
for {
l0 := len(bins0)
l1 := len(bins1)
var n0, n1 *pot
var p0, p1 int
var a0, a1 bool
for {
if !a0 && i0 < l0 && bins0[i0].po <= po {
n0 = bins0[i0]
p0 = n0.po
a0 = p0 == po
} else {
a0 = true
}
if !a1 && i1 < l1 && bins1[i1].po <= po {
n1 = bins1[i1]
p1 = n1.po
a1 = p1 == po
} else {
a1 = true
}
if a0 && a1 {
break
}
switch {
case (p0 < p1 || a1) && !a0:
bins = append(bins, n0)
i0++
n0 = nil
case (p1 < p0 || a0) && !a1:
bins = append(bins, n1)
i1++
n1 = nil
case p1 < po:
bl := len(bins)
bins = append(bins, nil)
ml := len(mis)
mis = append(mis, 0)
wg.Add(1)
go func(b, m int, m0, m1 *pot) {
defer wg.Done()
bins[b], mis[m] = union(m0, m1)
}(bl, ml, n0, n1)
i0++
i1++
n0 = nil
n1 = nil
}
}
if eq {
common++
pin = pin1
break
}
i := i0
if len(bins0) > i && bins0[i].po == po {
i++
}
var size0 int
for _, n := range bins0[i:] {
size0 += n.size
}
np := &pot{
pin: pin0,
bins: bins0[i:],
size: size0 + 1,
po: po,
}
bins2 := []*pot{np}
if n0 == nil {
pin0 = pin1
po = maxkeylen + 1
eq = true
common--
} else {
bins2 = append(bins2, n0.bins...)
pin0 = pin1
pin1 = n0.pin
po, eq = pin0.PO(pin1, n0.po)
}
bins0 = bins1
bins1 = bins2
i0 = i1
i1 = 0
}
wg.Wait()
for _, c := range mis {
common += c
}
n := &pot{
pin: pin,
bins: bins,
size: t0.size + t1.size - common,
po: t0.po,
}
return n, common
}
// Each(f) is a synchronous iterator over the bins of a node
// respecting an ordering
// proximity > pinnedness
func (t *Pot) Each(f func(PotVal, int) bool) bool {
t.lock.RLock()
n := t.pot
t.lock.RUnlock()
return n.each(f)
}
func (t *pot) each(f func(PotVal, int) bool) bool {
var next bool
for _, n := range t.bins {
next = n.each(f)
if !next {
return false
}
}
return f(t.pin, t.po)
}
// EachFrom(f, start) is a synchronous iterator over the elements of a pot
// within the inclusive range starting from proximity order start
// the function argument is passed the value and the proximity order wrt the root pin
// it does NOT include the pinned item of the root
// respecting an ordering
// proximity > pinnedness
// the iteration ends if the function return false or there are no more elements
// end of a po range can be implemented since po is passed to the function
func (t *Pot) EachFrom(f func(PotVal, int) bool, po int) bool {
t.lock.RLock()
n := t.pot
t.lock.RUnlock()
return n.eachFrom(f, po)
}
func (t *pot) eachFrom(f func(PotVal, int) bool, po int) bool {
var next bool
_, lim := t.getPos(po)
for i := lim; i < len(t.bins); i++ {
n := t.bins[i]
next = n.each(f)
if !next {
return false
}
}
return f(t.pin, t.po)
}
// EachBin iterates over bins of the pivot node and offers iterators to the caller on each
// subtree passing the proximity order and the size
// the iteration continues until the function's return value is false
// or there are no more subtries
func (t *Pot) EachBin(val PotVal, po int, f func(int, int, func(func(val PotVal, i int) bool) bool) bool) {
t.lock.RLock()
n := t.pot
t.lock.RUnlock()
n.eachBin(val, po, f)
}
func (t *pot) eachBin(val PotVal, po int, f func(int, int, func(func(val PotVal, i int) bool) bool) bool) {
if t == nil || t.size == 0 {
return
}
spr, _ := t.pin.PO(val, t.po)
_, lim := t.getPos(spr)
var size int
var n *pot
for i := 0; i < lim; i++ {
n = t.bins[i]
size += n.size
if n.po < po {
continue
}
if !f(n.po, n.size, n.each) {
return
}
}
if lim == len(t.bins) {
f(spr, 1, func(g func(PotVal, int) bool) bool {
return g(t.pin, spr)
})
return
}
n = t.bins[lim]
spo := spr
if n.po == spr {
spo++
size += n.size
}
if !f(spr, t.size-size, func(g func(PotVal, int) bool) bool {
return t.eachFrom(func(v PotVal, j int) bool {
return g(v, spr)
}, spo)
}) {
return
}
if spo > spr {
n.eachBin(val, spo, f)
}
}
// syncronous iterator over neighbours of any target val
// the order of elements retrieved reflect proximity order to the target
// TODO: add maximum proxbin to start range of iteration
func (t *Pot) EachNeighbour(val PotVal, f func(PotVal, int) bool) bool {
t.lock.RLock()
n := t.pot
t.lock.RUnlock()
return n.eachNeighbour(val, f)
}
func (t *pot) eachNeighbour(val PotVal, f func(PotVal, int) bool) bool {
if t == nil || t.size == 0 {
return false
}
var next bool
l := len(t.bins)
var n *pot
ir := l
il := l
po, eq := t.pin.PO(val, t.po)
if !eq {
n, il = t.getPos(po)
if n != nil {
next = n.eachNeighbour(val, f)
if !next {
return false
}
ir = il
} else {
ir = il - 1
}
}
next = f(t.pin, po)
if !next {
return false
}
for i := l - 1; i > ir; i-- {
next = t.bins[i].each(func(v PotVal, _ int) bool {
return f(v, po)
})
if !next {
return false
}
}
for i := il - 1; i >= 0; i-- {
n := t.bins[i]
next = n.each(func(v PotVal, _ int) bool {
return f(v, n.po)
})
if !next {
return false
}
}
return true
}
// EachNeighnbourAsync(val, max, maxPos, f, wait) is an asyncronous iterator
// over elements not closer than maxPos wrt val.
// val does not need to be match an element of the pot, but if it does, and
// maxPos is keylength than it is included in the iteration
// Calls to f are parallelised, the order of calls is undefined.
// proximity order is respected in that there is no element in the pot that
// is not visited if a closer node is visited.
// The iteration is finished when max number of nearest nodes is visited
// or if the entire there are no nodes not closer than maxPos that is not visited
// if wait is true, the iterator returns only if all calls to f are finished
// TODO: implement minPos for proper prox range iteration
func (t *Pot) EachNeighbourAsync(val PotVal, max int, maxPos int, f func(PotVal, int), wait bool) {
t.lock.RLock()
n := t.pot
t.lock.RUnlock()
if max > t.size {
max = t.size
}
var wg *sync.WaitGroup
if wait {
wg = &sync.WaitGroup{}
}
_ = n.eachNeighbourAsync(val, max, maxPos, f, wg)
if wait {
wg.Wait()
}
}
func (t *pot) eachNeighbourAsync(val PotVal, max int, maxPos int, f func(PotVal, int), wg *sync.WaitGroup) (extra int) {
l := len(t.bins)
var n *pot
il := l
ir := l
// ic := l
po, eq := t.pin.PO(val, t.po)
// if po is too close, set the pivot branch (pom) to maxPos
pom := po
if pom > maxPos {
pom = maxPos
}
n, il = t.getPos(pom)
ir = il
// if pivot branch exists and po is not too close, iterate on the pivot branch
if pom == po {
if n != nil {
m := n.size
if max < m {
m = max
}
max -= m
extra = n.eachNeighbourAsync(val, m, maxPos, f, wg)
} else {
if !eq {
ir--
}
}
} else {
extra++
max--
if n != nil {
il++
}
// before checking max, add up the extra elements
// on the close branches that are skipped (if po is too close)
for i := l - 1; i >= il; i-- {
s := t.bins[i]
m := s.size
if max < m {
m = max
}
max -= m
extra += m
}
}
var m int
if pom == po {
m, max, extra = need(1, max, extra)
if m <= 0 {
return
}
if wg != nil {
wg.Add(1)
}
go func() {
if wg != nil {
defer wg.Done()
}
f(t.pin, po)
}()
// otherwise iterats
for i := l - 1; i > ir; i-- {
n := t.bins[i]
m, max, extra = need(n.size, max, extra)
if m <= 0 {
return
}
if wg != nil {
wg.Add(m)
}
go func(pn *pot, pm int) {
pn.each(func(v PotVal, _ int) bool {
if wg != nil {
defer wg.Done()
}
f(v, po)
pm--
return pm > 0
})
}(n, m)
}
}
// iterate branches that are farther tham pom with their own po
for i := il - 1; i >= 0; i-- {
n := t.bins[i]
// the first time max is less than the size of the entire branch
// wait for the pivot thread to release extra elements
m, max, extra = need(n.size, max, extra)
if m <= 0 {
return
}
if wg != nil {
wg.Add(m)
}
go func(pn *pot, pm int) {
pn.each(func(v PotVal, _ int) bool {
if wg != nil {
defer wg.Done()
}
f(v, pn.po)
pm--
return pm > 0
})
}(n, m)
}
return max + extra
}
// getPos(n) returns the forking node at PO n and its index if it exists
// otherwise nil
// caller is suppoed to hold the lock
func (t *pot) getPos(po int) (n *pot, i int) {
for i, n = range t.bins {
if po > n.po {
continue
}
if po < n.po {
return nil, i
}
return n, i
}
return nil, len(t.bins)
}
// need(m, max, extra) uses max m out of extra, and then max
// if needed, returns the adjusted counts
func need(m, max, extra int) (int, int, int) {
if m <= extra {
return m, max, extra - m
}
max += extra - m
if max <= 0 {
return m + max, 0, 0
}
return m, max, 0
}
func (t *pot) String() string {
return t.sstring("")
}
func (t *pot) sstring(indent string) string {
if t == nil {
return "<nil>"
}
var s string
indent += " "
s += fmt.Sprintf("%v%v (%v) %v \n", indent, t.pin, t.po, t.size)
for _, n := range t.bins {
s += fmt.Sprintf("%v%v\n", indent, n.sstring(indent))
}
return s
}

715
pot/pot_test.go Normal file
View file

@ -0,0 +1,715 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package pot
import (
"errors"
"fmt"
"math/rand"
"runtime"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
)
const (
maxEachNeighbourTests = 420
maxEachNeighbour = 420
)
func init() {
glog.SetV(0)
glog.SetToStderr(true)
}
type testBVAddr struct {
*HashAddress
i int
}
func NewTestBVAddr(s string, i int) *testBVAddr {
return &testBVAddr{NewHashAddress(s), i}
}
func (a *testBVAddr) String() string {
return a.HashAddress.String()[:keylen]
}
func (self *testBVAddr) PO(val PotVal, po int) (int, bool) {
return self.HashAddress.PO(val.(*testBVAddr).HashAddress, po)
}
type testAddr struct {
*BoolAddress
i int
}
func NewTestAddr(s string, i int) *testAddr {
return &testAddr{NewBoolAddress(s), i}
}
func (self *testAddr) PO(val PotVal, po int) (int, bool) {
return self.BoolAddress.PO(val.(*testAddr).BoolAddress, po)
}
func str(v PotVal) string {
if v == nil {
return ""
}
return v.(*testAddr).String()
}
func randomTestAddr(n int, i int) *testAddr {
v := RandomAddress().Bin()[:n]
return NewTestAddr(v, i)
}
func randomTestBVAddr(n int, i int) *testBVAddr {
v := RandomAddress().Bin()[:n]
return NewTestBVAddr(v, i)
}
func indexes(t *Pot) (i []int, po []int) {
t.Each(func(v PotVal, p int) bool {
a := v.(*testAddr)
i = append(i, a.i)
po = append(po, p)
return true
})
return i, po
}
func testAdd(t *Pot, n int, values ...string) {
for i, val := range values {
t.Add(NewTestAddr(val, i+n))
}
}
// func RandomBoolAddress()
func TestPotAdd(t *testing.T) {
n := NewPot(NewTestAddr("001111", 0), 0)
// Pin set correctly
exp := "001111"
got := str(n.Pin())[:6]
if got != exp {
t.Fatalf("incorrect pinned value. Expected %v, got %v", exp, got)
}
// check size
goti := n.Size()
expi := 1
if goti != expi {
t.Fatalf("incorrect number of elements in Pot. Expected %v, got %v", expi, goti)
}
testAdd(n, 1, "011111", "001111", "011111", "000111")
// check size
goti = n.Size()
expi = 3
if goti != expi {
t.Fatalf("incorrect number of elements in Pot. Expected %v, got %v", expi, goti)
}
inds, po := indexes(n)
got = fmt.Sprintf("%v", inds)
exp = "[3 4 2]"
if got != exp {
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
}
got = fmt.Sprintf("%v", po)
exp = "[1 2 0]"
if got != exp {
t.Fatalf("incorrect po-s in iteration over Pot. Expected %v, got %v", exp, got)
}
}
// func RandomBoolAddress()
func TestPotRemove(t *testing.T) {
n := NewPot(NewTestAddr("001111", 0), 0)
n.Remove(NewTestAddr("001111", 0))
exp := ""
got := str(n.Pin())
if got != exp {
t.Fatalf("incorrect pinned value. Expected %v, got %v", exp, got)
}
testAdd(n, 1, "000000", "011111", "001111", "000111")
n.Remove(NewTestAddr("001111", 0))
goti := n.Size()
expi := 3
if goti != expi {
t.Fatalf("incorrect number of elements in Pot. Expected %v, got %v", expi, goti)
}
inds, po := indexes(n)
got = fmt.Sprintf("%v", inds)
exp = "[2 4 0]"
if got != exp {
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
}
got = fmt.Sprintf("%v", po)
exp = "[1 3 0]"
if got != exp {
t.Fatalf("incorrect po-s in iteration over Pot. Expected %v, got %v", exp, got)
}
// remove again
n.Remove(NewTestAddr("001111", 0))
inds, po = indexes(n)
got = fmt.Sprintf("%v", inds)
exp = "[2 4]"
if got != exp {
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
}
}
func TestPotSwap(t *testing.T) {
max := maxEachNeighbour
n := NewPot(nil, 0)
var m []*testBVAddr
for j := 0; j < 2*max; {
v := randomTestBVAddr(keylen, j)
_, found := n.Add(v)
if !found {
m = append(m, v)
j++
}
}
k := make(map[string]*testBVAddr)
for j := 0; j < max; {
v := randomTestBVAddr(keylen, 1)
_, found := k[v.String()]
if !found {
k[v.String()] = v
j++
}
}
for _, v := range k {
m = append(m, v)
}
f := func(v PotVal) PotVal {
tv := v.(*testBVAddr)
if tv.i < max {
return nil
}
tv.i = 0
return v
}
for _, val := range m {
n.Swap(val, func(v PotVal) PotVal {
if v == nil {
return val
}
return f(v)
})
}
sum := 0
n.Each(func(v PotVal, i int) bool {
sum++
tv := v.(*testBVAddr)
if tv.i > 1 {
t.Fatalf("item value incorrect, expected 0, got %v", tv.i)
}
return true
})
if sum != 2*max {
t.Fatalf("incorrect number of elements. expected %v, got %v", 2*max, sum)
}
if sum != n.Size() {
t.Fatalf("incorrect size. expected %v, got %v", sum, n.Size())
}
}
func checkPo(val PotVal) func(PotVal, int) error {
return func(v PotVal, po int) error {
// check the po
exp, _ := val.PO(v, 0)
if po != exp {
return fmt.Errorf("incorrect prox order for item %v in neighbour iteration for %v. Expected %v, got %v", v, val, exp, po)
}
return nil
}
}
func checkOrder(val PotVal) func(PotVal, int) error {
var po int = keylen
return func(v PotVal, p int) error {
if po < p {
return fmt.Errorf("incorrect order for item %v in neighbour iteration for %v. PO %v > %v (previous max)", v, val, p, po)
}
po = p
return nil
}
}
func checkValues(m map[string]bool, val PotVal) func(PotVal, int) error {
return func(v PotVal, po int) error {
duplicate, ok := m[v.String()]
if !ok {
return fmt.Errorf("alien value %v", v)
}
if duplicate {
return fmt.Errorf("duplicate value returned: %v", v)
}
m[v.String()] = true
return nil
}
}
var errNoCount = errors.New("not count")
func testPotEachNeighbour(n *Pot, val PotVal, expCount int, fs ...func(PotVal, int) error) error {
var err error
var count int
n.EachNeighbour(val, func(v PotVal, po int) bool {
for _, f := range fs {
err = f(v, po)
if err != nil {
return err.Error() == errNoCount.Error()
}
}
count++
if count == expCount {
return false
}
return true
})
if err == nil && count < expCount {
return fmt.Errorf("not enough neighbours returned, expected %v, got %v", expCount, count)
}
return err
}
const (
mergeTestCount = 5
mergeTestChoose = 5
)
func TestPotMergeOne(t *testing.T) {
pot1 := NewPot(nil, 0)
pot1.Add(NewTestAddr("10", 0))
pot1.Add(NewTestAddr("00", 0))
pot2 := NewPot(nil, 0)
pot2.Add(NewTestAddr("01", 0))
glog.V(logger.Debug).Infof("\n%v\n%v", pot2, pot1)
pot1.Merge(pot2)
count := 0
pot1.Each(func(val PotVal, i int) bool {
count++
return true
})
if count != 3 {
t.Fatalf("expected count to be 3, got %d\n%v\n%v", count, pot2, pot1)
}
}
func TestPotMergeCommon(t *testing.T) {
vs := make([]*testBVAddr, mergeTestCount)
for i := 0; i < maxEachNeighbourTests; i++ {
for i := 0; i < len(vs); i++ {
vs[i] = randomTestBVAddr(keylen, i)
}
max0 := rand.Intn(mergeTestChoose) + 1
max1 := rand.Intn(mergeTestChoose) + 1
n0 := NewPot(nil, 0)
n1 := NewPot(nil, 0)
glog.V(3).Infof("round %v: %v - %v", i, max0, max1)
m := make(map[string]bool)
for j := 0; j < max0; {
r := rand.Intn(max0)
v := vs[r]
// v := randomTestBVAddr(keylen, j)
_, found := n0.Add(v)
if !found {
m[v.String()] = false
j++
}
}
expAdded := 0
for j := 0; j < max1; {
r := rand.Intn(max1)
v := vs[r]
_, found := n1.Add(v)
if !found {
j++
}
_, found = m[v.String()]
if !found {
expAdded++
m[v.String()] = false
}
}
if i < 6 {
continue
}
expSize := len(m)
glog.V(4).Infof("%v-0: pin: %v, size: %v", i, n0.Pin(), max0)
glog.V(4).Infof("%v-1: pin: %v, size: %v", i, n1.Pin(), max1)
glog.V(4).Infof("%v: merged tree size: %v, newly added: %v", i, expSize, expAdded)
n, common := Union(n0, n1)
added := n1.Size() - common
size := n.Size()
if expSize != size {
t.Fatalf("%v: incorrect number of elements in merged pot, expected %v, got %v\n%v", i, expSize, size, n)
}
if expAdded != added {
t.Fatalf("%v: incorrect number of added elements in merged pot, expected %v, got %v", i, expAdded, added)
}
if !checkDuplicates(n.pot) {
t.Fatalf("%v: merged pot contains duplicates: \n%v", i, n)
}
for k, _ := range m {
_, found := n.Add(NewTestBVAddr(k, 0))
if !found {
t.Fatalf("%v: merged pot (size:%v, added: %v) missing element %v\n%v", i, size, added, k, n)
}
}
}
}
func TestPotMergeScale(t *testing.T) {
for i := 0; i < maxEachNeighbourTests; i++ {
max0 := rand.Intn(maxEachNeighbour) + 1
max1 := rand.Intn(maxEachNeighbour) + 1
n0 := NewPot(nil, 0)
n1 := NewPot(nil, 0)
glog.V(3).Infof("round %v: %v - %v", i, max0, max1)
m := make(map[string]bool)
for j := 0; j < max0; {
v := randomTestBVAddr(keylen, j)
// v := randomTestBVAddr(keylen, j)
_, found := n0.Add(v)
if !found {
m[v.String()] = false
j++
}
}
expAdded := 0
for j := 0; j < max1; {
v := randomTestBVAddr(keylen, j)
// v := randomTestBVAddr(keylen, j)
_, found := n1.Add(v)
if !found {
j++
}
_, found = m[v.String()]
if !found {
expAdded++
m[v.String()] = false
}
}
if i < 6 {
continue
}
expSize := len(m)
glog.V(4).Infof("%v-0: pin: %v, size: %v", i, n0.Pin(), max0)
glog.V(4).Infof("%v-1: pin: %v, size: %v", i, n1.Pin(), max1)
glog.V(4).Infof("%v: merged tree size: %v, newly added: %v", i, expSize, expAdded)
n, common := Union(n0, n1)
added := n1.Size() - common
size := n.Size()
if expSize != size {
t.Fatalf("%v: incorrect number of elements in merged pot, expected %v, got %v\n%v", i, expSize, size, n)
}
if expAdded != added {
t.Fatalf("%v: incorrect number of added elements in merged pot, expected %v, got %v", i, expAdded, added)
}
if !checkDuplicates(n.pot) {
t.Fatalf("%v: merged pot contains duplicates: \n%v", i, n)
}
for k, _ := range m {
_, found := n.Add(NewTestBVAddr(k, 0))
if !found {
t.Fatalf("%v: merged pot (size:%v, added: %v) missing element %v\n%v", i, size, added, k, n)
}
}
}
}
func checkDuplicates(t *pot) bool {
var po int = -1
for _, c := range t.bins {
if c == nil {
return false
}
if c.po <= po || !checkDuplicates(c) {
return false
}
po = c.po
}
return true
}
func TestPotEachNeighbourSync(t *testing.T) {
for i := 0; i < maxEachNeighbourTests; i++ {
max := rand.Intn(maxEachNeighbour/2) + maxEachNeighbour/2
pin := randomTestAddr(keylen, 0)
n := NewPot(pin, 0)
m := make(map[string]bool)
m[pin.String()] = false
for j := 1; j <= max; j++ {
v := randomTestAddr(keylen, j)
n.Add(v)
m[v.String()] = false
}
size := n.Size()
if size < 2 {
continue
}
count := rand.Intn(size/2) + size/2
val := randomTestAddr(keylen, max+1)
glog.V(3).Infof("%v: pin: %v, size: %v, val: %v, count: %v", i, n.Pin(), size, val, count)
err := testPotEachNeighbour(n, val, count, checkPo(val), checkOrder(val), checkValues(m, val))
if err != nil {
t.Fatal(err)
}
minPoFound := keylen
maxPoNotFound := 0
for k, found := range m {
po, _ := val.PO(NewTestAddr(k, 0), 0)
if found {
if po < minPoFound {
minPoFound = po
}
} else {
if po > maxPoNotFound {
maxPoNotFound = po
}
}
}
if minPoFound < maxPoNotFound {
t.Fatalf("incorrect neighbours returned: found one with PO %v < there was one not found with PO %v", minPoFound, maxPoNotFound)
}
}
}
func TestPotEachNeighbourAsync(t *testing.T) {
for i := 0; i < maxEachNeighbourTests; i++ {
max := rand.Intn(maxEachNeighbour/2) + maxEachNeighbour/2
n := NewPot(randomTestAddr(keylen, 0), 0)
var size int = 1
for j := 1; j <= max; j++ {
v := randomTestAddr(keylen, j)
_, found := n.Add(v)
if !found {
size++
}
}
if size != n.Size() {
t.Fatal(n)
}
if size < 2 {
continue
}
count := rand.Intn(size/2) + size/2
val := randomTestAddr(keylen, max+1)
mu := sync.Mutex{}
m := make(map[string]bool)
maxPos := rand.Intn(keylen)
glog.V(3).Infof("%v: pin: %v, size: %v, val: %v, count: %v, maxPos: %v", i, n.Pin(), size, val, count, maxPos)
msize := 0
remember := func(v PotVal, po int) error {
// mu.Lock()
// defer mu.Unlock()
if po > maxPos {
// glog.V(4).Infof("NOT ADD %v", v)
return errNoCount
}
// glog.V(4).Infof("ADD %v, %v", v, msize)
m[v.String()] = true
msize++
return nil
}
if i == 0 {
continue
}
err := testPotEachNeighbour(n, val, count, remember)
if err != nil {
glog.V(6).Info(err)
}
d := 0
forget := func(v PotVal, po int) {
mu.Lock()
defer mu.Unlock()
d++
// glog.V(4).Infof("DEL %v", v)
delete(m, v.String())
}
n.EachNeighbourAsync(val, count, maxPos, forget, true)
if d != msize {
t.Fatalf("incorrect number of neighbour calls in async iterator. expected %v, got %v", msize, d)
}
if len(m) != 0 {
t.Fatalf("incorrect neighbour calls in async iterator. %v items missed:\n%v", len(m), n)
}
}
}
func benchmarkEachNeighbourSync(t *testing.B, max, count int, d time.Duration) {
t.ReportAllocs()
pin := randomTestAddr(keylen, 0)
n := NewPot(pin, 0)
for j := 1; j <= max; {
v := randomTestAddr(keylen, j)
_, found := n.Add(v)
if !found {
j++
}
}
t.ResetTimer()
for i := 0; i < t.N; i++ {
val := randomTestAddr(keylen, max+1)
m := 0
n.EachNeighbour(val, func(v PotVal, po int) bool {
time.Sleep(d)
m++
if m == count {
return false
}
return true
})
}
t.StopTimer()
stats := new(runtime.MemStats)
runtime.ReadMemStats(stats)
// fmt.Println(stats.Sys)
}
func benchmarkEachNeighbourAsync(t *testing.B, max, count int, d time.Duration) {
t.ReportAllocs()
pin := randomTestAddr(keylen, 0)
n := NewPot(pin, 0)
for j := 1; j <= max; {
v := randomTestAddr(keylen, j)
_, found := n.Add(v)
if !found {
j++
}
}
t.ResetTimer()
for i := 0; i < t.N; i++ {
val := randomTestAddr(keylen, max+1)
n.EachNeighbourAsync(val, count, keylen, func(v PotVal, po int) {
time.Sleep(d)
}, true)
}
t.StopTimer()
stats := new(runtime.MemStats)
runtime.ReadMemStats(stats)
// fmt.Println(stats.Sys)
}
func BenchmarkEachNeighbourSync_3_1_0(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 10, 1*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_1_0(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 10, 1*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_2_0(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 100, 1*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_2_0(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 100, 1*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_3_0(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 1000, 1*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_3_0(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 1000, 1*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_1_1(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 10, 2*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_1_1(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 10, 2*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_2_1(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 100, 2*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_2_1(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 100, 2*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_3_1(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 1000, 2*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_3_1(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 1000, 2*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_1_2(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 10, 4*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_1_2(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 10, 4*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_2_2(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 100, 4*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_2_2(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 100, 4*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_3_2(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 1000, 4*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_3_2(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 1000, 4*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_1_3(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 10, 8*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_1_3(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 10, 8*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_2_3(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 100, 8*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_2_3(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 100, 8*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_3_3(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 1000, 8*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_3_3(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 1000, 8*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_1_4(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 10, 16*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_1_4(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 10, 16*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_2_4(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 100, 16*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_2_4(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 100, 16*time.Microsecond)
}
func BenchmarkEachNeighbourSync_3_3_4(t *testing.B) {
benchmarkEachNeighbourSync(t, 1000, 1000, 16*time.Microsecond)
}
func BenchmarkEachNeighboursAsync_3_3_4(t *testing.B) {
benchmarkEachNeighbourAsync(t, 1000, 1000, 16*time.Microsecond)
}