Skip to content

Commit

Permalink
Use fastRand instead of locked-rand in skiplist (#1173)
Browse files Browse the repository at this point in the history
The math/rand package (https://golang.org/src/math/rand/rand.go) uses
a global lock to allow concurrent access to the rand object.

The PR replaces `math.Rand` with `ristretto/z.FastRand()`. `FastRand`
is much faster than `math.Rand` and `rand.New(..)` generators.

The change improves concurrent writes to skiplist by ~30%
```go
func BenchmarkWrite(b *testing.B) {
	value := newValue(123)
	l := NewSkiplist(int64((b.N + 1) * MaxNodeSize))
	defer l.DecrRef()
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		rng := rand.New(rand.NewSource(time.Now().UnixNano()))
		for pb.Next() {
			l.Put(randomKey(rng), y.ValueStruct{Value: value, Meta: 0, UserMeta: 0})
		}
	})
}
```
```
name      old time/op  new time/op  delta
Write-16   657ns ± 3%   441ns ± 1%  -32.94%  (p=0.000 n=9+10)
```

(cherry picked from commit 9d6512b)
  • Loading branch information
Ibrahim Jarif committed Mar 24, 2020
1 parent 2cf1fca commit 0ea2ca8
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 6 deletions.
8 changes: 4 additions & 4 deletions skl/skl.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ package skl

import (
"math"
"math/rand"
"sync/atomic"
"unsafe"

"github.com/dgraph-io/badger/v2/y"
"github.com/dgraph-io/ristretto/z"
)

const (
Expand Down Expand Up @@ -165,9 +165,9 @@ func (s *node) casNextOffset(h int, old, val uint32) bool {
// return n != nil && y.CompareKeys(key, n.key) > 0
//}

func randomHeight() int {
func (s *Skiplist) randomHeight() int {
h := 1
for h < maxHeight && rand.Uint32() <= heightIncrease {
for h < maxHeight && z.FastRand() <= heightIncrease {
h++
}
return h
Expand Down Expand Up @@ -300,7 +300,7 @@ func (s *Skiplist) Put(key []byte, v y.ValueStruct) {
}

// We do need to create a new node.
height := randomHeight()
height := s.randomHeight()
x := newNode(s.arena, key, v, height)

// Try to increase s.height via CAS.
Expand Down
15 changes: 14 additions & 1 deletion skl/skl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ func BenchmarkReadWriteMap(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
for pb.Next() {
if rand.Float32() < readFrac {
if rng.Float32() < readFrac {
mutex.RLock()
_, ok := m[string(randomKey(rng))]
mutex.RUnlock()
Expand All @@ -516,3 +516,16 @@ func BenchmarkReadWriteMap(b *testing.B) {
})
}
}

func BenchmarkWrite(b *testing.B) {
value := newValue(123)
l := NewSkiplist(int64((b.N + 1) * MaxNodeSize))
defer l.DecrRef()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
for pb.Next() {
l.Put(randomKey(rng), y.ValueStruct{Value: value, Meta: 0, UserMeta: 0})
}
})
}
4 changes: 4 additions & 0 deletions table/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ func OpenTable(fd *os.File, opts Options) (*Table, error) {
if err := t.initBiggestAndSmallest(); err != nil {
return nil, errors.Wrapf(err, "failed to initialize table")
}

if opts.ChkMode == options.OnTableRead || opts.ChkMode == options.OnTableAndBlockRead {
if err := t.VerifyChecksum(); err != nil {
_ = fd.Close()
Expand Down Expand Up @@ -320,6 +321,9 @@ func (t *Table) readIndex() error {
readPos -= 4
buf := t.readNoFail(readPos, 4)
checksumLen := int(y.BytesToU32(buf))
if checksumLen < 0 {
return errors.New("checksum length less than zero. Data corrupted")
}

// Read checksum.
expectedChk := &pb.Checksum{}
Expand Down
5 changes: 4 additions & 1 deletion table/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,10 @@ func TestTableChecksum(t *testing.T) {
f := buildTestTable(t, "k", 10000, opts)
fi, err := f.Stat()
require.NoError(t, err, "unable to get file information")
f.WriteAt(rb, rand.Int63n(fi.Size()))
// Write random bytes at random location.
n, err := f.WriteAt(rb, rand.Int63n(fi.Size()))
require.NoError(t, err)
require.Equal(t, n, len(rb))

_, err = OpenTable(f, opts)
if err == nil || !strings.Contains(err.Error(), "checksum") {
Expand Down

0 comments on commit 0ea2ca8

Please sign in to comment.