From bad269a2024043301d2cf0a929741e635dc54a63 Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Tue, 11 Jun 2019 20:00:23 +0800 Subject: [PATCH 01/18] postings compression exploration Signed-off-by: naivewong <867245430@qq.com> --- encoding/encoding.go | 60 ++++++++- index/index.go | 37 ++++- index/postings.go | 257 +++++++++++++++++++++++++++++++++++ index/postings_test.go | 300 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 647 insertions(+), 7 deletions(-) diff --git a/encoding/encoding.go b/encoding/encoding.go index a732a604..e91b9dd7 100644 --- a/encoding/encoding.go +++ b/encoding/encoding.go @@ -29,11 +29,16 @@ var ( // Encbuf is a helper type to populate a byte slice with various types. type Encbuf struct { - B []byte - C [binary.MaxVarintLen64]byte + B []byte + C [binary.MaxVarintLen64]byte + Count uint8 +} + +func (e *Encbuf) Reset() { + e.B = e.B[:0] + e.Count = 0 } -func (e *Encbuf) Reset() { e.B = e.B[:0] } func (e *Encbuf) Get() []byte { return e.B } func (e *Encbuf) Len() int { return len(e.B) } @@ -82,6 +87,55 @@ func (e *Encbuf) PutHash(h hash.Hash) { e.B = h.Sum(e.B) } +type bit bool + +func (e *Encbuf) putBit(bit bit) { + if e.Count == 0 { + e.B = append(e.B, 0) + e.Count = 8 + } + + i := len(e.B) - 1 + + if bit { + e.B[i] |= 1 << (e.Count - 1) + } + + e.Count-- +} + +func (e *Encbuf) putByte(byt byte) { + if e.Count == 0 { + e.B = append(e.B, 0) + e.Count = 8 + } + + i := len(e.B) - 1 + + // fill up e.B with e.Count bits from byt + e.B[i] |= byt >> (8 - e.Count) + + e.B = append(e.B, 0) + i++ + e.B[i] = byt << e.Count +} + +func (e *Encbuf) PutBits(u uint64, nbits int) { + u <<= (64 - uint(nbits)) + for nbits >= 8 { + byt := byte(u >> 56) + e.putByte(byt) + u <<= 8 + nbits -= 8 + } + + for nbits > 0 { + e.putBit((u >> 63) == 1) + u <<= 1 + nbits-- + } +} + // Decbuf provides safe methods to extract data from a byte slice. It does all // necessary bounds checking and advancing of the byte slice. // Several datums can be extracted without checking for errors. However, before using diff --git a/index/index.go b/index/index.go index 6b333fa5..5c2496ec 100644 --- a/index/index.go +++ b/index/index.go @@ -21,6 +21,7 @@ import ( "io" "io/ioutil" "math" + "math/bits" "os" "path/filepath" "sort" @@ -522,9 +523,24 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { w.buf2.Reset() w.buf2.PutBE32int(len(refs)) - for _, r := range refs { - w.buf2.PutBE32(r) + switch postingsType { + case 1: + for _, r := range refs { + w.buf2.PutBE32(r) + } + case 2: + // The base. + w.buf2.PutUvarint32(refs[0]) + // The width. + width := bits.Len32(uint32(refs[len(refs)-1]-refs[0])) + w.buf2.PutByte(byte(width)) + for _, r := range refs { + w.buf2.PutBits(uint64(r-refs[0]), width) + } + case 3: + writeDeltaBlockPostings(&w.buf2, refs) } + w.uint32s = refs w.buf1.Reset() @@ -1028,8 +1044,21 @@ type Decoder struct { func (dec *Decoder) Postings(b []byte) (int, Postings, error) { d := encoding.Decbuf{B: b} n := d.Be32int() - l := d.Get() - return n, newBigEndianPostings(l), d.Err() + switch postingsType { + case 1: + l := d.Get() + return n, newBigEndianPostings(l), d.Err() + case 2: + base := uint32(d.Uvarint()) + width := int(d.Byte()) + l := d.Get() + return n, newBaseDeltaPostings(l, base, width, n), d.Err() + case 3: + l := d.Get() + return n, newDeltaBlockPostings(l, n), d.Err() + default: + return n, EmptyPostings(), d.Err() + } } // Series decodes a series entry from the given byte slice into lset and chks. diff --git a/index/postings.go b/index/postings.go index cef2d886..06f5e803 100644 --- a/index/postings.go +++ b/index/postings.go @@ -16,11 +16,13 @@ package index import ( "container/heap" "encoding/binary" + "math/bits" "runtime" "sort" "strings" "sync" + "github.com/prometheus/tsdb/encoding" "github.com/prometheus/tsdb/labels" ) @@ -689,3 +691,258 @@ func (it *bigEndianPostings) Seek(x uint64) bool { func (it *bigEndianPostings) Err() error { return nil } + +// 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock. +const postingsType = 3 + +type bitSlice struct { + bstream []byte + width int +} + +func (bs *bitSlice) readByte(idx int, count uint8) byte { + if count == 0 { + return bs.bstream[idx] + } + + byt := bs.bstream[idx] << count + byt |= bs.bstream[idx+1] >> (8 - count) + + return byt +} + +// This is to read the delta bitpack given an offset. +// Check whether out-of-bounds before using. +func (bs *bitSlice) readBits(offset int) uint64 { + idx := offset / 8 + count := uint8(offset % 8) + nbits := bs.width + var u uint64 + + for nbits >= 8 { + byt := bs.readByte(idx, count) + + u = (u << 8) | uint64(byt) + nbits -= 8 + idx += 1 + } + + if nbits == 0 { + return u + } + + if nbits > int(8 - count) { + u = (u << uint(8 - count)) | uint64((bs.bstream[idx]<>count) + nbits -= int(8 - count) + idx += 1 + + count = 0 + } + + u = (u << uint(nbits)) | uint64((bs.bstream[idx]<>(8-uint(nbits))) + return u +} + +// ┌──────────┬────────────────┬────────────┬────────────────┬─────┬────────────────┐ +// │ num <4b> │ base │ width <1b> │ delta 1 │ ... │ delta n │ +// └──────────┴────────────────┴────────────┴────────────────┴─────┴────────────────┘ +type baseDeltaPostings struct { + bs bitSlice + base uint32 + size int + idx int + cur uint64 +} + +func newBaseDeltaPostings(bstream []byte, base uint32, width int, size int) *baseDeltaPostings { + return &baseDeltaPostings{bs: bitSlice{bstream: bstream, width: width}, base: base, size: size, cur: uint64(base)} +} + +func (it *baseDeltaPostings) At() uint64 { + return it.cur +} + +func (it *baseDeltaPostings) Next() bool { + if it.size > it.idx { + it.cur = it.bs.readBits(it.idx*it.bs.width) + uint64(it.base) + it.idx += 1 + return true + } + return false +} + +func (it *baseDeltaPostings) Seek(x uint64) bool { + if it.cur >= x { + return true + } + + num := it.size - it.idx + // Do binary search between current position and end. + i := sort.Search(num, func(i int) bool { + return it.bs.readBits((i+it.idx)*it.bs.width) + uint64(it.base) >= x + }) + if i < num { + it.cur = it.bs.readBits((i+it.idx)*it.bs.width) + uint64(it.base) + it.idx += i + return true + } + it.idx += i + return false +} + +func (it *baseDeltaPostings) Err() error { + return nil +} + +const deltaBlockSize = 256 + +// Block format(delta is to the previous value). +// ┌────────────────┬───────────────┬────────────┬────────────────┬─────┬────────────────┐ +// │ base │ idx │ width <1b> │ delta 1 │ ... │ delta n │ +// └────────────────┴───────────────┴────────────┴────────────────┴─────┴────────────────┘ +type deltaBlockPostings struct { + bs bitSlice + size int + count int // count in current block. + idxBlock int + idx int + offset int // offset in bit. + cur uint64 +} + +func newDeltaBlockPostings(bstream []byte, size int) *deltaBlockPostings { + return &deltaBlockPostings{bs: bitSlice{bstream: bstream}, size: size} +} + +func (it *deltaBlockPostings) GetOff() int { + return it.offset +} +func (it *deltaBlockPostings) GetWidth() int { + return it.bs.width +} + +func (it *deltaBlockPostings) At() uint64 { + return it.cur +} + +func (it *deltaBlockPostings) Next() bool { + if it.offset >= len(it.bs.bstream) * 8 || it.idx >= it.size { + return false + } + if it.offset % (deltaBlockSize * 8) == 0 { + val, n := binary.Uvarint(it.bs.bstream[it.offset/8:]) + if n < 1 { + return false + } + it.cur = val + it.offset += n * 8 + val, n = binary.Uvarint(it.bs.bstream[it.offset/8:]) + if n < 1 { + return false + } + it.idx = int(val) + 1 + it.offset += n * 8 + val, n = binary.Uvarint(it.bs.bstream[it.offset/8:]) + if n < 1 { + return false + } + it.count = int(val) + it.offset += n * 8 + it.bs.width = int(it.bs.bstream[it.offset/8]) + it.offset += 8 + it.idxBlock = 1 + return true + } + + it.cur = it.bs.readBits(it.offset) + it.cur + it.offset += it.bs.width + it.idx += 1 + it.idxBlock += 1 + if it.idxBlock == it.count { + it.offset = ((it.offset-1) / (deltaBlockSize * 8) + 1) * deltaBlockSize * 8 + } + return true +} + +func (it *deltaBlockPostings) Seek(x uint64) bool { + if it.cur >= x { + return true + } + + startOff := it.offset / (deltaBlockSize * 8) * deltaBlockSize + num := len(it.bs.bstream) / deltaBlockSize - it.offset / (deltaBlockSize * 8) + // Do binary search between current position and end. + i := sort.Search(num, func(i int) bool { + val, _ := binary.Uvarint(it.bs.bstream[startOff+i*deltaBlockSize:]) + return val > x + }) + if i > 0 { + // Go to the previous block because the previous block + // may contain the first value >= x. + i -= 1 + } + it.offset = (startOff + i * deltaBlockSize) * 8 + for it.Next() { + if it.At() >= x { + return true + } + } + return false +} + +func (it *deltaBlockPostings) Err() error { + return nil +} + +func writeDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { + i := 0 + startLen := len(e.B) + for i < len(arr) { + e.PutUvarint32(arr[i]) // Put base. + e.PutUvarint64(uint64(i)) // Put idx. + remaining := (deltaBlockSize - (len(e.B) - startLen) % deltaBlockSize - 1) * 8 + deltas := []uint64{} + preVal := arr[i] + max := -1 + i += 1 + for i < len(arr) { + delta := uint64(arr[i] - preVal) + cur := bits.Len64(delta) + if cur <= max { + cur = max + } + if remaining - cur * (len(deltas) + 1) - (bits.Len(uint(len(deltas))) / 8 + 1) * 8 >= 0 { + deltas = append(deltas, delta) + max = cur + preVal = arr[i] + } else { + break + } + i += 1 + } + e.PutUvarint64(uint64(len(deltas) + 1)) + e.PutByte(byte(max)) + remaining -= (bits.Len(uint(len(deltas))) / 8 + 1) * 8 + for _, delta := range deltas { + e.PutBits(delta, max) + remaining -= max + } + + if i == len(arr) { + break + } + + for remaining >= 64 { + e.PutBits(uint64(0), 64) + remaining -= 64 + } + + if remaining > 0 { + e.PutBits(uint64(0), remaining) + } + e.Count = 0 + + // There can be one more extra 0. + e.B = e.B[:len(e.B)-(len(e.B)-startLen)%deltaBlockSize] + } +} diff --git a/index/postings_test.go b/index/postings_test.go index 1eed1dbf..1e877393 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -16,10 +16,12 @@ package index import ( "encoding/binary" "fmt" + "math/bits" "math/rand" "sort" "testing" + "github.com/prometheus/tsdb/encoding" "github.com/prometheus/tsdb/testutil" ) @@ -718,6 +720,304 @@ func TestBigEndian(t *testing.T) { }) } +func TestBaseDeltaPostings(t *testing.T) { + num := 1000 + // mock a list as postings + ls := make([]uint32, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 + } + + width := bits.Len32(ls[len(ls)-1]-ls[0]) + buf := encoding.Encbuf{} + for i := 0; i < num; i++ { + buf.PutBits(uint64(ls[i]-ls[0]), width) + } + // t.Log("(baseDeltaPostings) len of 1000 number = ", len(buf.Get())) + + t.Run("Iteration", func(t *testing.T) { + bdp := newBaseDeltaPostings(buf.Get(), ls[0], width, len(ls)) + for i := 0; i < num; i++ { + testutil.Assert(t, bdp.Next() == true, "") + testutil.Equals(t, uint64(ls[i]), bdp.At()) + } + + testutil.Assert(t, bdp.Next() == false, "") + testutil.Assert(t, bdp.Err() == nil, "") + }) + + t.Run("Seek", func(t *testing.T) { + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[4], ls[4], true, + }, + { + ls[500] - 1, ls[500], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[0], ls[601], true, + }, + { + ls[600], ls[601], true, + }, + { + ls[999], ls[999], true, + }, + { + ls[999] + 10, ls[999], false, + }, + } + + bdp := newBaseDeltaPostings(buf.Get(), ls[0], width, len(ls)) + + for _, v := range table { + testutil.Equals(t, v.found, bdp.Seek(uint64(v.seek))) + testutil.Equals(t, uint64(v.val), bdp.At()) + testutil.Assert(t, bdp.Err() == nil, "") + } + }) +} + +func TestDeltaBlockPostings(t *testing.T) { + num := 1000 + // mock a list as postings + ls := make([]uint32, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 + } + + buf := encoding.Encbuf{} + writeDeltaBlockPostings(&buf, ls) + // t.Log("(deltaBlockPostings) len of 1000 number = ", len(buf.Get())) + + t.Run("Iteration", func(t *testing.T) { + dbp := newDeltaBlockPostings(buf.Get(), len(ls)) + for i := 0; i < num; i++ { + testutil.Assert(t, dbp.Next() == true, "") + if uint64(ls[i]) != dbp.At() { + t.Log(i, dbp.GetOff(), "width=", dbp.GetWidth()) + } + testutil.Equals(t, uint64(ls[i]), dbp.At()) + } + + testutil.Assert(t, dbp.Next() == false, "") + testutil.Assert(t, dbp.Err() == nil, "") + }) + + t.Run("Seek", func(t *testing.T) { + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[4], ls[4], true, + }, + { + ls[500] - 1, ls[500], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[0], ls[601], true, + }, + { + ls[600], ls[601], true, + }, + { + ls[999], ls[999], true, + }, + { + ls[999] + 10, ls[999], false, + }, + } + + dbp := newDeltaBlockPostings(buf.Get(), len(ls)) + + for _, v := range table { + testutil.Equals(t, v.found, dbp.Seek(uint64(v.seek))) + testutil.Equals(t, uint64(v.val), dbp.At()) + testutil.Assert(t, dbp.Err() == nil, "") + } + }) +} + +func BenchmarkPostings(b *testing.B) { + num := 100000 + // mock a list as postings + ls := make([]uint32, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 + } + + // bigEndianPostings. + bufBE := make([]byte, num*4) + for i := 0; i < num; i++ { + b := bufBE[i*4 : i*4+4] + binary.BigEndian.PutUint32(b, ls[i]) + } + + // baseDeltaPostings. + width := bits.Len32(ls[len(ls)-1]-ls[0]) + bufBD := encoding.Encbuf{} + for i := 0; i < num; i++ { + bufBD.PutBits(uint64(ls[i]-ls[0]), width) + } + + // deltaBlockPostings. + bufDB := encoding.Encbuf{} + writeDeltaBlockPostings(&bufDB, ls) + + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[4], ls[4], true, + }, + { + ls[5000] - 1, ls[5000], true, + }, + { + ls[6000] + 1, ls[6001], true, + }, + { + ls[6000] + 1, ls[6001], true, + }, + { + ls[6000] + 1, ls[6001], true, + }, + { + ls[0], ls[6001], true, + }, + { + ls[6000], ls[6001], true, + }, + { + ls[99999], ls[99999], true, + }, + { + ls[99999] + 10, ls[99999], false, + }, + } + + b.Run("bigEndianIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + bep := newBigEndianPostings(bufBE) + + for i := 0; i < num; i++ { + testutil.Assert(bench, bep.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), bep.At()) + } + testutil.Assert(bench, bep.Next() == false, "") + testutil.Assert(bench, bep.Err() == nil, "") + } + }) + b.Run("baseDeltaIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) + + for i := 0; i < num; i++ { + testutil.Assert(bench, bdp.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), bdp.At()) + } + testutil.Assert(bench, bdp.Next() == false, "") + testutil.Assert(bench, bdp.Err() == nil, "") + } + }) + b.Run("deltaBlockIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + dbp := newDeltaBlockPostings(bufDB.Get(), len(ls)) + + for i := 0; i < num; i++ { + testutil.Assert(bench, dbp.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), dbp.At()) + } + testutil.Assert(bench, dbp.Next() == false, "") + testutil.Assert(bench, dbp.Err() == nil, "") + } + }) + + b.Run("bigEndianSeek", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + bep := newBigEndianPostings(bufBE) + + for _, v := range table { + testutil.Equals(bench, v.found, bep.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), bep.At()) + testutil.Assert(bench, bep.Err() == nil, "") + } + } + }) + b.Run("baseDeltaSeek", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) + + for _, v := range table { + testutil.Equals(bench, v.found, bdp.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), bdp.At()) + testutil.Assert(bench, bdp.Err() == nil, "") + } + } + }) + b.Run("deltaBlockSeek", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + dbp := newDeltaBlockPostings(bufDB.Get(), len(ls)) + + for _, v := range table { + testutil.Equals(bench, v.found, dbp.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), dbp.At()) + testutil.Assert(bench, dbp.Err() == nil, "") + } + } + }) +} + func TestIntersectWithMerge(t *testing.T) { // One of the reproducible cases for: // https://github.com/prometheus/prometheus/issues/2616 From bf6c0aee0e407ec18445722df65c3d02f6e067c0 Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Thu, 13 Jun 2019 16:08:38 +0800 Subject: [PATCH 02/18] fix bug in Seek and add another idea Signed-off-by: naivewong <867245430@qq.com> --- index/index.go | 5 + index/postings.go | 238 +++++++++++++++++++++++++++++++++++------ index/postings_test.go | 107 ++++++++++++++++++ 3 files changed, 320 insertions(+), 30 deletions(-) diff --git a/index/index.go b/index/index.go index 5c2496ec..a12d037e 100644 --- a/index/index.go +++ b/index/index.go @@ -539,6 +539,8 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { } case 3: writeDeltaBlockPostings(&w.buf2, refs) + case 4: + writeBaseDeltaBlockPostings(&w.buf2, refs) } w.uint32s = refs @@ -1056,6 +1058,9 @@ func (dec *Decoder) Postings(b []byte) (int, Postings, error) { case 3: l := d.Get() return n, newDeltaBlockPostings(l, n), d.Err() + case 4: + l := d.Get() + return n, newBaseDeltaBlockPostings(l, n), d.Err() default: return n, EmptyPostings(), d.Err() } diff --git a/index/postings.go b/index/postings.go index 06f5e803..e2885322 100644 --- a/index/postings.go +++ b/index/postings.go @@ -692,8 +692,8 @@ func (it *bigEndianPostings) Err() error { return nil } -// 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock. -const postingsType = 3 +// 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock, 4 is baseDeltaBlock. +const postingsType = 4 type bitSlice struct { bstream []byte @@ -778,8 +778,9 @@ func (it *baseDeltaPostings) Seek(x uint64) bool { num := it.size - it.idx // Do binary search between current position and end. + x -= uint64(it.base) i := sort.Search(num, func(i int) bool { - return it.bs.readBits((i+it.idx)*it.bs.width) + uint64(it.base) >= x + return it.bs.readBits((i+it.idx)*it.bs.width) >= x }) if i < num { it.cur = it.bs.readBits((i+it.idx)*it.bs.width) + uint64(it.base) @@ -794,14 +795,14 @@ func (it *baseDeltaPostings) Err() error { return nil } -const deltaBlockSize = 256 +const deltaBlockSize = 128 // Block format(delta is to the previous value). -// ┌────────────────┬───────────────┬────────────┬────────────────┬─────┬────────────────┐ -// │ base │ idx │ width <1b> │ delta 1 │ ... │ delta n │ -// └────────────────┴───────────────┴────────────┴────────────────┴─────┴────────────────┘ +// ┌────────────────┬───────────────┬─────────────────┬────────────┬────────────────┬─────┬────────────────┐ +// │ base │ idx │ count │ width <1b> │ delta 1 │ ... │ delta n │ +// └────────────────┴───────────────┴─────────────────┴────────────┴────────────────┴─────┴────────────────┘ type deltaBlockPostings struct { - bs bitSlice + bs bitSlice size int count int // count in current block. idxBlock int @@ -826,29 +827,29 @@ func (it *deltaBlockPostings) At() uint64 { } func (it *deltaBlockPostings) Next() bool { - if it.offset >= len(it.bs.bstream) * 8 || it.idx >= it.size { + if it.offset >= len(it.bs.bstream) << 3 || it.idx >= it.size { return false } - if it.offset % (deltaBlockSize * 8) == 0 { - val, n := binary.Uvarint(it.bs.bstream[it.offset/8:]) + if it.offset % (deltaBlockSize << 3) == 0 { + val, n := binary.Uvarint(it.bs.bstream[it.offset>>3:]) if n < 1 { return false } it.cur = val - it.offset += n * 8 - val, n = binary.Uvarint(it.bs.bstream[it.offset/8:]) + it.offset += n << 3 + val, n = binary.Uvarint(it.bs.bstream[it.offset>>3:]) if n < 1 { return false } it.idx = int(val) + 1 - it.offset += n * 8 - val, n = binary.Uvarint(it.bs.bstream[it.offset/8:]) + it.offset += n << 3 + val, n = binary.Uvarint(it.bs.bstream[it.offset>>3:]) if n < 1 { return false } it.count = int(val) - it.offset += n * 8 - it.bs.width = int(it.bs.bstream[it.offset/8]) + it.offset += n << 3 + it.bs.width = int(it.bs.bstream[it.offset>>3]) it.offset += 8 it.idxBlock = 1 return true @@ -859,7 +860,7 @@ func (it *deltaBlockPostings) Next() bool { it.idx += 1 it.idxBlock += 1 if it.idxBlock == it.count { - it.offset = ((it.offset-1) / (deltaBlockSize * 8) + 1) * deltaBlockSize * 8 + it.offset = ((it.offset-1) / (deltaBlockSize << 3) + 1) * deltaBlockSize << 3 } return true } @@ -869,8 +870,8 @@ func (it *deltaBlockPostings) Seek(x uint64) bool { return true } - startOff := it.offset / (deltaBlockSize * 8) * deltaBlockSize - num := len(it.bs.bstream) / deltaBlockSize - it.offset / (deltaBlockSize * 8) + startOff := (it.offset - 1) / (deltaBlockSize << 3) * deltaBlockSize + num := (len(it.bs.bstream) - 1) / deltaBlockSize - (it.offset - 1) / (deltaBlockSize << 3) + 1 // Do binary search between current position and end. i := sort.Search(num, func(i int) bool { val, _ := binary.Uvarint(it.bs.bstream[startOff+i*deltaBlockSize:]) @@ -881,7 +882,7 @@ func (it *deltaBlockPostings) Seek(x uint64) bool { // may contain the first value >= x. i -= 1 } - it.offset = (startOff + i * deltaBlockSize) * 8 + it.offset = (startOff + i * deltaBlockSize) << 3 for it.Next() { if it.At() >= x { return true @@ -897,21 +898,25 @@ func (it *deltaBlockPostings) Err() error { func writeDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { i := 0 startLen := len(e.B) + deltas := []uint32{} + var remaining int + var preVal uint32 + var max int for i < len(arr) { e.PutUvarint32(arr[i]) // Put base. e.PutUvarint64(uint64(i)) // Put idx. - remaining := (deltaBlockSize - (len(e.B) - startLen) % deltaBlockSize - 1) * 8 - deltas := []uint64{} - preVal := arr[i] - max := -1 + remaining = (deltaBlockSize - (len(e.B) - startLen) % deltaBlockSize - 1) << 3 + deltas = deltas[:0] + preVal = arr[i] + max = -1 i += 1 for i < len(arr) { - delta := uint64(arr[i] - preVal) - cur := bits.Len64(delta) + delta := arr[i] - preVal + cur := bits.Len32(delta) if cur <= max { cur = max } - if remaining - cur * (len(deltas) + 1) - (bits.Len(uint(len(deltas))) / 8 + 1) * 8 >= 0 { + if remaining - cur * (len(deltas) + 1) - (((bits.Len(uint(len(deltas))) >> 3) + 1) << 3) >= 0 { deltas = append(deltas, delta) max = cur preVal = arr[i] @@ -922,9 +927,182 @@ func writeDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { } e.PutUvarint64(uint64(len(deltas) + 1)) e.PutByte(byte(max)) - remaining -= (bits.Len(uint(len(deltas))) / 8 + 1) * 8 + remaining -= ((bits.Len(uint(len(deltas))) >> 3) + 1) << 3 for _, delta := range deltas { - e.PutBits(delta, max) + e.PutBits(uint64(delta), max) + remaining -= max + } + + if i == len(arr) { + break + } + + for remaining >= 64 { + e.PutBits(uint64(0), 64) + remaining -= 64 + } + + if remaining > 0 { + e.PutBits(uint64(0), remaining) + } + e.Count = 0 + + // There can be one more extra 0. + e.B = e.B[:len(e.B)-(len(e.B)-startLen)%deltaBlockSize] + } +} + +// Block format(delta is to the base). +// ┌────────────────┬───────────────┬─────────────────┬────────────┬────────────────┬─────┬────────────────┐ +// │ base │ idx │ count │ width <1b> │ delta 1 │ ... │ delta n │ +// └────────────────┴───────────────┴─────────────────┴────────────┴────────────────┴─────┴────────────────┘ +type baseDeltaBlockPostings struct { + bs bitSlice + size int + count int // count in current block. + idxBlock int + idx int + offset int // offset in bit. + cur uint64 + base uint64 +} + +func newBaseDeltaBlockPostings(bstream []byte, size int) *baseDeltaBlockPostings { + return &baseDeltaBlockPostings{bs: bitSlice{bstream: bstream}, size: size} +} + +func (it *baseDeltaBlockPostings) GetOff() int { + return it.offset +} +func (it *baseDeltaBlockPostings) GetWidth() int { + return it.bs.width +} + +func (it *baseDeltaBlockPostings) At() uint64 { + return it.cur +} + +func (it *baseDeltaBlockPostings) Next() bool { + if it.offset >= len(it.bs.bstream) << 3 || it.idx >= it.size { + return false + } + if it.offset % (deltaBlockSize << 3) == 0 { + val, n := binary.Uvarint(it.bs.bstream[it.offset>>3:]) + if n < 1 { + return false + } + it.cur = val + it.base = val + it.offset += n << 3 + val, n = binary.Uvarint(it.bs.bstream[it.offset>>3:]) + if n < 1 { + return false + } + it.idx = int(val) + 1 + it.offset += n << 3 + val, n = binary.Uvarint(it.bs.bstream[it.offset>>3:]) + if n < 1 { + return false + } + it.count = int(val) + it.offset += n << 3 + it.bs.width = int(it.bs.bstream[it.offset>>3]) + it.offset += 8 + it.idxBlock = 1 + return true + } + + it.cur = it.bs.readBits(it.offset) + it.base + it.offset += it.bs.width + it.idx += 1 + it.idxBlock += 1 + if it.idxBlock == it.count { + it.offset = ((it.offset-1) / (deltaBlockSize << 3) + 1) * deltaBlockSize << 3 + } + return true +} + +func (it *baseDeltaBlockPostings) Seek(x uint64) bool { + if it.cur >= x { + return true + } + + startOff := (it.offset - 1) / (deltaBlockSize << 3) * deltaBlockSize + num := (len(it.bs.bstream) - 1) / deltaBlockSize - (it.offset - 1) / (deltaBlockSize << 3) + 1 + // Do binary search between current position and end. + i := sort.Search(num, func(i int) bool { + val, _ := binary.Uvarint(it.bs.bstream[startOff+i*deltaBlockSize:]) + return val > x + }) + if i > 0 { + // Go to the previous block because the previous block + // may contain the first value >= x. + i -= 1 + } + it.offset = (startOff + i * deltaBlockSize) << 3 + + // Read base, idx, and width. + it.Next() + if x <= it.base { + return true + } else { + temp := x - it.base + j := sort.Search(it.count - it.idxBlock, func(i int) bool { + return it.bs.readBits(it.offset + i * it.bs.width) >= temp + }) + + if j < it.count - it.idxBlock { + it.offset += j * it.bs.width + it.cur = it.bs.readBits(it.offset) + it.base + it.offset += it.bs.width + it.idxBlock += j + 1 + it.idx += j + 1 + if it.idxBlock == it.count { + it.offset = ((it.offset-1) / (deltaBlockSize << 3) + 1) * deltaBlockSize << 3 + } + } else { + it.offset = (startOff + (i + 1) * deltaBlockSize) << 3 + return it.Next() + } + return true + } +} + +func (it *baseDeltaBlockPostings) Err() error { + return nil +} + +func writeBaseDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { + i := 0 + startLen := len(e.B) + deltas := []uint32{} + var remaining int + var base uint32 + var max int + for i < len(arr) { + e.PutUvarint32(arr[i]) // Put base. + e.PutUvarint64(uint64(i)) // Put idx. + remaining = (deltaBlockSize - (len(e.B) - startLen) % deltaBlockSize - 1) << 3 + deltas = deltas[:0] + base = arr[i] + max = -1 + i += 1 + for i < len(arr) { + delta := arr[i] - base + cur := bits.Len32(delta) + if remaining - cur * (len(deltas) + 1) - (((bits.Len(uint(len(deltas))) >> 3) + 1) << 3) >= 0 { + deltas = append(deltas, delta) + max = cur + } else { + break + } + i += 1 + } + e.PutUvarint64(uint64(len(deltas) + 1)) + e.PutByte(byte(max)) + remaining -= ((bits.Len(uint(len(deltas))) >> 3) + 1) << 3 + for _, delta := range deltas { + e.PutBits(uint64(delta), max) remaining -= max } diff --git a/index/postings_test.go b/index/postings_test.go index 1e877393..eddc7906 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -870,6 +870,81 @@ func TestDeltaBlockPostings(t *testing.T) { }) } +func TestBaseDeltaBlockPostings(t *testing.T) { + num := 1000 + // mock a list as postings + ls := make([]uint32, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 + } + + buf := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&buf, ls) + // t.Log("(deltaBlockPostings) len of 1000 number = ", len(buf.Get())) + + t.Run("Iteration", func(t *testing.T) { + dbp := newBaseDeltaBlockPostings(buf.Get(), len(ls)) + for i := 0; i < num; i++ { + testutil.Assert(t, dbp.Next() == true, "") + if uint64(ls[i]) != dbp.At() { + t.Log(i, dbp.GetOff(), "width=", dbp.GetWidth()) + } + testutil.Equals(t, uint64(ls[i]), dbp.At()) + } + + testutil.Assert(t, dbp.Next() == false, "") + testutil.Assert(t, dbp.Err() == nil, "") + }) + + t.Run("Seek", func(t *testing.T) { + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[4], ls[4], true, + }, + { + ls[500] - 1, ls[500], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[0], ls[601], true, + }, + { + ls[600], ls[601], true, + }, + { + ls[999], ls[999], true, + }, + { + ls[999] + 10, ls[999], false, + }, + } + + dbp := newBaseDeltaBlockPostings(buf.Get(), len(ls)) + + for _, v := range table { + testutil.Equals(t, v.found, dbp.Seek(uint64(v.seek))) + testutil.Equals(t, uint64(v.val), dbp.At()) + testutil.Assert(t, dbp.Err() == nil, "") + } + }) +} + func BenchmarkPostings(b *testing.B) { num := 100000 // mock a list as postings @@ -897,6 +972,11 @@ func BenchmarkPostings(b *testing.B) { bufDB := encoding.Encbuf{} writeDeltaBlockPostings(&bufDB, ls) + // baseDeltaBlockPostings. + bufBDB := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB, ls) + // b.Log(len(bufBDB.Get())) + table := []struct { seek uint32 val uint32 @@ -976,6 +1056,20 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, dbp.Err() == nil, "") } }) + b.Run("baseDeltaBlockIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + bdbp := newBaseDeltaBlockPostings(bufBDB.Get(), len(ls)) + + for i := 0; i < num; i++ { + testutil.Assert(bench, bdbp.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), bdbp.At()) + } + testutil.Assert(bench, bdbp.Next() == false, "") + testutil.Assert(bench, bdbp.Err() == nil, "") + } + }) b.Run("bigEndianSeek", func(bench *testing.B) { bench.ResetTimer() @@ -1016,6 +1110,19 @@ func BenchmarkPostings(b *testing.B) { } } }) + b.Run("baseDeltaBlockSeek", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + bdbp := newBaseDeltaBlockPostings(bufBDB.Get(), len(ls)) + + for _, v := range table { + testutil.Equals(bench, v.found, bdbp.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), bdbp.At()) + testutil.Assert(bench, bdbp.Err() == nil, "") + } + } + }) } func TestIntersectWithMerge(t *testing.T) { From 7cfcf3d3fd034c06b60d8440e9d275ecba98b08d Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Tue, 18 Jun 2019 00:04:12 +0800 Subject: [PATCH 03/18] add bitmapPostings Signed-off-by: naivewong <867245430@qq.com> --- encoding/encoding.go | 4 +- index/index.go | 7 +- index/index_test.go | 7 ++ index/postings.go | 185 ++++++++++++++++++++++++++++++++++------- index/postings_test.go | 126 +++++++++++++++++++++++++--- 5 files changed, 284 insertions(+), 45 deletions(-) diff --git a/encoding/encoding.go b/encoding/encoding.go index e91b9dd7..6658c605 100644 --- a/encoding/encoding.go +++ b/encoding/encoding.go @@ -34,8 +34,8 @@ type Encbuf struct { Count uint8 } -func (e *Encbuf) Reset() { - e.B = e.B[:0] +func (e *Encbuf) Reset() { + e.B = e.B[:0] e.Count = 0 } diff --git a/index/index.go b/index/index.go index a12d037e..2e66f09b 100644 --- a/index/index.go +++ b/index/index.go @@ -532,7 +532,7 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { // The base. w.buf2.PutUvarint32(refs[0]) // The width. - width := bits.Len32(uint32(refs[len(refs)-1]-refs[0])) + width := bits.Len32(uint32(refs[len(refs)-1] - refs[0])) w.buf2.PutByte(byte(width)) for _, r := range refs { w.buf2.PutBits(uint64(r-refs[0]), width) @@ -541,6 +541,8 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { writeDeltaBlockPostings(&w.buf2, refs) case 4: writeBaseDeltaBlockPostings(&w.buf2, refs) + case 5: + writeBitmapPostings(&w.buf2, refs) } w.uint32s = refs @@ -1061,6 +1063,9 @@ func (dec *Decoder) Postings(b []byte) (int, Postings, error) { case 4: l := d.Get() return n, newBaseDeltaBlockPostings(l, n), d.Err() + case 5: + l := d.Get() + return n, newBitmapPostings(l), d.Err() default: return n, EmptyPostings(), d.Err() } diff --git a/index/index_test.go b/index/index_test.go index 43b737c7..fb1f0405 100644 --- a/index/index_test.go +++ b/index/index_test.go @@ -25,6 +25,7 @@ import ( "github.com/prometheus/tsdb/chunkenc" "github.com/prometheus/tsdb/chunks" "github.com/prometheus/tsdb/encoding" + "github.com/prometheus/tsdb/fileutil" "github.com/prometheus/tsdb/labels" "github.com/prometheus/tsdb/testutil" ) @@ -338,6 +339,12 @@ func TestPersistence_index_e2e(t *testing.T) { err = iw.Close() testutil.Ok(t, err) + f, err := fileutil.OpenMmapFile(filepath.Join(dir, indexFilename)) + testutil.Ok(t, err) + toc, err := NewTOCFromByteSlice(realByteSlice(f.Bytes())) + testutil.Ok(t, err) + t.Log("size of postings =", toc.LabelIndicesTable-toc.Postings) + ir, err := NewFileReader(filepath.Join(dir, indexFilename)) testutil.Ok(t, err) diff --git a/index/postings.go b/index/postings.go index e2885322..25950d07 100644 --- a/index/postings.go +++ b/index/postings.go @@ -692,8 +692,8 @@ func (it *bigEndianPostings) Err() error { return nil } -// 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock, 4 is baseDeltaBlock. -const postingsType = 4 +// 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock, 4 is baseDeltaBlock, 5 is bitmapPostings. +const postingsType = 5 type bitSlice struct { bstream []byte @@ -731,8 +731,8 @@ func (bs *bitSlice) readBits(offset int) uint64 { return u } - if nbits > int(8 - count) { - u = (u << uint(8 - count)) | uint64((bs.bstream[idx]<>count) + if nbits > int(8-count) { + u = (u << uint(8-count)) | uint64((bs.bstream[idx]<>count) nbits -= int(8 - count) idx += 1 @@ -827,10 +827,10 @@ func (it *deltaBlockPostings) At() uint64 { } func (it *deltaBlockPostings) Next() bool { - if it.offset >= len(it.bs.bstream) << 3 || it.idx >= it.size { + if it.offset >= len(it.bs.bstream)<<3 || it.idx >= it.size { return false } - if it.offset % (deltaBlockSize << 3) == 0 { + if it.offset%(deltaBlockSize<<3) == 0 { val, n := binary.Uvarint(it.bs.bstream[it.offset>>3:]) if n < 1 { return false @@ -854,13 +854,13 @@ func (it *deltaBlockPostings) Next() bool { it.idxBlock = 1 return true } - + it.cur = it.bs.readBits(it.offset) + it.cur it.offset += it.bs.width it.idx += 1 it.idxBlock += 1 if it.idxBlock == it.count { - it.offset = ((it.offset-1) / (deltaBlockSize << 3) + 1) * deltaBlockSize << 3 + it.offset = ((it.offset-1)/(deltaBlockSize<<3) + 1) * deltaBlockSize << 3 } return true } @@ -871,18 +871,18 @@ func (it *deltaBlockPostings) Seek(x uint64) bool { } startOff := (it.offset - 1) / (deltaBlockSize << 3) * deltaBlockSize - num := (len(it.bs.bstream) - 1) / deltaBlockSize - (it.offset - 1) / (deltaBlockSize << 3) + 1 + num := (len(it.bs.bstream)-1)/deltaBlockSize - (it.offset-1)/(deltaBlockSize<<3) + 1 // Do binary search between current position and end. i := sort.Search(num, func(i int) bool { val, _ := binary.Uvarint(it.bs.bstream[startOff+i*deltaBlockSize:]) return val > x }) if i > 0 { - // Go to the previous block because the previous block + // Go to the previous block because the previous block // may contain the first value >= x. i -= 1 } - it.offset = (startOff + i * deltaBlockSize) << 3 + it.offset = (startOff + i*deltaBlockSize) << 3 for it.Next() { if it.At() >= x { return true @@ -903,9 +903,9 @@ func writeDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { var preVal uint32 var max int for i < len(arr) { - e.PutUvarint32(arr[i]) // Put base. + e.PutUvarint32(arr[i]) // Put base. e.PutUvarint64(uint64(i)) // Put idx. - remaining = (deltaBlockSize - (len(e.B) - startLen) % deltaBlockSize - 1) << 3 + remaining = (deltaBlockSize - (len(e.B)-startLen)%deltaBlockSize - 1) << 3 deltas = deltas[:0] preVal = arr[i] max = -1 @@ -916,7 +916,7 @@ func writeDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { if cur <= max { cur = max } - if remaining - cur * (len(deltas) + 1) - (((bits.Len(uint(len(deltas))) >> 3) + 1) << 3) >= 0 { + if remaining-cur*(len(deltas)+1)-(((bits.Len(uint(len(deltas)))>>3)+1)<<3) >= 0 { deltas = append(deltas, delta) max = cur preVal = arr[i] @@ -946,7 +946,7 @@ func writeDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { e.PutBits(uint64(0), remaining) } e.Count = 0 - + // There can be one more extra 0. e.B = e.B[:len(e.B)-(len(e.B)-startLen)%deltaBlockSize] } @@ -983,10 +983,10 @@ func (it *baseDeltaBlockPostings) At() uint64 { } func (it *baseDeltaBlockPostings) Next() bool { - if it.offset >= len(it.bs.bstream) << 3 || it.idx >= it.size { + if it.offset >= len(it.bs.bstream)<<3 || it.idx >= it.size { return false } - if it.offset % (deltaBlockSize << 3) == 0 { + if it.offset%(deltaBlockSize<<3) == 0 { val, n := binary.Uvarint(it.bs.bstream[it.offset>>3:]) if n < 1 { return false @@ -1011,13 +1011,13 @@ func (it *baseDeltaBlockPostings) Next() bool { it.idxBlock = 1 return true } - + it.cur = it.bs.readBits(it.offset) + it.base it.offset += it.bs.width it.idx += 1 it.idxBlock += 1 if it.idxBlock == it.count { - it.offset = ((it.offset-1) / (deltaBlockSize << 3) + 1) * deltaBlockSize << 3 + it.offset = ((it.offset-1)/(deltaBlockSize<<3) + 1) * deltaBlockSize << 3 } return true } @@ -1028,40 +1028,40 @@ func (it *baseDeltaBlockPostings) Seek(x uint64) bool { } startOff := (it.offset - 1) / (deltaBlockSize << 3) * deltaBlockSize - num := (len(it.bs.bstream) - 1) / deltaBlockSize - (it.offset - 1) / (deltaBlockSize << 3) + 1 + num := (len(it.bs.bstream)-1)/deltaBlockSize - (it.offset-1)/(deltaBlockSize<<3) + 1 // Do binary search between current position and end. i := sort.Search(num, func(i int) bool { val, _ := binary.Uvarint(it.bs.bstream[startOff+i*deltaBlockSize:]) return val > x }) if i > 0 { - // Go to the previous block because the previous block + // Go to the previous block because the previous block // may contain the first value >= x. i -= 1 } - it.offset = (startOff + i * deltaBlockSize) << 3 - + it.offset = (startOff + i*deltaBlockSize) << 3 + // Read base, idx, and width. it.Next() if x <= it.base { return true } else { temp := x - it.base - j := sort.Search(it.count - it.idxBlock, func(i int) bool { - return it.bs.readBits(it.offset + i * it.bs.width) >= temp + j := sort.Search(it.count-it.idxBlock, func(i int) bool { + return it.bs.readBits(it.offset+i*it.bs.width) >= temp }) - if j < it.count - it.idxBlock { + if j < it.count-it.idxBlock { it.offset += j * it.bs.width it.cur = it.bs.readBits(it.offset) + it.base it.offset += it.bs.width it.idxBlock += j + 1 it.idx += j + 1 if it.idxBlock == it.count { - it.offset = ((it.offset-1) / (deltaBlockSize << 3) + 1) * deltaBlockSize << 3 + it.offset = ((it.offset-1)/(deltaBlockSize<<3) + 1) * deltaBlockSize << 3 } } else { - it.offset = (startOff + (i + 1) * deltaBlockSize) << 3 + it.offset = (startOff + (i+1)*deltaBlockSize) << 3 return it.Next() } return true @@ -1080,9 +1080,9 @@ func writeBaseDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { var base uint32 var max int for i < len(arr) { - e.PutUvarint32(arr[i]) // Put base. + e.PutUvarint32(arr[i]) // Put base. e.PutUvarint64(uint64(i)) // Put idx. - remaining = (deltaBlockSize - (len(e.B) - startLen) % deltaBlockSize - 1) << 3 + remaining = (deltaBlockSize - (len(e.B)-startLen)%deltaBlockSize - 1) << 3 deltas = deltas[:0] base = arr[i] max = -1 @@ -1090,7 +1090,7 @@ func writeBaseDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { for i < len(arr) { delta := arr[i] - base cur := bits.Len32(delta) - if remaining - cur * (len(deltas) + 1) - (((bits.Len(uint(len(deltas))) >> 3) + 1) << 3) >= 0 { + if remaining-cur*(len(deltas)+1)-(((bits.Len(uint(len(deltas)))>>3)+1)<<3) >= 0 { deltas = append(deltas, delta) max = cur } else { @@ -1119,8 +1119,129 @@ func writeBaseDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { e.PutBits(uint64(0), remaining) } e.Count = 0 - + // There can be one more extra 0. e.B = e.B[:len(e.B)-(len(e.B)-startLen)%deltaBlockSize] } } + +// 8bits -> 256/8=32bytes, 12bits -> 4096/8=512bytes, 16bits -> 65536/8=8192bytes. +const bitmapBits = 8 + +// Bitmap block format. +// ┌──────────┬────────┐ +// │ key <4b> │ bitmap │ +// └──────────┴────────┘ +type bitmapPostings struct { + bs []byte + cur uint64 + inside bool + idx1 int + idx2 int + bitmapSize int + key uint32 +} + +func newBitmapPostings(bstream []byte) *bitmapPostings { + return &bitmapPostings{bs: bstream, bitmapSize: 1 << (bitmapBits - 3)} +} + +func (it *bitmapPostings) At() uint64 { + return it.cur +} + +func (it *bitmapPostings) Next() bool { + if it.inside { + for it.idx1 < it.bitmapSize { + if it.bs[it.idx1+4] == byte(0) { + it.idx1 += 1 + continue + } + for it.idx1 < it.bitmapSize { + if it.bs[it.idx1+4]&(1<= it.bitmapSize { + it.key = binary.BigEndian.Uint32(it.bs) + it.inside = true + return it.Next() + } else { + return false + } + } +} + +func (it *bitmapPostings) Seek(x uint64) bool { + if it.cur >= x { + return true + } + curKey := uint32(x) >> bitmapBits + // curVal := uint32(x) & uint32((1 << uint(bitmapBits)) - 1) + i := sort.Search(len(it.bs)/(it.bitmapSize+4), func(i int) bool { + return binary.BigEndian.Uint32(it.bs[i*(it.bitmapSize+4):]) > curKey + }) + if i > 0 { + i -= 1 + if i > 0 { + it.idx1 = 0 + it.idx2 = 0 + it.bs = it.bs[i*(it.bitmapSize+4):] + it.inside = false + } + } + for it.Next() { + if it.At() >= x { + return true + } + } + return false +} + +func (it *bitmapPostings) Err() error { + return nil +} + +func writeBitmapPostings(e *encoding.Encbuf, arr []uint32) { + key := uint32(0xffffffff) + bitmapSize := 1 << (bitmapBits - 3) + mask := uint32((1 << uint(bitmapBits)) - 1) + var curKey uint32 + var curVal uint32 + var offset int // The starting offset of the bitmap of each block. + var idx1 int + var idx2 int + for _, val := range arr { + curKey = val >> bitmapBits + curVal = val & mask + idx1 = int(curVal) >> 3 + idx2 = int(curVal) % 8 + if curKey != key { + key = curKey + e.PutBE32(uint32(key)) + offset = len(e.Get()) + for i := 0; i < bitmapSize; i++ { + e.PutByte(byte(0)) + } + } + e.B[offset+idx1] |= 1 << uint(7-idx2) + } +} diff --git a/index/postings_test.go b/index/postings_test.go index eddc7906..35f465e8 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -729,7 +729,7 @@ func TestBaseDeltaPostings(t *testing.T) { ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 } - width := bits.Len32(ls[len(ls)-1]-ls[0]) + width := bits.Len32(ls[len(ls)-1] - ls[0]) buf := encoding.Encbuf{} for i := 0; i < num; i++ { buf.PutBits(uint64(ls[i]-ls[0]), width) @@ -945,6 +945,80 @@ func TestBaseDeltaBlockPostings(t *testing.T) { }) } +func TestBitmapPostings(t *testing.T) { + num := 1000 + // mock a list as postings + ls := make([]uint32, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 + // ls[i] = ls[i-1] + 2 + } + + buf := encoding.Encbuf{} + writeBitmapPostings(&buf, ls) + // t.Log("len", len(buf.Get())) + + t.Run("Iteration", func(t *testing.T) { + bp := newBitmapPostings(buf.Get()) + for i := 0; i < num; i++ { + testutil.Assert(t, bp.Next() == true, "") + // t.Log("ls[i] =", ls[i], "bp.At() =", bp.At()) + testutil.Equals(t, uint64(ls[i]), bp.At()) + } + + testutil.Assert(t, bp.Next() == false, "") + testutil.Assert(t, bp.Err() == nil, "") + }) + + t.Run("Seek", func(t *testing.T) { + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[4], ls[4], true, + }, + { + ls[500] - 1, ls[500], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[0], ls[601], true, + }, + { + ls[600], ls[601], true, + }, + { + ls[999], ls[999], true, + }, + { + ls[999] + 10, ls[999], false, + }, + } + + bp := newBitmapPostings(buf.Get()) + + for _, v := range table { + testutil.Equals(t, v.found, bp.Seek(uint64(v.seek))) + testutil.Equals(t, uint64(v.val), bp.At()) + testutil.Assert(t, bp.Err() == nil, "") + } + }) +} + func BenchmarkPostings(b *testing.B) { num := 100000 // mock a list as postings @@ -962,7 +1036,7 @@ func BenchmarkPostings(b *testing.B) { } // baseDeltaPostings. - width := bits.Len32(ls[len(ls)-1]-ls[0]) + width := bits.Len32(ls[len(ls)-1] - ls[0]) bufBD := encoding.Encbuf{} for i := 0; i < num; i++ { bufBD.PutBits(uint64(ls[i]-ls[0]), width) @@ -977,6 +1051,11 @@ func BenchmarkPostings(b *testing.B) { writeBaseDeltaBlockPostings(&bufBDB, ls) // b.Log(len(bufBDB.Get())) + // bitmapPostings. + bufBM := encoding.Encbuf{} + writeBitmapPostings(&bufBM, ls) + // b.Log("bitmapPostings size", bitmapBits, "bits =", len(bufBM.Get())) + table := []struct { seek uint32 val uint32 @@ -1019,7 +1098,7 @@ func BenchmarkPostings(b *testing.B) { bench.ReportAllocs() for j := 0; j < bench.N; j++ { bep := newBigEndianPostings(bufBE) - + for i := 0; i < num; i++ { testutil.Assert(bench, bep.Next() == true, "") testutil.Equals(bench, uint64(ls[i]), bep.At()) @@ -1033,7 +1112,7 @@ func BenchmarkPostings(b *testing.B) { bench.ReportAllocs() for j := 0; j < bench.N; j++ { bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) - + for i := 0; i < num; i++ { testutil.Assert(bench, bdp.Next() == true, "") testutil.Equals(bench, uint64(ls[i]), bdp.At()) @@ -1047,7 +1126,7 @@ func BenchmarkPostings(b *testing.B) { bench.ReportAllocs() for j := 0; j < bench.N; j++ { dbp := newDeltaBlockPostings(bufDB.Get(), len(ls)) - + for i := 0; i < num; i++ { testutil.Assert(bench, dbp.Next() == true, "") testutil.Equals(bench, uint64(ls[i]), dbp.At()) @@ -1061,7 +1140,7 @@ func BenchmarkPostings(b *testing.B) { bench.ReportAllocs() for j := 0; j < bench.N; j++ { bdbp := newBaseDeltaBlockPostings(bufBDB.Get(), len(ls)) - + for i := 0; i < num; i++ { testutil.Assert(bench, bdbp.Next() == true, "") testutil.Equals(bench, uint64(ls[i]), bdbp.At()) @@ -1070,13 +1149,27 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, bdbp.Err() == nil, "") } }) + b.Run("bitmapPostingsIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + bm := newBitmapPostings(bufBM.Get()) + + for i := 0; i < num; i++ { + testutil.Assert(bench, bm.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), bm.At()) + } + testutil.Assert(bench, bm.Next() == false, "") + testutil.Assert(bench, bm.Err() == nil, "") + } + }) b.Run("bigEndianSeek", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { bep := newBigEndianPostings(bufBE) - + for _, v := range table { testutil.Equals(bench, v.found, bep.Seek(uint64(v.seek))) testutil.Equals(bench, uint64(v.val), bep.At()) @@ -1089,7 +1182,7 @@ func BenchmarkPostings(b *testing.B) { bench.ReportAllocs() for j := 0; j < bench.N; j++ { bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) - + for _, v := range table { testutil.Equals(bench, v.found, bdp.Seek(uint64(v.seek))) testutil.Equals(bench, uint64(v.val), bdp.At()) @@ -1102,7 +1195,7 @@ func BenchmarkPostings(b *testing.B) { bench.ReportAllocs() for j := 0; j < bench.N; j++ { dbp := newDeltaBlockPostings(bufDB.Get(), len(ls)) - + for _, v := range table { testutil.Equals(bench, v.found, dbp.Seek(uint64(v.seek))) testutil.Equals(bench, uint64(v.val), dbp.At()) @@ -1115,7 +1208,7 @@ func BenchmarkPostings(b *testing.B) { bench.ReportAllocs() for j := 0; j < bench.N; j++ { bdbp := newBaseDeltaBlockPostings(bufBDB.Get(), len(ls)) - + for _, v := range table { testutil.Equals(bench, v.found, bdbp.Seek(uint64(v.seek))) testutil.Equals(bench, uint64(v.val), bdbp.At()) @@ -1123,6 +1216,19 @@ func BenchmarkPostings(b *testing.B) { } } }) + b.Run("bitmapPostingsSeek", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + bm := newBitmapPostings(bufBM.Get()) + + for _, v := range table { + testutil.Equals(bench, v.found, bm.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), bm.At()) + testutil.Assert(bench, bm.Err() == nil, "") + } + } + }) } func TestIntersectWithMerge(t *testing.T) { From 99ce72a8a354a0befa77e21dee083078aa4f59ab Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Tue, 18 Jun 2019 23:49:40 +0800 Subject: [PATCH 04/18] add roaringBitmapPostings Signed-off-by: naivewong <867245430@qq.com> --- index/index.go | 5 + index/postings.go | 240 ++++++++++++++++++++++++++++++++++++++++- index/postings_test.go | 114 +++++++++++++++++++- 3 files changed, 355 insertions(+), 4 deletions(-) diff --git a/index/index.go b/index/index.go index 2e66f09b..0599c13e 100644 --- a/index/index.go +++ b/index/index.go @@ -543,6 +543,8 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { writeBaseDeltaBlockPostings(&w.buf2, refs) case 5: writeBitmapPostings(&w.buf2, refs) + case 6: + writeRoaringBitmapPostings(&w.buf2, refs) } w.uint32s = refs @@ -1066,6 +1068,9 @@ func (dec *Decoder) Postings(b []byte) (int, Postings, error) { case 5: l := d.Get() return n, newBitmapPostings(l), d.Err() + case 6: + l := d.Get() + return n, newRoaringBitmapPostings(l), d.Err() default: return n, EmptyPostings(), d.Err() } diff --git a/index/postings.go b/index/postings.go index 25950d07..90270cf7 100644 --- a/index/postings.go +++ b/index/postings.go @@ -692,8 +692,8 @@ func (it *bigEndianPostings) Err() error { return nil } -// 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock, 4 is baseDeltaBlock, 5 is bitmapPostings. -const postingsType = 5 +// 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock, 4 is baseDeltaBlock, 5 is bitmapPostings, 6 is roaringBitmapPostings. +const postingsType = 6 type bitSlice struct { bstream []byte @@ -1245,3 +1245,239 @@ func writeBitmapPostings(e *encoding.Encbuf, arr []uint32) { e.B[offset+idx1] |= 1 << uint(7-idx2) } } + +// roaringBitmap block format, type 0 = array, type 1 = bitmap. +// ┌──────────┬──────────┬────────┐ +// │ key <4b> │ type<1b> │ bitmap │ +// └──────────┴──────────┴────────┘ +type roaringBitmapPostings struct { + bs []byte + cur uint64 + inside bool + idx int + idx1 int + idx2 int + footerAddr int + bitmapSize int + valueSize int + key uint32 + numBlock int + blockIdx int + blockType byte + nextBlock int +} + +func newRoaringBitmapPostings(bstream []byte) *roaringBitmapPostings { + if len(bstream) <= 4 { + return nil + } + x := binary.BigEndian.Uint32(bstream) + return &roaringBitmapPostings{bs: bstream[4:], bitmapSize: 1 << (bitmapBits - 3), valueSize: bitmapBits >> 3, numBlock: (len(bstream)-int(x))/4 - 1, footerAddr: int(x)} +} + +func (it *roaringBitmapPostings) At() uint64 { + return it.cur +} + +func (it *roaringBitmapPostings) Next() bool { + if it.inside { + if it.blockType == 0 { + if it.idx < it.nextBlock { + it.cur = 0 + for i := 0; i < it.valueSize; i++ { + it.cur = (it.cur << 8) + uint64(it.bs[it.idx+i]) + } + it.idx += it.valueSize + it.cur += uint64(it.key) + return true + } + } else { + for it.idx1 < it.bitmapSize { + if it.bs[it.idx+it.idx1] == byte(0) { + it.idx1 += 1 + continue + } + for it.idx1 < it.bitmapSize { + if it.bs[it.idx+it.idx1]&(1<= x { + return true + } + curKey := uint32(x) >> bitmapBits + i := sort.Search(it.numBlock-it.blockIdx, func(i int) bool { + off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+4*(it.blockIdx+i):])) + return binary.BigEndian.Uint32(it.bs[off:]) > curKey + }) + if i > 0 { + i -= 1 + if i > 0 { + it.idx1 = 0 + it.idx2 = 0 + it.inside = false + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+4*(it.blockIdx+i):])) + } + } + it.blockIdx += i + if it.Next() { + if it.cur >= x { + return true + } + if it.blockType == 0 { + // If encoding with array, binary search. + num := (it.nextBlock - it.idx) / it.valueSize + j := sort.Search(num, func(i int) bool { + var temp uint64 + for j := 0; j < it.valueSize; j++ { + temp = (temp << 8) + uint64(it.bs[it.idx+j+i*it.valueSize]) + } + temp += uint64(it.key) + return temp >= x + }) + it.cur = 0 + for i := 0; i < it.valueSize; i++ { + it.cur = (it.cur<<8) + uint64(it.bs[it.idx+i+j*it.valueSize]) + } + it.cur += uint64(it.key) + it.idx += (j + 1) * it.valueSize + if j == num { + // The first element in next block should be >= x. + return it.Next() + } + return true + } else { + // If encoding with bitmap, loop next. + for it.Next() { + if it.cur >= x { + return true + } + } + return false + } + } else { + return false + } +} + +func (it *roaringBitmapPostings) Err() error { + return nil +} + +func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { + key := uint32(0xffffffff) + bitmapSize := 1 << (bitmapBits - 3) + valueSize := bitmapBits >> 3 + thres := (1 << bitmapBits) / bitmapBits + mask := uint32((1 << uint(bitmapBits)) - 1) + var curKey uint32 + var curVal uint32 + var offset int // The starting offset of the bitmap of each block. + var idx int + var idx1 int + var idx2 int + var startingOffs []uint32 + var vals []int + c := make([]byte, 4) + startOff := len(e.Get()) + e.PutBE32(0) // Footer starting offset. + for idx < len(arr) { + curKey = arr[idx] >> bitmapBits + curVal = arr[idx] & mask + if curKey != key { + if idx != 0 { + startingOffs = append(startingOffs, uint32(len(e.B))) + e.PutBE32(uint32(key)) + if len(vals) > thres { + e.PutByte(byte(1)) + offset = len(e.Get()) + for i := 0; i < bitmapSize; i++ { + e.PutByte(byte(0)) + } + for _, val := range vals { + idx1 = val >> 3 + idx2 = val % 8 + e.B[offset+idx1] |= 1 << uint(7-idx2) + } + } else { + e.PutByte(byte(0)) + for _, val := range vals { + binary.BigEndian.PutUint32(c[:], uint32(val)) + for i := 4 - valueSize; i < 4; i++ { + e.PutByte(c[i]) + } + } + } + vals = vals[:0] + } + key = curKey + } + vals = append(vals, int(curVal)) + idx += 1 + } + startingOffs = append(startingOffs, uint32(len(e.B))) + e.PutBE32(uint32(key)) + if len(vals) > thres { + e.PutByte(byte(1)) + offset = len(e.Get()) + for i := 0; i < bitmapSize; i++ { + e.PutByte(byte(0)) + } + for _, val := range vals { + idx1 = val >> 3 + idx2 = val % 8 + e.B[offset+idx1] |= 1 << uint(7-idx2) + } + } else { + e.PutByte(byte(0)) + for _, val := range vals { + binary.BigEndian.PutUint32(c[:], uint32(val)) + for i := 4 - valueSize; i < 4; i++ { + e.PutByte(c[i]) + } + } + } + binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-4-startOff)) + for _, off := range startingOffs { + e.PutBE32(off - 4 - uint32(startOff)) + } +} diff --git a/index/postings_test.go b/index/postings_test.go index 35f465e8..4afe43e5 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -1019,6 +1019,81 @@ func TestBitmapPostings(t *testing.T) { }) } +func TestRoaringBitmapPostings(t *testing.T) { + num := 1000 + // mock a list as postings + ls := make([]uint32, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint32(rand.Int31n(15)) + 2 + // ls[i] = ls[i-1] + 10 + } + + buf := encoding.Encbuf{} + writeRoaringBitmapPostings(&buf, ls) + // t.Log("len", len(buf.Get())) + + t.Run("Iteration", func(t *testing.T) { + rbp := newRoaringBitmapPostings(buf.Get()) + for i := 0; i < num; i++ { + testutil.Assert(t, rbp.Next() == true, "") + // t.Log("ls[i] =", ls[i], "rbp.At() =", rbp.At()) + testutil.Equals(t, uint64(ls[i]), rbp.At()) + } + + testutil.Assert(t, rbp.Next() == false, "") + testutil.Assert(t, rbp.Err() == nil, "") + }) + + t.Run("Seek", func(t *testing.T) { + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[4], ls[4], true, + }, + { + ls[500] - 1, ls[500], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[0], ls[601], true, + }, + { + ls[600], ls[601], true, + }, + { + ls[999], ls[999], true, + }, + { + ls[999] + 10, ls[999], false, + }, + } + + rbp := newRoaringBitmapPostings(buf.Get()) + + for _, v := range table { + // t.Log("i", i) + testutil.Equals(t, v.found, rbp.Seek(uint64(v.seek))) + testutil.Equals(t, uint64(v.val), rbp.At()) + testutil.Assert(t, rbp.Err() == nil, "") + } + }) +} + func BenchmarkPostings(b *testing.B) { num := 100000 // mock a list as postings @@ -1034,6 +1109,7 @@ func BenchmarkPostings(b *testing.B) { b := bufBE[i*4 : i*4+4] binary.BigEndian.PutUint32(b, ls[i]) } + b.Log("bigEndianPostings size =", len(bufBE)) // baseDeltaPostings. width := bits.Len32(ls[len(ls)-1] - ls[0]) @@ -1041,20 +1117,27 @@ func BenchmarkPostings(b *testing.B) { for i := 0; i < num; i++ { bufBD.PutBits(uint64(ls[i]-ls[0]), width) } + b.Log("baseDeltaPostings size =", len(bufBD.Get())) // deltaBlockPostings. bufDB := encoding.Encbuf{} writeDeltaBlockPostings(&bufDB, ls) + b.Log("deltaBlockPostings size =", len(bufDB.Get())) // baseDeltaBlockPostings. bufBDB := encoding.Encbuf{} writeBaseDeltaBlockPostings(&bufBDB, ls) - // b.Log(len(bufBDB.Get())) + b.Log("baseDeltaBlockPostings size =", len(bufBDB.Get())) // bitmapPostings. bufBM := encoding.Encbuf{} writeBitmapPostings(&bufBM, ls) - // b.Log("bitmapPostings size", bitmapBits, "bits =", len(bufBM.Get())) + b.Log("bitmapPostings bits", bitmapBits, "size =", len(bufBM.Get())) + + // roaringBitmapPostings. + bufRBM := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM, ls) + b.Log("roaringBitmapPostings bits", bitmapBits, "size =", len(bufRBM.Get())) table := []struct { seek uint32 @@ -1163,6 +1246,20 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, bm.Err() == nil, "") } }) + b.Run("roaringBitmapPostingsIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + rbm := newRoaringBitmapPostings(bufRBM.Get()) + + for i := 0; i < num; i++ { + testutil.Assert(bench, rbm.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), rbm.At()) + } + testutil.Assert(bench, rbm.Next() == false, "") + testutil.Assert(bench, rbm.Err() == nil, "") + } + }) b.Run("bigEndianSeek", func(bench *testing.B) { bench.ResetTimer() @@ -1229,6 +1326,19 @@ func BenchmarkPostings(b *testing.B) { } } }) + b.Run("roaringBitmapPostingsSeek", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + rbm := newRoaringBitmapPostings(bufRBM.Get()) + + for _, v := range table { + testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), rbm.At()) + testutil.Assert(bench, rbm.Err() == nil, "") + } + } + }) } func TestIntersectWithMerge(t *testing.T) { From 5623a3306f6e0fb27565e4aea8b4ac10c98a696e Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Wed, 19 Jun 2019 12:22:20 +0800 Subject: [PATCH 05/18] update BenchmarkPostings Signed-off-by: naivewong <867245430@qq.com> --- index/postings_test.go | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/index/postings_test.go b/index/postings_test.go index 4afe43e5..823a56d9 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -1148,25 +1148,46 @@ func BenchmarkPostings(b *testing.B) { ls[0] - 1, ls[0], true, }, { - ls[4], ls[4], true, + ls[1000], ls[1000], true, }, { - ls[5000] - 1, ls[5000], true, + ls[2000], ls[2000], true, }, { - ls[6000] + 1, ls[6001], true, + ls[3000], ls[3000], true, }, { - ls[6000] + 1, ls[6001], true, + ls[4000], ls[4000], true, }, { - ls[6000] + 1, ls[6001], true, + ls[5000], ls[5000], true, }, { - ls[0], ls[6001], true, + ls[6000], ls[6000], true, }, { - ls[6000], ls[6001], true, + ls[10000], ls[10000], true, + }, + { + ls[20000], ls[20000], true, + }, + { + ls[30000], ls[30000], true, + }, + { + ls[40000], ls[40000], true, + }, + { + ls[50000], ls[50000], true, + }, + { + ls[60000], ls[60000], true, + }, + { + ls[70000], ls[70000], true, + }, + { + ls[80000], ls[80000], true, }, { ls[99999], ls[99999], true, From 591aebd3a9ea7e0d025add466c5f3eef9e55daa7 Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Thu, 20 Jun 2019 13:00:33 +0800 Subject: [PATCH 06/18] improve roaringBitmapPostings Signed-off-by: naivewong <867245430@qq.com> --- index/index.go | 20 +++++ index/postings.go | 188 +++++++++++++++++++++++++++++----------------- 2 files changed, 138 insertions(+), 70 deletions(-) diff --git a/index/index.go b/index/index.go index 0599c13e..ab06f7c5 100644 --- a/index/index.go +++ b/index/index.go @@ -137,6 +137,10 @@ type Writer struct { Version int } +func (w *Writer) GetP() uint64 { + return w.pos +} + // TOC represents index Table Of Content that states where each section of index starts. type TOC struct { Symbols uint64 @@ -545,6 +549,15 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { writeBitmapPostings(&w.buf2, refs) case 6: writeRoaringBitmapPostings(&w.buf2, refs) + // if len(refs) < 32 { + // w.buf2.PutByte(0) + // for _, r := range refs { + // w.buf2.PutBE32(r) + // } + // } else { + // w.buf2.PutByte(1) + // writeRoaringBitmapPostings(&w.buf2, refs) + // } } w.uint32s = refs @@ -1071,6 +1084,13 @@ func (dec *Decoder) Postings(b []byte) (int, Postings, error) { case 6: l := d.Get() return n, newRoaringBitmapPostings(l), d.Err() + // typ := d.Byte() + // l := d.Get() + // if typ == 0 { + // return n, newBigEndianPostings(l), d.Err() + // } else { + // return n, newRoaringBitmapPostings(l), d.Err() + // } default: return n, EmptyPostings(), d.Err() } diff --git a/index/postings.go b/index/postings.go index 90270cf7..9f190340 100644 --- a/index/postings.go +++ b/index/postings.go @@ -1247,16 +1247,16 @@ func writeBitmapPostings(e *encoding.Encbuf, arr []uint32) { } // roaringBitmap block format, type 0 = array, type 1 = bitmap. -// ┌──────────┬──────────┬────────┐ -// │ key <4b> │ type<1b> │ bitmap │ -// └──────────┴──────────┴────────┘ +// ┌───────────────┬──────────┬──────────────┬────────────────┬────────────┬─────────────────────┬─────┬─────────────────────┐ +// │ key │ type<1b> │ bitmap/array │ numBlocks <4b> │ width <1b> │ block 1 addr │ ... │ block n addr │ +// └───────────────┴──────────┴──────────────┴────────────────┴────────────┴─────────────────────┴─────┴─────────────────────┘ type roaringBitmapPostings struct { bs []byte cur uint64 inside bool - idx int - idx1 int - idx2 int + idx int // The current offset inside the bs. + idx1 int // The offset in the bitmap in current block in bytes. + idx2 int // The offset in the current byte in the bitmap ([0,8)). footerAddr int bitmapSize int valueSize int @@ -1265,6 +1265,7 @@ type roaringBitmapPostings struct { blockIdx int blockType byte nextBlock int + width int } func newRoaringBitmapPostings(bstream []byte) *roaringBitmapPostings { @@ -1272,7 +1273,7 @@ func newRoaringBitmapPostings(bstream []byte) *roaringBitmapPostings { return nil } x := binary.BigEndian.Uint32(bstream) - return &roaringBitmapPostings{bs: bstream[4:], bitmapSize: 1 << (bitmapBits - 3), valueSize: bitmapBits >> 3, numBlock: (len(bstream)-int(x))/4 - 1, footerAddr: int(x)} + return &roaringBitmapPostings{bs: bstream[4:], bitmapSize: 1 << (bitmapBits - 3), valueSize: bitmapBits >> 3, numBlock: int(binary.BigEndian.Uint32(bstream[4+int(x):])), footerAddr: int(x), width: int(bstream[8+int(x)])} } func (it *roaringBitmapPostings) At() uint64 { @@ -1280,8 +1281,8 @@ func (it *roaringBitmapPostings) At() uint64 { } func (it *roaringBitmapPostings) Next() bool { - if it.inside { - if it.blockType == 0 { + if it.inside { // Already entered the block. + if it.blockType == 0 { // Type array. if it.idx < it.nextBlock { it.cur = 0 for i := 0; i < it.valueSize; i++ { @@ -1291,7 +1292,7 @@ func (it *roaringBitmapPostings) Next() bool { it.cur += uint64(it.key) return true } - } else { + } else { // Type bitmap. for it.idx1 < it.bitmapSize { if it.bs[it.idx+it.idx1] == byte(0) { it.idx1 += 1 @@ -1322,14 +1323,16 @@ func (it *roaringBitmapPostings) Next() bool { it.blockIdx += 1 it.inside = false return it.Next() - } else { + } else { // Not yet entered the block. if it.idx < it.footerAddr { - it.key = binary.BigEndian.Uint32(it.bs[it.idx:]) << bitmapBits - it.blockType = it.bs[it.idx+4] - it.idx += 5 + val, size := binary.Uvarint(it.bs[it.idx:]) + it.key = uint32(val) << bitmapBits + it.idx += size + it.blockType = it.bs[it.idx] + it.idx += 1 it.inside = true if it.blockIdx != it.numBlock-1 { - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+(it.blockIdx+1)*4:])) + it.nextBlock = int(it.readBits((it.footerAddr+5)*8+(it.blockIdx+1)*it.width)) } else { it.nextBlock = it.footerAddr } @@ -1346,8 +1349,9 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { } curKey := uint32(x) >> bitmapBits i := sort.Search(it.numBlock-it.blockIdx, func(i int) bool { - off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+4*(it.blockIdx+i):])) - return binary.BigEndian.Uint32(it.bs[off:]) > curKey + off := int(it.readBits((it.footerAddr+5)*8+(it.blockIdx+i)*it.width)) + x, _ := binary.Uvarint(it.bs[off:]) + return uint32(x) > curKey }) if i > 0 { i -= 1 @@ -1355,7 +1359,8 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { it.idx1 = 0 it.idx2 = 0 it.inside = false - it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+4*(it.blockIdx+i):])) + // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+4*(it.blockIdx+i):])) + it.idx = int(it.readBits((it.footerAddr+5)*8+(it.blockIdx+i)*it.width)) } } it.blockIdx += i @@ -1403,59 +1408,60 @@ func (it *roaringBitmapPostings) Err() error { return nil } -func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { - key := uint32(0xffffffff) - bitmapSize := 1 << (bitmapBits - 3) - valueSize := bitmapBits >> 3 - thres := (1 << bitmapBits) / bitmapBits - mask := uint32((1 << uint(bitmapBits)) - 1) - var curKey uint32 - var curVal uint32 - var offset int // The starting offset of the bitmap of each block. - var idx int - var idx1 int - var idx2 int - var startingOffs []uint32 - var vals []int - c := make([]byte, 4) - startOff := len(e.Get()) - e.PutBE32(0) // Footer starting offset. - for idx < len(arr) { - curKey = arr[idx] >> bitmapBits - curVal = arr[idx] & mask - if curKey != key { - if idx != 0 { - startingOffs = append(startingOffs, uint32(len(e.B))) - e.PutBE32(uint32(key)) - if len(vals) > thres { - e.PutByte(byte(1)) - offset = len(e.Get()) - for i := 0; i < bitmapSize; i++ { - e.PutByte(byte(0)) - } - for _, val := range vals { - idx1 = val >> 3 - idx2 = val % 8 - e.B[offset+idx1] |= 1 << uint(7-idx2) - } - } else { - e.PutByte(byte(0)) - for _, val := range vals { - binary.BigEndian.PutUint32(c[:], uint32(val)) - for i := 4 - valueSize; i < 4; i++ { - e.PutByte(c[i]) - } - } - } - vals = vals[:0] - } - key = curKey - } - vals = append(vals, int(curVal)) +// Read key of the block starting from off. +// func (it *roaringBitmapPostings) readKey(off int) uint32 { +// key := uint32(0) +// for i := 0; i < 4 - it.valueSize; i ++ { +// key = (key << 8) + uint32(it.bs[off+i]) +// } +// return key +// } + +func (it *roaringBitmapPostings) readByte(idx int, count uint8) byte { + if count == 0 { + return it.bs[idx] + } + byt := it.bs[idx] << count + byt |= it.bs[idx+1] >> (8 - count) + + return byt +} + +func (it *roaringBitmapPostings) readBits(offset int) uint64 { + idx := offset / 8 + count := uint8(offset % 8) + nbits := it.width + var u uint64 + + for nbits >= 8 { + byt := it.readByte(idx, count) + + u = (u << 8) | uint64(byt) + nbits -= 8 idx += 1 } - startingOffs = append(startingOffs, uint32(len(e.B))) - e.PutBE32(uint32(key)) + + if nbits == 0 { + return u + } + + if nbits > int(8-count) { + u = (u << uint(8-count)) | uint64((it.bs[idx]<>count) + nbits -= int(8 - count) + idx += 1 + + count = 0 + } + + u = (u << uint(nbits)) | uint64((it.bs[idx]<>(8-uint(nbits))) + return u +} + +func writeRoaringBitmapBlock(e *encoding.Encbuf, vals []int, c []byte, key uint32, thres int, bitmapSize int, valueSize int) { + var offset int // The starting offset of the bitmap of each block. + var idx1 int // The offset in the bitmap in current block in bytes. + var idx2 int // The offset in the current byte in the bitmap ([0,8)). + e.PutUvarint32(key) if len(vals) > thres { e.PutByte(byte(1)) offset = len(e.Get()) @@ -1476,8 +1482,50 @@ func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { } } } +} + +func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { + key := uint32(0xffffffff) // The initial key should be unique. + bitmapSize := 1 << (bitmapBits - 3) // Bitmap size in bytes. + valueSize := bitmapBits >> 3 // The size of the element in array in bytes. + thres := (1 << bitmapBits) / bitmapBits // Threshold of number of elements in the block for choosing encoding type. + mask := uint32((1 << uint(bitmapBits)) - 1) // Mask for the elements in the block. + var curKey uint32 + var curVal uint32 + var idx int // Index of current element in arr. + var startingOffs []uint32 // The starting offsets of each block. + var vals []int // The converted values in the current block. + c := make([]byte, 4) + startOff := len(e.Get()) + e.PutBE32(0) // Footer starting offset. + for idx < len(arr) { + curKey = arr[idx] >> bitmapBits // Key of block. + curVal = arr[idx] & mask // Value inside block. + if curKey != key { + // Move to next block. + if idx != 0 { + startingOffs = append(startingOffs, uint32(len(e.B))) + writeRoaringBitmapBlock(e, vals, c, key, thres, bitmapSize, valueSize) + vals = vals[:0] + } + key = curKey + } + vals = append(vals, int(curVal)) + idx += 1 + } + startingOffs = append(startingOffs, uint32(len(e.B))) + writeRoaringBitmapBlock(e, vals, c, key, thres, bitmapSize, valueSize) + + // Put footer starting offset. binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-4-startOff)) + width := bits.Len32(startingOffs[len(startingOffs)-1] - 4 - uint32(startOff)) + if width == 0 { + // key 0 will result in o width. + width += 1 + } + e.PutBE32(uint32(len(startingOffs))) // Number of blocks. + e.PutByte(byte(width)) for _, off := range startingOffs { - e.PutBE32(off - 4 - uint32(startOff)) + e.PutBits(uint64(off - 4 - uint32(startOff)), width) } } From 22f8cbb2867118a5f875e3c53cd3f7dfc08661e4 Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Thu, 20 Jun 2019 13:47:34 +0800 Subject: [PATCH 07/18] add benchmark for postings intersection for different postings Signed-off-by: naivewong <867245430@qq.com> --- index/postings_test.go | 344 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) diff --git a/index/postings_test.go b/index/postings_test.go index 823a56d9..bacaa22d 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -1362,6 +1362,350 @@ func BenchmarkPostings(b *testing.B) { }) } +func BenchmarkPostingsIntersect(t *testing.B) { + // bigEndianPostings. + t.Run("BELongPostings1", func(bench *testing.B) { + var a, b, c, d []uint32 + + for i := 0; i < 10000000; i += 2 { + a = append(a, uint32(i)) + } + for i := 5000000; i < 5000100; i += 4 { + b = append(b, uint32(i)) + } + for i := 5090000; i < 5090600; i += 4 { + b = append(b, uint32(i)) + } + for i := 4990000; i < 5100000; i++ { + c = append(c, uint32(i)) + } + for i := 4000000; i < 6000000; i++ { + d = append(d, uint32(i)) + } + + bufBE1 := make([]byte, len(a)*4) + for i := 0; i < len(a); i++ { + bs := bufBE1[i*4 : i*4+4] + binary.BigEndian.PutUint32(bs, a[i]) + } + bufBE2 := make([]byte, len(b)*4) + for i := 0; i < len(b); i++ { + bs := bufBE2[i*4 : i*4+4] + binary.BigEndian.PutUint32(bs, b[i]) + } + bufBE3 := make([]byte, len(c)*4) + for i := 0; i < len(c); i++ { + bs := bufBE3[i*4 : i*4+4] + binary.BigEndian.PutUint32(bs, c[i]) + } + bufBE4 := make([]byte, len(d)*4) + for i := 0; i < len(d); i++ { + bs := bufBE4[i*4 : i*4+4] + binary.BigEndian.PutUint32(bs, d[i]) + } + + i1 := newBigEndianPostings(bufBE1) + i2 := newBigEndianPostings(bufBE2) + i3 := newBigEndianPostings(bufBE3) + i4 := newBigEndianPostings(bufBE4) + + bench.ResetTimer() + bench.ReportAllocs() + for i := 0; i < bench.N; i++ { + if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { + bench.Fatal(err) + } + } + }) + // bigEndianPostings. + t.Run("BELongPostings2", func(bench *testing.B) { + var a, b, c, d []uint32 + + for i := 0; i < 12500000; i++ { + a = append(a, uint32(i)) + } + for i := 7500000; i < 12500000; i++ { + b = append(b, uint32(i)) + } + for i := 9000000; i < 20000000; i++ { + c = append(c, uint32(i)) + } + for i := 10000000; i < 12000000; i++ { + d = append(d, uint32(i)) + } + + bufBE1 := make([]byte, len(a)*4) + for i := 0; i < len(a); i++ { + bs := bufBE1[i*4 : i*4+4] + binary.BigEndian.PutUint32(bs, a[i]) + } + bufBE2 := make([]byte, len(b)*4) + for i := 0; i < len(b); i++ { + bs := bufBE2[i*4 : i*4+4] + binary.BigEndian.PutUint32(bs, b[i]) + } + bufBE3 := make([]byte, len(c)*4) + for i := 0; i < len(c); i++ { + bs := bufBE3[i*4 : i*4+4] + binary.BigEndian.PutUint32(bs, c[i]) + } + bufBE4 := make([]byte, len(d)*4) + for i := 0; i < len(d); i++ { + bs := bufBE4[i*4 : i*4+4] + binary.BigEndian.PutUint32(bs, d[i]) + } + + i1 := newBigEndianPostings(bufBE1) + i2 := newBigEndianPostings(bufBE2) + i3 := newBigEndianPostings(bufBE3) + i4 := newBigEndianPostings(bufBE4) + + bench.ResetTimer() + bench.ReportAllocs() + for i := 0; i < bench.N; i++ { + if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { + bench.Fatal(err) + } + } + }) + // Many matchers(k >> n). + t.Run("BEManyPostings", func(bench *testing.B) { + var its []Postings + + // 100000 matchers(k=100000). + for i := 0; i < 100000; i++ { + var temp []uint32 + for j := 1; j < 100; j++ { + temp = append(temp, uint32(j)) + } + bufBE := make([]byte, len(temp)*4) + for i := 0; i < len(temp); i++ { + bs := bufBE[i*4 : i*4+4] + binary.BigEndian.PutUint32(bs, temp[i]) + } + its = append(its, newBigEndianPostings(bufBE)) + } + + bench.ResetTimer() + bench.ReportAllocs() + for i := 0; i < bench.N; i++ { + if _, err := ExpandPostings(Intersect(its...)); err != nil { + bench.Fatal(err) + } + } + }) + + // baseDeltaBlockPostings. + t.Run("BDBLongPostings1", func(bench *testing.B) { + var a, b, c, d []uint32 + + for i := 0; i < 10000000; i += 2 { + a = append(a, uint32(i)) + } + for i := 5000000; i < 5000100; i += 4 { + b = append(b, uint32(i)) + } + for i := 5090000; i < 5090600; i += 4 { + b = append(b, uint32(i)) + } + for i := 4990000; i < 5100000; i++ { + c = append(c, uint32(i)) + } + for i := 4000000; i < 6000000; i++ { + d = append(d, uint32(i)) + } + + bufBDB1 := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB1, a) + bufBDB2 := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB2, b) + bufBDB3 := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB3, c) + bufBDB4 := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB4, d) + + i1 := newBaseDeltaBlockPostings(bufBDB1.Get(), len(a)) + i2 := newBaseDeltaBlockPostings(bufBDB2.Get(), len(b)) + i3 := newBaseDeltaBlockPostings(bufBDB3.Get(), len(c)) + i4 := newBaseDeltaBlockPostings(bufBDB4.Get(), len(d)) + + bench.ResetTimer() + bench.ReportAllocs() + for i := 0; i < bench.N; i++ { + if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { + bench.Fatal(err) + } + } + }) + // baseDeltaBlockPostings. + t.Run("BDBLongPostings2", func(bench *testing.B) { + var a, b, c, d []uint32 + + for i := 0; i < 12500000; i++ { + a = append(a, uint32(i)) + } + for i := 7500000; i < 12500000; i++ { + b = append(b, uint32(i)) + } + for i := 9000000; i < 20000000; i++ { + c = append(c, uint32(i)) + } + for i := 10000000; i < 12000000; i++ { + d = append(d, uint32(i)) + } + + bufBDB1 := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB1, a) + bufBDB2 := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB2, b) + bufBDB3 := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB3, c) + bufBDB4 := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB4, d) + + i1 := newBaseDeltaBlockPostings(bufBDB1.Get(), len(a)) + i2 := newBaseDeltaBlockPostings(bufBDB2.Get(), len(b)) + i3 := newBaseDeltaBlockPostings(bufBDB3.Get(), len(c)) + i4 := newBaseDeltaBlockPostings(bufBDB4.Get(), len(d)) + + bench.ResetTimer() + bench.ReportAllocs() + for i := 0; i < bench.N; i++ { + if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { + bench.Fatal(err) + } + } + }) + // Many matchers(k >> n). + t.Run("BDBManyPostings", func(bench *testing.B) { + var its []Postings + + // 100000 matchers(k=100000). + for i := 0; i < 100000; i++ { + var temp []uint32 + for j := 1; j < 100; j++ { + temp = append(temp, uint32(j)) + } + bufBDB := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB, temp) + its = append(its, newBaseDeltaBlockPostings(bufBDB.Get(), len(temp))) + } + + bench.ResetTimer() + bench.ReportAllocs() + for i := 0; i < bench.N; i++ { + if _, err := ExpandPostings(Intersect(its...)); err != nil { + bench.Fatal(err) + } + } + }) + + // roaringBitmapPostings. + t.Run("RBMLongPostings1", func(bench *testing.B) { + var a, b, c, d []uint32 + + for i := 0; i < 10000000; i += 2 { + a = append(a, uint32(i)) + } + for i := 5000000; i < 5000100; i += 4 { + b = append(b, uint32(i)) + } + for i := 5090000; i < 5090600; i += 4 { + b = append(b, uint32(i)) + } + for i := 4990000; i < 5100000; i++ { + c = append(c, uint32(i)) + } + for i := 4000000; i < 6000000; i++ { + d = append(d, uint32(i)) + } + + bufRBM1 := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM1, a) + bufRBM2 := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM2, b) + bufRBM3 := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM3, c) + bufRBM4 := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM4, d) + + i1 := newRoaringBitmapPostings(bufRBM1.Get()) + i2 := newRoaringBitmapPostings(bufRBM2.Get()) + i3 := newRoaringBitmapPostings(bufRBM3.Get()) + i4 := newRoaringBitmapPostings(bufRBM4.Get()) + + bench.ResetTimer() + bench.ReportAllocs() + for i := 0; i < bench.N; i++ { + if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { + bench.Fatal(err) + } + } + }) + // roaringBitmapPostings. + t.Run("RBMLongPostings2", func(bench *testing.B) { + var a, b, c, d []uint32 + + for i := 0; i < 12500000; i++ { + a = append(a, uint32(i)) + } + for i := 7500000; i < 12500000; i++ { + b = append(b, uint32(i)) + } + for i := 9000000; i < 20000000; i++ { + c = append(c, uint32(i)) + } + for i := 10000000; i < 12000000; i++ { + d = append(d, uint32(i)) + } + + bufRBM1 := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM1, a) + bufRBM2 := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM2, b) + bufRBM3 := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM3, c) + bufRBM4 := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM4, d) + + i1 := newRoaringBitmapPostings(bufRBM1.Get()) + i2 := newRoaringBitmapPostings(bufRBM2.Get()) + i3 := newRoaringBitmapPostings(bufRBM3.Get()) + i4 := newRoaringBitmapPostings(bufRBM4.Get()) + + bench.ResetTimer() + bench.ReportAllocs() + for i := 0; i < bench.N; i++ { + if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { + bench.Fatal(err) + } + } + }) + // Many matchers(k >> n). + t.Run("RBMManyPostings", func(bench *testing.B) { + var its []Postings + + // 100000 matchers(k=100000). + for i := 0; i < 100000; i++ { + var temp []uint32 + for j := 1; j < 100; j++ { + temp = append(temp, uint32(j)) + } + bufRBM := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM, temp) + its = append(its, newRoaringBitmapPostings(bufRBM.Get())) + } + + bench.ResetTimer() + bench.ReportAllocs() + for i := 0; i < bench.N; i++ { + if _, err := ExpandPostings(Intersect(its...)); err != nil { + bench.Fatal(err) + } + } + }) +} + func TestIntersectWithMerge(t *testing.T) { // One of the reproducible cases for: // https://github.com/prometheus/prometheus/issues/2616 From ea7b5b52617b5e393367d460e12c966f1cab4247 Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Fri, 21 Jun 2019 18:56:44 +0800 Subject: [PATCH 08/18] improve roaringBitmapPostings performance Signed-off-by: naivewong <867245430@qq.com> --- index/postings.go | 308 +++++++++++++++++++++++++++++------------ index/postings_test.go | 199 ++++++++++++++------------ 2 files changed, 327 insertions(+), 180 deletions(-) diff --git a/index/postings.go b/index/postings.go index 9f190340..be11071f 100644 --- a/index/postings.go +++ b/index/postings.go @@ -795,7 +795,7 @@ func (it *baseDeltaPostings) Err() error { return nil } -const deltaBlockSize = 128 +const deltaBlockSize = 256 // Block format(delta is to the previous value). // ┌────────────────┬───────────────┬─────────────────┬────────────┬────────────────┬─────┬────────────────┐ @@ -1039,13 +1039,8 @@ func (it *baseDeltaBlockPostings) Seek(x uint64) bool { // may contain the first value >= x. i -= 1 } - it.offset = (startOff + i*deltaBlockSize) << 3 - // Read base, idx, and width. - it.Next() - if x <= it.base { - return true - } else { + if i == 0 && it.idx > 0 { temp := x - it.base j := sort.Search(it.count-it.idxBlock, func(i int) bool { return it.bs.readBits(it.offset+i*it.bs.width) >= temp @@ -1065,6 +1060,34 @@ func (it *baseDeltaBlockPostings) Seek(x uint64) bool { return it.Next() } return true + } else { + it.offset = (startOff + i*deltaBlockSize) << 3 + + // Read base, idx, and width. + it.Next() + if x <= it.base { + return true + } else { + temp := x - it.base + j := sort.Search(it.count-it.idxBlock, func(i int) bool { + return it.bs.readBits(it.offset+i*it.bs.width) >= temp + }) + + if j < it.count-it.idxBlock { + it.offset += j * it.bs.width + it.cur = it.bs.readBits(it.offset) + it.base + it.offset += it.bs.width + it.idxBlock += j + 1 + it.idx += j + 1 + if it.idxBlock == it.count { + it.offset = ((it.offset-1)/(deltaBlockSize<<3) + 1) * deltaBlockSize << 3 + } + } else { + it.offset = (startOff + (i+1)*deltaBlockSize) << 3 + return it.Next() + } + return true + } } } @@ -1246,10 +1269,27 @@ func writeBitmapPostings(e *encoding.Encbuf, arr []uint32) { } } +var rbpMasks []byte +var rbpValueMask uint32 +var rbpValueSize int +var rbpBitmapSize int +func init() { + for i := 7; i >= 0; i-- { + rbpMasks = append(rbpMasks, byte(1<> 3 +} + // roaringBitmap block format, type 0 = array, type 1 = bitmap. -// ┌───────────────┬──────────┬──────────────┬────────────────┬────────────┬─────────────────────┬─────┬─────────────────────┐ -// │ key │ type<1b> │ bitmap/array │ numBlocks <4b> │ width <1b> │ block 1 addr │ ... │ block n addr │ -// └───────────────┴──────────┴──────────────┴────────────────┴────────────┴─────────────────────┴─────┴─────────────────────┘ +// ┌───────────────┬──────────┬──────────────┐ +// │ key │ type<1b> │ bitmap/array │ +// └───────────────┴──────────┴──────────────┘ +// footer format. +// ┌────────────┬─────────────────────┬─────┬─────────────────────┐ +// │ width <1b> │ block 1 addr │ ... │ block n addr │ +// └────────────┴─────────────────────┴─────┴─────────────────────┘ type roaringBitmapPostings struct { bs []byte cur uint64 @@ -1258,14 +1298,13 @@ type roaringBitmapPostings struct { idx1 int // The offset in the bitmap in current block in bytes. idx2 int // The offset in the current byte in the bitmap ([0,8)). footerAddr int - bitmapSize int - valueSize int key uint32 numBlock int blockIdx int blockType byte nextBlock int width int + addrMask uint32 } func newRoaringBitmapPostings(bstream []byte) *roaringBitmapPostings { @@ -1273,7 +1312,9 @@ func newRoaringBitmapPostings(bstream []byte) *roaringBitmapPostings { return nil } x := binary.BigEndian.Uint32(bstream) - return &roaringBitmapPostings{bs: bstream[4:], bitmapSize: 1 << (bitmapBits - 3), valueSize: bitmapBits >> 3, numBlock: int(binary.BigEndian.Uint32(bstream[4+int(x):])), footerAddr: int(x), width: int(bstream[8+int(x)])} + // return &roaringBitmapPostings{bs: bstream[4:], numBlock: int(binary.BigEndian.Uint32(bstream[4+int(x):])), footerAddr: int(x), width: int(bstream[8+int(x)])} + // return &roaringBitmapPostings{bs: bstream[4:], numBlock: (len(bstream)-int(x))/4 - 1, footerAddr: int(x)} + return &roaringBitmapPostings{bs: bstream[4:], numBlock: (len(bstream)-int(x)-5)/int(bstream[4+int(x)]), footerAddr: int(x), width: int(bstream[4+int(x)]), addrMask: uint32((1<<(8*uint(bstream[4+int(x)])))-1)} } func (it *roaringBitmapPostings) At() uint64 { @@ -1284,39 +1325,32 @@ func (it *roaringBitmapPostings) Next() bool { if it.inside { // Already entered the block. if it.blockType == 0 { // Type array. if it.idx < it.nextBlock { - it.cur = 0 - for i := 0; i < it.valueSize; i++ { - it.cur = (it.cur << 8) + uint64(it.bs[it.idx+i]) - } - it.idx += it.valueSize - it.cur += uint64(it.key) + it.cur = uint64(it.key) | uint64(it.bs[it.idx]) + it.idx += 1 return true } } else { // Type bitmap. - for it.idx1 < it.bitmapSize { - if it.bs[it.idx+it.idx1] == byte(0) { - it.idx1 += 1 - continue - } - for it.idx1 < it.bitmapSize { - if it.bs[it.idx+it.idx1]&(1<= curVal + }) + if j == num { + // The first element in next block should be >= x. + it.idx = it.nextBlock + it.inside = false + return it.Next() + } + + it.cur = uint64(it.key) | uint64(it.bs[it.idx+j]) + it.idx += j + 1 + return true + } else { + // If encoding with bitmap, go to the exact location of value of x. + it.idx1 = int(curVal >> 3) + it.idx2 = int(curVal % 8) + if it.bs[it.idx+it.idx1]&rbpMasks[it.idx2] != 0 { // Found x. + it.cur = uint64(it.key) | uint64(it.idx1*8+it.idx2) + it.idx2 += 1 + if it.idx2 == 8 { + it.idx1 += 1 + it.idx2 = 0 + } + return true + } else { + it.idx2 += 1 + if it.idx2 == 8 { + it.idx1 += 1 + it.idx2 = 0 + } + return it.Next() + } + } +} + func (it *roaringBitmapPostings) Seek(x uint64) bool { if it.cur >= x { return true } curKey := uint32(x) >> bitmapBits - i := sort.Search(it.numBlock-it.blockIdx, func(i int) bool { - off := int(it.readBits((it.footerAddr+5)*8+(it.blockIdx+i)*it.width)) - x, _ := binary.Uvarint(it.bs[off:]) - return uint32(x) > curKey - }) - if i > 0 { - i -= 1 - if i > 0 { + if it.inside && it.key >> bitmapBits == curKey { + // Fast path. + return it.seekInBlock(x) + } else { + i := sort.Search(it.numBlock-it.blockIdx, func(i int) bool { + // off := int(it.readBits(((it.footerAddr+5)<<3)+(it.blockIdx+i)*it.width)) + // off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+4*(it.blockIdx+i):])) + // off := it.readBytes(it.footerAddr+1+(it.blockIdx+i)*it.width) + off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-4+it.width:])&it.addrMask) + k, _ := binary.Uvarint(it.bs[off:]) + return uint32(k) >= curKey + // return binary.BigEndian.Uint32(it.bs[off:]) > curKey + }) + if i == it.numBlock-it.blockIdx { + return false + } + if i != 0 { // i > 0. it.idx1 = 0 it.idx2 = 0 it.inside = false // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+4*(it.blockIdx+i):])) - it.idx = int(it.readBits((it.footerAddr+5)*8+(it.blockIdx+i)*it.width)) + // it.idx = int(it.readBits(((it.footerAddr+5)<<3)+(it.blockIdx+i)*it.width)) + // it.idx = it.readBytes(it.footerAddr+1+(it.blockIdx+i)*it.width) + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-4+it.width:])&it.addrMask) } + it.blockIdx += i } - it.blockIdx += i - if it.Next() { - if it.cur >= x { - return true - } - if it.blockType == 0 { - // If encoding with array, binary search. - num := (it.nextBlock - it.idx) / it.valueSize - j := sort.Search(num, func(i int) bool { - var temp uint64 - for j := 0; j < it.valueSize; j++ { - temp = (temp << 8) + uint64(it.bs[it.idx+j+i*it.valueSize]) - } - temp += uint64(it.key) - return temp >= x - }) - it.cur = 0 - for i := 0; i < it.valueSize; i++ { - it.cur = (it.cur<<8) + uint64(it.bs[it.idx+i+j*it.valueSize]) - } - it.cur += uint64(it.key) - it.idx += (j + 1) * it.valueSize - if j == num { - // The first element in next block should be >= x. - return it.Next() - } - return true + + val, size := binary.Uvarint(it.bs[it.idx:]) + // If the key of current block doesn't match, directly go to the next block. + if uint32(val) != curKey { + if it.blockIdx == it.numBlock-1 { + it.idx = it.footerAddr + return false } else { - // If encoding with bitmap, loop next. - for it.Next() { - if it.cur >= x { - return true + it.blockIdx += 1 + // it.idx = int(it.readBits((it.footerAddr+5)*8+it.blockIdx*it.width)) + // it.idx = it.readBytes(it.footerAddr+1+it.blockIdx*it.width) + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-4+it.width:])&it.addrMask) + // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+it.blockIdx*4:])) + val, size := binary.Uvarint(it.bs[it.idx:]) + it.key = uint32(val) << bitmapBits + it.idx += size + it.blockType = it.bs[it.idx] + it.idx += 1 + it.inside = true + if it.blockType == 0 { + if it.blockIdx != it.numBlock-1 { + // it.nextBlock = int(it.readBits((it.footerAddr+5)*8+(it.blockIdx+1)*it.width)) + // it.nextBlock = it.readBytes(it.footerAddr+1+(it.blockIdx+1)*it.width) + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-4+it.width:])&it.addrMask) + // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+(it.blockIdx+1)*4:])) + } else { + it.nextBlock = it.footerAddr } } - return false + return it.Next() } - } else { - return false } + it.key = uint32(val) << bitmapBits + it.idx += size + it.blockType = it.bs[it.idx] + it.idx += 1 + it.inside = true + + if it.blockType == 0 { + if it.blockIdx != it.numBlock-1 { + // it.nextBlock = int(it.readBits((it.footerAddr+5)*8+(it.blockIdx+1)*it.width)) + // it.nextBlock = it.readBytes(it.footerAddr+1+(it.blockIdx+1)*it.width) + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-4+it.width:])&it.addrMask) + // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+(it.blockIdx+1)*4:])) + } else { + it.nextBlock = it.footerAddr + } + } + return it.seekInBlock(x) } func (it *roaringBitmapPostings) Err() error { @@ -1417,6 +1520,14 @@ func (it *roaringBitmapPostings) Err() error { // return key // } +func (it *roaringBitmapPostings) readBytes(off int) int { + val := 0 + for i := 0; i < it.width; i ++ { + val = (val << 8) | int(it.bs[off+i]) + } + return val +} + func (it *roaringBitmapPostings) readByte(idx int, count uint8) byte { if count == 0 { return it.bs[idx] @@ -1428,7 +1539,7 @@ func (it *roaringBitmapPostings) readByte(idx int, count uint8) byte { } func (it *roaringBitmapPostings) readBits(offset int) uint64 { - idx := offset / 8 + idx := offset >> 3 count := uint8(offset % 8) nbits := it.width var u uint64 @@ -1484,6 +1595,12 @@ func writeRoaringBitmapBlock(e *encoding.Encbuf, vals []int, c []byte, key uint3 } } +func putBytes(e *encoding.Encbuf, val uint32, width int) { + for i := width - 1; i >= 0; i-- { + e.PutByte(byte((val>>(8*uint(i))&0xff))) + } +} + func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { key := uint32(0xffffffff) // The initial key should be unique. bitmapSize := 1 << (bitmapBits - 3) // Bitmap size in bytes. @@ -1523,9 +1640,18 @@ func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { // key 0 will result in o width. width += 1 } - e.PutBE32(uint32(len(startingOffs))) // Number of blocks. - e.PutByte(byte(width)) + // e.PutBE32(uint32(len(startingOffs))) // Number of blocks. + // e.PutByte(byte(width)) + // for _, off := range startingOffs { + // e.PutBits(uint64(off - 4 - uint32(startOff)), width) + // } + + e.PutByte(byte((width+7)/8)) for _, off := range startingOffs { - e.PutBits(uint64(off - 4 - uint32(startOff)), width) + putBytes(e, off - 4 - uint32(startOff), (width+7)/8) } + + // for _, off := range startingOffs { + // e.PutBE32(off - 4 - uint32(startOff)) + // } } diff --git a/index/postings_test.go b/index/postings_test.go index bacaa22d..0803fed8 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -1100,7 +1100,7 @@ func BenchmarkPostings(b *testing.B) { ls := make([]uint32, num) ls[0] = 2 for i := 1; i < num; i++ { - ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 + ls[i] = ls[i-1] + uint32(rand.Int31n(15)) + 2 } // bigEndianPostings. @@ -1151,43 +1151,64 @@ func BenchmarkPostings(b *testing.B) { ls[1000], ls[1000], true, }, { - ls[2000], ls[2000], true, + ls[1001], ls[1001], true, + }, + { + ls[2000]+1, ls[2001], true, }, { ls[3000], ls[3000], true, }, { - ls[4000], ls[4000], true, + ls[3001], ls[3001], true, + }, + { + ls[4000]+1, ls[4001], true, }, { ls[5000], ls[5000], true, }, { - ls[6000], ls[6000], true, + ls[5001], ls[5001], true, + }, + { + ls[6000]+1, ls[6001], true, }, { ls[10000], ls[10000], true, }, { - ls[20000], ls[20000], true, + ls[10001], ls[10001], true, + }, + { + ls[20000]+1, ls[20001], true, }, { ls[30000], ls[30000], true, }, { - ls[40000], ls[40000], true, + ls[30001], ls[30001], true, + }, + { + ls[40000]+1, ls[40001], true, }, { ls[50000], ls[50000], true, }, { - ls[60000], ls[60000], true, + ls[50001], ls[50001], true, + }, + { + ls[60000]+1, ls[60001], true, }, { ls[70000], ls[70000], true, }, { - ls[80000], ls[80000], true, + ls[70001], ls[70001], true, + }, + { + ls[80000]+1, ls[80001], true, }, { ls[99999], ls[99999], true, @@ -1211,34 +1232,34 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, bep.Err() == nil, "") } }) - b.Run("baseDeltaIteration", func(bench *testing.B) { - bench.ResetTimer() - bench.ReportAllocs() - for j := 0; j < bench.N; j++ { - bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) - - for i := 0; i < num; i++ { - testutil.Assert(bench, bdp.Next() == true, "") - testutil.Equals(bench, uint64(ls[i]), bdp.At()) - } - testutil.Assert(bench, bdp.Next() == false, "") - testutil.Assert(bench, bdp.Err() == nil, "") - } - }) - b.Run("deltaBlockIteration", func(bench *testing.B) { - bench.ResetTimer() - bench.ReportAllocs() - for j := 0; j < bench.N; j++ { - dbp := newDeltaBlockPostings(bufDB.Get(), len(ls)) - - for i := 0; i < num; i++ { - testutil.Assert(bench, dbp.Next() == true, "") - testutil.Equals(bench, uint64(ls[i]), dbp.At()) - } - testutil.Assert(bench, dbp.Next() == false, "") - testutil.Assert(bench, dbp.Err() == nil, "") - } - }) + // b.Run("baseDeltaIteration", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) + + // for i := 0; i < num; i++ { + // testutil.Assert(bench, bdp.Next() == true, "") + // testutil.Equals(bench, uint64(ls[i]), bdp.At()) + // } + // testutil.Assert(bench, bdp.Next() == false, "") + // testutil.Assert(bench, bdp.Err() == nil, "") + // } + // }) + // b.Run("deltaBlockIteration", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // dbp := newDeltaBlockPostings(bufDB.Get(), len(ls)) + + // for i := 0; i < num; i++ { + // testutil.Assert(bench, dbp.Next() == true, "") + // testutil.Equals(bench, uint64(ls[i]), dbp.At()) + // } + // testutil.Assert(bench, dbp.Next() == false, "") + // testutil.Assert(bench, dbp.Err() == nil, "") + // } + // }) b.Run("baseDeltaBlockIteration", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() @@ -1253,20 +1274,20 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, bdbp.Err() == nil, "") } }) - b.Run("bitmapPostingsIteration", func(bench *testing.B) { - bench.ResetTimer() - bench.ReportAllocs() - for j := 0; j < bench.N; j++ { - bm := newBitmapPostings(bufBM.Get()) - - for i := 0; i < num; i++ { - testutil.Assert(bench, bm.Next() == true, "") - testutil.Equals(bench, uint64(ls[i]), bm.At()) - } - testutil.Assert(bench, bm.Next() == false, "") - testutil.Assert(bench, bm.Err() == nil, "") - } - }) + // b.Run("bitmapPostingsIteration", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // bm := newBitmapPostings(bufBM.Get()) + + // for i := 0; i < num; i++ { + // testutil.Assert(bench, bm.Next() == true, "") + // testutil.Equals(bench, uint64(ls[i]), bm.At()) + // } + // testutil.Assert(bench, bm.Next() == false, "") + // testutil.Assert(bench, bm.Err() == nil, "") + // } + // }) b.Run("roaringBitmapPostingsIteration", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() @@ -1295,32 +1316,32 @@ func BenchmarkPostings(b *testing.B) { } } }) - b.Run("baseDeltaSeek", func(bench *testing.B) { - bench.ResetTimer() - bench.ReportAllocs() - for j := 0; j < bench.N; j++ { - bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) - - for _, v := range table { - testutil.Equals(bench, v.found, bdp.Seek(uint64(v.seek))) - testutil.Equals(bench, uint64(v.val), bdp.At()) - testutil.Assert(bench, bdp.Err() == nil, "") - } - } - }) - b.Run("deltaBlockSeek", func(bench *testing.B) { - bench.ResetTimer() - bench.ReportAllocs() - for j := 0; j < bench.N; j++ { - dbp := newDeltaBlockPostings(bufDB.Get(), len(ls)) - - for _, v := range table { - testutil.Equals(bench, v.found, dbp.Seek(uint64(v.seek))) - testutil.Equals(bench, uint64(v.val), dbp.At()) - testutil.Assert(bench, dbp.Err() == nil, "") - } - } - }) + // b.Run("baseDeltaSeek", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) + + // for _, v := range table { + // testutil.Equals(bench, v.found, bdp.Seek(uint64(v.seek))) + // testutil.Equals(bench, uint64(v.val), bdp.At()) + // testutil.Assert(bench, bdp.Err() == nil, "") + // } + // } + // }) + // b.Run("deltaBlockSeek", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // dbp := newDeltaBlockPostings(bufDB.Get(), len(ls)) + + // for _, v := range table { + // testutil.Equals(bench, v.found, dbp.Seek(uint64(v.seek))) + // testutil.Equals(bench, uint64(v.val), dbp.At()) + // testutil.Assert(bench, dbp.Err() == nil, "") + // } + // } + // }) b.Run("baseDeltaBlockSeek", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() @@ -1334,19 +1355,19 @@ func BenchmarkPostings(b *testing.B) { } } }) - b.Run("bitmapPostingsSeek", func(bench *testing.B) { - bench.ResetTimer() - bench.ReportAllocs() - for j := 0; j < bench.N; j++ { - bm := newBitmapPostings(bufBM.Get()) - - for _, v := range table { - testutil.Equals(bench, v.found, bm.Seek(uint64(v.seek))) - testutil.Equals(bench, uint64(v.val), bm.At()) - testutil.Assert(bench, bm.Err() == nil, "") - } - } - }) + // b.Run("bitmapPostingsSeek", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // bm := newBitmapPostings(bufBM.Get()) + + // for _, v := range table { + // testutil.Equals(bench, v.found, bm.Seek(uint64(v.seek))) + // testutil.Equals(bench, uint64(v.val), bm.At()) + // testutil.Assert(bench, bm.Err() == nil, "") + // } + // } + // }) b.Run("roaringBitmapPostingsSeek", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() From ce55654b9f9d732d6019821d5040a78200f7dfc0 Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Tue, 25 Jun 2019 21:59:27 +0800 Subject: [PATCH 09/18] add new baseDeltaPostings Signed-off-by: naivewong <867245430@qq.com> --- index/index.go | 11 +++++-- index/postings.go | 47 ++++++++++++++++++++---------- index/postings_test.go | 66 +++++++++++++++++++++++------------------- 3 files changed, 76 insertions(+), 48 deletions(-) diff --git a/index/index.go b/index/index.go index ab06f7c5..d42d59bd 100644 --- a/index/index.go +++ b/index/index.go @@ -536,10 +536,15 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { // The base. w.buf2.PutUvarint32(refs[0]) // The width. - width := bits.Len32(uint32(refs[len(refs)-1] - refs[0])) + width := (bits.Len32(refs[len(refs)-1] - refs[0]) + 7) >> 3 + if width == 0 { + width = 1 + } w.buf2.PutByte(byte(width)) - for _, r := range refs { - w.buf2.PutBits(uint64(r-refs[0]), width) + for i := 0; i < len(refs); i++ { + for j := width - 1; j >= 0; j-- { + w.buf2.B = append(w.buf2.B, byte(((refs[i]-refs[0])>>(8*uint(j))&0xff))) + } } case 3: writeDeltaBlockPostings(&w.buf2, refs) diff --git a/index/postings.go b/index/postings.go index be11071f..cca383b5 100644 --- a/index/postings.go +++ b/index/postings.go @@ -693,7 +693,7 @@ func (it *bigEndianPostings) Err() error { } // 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock, 4 is baseDeltaBlock, 5 is bitmapPostings, 6 is roaringBitmapPostings. -const postingsType = 6 +const postingsType = 2 type bitSlice struct { bstream []byte @@ -747,15 +747,26 @@ func (bs *bitSlice) readBits(offset int) uint64 { // │ num <4b> │ base │ width <1b> │ delta 1 │ ... │ delta n │ // └──────────┴────────────────┴────────────┴────────────────┴─────┴────────────────┘ type baseDeltaPostings struct { - bs bitSlice - base uint32 - size int - idx int - cur uint64 + bs []byte + width int + base uint32 + size int + idx int + cur uint64 + mask uint32 + prel int } func newBaseDeltaPostings(bstream []byte, base uint32, width int, size int) *baseDeltaPostings { - return &baseDeltaPostings{bs: bitSlice{bstream: bstream, width: width}, base: base, size: size, cur: uint64(base)} + return &baseDeltaPostings{bs: bstream, width: width, base: base, size: size, cur: uint64(base), mask: (uint32(1) << (uint32(width)<<3)) - 1, prel: 4 - width} +} + +func (it *baseDeltaPostings) readBytes(off int) int { + val := 0 + for i := 0; i < it.width; i ++ { + val = (val << 8) | int(it.bs[off+i]) + } + return val } func (it *baseDeltaPostings) At() uint64 { @@ -763,9 +774,14 @@ func (it *baseDeltaPostings) At() uint64 { } func (it *baseDeltaPostings) Next() bool { - if it.size > it.idx { - it.cur = it.bs.readBits(it.idx*it.bs.width) + uint64(it.base) - it.idx += 1 + if it.idx < it.size*it.width { + if it.idx-it.prel < 0 { + it.cur = uint64(it.readBytes(it.idx)) + uint64(it.base) + it.idx += it.width + return true + } + it.cur = uint64(binary.BigEndian.Uint32(it.bs[it.idx-it.prel:])&it.mask) + uint64(it.base) + it.idx += it.width return true } return false @@ -776,18 +792,19 @@ func (it *baseDeltaPostings) Seek(x uint64) bool { return true } - num := it.size - it.idx + num := it.size - it.idx / it.width // Do binary search between current position and end. x -= uint64(it.base) i := sort.Search(num, func(i int) bool { - return it.bs.readBits((i+it.idx)*it.bs.width) >= x + return uint64(binary.BigEndian.Uint32(it.bs[it.idx+i*it.width-it.prel:])&it.mask) >= x }) if i < num { - it.cur = it.bs.readBits((i+it.idx)*it.bs.width) + uint64(it.base) - it.idx += i + it.idx += i*it.width + it.cur = uint64(it.base) + uint64(binary.BigEndian.Uint32(it.bs[it.idx-it.prel:])&it.mask) + it.idx += it.width return true } - it.idx += i + it.idx += i*it.width return false } diff --git a/index/postings_test.go b/index/postings_test.go index 0803fed8..69f8db78 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -729,10 +729,12 @@ func TestBaseDeltaPostings(t *testing.T) { ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 } - width := bits.Len32(ls[len(ls)-1] - ls[0]) + width := (bits.Len32(ls[len(ls)-1] - ls[0]) + 7) >> 3 buf := encoding.Encbuf{} for i := 0; i < num; i++ { - buf.PutBits(uint64(ls[i]-ls[0]), width) + for j := width - 1; j >= 0; j-- { + buf.B = append(buf.B, byte(((ls[i]-ls[0])>>(8*uint(j))&0xff))) + } } // t.Log("(baseDeltaPostings) len of 1000 number = ", len(buf.Get())) @@ -1100,7 +1102,8 @@ func BenchmarkPostings(b *testing.B) { ls := make([]uint32, num) ls[0] = 2 for i := 1; i < num; i++ { - ls[i] = ls[i-1] + uint32(rand.Int31n(15)) + 2 + ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 + // ls[i] = ls[i-1] + 2 } // bigEndianPostings. @@ -1112,10 +1115,13 @@ func BenchmarkPostings(b *testing.B) { b.Log("bigEndianPostings size =", len(bufBE)) // baseDeltaPostings. - width := bits.Len32(ls[len(ls)-1] - ls[0]) + width := (bits.Len32(ls[len(ls)-1] - ls[0]) + 7) >> 3 bufBD := encoding.Encbuf{} for i := 0; i < num; i++ { - bufBD.PutBits(uint64(ls[i]-ls[0]), width) + for j := width - 1; j >= 0; j-- { + bufBD.B = append(bufBD.B, byte(((ls[i]-ls[0])>>(8*uint(j))&0xff))) + } + // bufBD.PutBits(uint64(ls[i]-ls[0]), width) } b.Log("baseDeltaPostings size =", len(bufBD.Get())) @@ -1232,20 +1238,20 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, bep.Err() == nil, "") } }) - // b.Run("baseDeltaIteration", func(bench *testing.B) { - // bench.ResetTimer() - // bench.ReportAllocs() - // for j := 0; j < bench.N; j++ { - // bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) + b.Run("baseDeltaIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) - // for i := 0; i < num; i++ { - // testutil.Assert(bench, bdp.Next() == true, "") - // testutil.Equals(bench, uint64(ls[i]), bdp.At()) - // } - // testutil.Assert(bench, bdp.Next() == false, "") - // testutil.Assert(bench, bdp.Err() == nil, "") - // } - // }) + for i := 0; i < num; i++ { + testutil.Assert(bench, bdp.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), bdp.At()) + } + testutil.Assert(bench, bdp.Next() == false, "") + testutil.Assert(bench, bdp.Err() == nil, "") + } + }) // b.Run("deltaBlockIteration", func(bench *testing.B) { // bench.ResetTimer() // bench.ReportAllocs() @@ -1316,19 +1322,19 @@ func BenchmarkPostings(b *testing.B) { } } }) - // b.Run("baseDeltaSeek", func(bench *testing.B) { - // bench.ResetTimer() - // bench.ReportAllocs() - // for j := 0; j < bench.N; j++ { - // bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) + b.Run("baseDeltaSeek", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) - // for _, v := range table { - // testutil.Equals(bench, v.found, bdp.Seek(uint64(v.seek))) - // testutil.Equals(bench, uint64(v.val), bdp.At()) - // testutil.Assert(bench, bdp.Err() == nil, "") - // } - // } - // }) + for _, v := range table { + testutil.Equals(bench, v.found, bdp.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), bdp.At()) + testutil.Assert(bench, bdp.Err() == nil, "") + } + } + }) // b.Run("deltaBlockSeek", func(bench *testing.B) { // bench.ResetTimer() // bench.ReportAllocs() From b3f2b5e2d101e0a8a5030d03caa7c027e3f135ba Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Fri, 28 Jun 2019 13:48:10 +0800 Subject: [PATCH 10/18] improve baseDeltaBlockPostings Signed-off-by: naivewong <867245430@qq.com> --- index/index.go | 2 +- index/postings.go | 229 +++++++++++++++++++++++------------------ index/postings_test.go | 46 ++++++--- 3 files changed, 157 insertions(+), 120 deletions(-) diff --git a/index/index.go b/index/index.go index d42d59bd..b0a8e06f 100644 --- a/index/index.go +++ b/index/index.go @@ -1082,7 +1082,7 @@ func (dec *Decoder) Postings(b []byte) (int, Postings, error) { return n, newDeltaBlockPostings(l, n), d.Err() case 4: l := d.Get() - return n, newBaseDeltaBlockPostings(l, n), d.Err() + return n, newBaseDeltaBlockPostings(l), d.Err() case 5: l := d.Get() return n, newBitmapPostings(l), d.Err() diff --git a/index/postings.go b/index/postings.go index cca383b5..5de3eb8e 100644 --- a/index/postings.go +++ b/index/postings.go @@ -14,6 +14,8 @@ package index import ( + // "time" + // "fmt" "container/heap" "encoding/binary" "math/bits" @@ -693,7 +695,7 @@ func (it *bigEndianPostings) Err() error { } // 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock, 4 is baseDeltaBlock, 5 is bitmapPostings, 6 is roaringBitmapPostings. -const postingsType = 2 +const postingsType = 4 type bitSlice struct { bstream []byte @@ -812,7 +814,8 @@ func (it *baseDeltaPostings) Err() error { return nil } -const deltaBlockSize = 256 +const deltaBlockSize = 32 +const deltaBlockBits = 5 // Block format(delta is to the previous value). // ┌────────────────┬───────────────┬─────────────────┬────────────┬────────────────┬─────┬────────────────┐ @@ -970,29 +973,23 @@ func writeDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { } // Block format(delta is to the base). -// ┌────────────────┬───────────────┬─────────────────┬────────────┬────────────────┬─────┬────────────────┐ -// │ base │ idx │ count │ width <1b> │ delta 1 │ ... │ delta n │ -// └────────────────┴───────────────┴─────────────────┴────────────┴────────────────┴─────┴────────────────┘ +// ┌────────────────┬─────────────────┬────────────┬─────────────────┬─────┬─────────────────┐ +// │ base │ count │ width <1b> │ delta 1 │ ... │ delta n │ +// └────────────────┴─────────────────┴────────────┴─────────────────┴─────┴─────────────────┘ type baseDeltaBlockPostings struct { bs bitSlice - size int count int // count in current block. idxBlock int idx int offset int // offset in bit. cur uint64 base uint64 + mask uint32 + prel int } -func newBaseDeltaBlockPostings(bstream []byte, size int) *baseDeltaBlockPostings { - return &baseDeltaBlockPostings{bs: bitSlice{bstream: bstream}, size: size} -} - -func (it *baseDeltaBlockPostings) GetOff() int { - return it.offset -} -func (it *baseDeltaBlockPostings) GetWidth() int { - return it.bs.width +func newBaseDeltaBlockPostings(bstream []byte) *baseDeltaBlockPostings { + return &baseDeltaBlockPostings{bs: bitSlice{bstream: bstream}} } func (it *baseDeltaBlockPostings) At() uint64 { @@ -1000,41 +997,31 @@ func (it *baseDeltaBlockPostings) At() uint64 { } func (it *baseDeltaBlockPostings) Next() bool { - if it.offset >= len(it.bs.bstream)<<3 || it.idx >= it.size { + if it.offset >= len(it.bs.bstream) { return false } - if it.offset%(deltaBlockSize<<3) == 0 { - val, n := binary.Uvarint(it.bs.bstream[it.offset>>3:]) - if n < 1 { - return false - } + if it.offset%deltaBlockSize == 0 { + val, n := binary.Uvarint(it.bs.bstream[it.offset:]) it.cur = val it.base = val - it.offset += n << 3 - val, n = binary.Uvarint(it.bs.bstream[it.offset>>3:]) - if n < 1 { - return false - } - it.idx = int(val) + 1 - it.offset += n << 3 - val, n = binary.Uvarint(it.bs.bstream[it.offset>>3:]) - if n < 1 { - return false - } + it.offset += n + + val, n = binary.Uvarint(it.bs.bstream[it.offset:]) it.count = int(val) - it.offset += n << 3 - it.bs.width = int(it.bs.bstream[it.offset>>3]) - it.offset += 8 + it.offset += n + it.bs.width = int(it.bs.bstream[it.offset]) + it.mask = (uint32(1) << uint(8 * it.bs.width)) - 1 + it.prel = 4 - it.bs.width + it.offset += 1 it.idxBlock = 1 return true } - it.cur = it.bs.readBits(it.offset) + it.base + it.cur = uint64(binary.BigEndian.Uint32(it.bs.bstream[it.offset-it.prel:])&it.mask) + it.base it.offset += it.bs.width - it.idx += 1 it.idxBlock += 1 if it.idxBlock == it.count { - it.offset = ((it.offset-1)/(deltaBlockSize<<3) + 1) * deltaBlockSize << 3 + it.offset = (((it.offset-1)>>deltaBlockBits) + 1) << deltaBlockBits } return true } @@ -1043,69 +1030,109 @@ func (it *baseDeltaBlockPostings) Seek(x uint64) bool { if it.cur >= x { return true } - - startOff := (it.offset - 1) / (deltaBlockSize << 3) * deltaBlockSize - num := (len(it.bs.bstream)-1)/deltaBlockSize - (it.offset-1)/(deltaBlockSize<<3) + 1 - // Do binary search between current position and end. - i := sort.Search(num, func(i int) bool { - val, _ := binary.Uvarint(it.bs.bstream[startOff+i*deltaBlockSize:]) - return val > x - }) - if i > 0 { - // Go to the previous block because the previous block - // may contain the first value >= x. - i -= 1 + if it.offset >= len(it.bs.bstream) { + return false + } + startOff := (((it.offset)>>deltaBlockBits)+1)<>deltaBlockBits) - (startOff>>deltaBlockBits) + 1 + if num > 0 { + // Fast path to check if the binary search among blocks is needed. + val, _ := binary.Uvarint(it.bs.bstream[startOff:]) + if val <= x { + // Do binary search between current position and end. + i := sort.Search(num, func(i int) bool { + val, _ := binary.Uvarint(it.bs.bstream[startOff+(i< x + }) + if i > 0 { + // Go to the previous block because the previous block + // may contain the first value >= x. + i -= 1 + } + it.offset = startOff + (i<= temp + }) + if j < it.count-it.idxBlock { + it.offset += j * it.bs.width + it.cur = uint64(binary.BigEndian.Uint32(it.bs.bstream[it.offset-it.prel:])&it.mask) + it.base + it.idxBlock += j + 1 + if it.idxBlock == it.count { + // it.offset = startOff + ((i+1)<>deltaBlockBits)+i+1)<>deltaBlockBits)+i+1)< 0 { + // Search in current block. + startOff -= deltaBlockSize + if it.offset == startOff { + // Read base, and width. + val, n := binary.Uvarint(it.bs.bstream[it.offset:]) + it.cur = val + it.base = val + it.offset += n + val, n = binary.Uvarint(it.bs.bstream[it.offset:]) + it.count = int(val) + it.offset += n + it.bs.width = int(it.bs.bstream[it.offset]) + it.mask = (uint32(1) << uint(8 * it.bs.width)) - 1 + it.prel = 4 - it.bs.width + it.offset += 1 + it.idxBlock = 1 + } + if x <= it.base { + return true + } else { temp := x - it.base j := sort.Search(it.count-it.idxBlock, func(i int) bool { - return it.bs.readBits(it.offset+i*it.bs.width) >= temp + return uint64(binary.BigEndian.Uint32(it.bs.bstream[it.offset+i*it.bs.width-it.prel:])&it.mask) >= temp }) - if j < it.count-it.idxBlock { it.offset += j * it.bs.width - it.cur = it.bs.readBits(it.offset) + it.base - it.offset += it.bs.width + it.cur = uint64(binary.BigEndian.Uint32(it.bs.bstream[it.offset-it.prel:])&it.mask) + it.base it.idxBlock += j + 1 - it.idx += j + 1 if it.idxBlock == it.count { - it.offset = ((it.offset-1)/(deltaBlockSize<<3) + 1) * deltaBlockSize << 3 + // it.offset = startOff + deltaBlockSize + it.offset = ((startOff>>deltaBlockBits)+1)<>deltaBlockBits)+1)<= temp - }) - - if j < it.count-it.idxBlock { - it.offset += j * it.bs.width - it.cur = it.bs.readBits(it.offset) + it.base - it.offset += it.bs.width - it.idxBlock += j + 1 - it.idx += j + 1 - if it.idxBlock == it.count { - it.offset = ((it.offset-1)/(deltaBlockSize<<3) + 1) * deltaBlockSize << 3 - } - } else { - it.offset = (startOff + (i+1)*deltaBlockSize) << 3 - return it.Next() - } - return true - } } + } func (it *baseDeltaBlockPostings) Err() error { @@ -1121,16 +1148,18 @@ func writeBaseDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { var max int for i < len(arr) { e.PutUvarint32(arr[i]) // Put base. - e.PutUvarint64(uint64(i)) // Put idx. - remaining = (deltaBlockSize - (len(e.B)-startLen)%deltaBlockSize - 1) << 3 + remaining = deltaBlockSize - (len(e.B)-startLen)%deltaBlockSize - 1 deltas = deltas[:0] base = arr[i] max = -1 i += 1 for i < len(arr) { delta := arr[i] - base - cur := bits.Len32(delta) - if remaining-cur*(len(deltas)+1)-(((bits.Len(uint(len(deltas)))>>3)+1)<<3) >= 0 { + cur := (bits.Len32(delta) + 7) >> 3 + if cur == 0 { + cur = 1 + } + if remaining-cur*(len(deltas)+1)-((bits.Len(uint(len(deltas)))>>3)+1) >= 0 { deltas = append(deltas, delta) max = cur } else { @@ -1140,9 +1169,11 @@ func writeBaseDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { } e.PutUvarint64(uint64(len(deltas) + 1)) e.PutByte(byte(max)) - remaining -= ((bits.Len(uint(len(deltas))) >> 3) + 1) << 3 + remaining -= ((bits.Len(uint(len(deltas))) >> 3) + 1) for _, delta := range deltas { - e.PutBits(uint64(delta), max) + for j := max - 1; j >= 0; j-- { + e.B = append(e.B, byte((delta>>(8*uint(j))&0xff))) + } remaining -= max } @@ -1150,18 +1181,10 @@ func writeBaseDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { break } - for remaining >= 64 { - e.PutBits(uint64(0), 64) - remaining -= 64 - } - - if remaining > 0 { - e.PutBits(uint64(0), remaining) + for remaining > 0 { + e.PutByte(0) + remaining -= 1 } - e.Count = 0 - - // There can be one more extra 0. - e.B = e.B[:len(e.B)-(len(e.B)-startLen)%deltaBlockSize] } } diff --git a/index/postings_test.go b/index/postings_test.go index 69f8db78..ffa25878 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -886,12 +886,9 @@ func TestBaseDeltaBlockPostings(t *testing.T) { // t.Log("(deltaBlockPostings) len of 1000 number = ", len(buf.Get())) t.Run("Iteration", func(t *testing.T) { - dbp := newBaseDeltaBlockPostings(buf.Get(), len(ls)) + dbp := newBaseDeltaBlockPostings(buf.Get()) for i := 0; i < num; i++ { testutil.Assert(t, dbp.Next() == true, "") - if uint64(ls[i]) != dbp.At() { - t.Log(i, dbp.GetOff(), "width=", dbp.GetWidth()) - } testutil.Equals(t, uint64(ls[i]), dbp.At()) } @@ -937,9 +934,10 @@ func TestBaseDeltaBlockPostings(t *testing.T) { }, } - dbp := newBaseDeltaBlockPostings(buf.Get(), len(ls)) + dbp := newBaseDeltaBlockPostings(buf.Get()) for _, v := range table { + // fmt.Println(i) testutil.Equals(t, v.found, dbp.Seek(uint64(v.seek))) testutil.Equals(t, uint64(v.val), dbp.At()) testutil.Assert(t, dbp.Err() == nil, "") @@ -1228,7 +1226,9 @@ func BenchmarkPostings(b *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { + // bench.StopTimer() bep := newBigEndianPostings(bufBE) + // bench.StartTimer() for i := 0; i < num; i++ { testutil.Assert(bench, bep.Next() == true, "") @@ -1242,7 +1242,9 @@ func BenchmarkPostings(b *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { + // bench.StopTimer() bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) + // bench.StartTimer() for i := 0; i < num; i++ { testutil.Assert(bench, bdp.Next() == true, "") @@ -1270,7 +1272,9 @@ func BenchmarkPostings(b *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { - bdbp := newBaseDeltaBlockPostings(bufBDB.Get(), len(ls)) + // bench.StopTimer() + bdbp := newBaseDeltaBlockPostings(bufBDB.Get()) + // bench.StartTimer() for i := 0; i < num; i++ { testutil.Assert(bench, bdbp.Next() == true, "") @@ -1298,7 +1302,9 @@ func BenchmarkPostings(b *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { + // bench.StopTimer() rbm := newRoaringBitmapPostings(bufRBM.Get()) + // bench.StartTimer() for i := 0; i < num; i++ { testutil.Assert(bench, rbm.Next() == true, "") @@ -1313,7 +1319,9 @@ func BenchmarkPostings(b *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { + // bench.StopTimer() bep := newBigEndianPostings(bufBE) + // bench.StartTimer() for _, v := range table { testutil.Equals(bench, v.found, bep.Seek(uint64(v.seek))) @@ -1326,7 +1334,9 @@ func BenchmarkPostings(b *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { + // bench.StopTimer() bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) + // bench.StartTimer() for _, v := range table { testutil.Equals(bench, v.found, bdp.Seek(uint64(v.seek))) @@ -1352,7 +1362,9 @@ func BenchmarkPostings(b *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { - bdbp := newBaseDeltaBlockPostings(bufBDB.Get(), len(ls)) + // bench.StopTimer() + bdbp := newBaseDeltaBlockPostings(bufBDB.Get()) + // bench.StartTimer() for _, v := range table { testutil.Equals(bench, v.found, bdbp.Seek(uint64(v.seek))) @@ -1378,7 +1390,9 @@ func BenchmarkPostings(b *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { + // bench.StopTimer() rbm := newRoaringBitmapPostings(bufRBM.Get()) + // bench.StartTimer() for _, v := range table { testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) @@ -1551,10 +1565,10 @@ func BenchmarkPostingsIntersect(t *testing.B) { bufBDB4 := encoding.Encbuf{} writeBaseDeltaBlockPostings(&bufBDB4, d) - i1 := newBaseDeltaBlockPostings(bufBDB1.Get(), len(a)) - i2 := newBaseDeltaBlockPostings(bufBDB2.Get(), len(b)) - i3 := newBaseDeltaBlockPostings(bufBDB3.Get(), len(c)) - i4 := newBaseDeltaBlockPostings(bufBDB4.Get(), len(d)) + i1 := newBaseDeltaBlockPostings(bufBDB1.Get()) + i2 := newBaseDeltaBlockPostings(bufBDB2.Get()) + i3 := newBaseDeltaBlockPostings(bufBDB3.Get()) + i4 := newBaseDeltaBlockPostings(bufBDB4.Get()) bench.ResetTimer() bench.ReportAllocs() @@ -1590,10 +1604,10 @@ func BenchmarkPostingsIntersect(t *testing.B) { bufBDB4 := encoding.Encbuf{} writeBaseDeltaBlockPostings(&bufBDB4, d) - i1 := newBaseDeltaBlockPostings(bufBDB1.Get(), len(a)) - i2 := newBaseDeltaBlockPostings(bufBDB2.Get(), len(b)) - i3 := newBaseDeltaBlockPostings(bufBDB3.Get(), len(c)) - i4 := newBaseDeltaBlockPostings(bufBDB4.Get(), len(d)) + i1 := newBaseDeltaBlockPostings(bufBDB1.Get()) + i2 := newBaseDeltaBlockPostings(bufBDB2.Get()) + i3 := newBaseDeltaBlockPostings(bufBDB3.Get()) + i4 := newBaseDeltaBlockPostings(bufBDB4.Get()) bench.ResetTimer() bench.ReportAllocs() @@ -1615,7 +1629,7 @@ func BenchmarkPostingsIntersect(t *testing.B) { } bufBDB := encoding.Encbuf{} writeBaseDeltaBlockPostings(&bufBDB, temp) - its = append(its, newBaseDeltaBlockPostings(bufBDB.Get(), len(temp))) + its = append(its, newBaseDeltaBlockPostings(bufBDB.Get())) } bench.ResetTimer() From 3669b4401d2155ddf8c0af7e81aa78d54a6bb85f Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Tue, 2 Jul 2019 16:58:21 +0800 Subject: [PATCH 11/18] add 64bit support for baseDeltaPostings Signed-off-by: naivewong <867245430@qq.com> --- index/index.go | 2 +- index/postings.go | 93 +++++++++++++++++++++--------------------- index/postings_test.go | 8 ++-- 3 files changed, 52 insertions(+), 51 deletions(-) diff --git a/index/index.go b/index/index.go index b0a8e06f..0015076a 100644 --- a/index/index.go +++ b/index/index.go @@ -1073,7 +1073,7 @@ func (dec *Decoder) Postings(b []byte) (int, Postings, error) { l := d.Get() return n, newBigEndianPostings(l), d.Err() case 2: - base := uint32(d.Uvarint()) + base := uint64(d.Uvarint()) width := int(d.Byte()) l := d.Get() return n, newBaseDeltaPostings(l, base, width, n), d.Err() diff --git a/index/postings.go b/index/postings.go index 5de3eb8e..f9f9df2f 100644 --- a/index/postings.go +++ b/index/postings.go @@ -751,21 +751,21 @@ func (bs *bitSlice) readBits(offset int) uint64 { type baseDeltaPostings struct { bs []byte width int - base uint32 + base uint64 size int idx int cur uint64 - mask uint32 + mask uint64 prel int } -func newBaseDeltaPostings(bstream []byte, base uint32, width int, size int) *baseDeltaPostings { - return &baseDeltaPostings{bs: bstream, width: width, base: base, size: size, cur: uint64(base), mask: (uint32(1) << (uint32(width)<<3)) - 1, prel: 4 - width} +func newBaseDeltaPostings(bstream []byte, base uint64, width int, size int) *baseDeltaPostings { + return &baseDeltaPostings{bs: bstream, width: width, base: base, size: size, cur: uint64(base), mask: (uint64(1) << (uint64(width) << 3)) - 1, prel: 8 - width} } func (it *baseDeltaPostings) readBytes(off int) int { val := 0 - for i := 0; i < it.width; i ++ { + for i := 0; i < it.width; i++ { val = (val << 8) | int(it.bs[off+i]) } return val @@ -778,11 +778,11 @@ func (it *baseDeltaPostings) At() uint64 { func (it *baseDeltaPostings) Next() bool { if it.idx < it.size*it.width { if it.idx-it.prel < 0 { - it.cur = uint64(it.readBytes(it.idx)) + uint64(it.base) + it.cur = uint64(it.readBytes(it.idx)) + it.base it.idx += it.width return true } - it.cur = uint64(binary.BigEndian.Uint32(it.bs[it.idx-it.prel:])&it.mask) + uint64(it.base) + it.cur = binary.BigEndian.Uint64(it.bs[it.idx-it.prel:])&it.mask + it.base it.idx += it.width return true } @@ -794,15 +794,15 @@ func (it *baseDeltaPostings) Seek(x uint64) bool { return true } - num := it.size - it.idx / it.width + num := it.size - it.idx/it.width // Do binary search between current position and end. - x -= uint64(it.base) + x -= it.base i := sort.Search(num, func(i int) bool { - return uint64(binary.BigEndian.Uint32(it.bs[it.idx+i*it.width-it.prel:])&it.mask) >= x + return binary.BigEndian.Uint64(it.bs[it.idx+i*it.width-it.prel:])&it.mask >= x }) if i < num { - it.idx += i*it.width - it.cur = uint64(it.base) + uint64(binary.BigEndian.Uint32(it.bs[it.idx-it.prel:])&it.mask) + it.idx += i * it.width + it.cur = it.base + (binary.BigEndian.Uint64(it.bs[it.idx-it.prel:])&it.mask) it.idx += it.width return true } @@ -814,8 +814,8 @@ func (it *baseDeltaPostings) Err() error { return nil } -const deltaBlockSize = 32 -const deltaBlockBits = 5 +const deltaBlockSize = 4096 +const deltaBlockBits = 12 // Block format(delta is to the previous value). // ┌────────────────┬───────────────┬─────────────────┬────────────┬────────────────┬─────┬────────────────┐ @@ -1021,7 +1021,7 @@ func (it *baseDeltaBlockPostings) Next() bool { it.offset += it.bs.width it.idxBlock += 1 if it.idxBlock == it.count { - it.offset = (((it.offset-1)>>deltaBlockBits) + 1) << deltaBlockBits + it.offset = (((it.offset - 1) >> deltaBlockBits) + 1) << deltaBlockBits } return true } @@ -1033,8 +1033,8 @@ func (it *baseDeltaBlockPostings) Seek(x uint64) bool { if it.offset >= len(it.bs.bstream) { return false } - startOff := (((it.offset)>>deltaBlockBits)+1)<>deltaBlockBits) - (startOff>>deltaBlockBits) + 1 + startOff := (((it.offset) >> deltaBlockBits) + 1) << deltaBlockBits + num := (len(it.bs.bstream) >> deltaBlockBits) - (startOff >> deltaBlockBits) + 1 if num > 0 { // Fast path to check if the binary search among blocks is needed. val, _ := binary.Uvarint(it.bs.bstream[startOff:]) @@ -1049,7 +1049,7 @@ func (it *baseDeltaBlockPostings) Seek(x uint64) bool { // may contain the first value >= x. i -= 1 } - it.offset = startOff + (i<>deltaBlockBits)+i+1)<> deltaBlockBits) + i + 1) << deltaBlockBits } else { it.offset += it.bs.width } } else { // it.offset = startOff + ((i+1)<>deltaBlockBits)+i+1)<> deltaBlockBits) + i + 1) << deltaBlockBits return it.Next() } return true @@ -1103,7 +1103,7 @@ func (it *baseDeltaBlockPostings) Seek(x uint64) bool { it.count = int(val) it.offset += n it.bs.width = int(it.bs.bstream[it.offset]) - it.mask = (uint32(1) << uint(8 * it.bs.width)) - 1 + it.mask = (uint32(1) << uint(8*it.bs.width)) - 1 it.prel = 4 - it.bs.width it.offset += 1 it.idxBlock = 1 @@ -1121,13 +1121,13 @@ func (it *baseDeltaBlockPostings) Seek(x uint64) bool { it.idxBlock += j + 1 if it.idxBlock == it.count { // it.offset = startOff + deltaBlockSize - it.offset = ((startOff>>deltaBlockBits)+1)<> deltaBlockBits) + 1) << deltaBlockBits } else { it.offset += it.bs.width } } else { // it.offset = startOff + deltaBlockSize - it.offset = ((startOff>>deltaBlockBits)+1)<> deltaBlockBits) + 1) << deltaBlockBits return it.Next() } return true @@ -1147,7 +1147,7 @@ func writeBaseDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { var base uint32 var max int for i < len(arr) { - e.PutUvarint32(arr[i]) // Put base. + e.PutUvarint32(arr[i]) // Put base. remaining = deltaBlockSize - (len(e.B)-startLen)%deltaBlockSize - 1 deltas = deltas[:0] base = arr[i] @@ -1172,7 +1172,7 @@ func writeBaseDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { remaining -= ((bits.Len(uint(len(deltas))) >> 3) + 1) for _, delta := range deltas { for j := max - 1; j >= 0; j-- { - e.B = append(e.B, byte((delta>>(8*uint(j))&0xff))) + e.B = append(e.B, byte((delta >> (8 * uint(j)) & 0xff))) } remaining -= max } @@ -1313,6 +1313,7 @@ var rbpMasks []byte var rbpValueMask uint32 var rbpValueSize int var rbpBitmapSize int + func init() { for i := 7; i >= 0; i-- { rbpMasks = append(rbpMasks, byte(1<> bitmapBits - if it.inside && it.key >> bitmapBits == curKey { + if it.inside && it.key>>bitmapBits == curKey { // Fast path. return it.seekInBlock(x) } else { @@ -1477,7 +1478,7 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { // off := int(it.readBits(((it.footerAddr+5)<<3)+(it.blockIdx+i)*it.width)) // off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+4*(it.blockIdx+i):])) // off := it.readBytes(it.footerAddr+1+(it.blockIdx+i)*it.width) - off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-4+it.width:])&it.addrMask) + off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-4+it.width:]) & it.addrMask) k, _ := binary.Uvarint(it.bs[off:]) return uint32(k) >= curKey // return binary.BigEndian.Uint32(it.bs[off:]) > curKey @@ -1492,7 +1493,7 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+4*(it.blockIdx+i):])) // it.idx = int(it.readBits(((it.footerAddr+5)<<3)+(it.blockIdx+i)*it.width)) // it.idx = it.readBytes(it.footerAddr+1+(it.blockIdx+i)*it.width) - it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-4+it.width:])&it.addrMask) + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-4+it.width:]) & it.addrMask) } it.blockIdx += i } @@ -1507,7 +1508,7 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { it.blockIdx += 1 // it.idx = int(it.readBits((it.footerAddr+5)*8+it.blockIdx*it.width)) // it.idx = it.readBytes(it.footerAddr+1+it.blockIdx*it.width) - it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-4+it.width:])&it.addrMask) + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-4+it.width:]) & it.addrMask) // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+it.blockIdx*4:])) val, size := binary.Uvarint(it.bs[it.idx:]) it.key = uint32(val) << bitmapBits @@ -1519,7 +1520,7 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { if it.blockIdx != it.numBlock-1 { // it.nextBlock = int(it.readBits((it.footerAddr+5)*8+(it.blockIdx+1)*it.width)) // it.nextBlock = it.readBytes(it.footerAddr+1+(it.blockIdx+1)*it.width) - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-4+it.width:])&it.addrMask) + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-4+it.width:]) & it.addrMask) // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+(it.blockIdx+1)*4:])) } else { it.nextBlock = it.footerAddr @@ -1538,7 +1539,7 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { if it.blockIdx != it.numBlock-1 { // it.nextBlock = int(it.readBits((it.footerAddr+5)*8+(it.blockIdx+1)*it.width)) // it.nextBlock = it.readBytes(it.footerAddr+1+(it.blockIdx+1)*it.width) - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-4+it.width:])&it.addrMask) + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-4+it.width:]) & it.addrMask) // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+(it.blockIdx+1)*4:])) } else { it.nextBlock = it.footerAddr @@ -1562,7 +1563,7 @@ func (it *roaringBitmapPostings) Err() error { func (it *roaringBitmapPostings) readBytes(off int) int { val := 0 - for i := 0; i < it.width; i ++ { + for i := 0; i < it.width; i++ { val = (val << 8) | int(it.bs[off+i]) } return val @@ -1610,8 +1611,8 @@ func (it *roaringBitmapPostings) readBits(offset int) uint64 { func writeRoaringBitmapBlock(e *encoding.Encbuf, vals []int, c []byte, key uint32, thres int, bitmapSize int, valueSize int) { var offset int // The starting offset of the bitmap of each block. - var idx1 int // The offset in the bitmap in current block in bytes. - var idx2 int // The offset in the current byte in the bitmap ([0,8)). + var idx1 int // The offset in the bitmap in current block in bytes. + var idx2 int // The offset in the current byte in the bitmap ([0,8)). e.PutUvarint32(key) if len(vals) > thres { e.PutByte(byte(1)) @@ -1637,27 +1638,27 @@ func writeRoaringBitmapBlock(e *encoding.Encbuf, vals []int, c []byte, key uint3 func putBytes(e *encoding.Encbuf, val uint32, width int) { for i := width - 1; i >= 0; i-- { - e.PutByte(byte((val>>(8*uint(i))&0xff))) + e.PutByte(byte((val >> (8 * uint(i)) & 0xff))) } } func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { - key := uint32(0xffffffff) // The initial key should be unique. - bitmapSize := 1 << (bitmapBits - 3) // Bitmap size in bytes. - valueSize := bitmapBits >> 3 // The size of the element in array in bytes. - thres := (1 << bitmapBits) / bitmapBits // Threshold of number of elements in the block for choosing encoding type. + key := uint32(0xffffffff) // The initial key should be unique. + bitmapSize := 1 << (bitmapBits - 3) // Bitmap size in bytes. + valueSize := bitmapBits >> 3 // The size of the element in array in bytes. + thres := (1 << bitmapBits) / bitmapBits // Threshold of number of elements in the block for choosing encoding type. mask := uint32((1 << uint(bitmapBits)) - 1) // Mask for the elements in the block. var curKey uint32 var curVal uint32 - var idx int // Index of current element in arr. + var idx int // Index of current element in arr. var startingOffs []uint32 // The starting offsets of each block. - var vals []int // The converted values in the current block. + var vals []int // The converted values in the current block. c := make([]byte, 4) startOff := len(e.Get()) e.PutBE32(0) // Footer starting offset. for idx < len(arr) { curKey = arr[idx] >> bitmapBits // Key of block. - curVal = arr[idx] & mask // Value inside block. + curVal = arr[idx] & mask // Value inside block. if curKey != key { // Move to next block. if idx != 0 { @@ -1686,9 +1687,9 @@ func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { // e.PutBits(uint64(off - 4 - uint32(startOff)), width) // } - e.PutByte(byte((width+7)/8)) + e.PutByte(byte((width + 7) / 8)) for _, off := range startingOffs { - putBytes(e, off - 4 - uint32(startOff), (width+7)/8) + putBytes(e, off-4-uint32(startOff), (width+7)/8) } // for _, off := range startingOffs { diff --git a/index/postings_test.go b/index/postings_test.go index ffa25878..3fe7e5a2 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -739,7 +739,7 @@ func TestBaseDeltaPostings(t *testing.T) { // t.Log("(baseDeltaPostings) len of 1000 number = ", len(buf.Get())) t.Run("Iteration", func(t *testing.T) { - bdp := newBaseDeltaPostings(buf.Get(), ls[0], width, len(ls)) + bdp := newBaseDeltaPostings(buf.Get(), uint64(ls[0]), width, len(ls)) for i := 0; i < num; i++ { testutil.Assert(t, bdp.Next() == true, "") testutil.Equals(t, uint64(ls[i]), bdp.At()) @@ -787,7 +787,7 @@ func TestBaseDeltaPostings(t *testing.T) { }, } - bdp := newBaseDeltaPostings(buf.Get(), ls[0], width, len(ls)) + bdp := newBaseDeltaPostings(buf.Get(), uint64(ls[0]), width, len(ls)) for _, v := range table { testutil.Equals(t, v.found, bdp.Seek(uint64(v.seek))) @@ -1243,7 +1243,7 @@ func BenchmarkPostings(b *testing.B) { bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) + bdp := newBaseDeltaPostings(bufBD.Get(), uint64(ls[0]), width, len(ls)) // bench.StartTimer() for i := 0; i < num; i++ { @@ -1335,7 +1335,7 @@ func BenchmarkPostings(b *testing.B) { bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - bdp := newBaseDeltaPostings(bufBD.Get(), ls[0], width, len(ls)) + bdp := newBaseDeltaPostings(bufBD.Get(), uint64(ls[0]), width, len(ls)) // bench.StartTimer() for _, v := range table { From 430064a2375369698fd6193a538ad8a4cb1c6665 Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Wed, 3 Jul 2019 21:18:01 +0800 Subject: [PATCH 12/18] add 64bit support for baseDeltaBlockPostings Signed-off-by: naivewong <867245430@qq.com> --- index/postings.go | 115 +++++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 31 deletions(-) diff --git a/index/postings.go b/index/postings.go index f9f9df2f..73d438bc 100644 --- a/index/postings.go +++ b/index/postings.go @@ -695,7 +695,7 @@ func (it *bigEndianPostings) Err() error { } // 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock, 4 is baseDeltaBlock, 5 is bitmapPostings, 6 is roaringBitmapPostings. -const postingsType = 4 +const postingsType = 2 type bitSlice struct { bstream []byte @@ -763,30 +763,25 @@ func newBaseDeltaPostings(bstream []byte, base uint64, width int, size int) *bas return &baseDeltaPostings{bs: bstream, width: width, base: base, size: size, cur: uint64(base), mask: (uint64(1) << (uint64(width) << 3)) - 1, prel: 8 - width} } -func (it *baseDeltaPostings) readBytes(off int) int { - val := 0 - for i := 0; i < it.width; i++ { - val = (val << 8) | int(it.bs[off+i]) - } - return val -} - func (it *baseDeltaPostings) At() uint64 { return it.cur } func (it *baseDeltaPostings) Next() bool { - if it.idx < it.size*it.width { - if it.idx-it.prel < 0 { - it.cur = uint64(it.readBytes(it.idx)) + it.base - it.idx += it.width - return true - } + if it.idx >= it.size*it.width { + return false + } + if it.idx-it.prel >= 0 { it.cur = binary.BigEndian.Uint64(it.bs[it.idx-it.prel:])&it.mask + it.base - it.idx += it.width - return true + } else { + it.cur = 0 + for i := 0; i < it.width; i++ { + it.cur = (it.cur << 8) | uint64(it.bs[it.idx+i]) + } + it.cur += it.base } - return false + it.idx += it.width + return true } func (it *baseDeltaPostings) Seek(x uint64) bool { @@ -984,7 +979,7 @@ type baseDeltaBlockPostings struct { offset int // offset in bit. cur uint64 base uint64 - mask uint32 + mask uint64 prel int } @@ -1010,14 +1005,23 @@ func (it *baseDeltaBlockPostings) Next() bool { it.count = int(val) it.offset += n it.bs.width = int(it.bs.bstream[it.offset]) - it.mask = (uint32(1) << uint(8 * it.bs.width)) - 1 - it.prel = 4 - it.bs.width + it.mask = (uint64(1) << uint(8 * it.bs.width)) - 1 + it.prel = 8 - it.bs.width it.offset += 1 it.idxBlock = 1 return true } - it.cur = uint64(binary.BigEndian.Uint32(it.bs.bstream[it.offset-it.prel:])&it.mask) + it.base + if it.offset-it.prel >= 0 { + it.cur = binary.BigEndian.Uint64(it.bs.bstream[it.offset-it.prel:])&it.mask + it.base + } else { + it.cur = 0 + for i := 0; i < it.bs.width; i++ { + it.cur = (it.cur << 8) | uint64(it.bs.bstream[it.offset+i]) + } + it.cur += it.base + } + // it.cur = (binary.BigEndian.Uint64(it.bs.bstream[it.offset-it.prel:])&it.mask) + it.base it.offset += it.bs.width it.idxBlock += 1 if it.idxBlock == it.count { @@ -1060,8 +1064,8 @@ func (it *baseDeltaBlockPostings) Seek(x uint64) bool { it.count = int(val) it.offset += n it.bs.width = int(it.bs.bstream[it.offset]) - it.mask = (uint32(1) << uint(8 * it.bs.width)) - 1 - it.prel = 4 - it.bs.width + it.mask = (uint64(1) << uint(8 * it.bs.width)) - 1 + it.prel = 8 - it.bs.width it.offset += 1 it.idxBlock = 1 if x <= it.base { @@ -1069,11 +1073,11 @@ func (it *baseDeltaBlockPostings) Seek(x uint64) bool { } else { temp := x - it.base j := sort.Search(it.count-it.idxBlock, func(i int) bool { - return uint64(binary.BigEndian.Uint32(it.bs.bstream[it.offset+i*it.bs.width-it.prel:])&it.mask) >= temp + return (binary.BigEndian.Uint64(it.bs.bstream[it.offset+i*it.bs.width-it.prel:])&it.mask) >= temp }) if j < it.count-it.idxBlock { it.offset += j * it.bs.width - it.cur = uint64(binary.BigEndian.Uint32(it.bs.bstream[it.offset-it.prel:])&it.mask) + it.base + it.cur = (binary.BigEndian.Uint64(it.bs.bstream[it.offset-it.prel:])&it.mask) + it.base it.idxBlock += j + 1 if it.idxBlock == it.count { // it.offset = startOff + ((i+1)<= temp + return (binary.BigEndian.Uint64(it.bs.bstream[it.offset+i*it.bs.width-it.prel:])&it.mask) >= temp }) if j < it.count-it.idxBlock { it.offset += j * it.bs.width - it.cur = uint64(binary.BigEndian.Uint32(it.bs.bstream[it.offset-it.prel:])&it.mask) + it.base + it.cur = (binary.BigEndian.Uint64(it.bs.bstream[it.offset-it.prel:])&it.mask) + it.base it.idxBlock += j + 1 if it.idxBlock == it.count { // it.offset = startOff + deltaBlockSize @@ -1172,7 +1176,56 @@ func writeBaseDeltaBlockPostings(e *encoding.Encbuf, arr []uint32) { remaining -= ((bits.Len(uint(len(deltas))) >> 3) + 1) for _, delta := range deltas { for j := max - 1; j >= 0; j-- { - e.B = append(e.B, byte((delta >> (8 * uint(j)) & 0xff))) + e.B = append(e.B, byte((delta >> (uint(j) << 3) & 0xff))) + } + remaining -= max + } + + if i == len(arr) { + break + } + + for remaining > 0 { + e.PutByte(0) + remaining -= 1 + } + } +} + +func writeBaseDeltaBlockPostings64(e *encoding.Encbuf, arr []uint64) { + i := 0 + startLen := len(e.B) + deltas := []uint64{} + var remaining int + var base uint64 + var max int + for i < len(arr) { + e.PutUvarint64(arr[i]) // Put base. + remaining = deltaBlockSize - (len(e.B)-startLen)%deltaBlockSize - 1 + deltas = deltas[:0] + base = arr[i] + max = -1 + i += 1 + for i < len(arr) { + delta := arr[i] - base + cur := (bits.Len64(delta) + 7) >> 3 + if cur == 0 { + cur = 1 + } + if remaining-cur*(len(deltas)+1)-((bits.Len(uint(len(deltas)))>>3)+1) >= 0 { + deltas = append(deltas, delta) + max = cur + } else { + break + } + i += 1 + } + e.PutUvarint64(uint64(len(deltas) + 1)) + e.PutByte(byte(max)) + remaining -= ((bits.Len(uint(len(deltas))) >> 3) + 1) + for _, delta := range deltas { + for j := max - 1; j >= 0; j-- { + e.B = append(e.B, byte((delta >> (uint(j) << 3) & 0xff))) } remaining -= max } From 95f3c7983aaeb6c5cfb96e9852333f15aa3bc495 Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Thu, 4 Jul 2019 11:33:13 +0800 Subject: [PATCH 13/18] add 64bit support for roaringBitmapPostings Signed-off-by: naivewong <867245430@qq.com> --- index/index.go | 3 + index/postings.go | 150 +++++++++++++++++++++++++++++------------ index/postings_test.go | 81 ++++++++++++++++++++++ 3 files changed, 192 insertions(+), 42 deletions(-) diff --git a/index/index.go b/index/index.go index 0015076a..60aa9d74 100644 --- a/index/index.go +++ b/index/index.go @@ -541,6 +541,9 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { width = 1 } w.buf2.PutByte(byte(width)) + for i := 0; i < 8 - width; i++ { + w.buf2.PutByte(0) + } for i := 0; i < len(refs); i++ { for j := width - 1; j >= 0; j-- { w.buf2.B = append(w.buf2.B, byte(((refs[i]-refs[0])>>(8*uint(j))&0xff))) diff --git a/index/postings.go b/index/postings.go index 73d438bc..4f8ef0a6 100644 --- a/index/postings.go +++ b/index/postings.go @@ -754,13 +754,14 @@ type baseDeltaPostings struct { base uint64 size int idx int + i int cur uint64 mask uint64 prel int } func newBaseDeltaPostings(bstream []byte, base uint64, width int, size int) *baseDeltaPostings { - return &baseDeltaPostings{bs: bstream, width: width, base: base, size: size, cur: uint64(base), mask: (uint64(1) << (uint64(width) << 3)) - 1, prel: 8 - width} + return &baseDeltaPostings{bs: bstream, width: width, base: base, size: size, idx: 8 - width, cur: uint64(base), mask: (uint64(1) << (uint64(width) << 3)) - 1, prel: 8 - width} } func (it *baseDeltaPostings) At() uint64 { @@ -768,19 +769,12 @@ func (it *baseDeltaPostings) At() uint64 { } func (it *baseDeltaPostings) Next() bool { - if it.idx >= it.size*it.width { + if it.i >= it.size { return false } - if it.idx-it.prel >= 0 { - it.cur = binary.BigEndian.Uint64(it.bs[it.idx-it.prel:])&it.mask + it.base - } else { - it.cur = 0 - for i := 0; i < it.width; i++ { - it.cur = (it.cur << 8) | uint64(it.bs[it.idx+i]) - } - it.cur += it.base - } + it.cur = binary.BigEndian.Uint64(it.bs[it.idx-it.prel:])&it.mask + it.base it.idx += it.width + it.i += 1 return true } @@ -789,8 +783,7 @@ func (it *baseDeltaPostings) Seek(x uint64) bool { return true } - num := it.size - it.idx/it.width - // Do binary search between current position and end. + num := it.size - it.i x -= it.base i := sort.Search(num, func(i int) bool { return binary.BigEndian.Uint64(it.bs[it.idx+i*it.width-it.prel:])&it.mask >= x @@ -799,9 +792,9 @@ func (it *baseDeltaPostings) Seek(x uint64) bool { it.idx += i * it.width it.cur = it.base + (binary.BigEndian.Uint64(it.bs[it.idx-it.prel:])&it.mask) it.idx += it.width + it.i += i + 1 return true } - it.idx += i*it.width return false } @@ -809,8 +802,8 @@ func (it *baseDeltaPostings) Err() error { return nil } -const deltaBlockSize = 4096 -const deltaBlockBits = 12 +const deltaBlockSize = 32 +const deltaBlockBits = 5 // Block format(delta is to the previous value). // ┌────────────────┬───────────────┬─────────────────┬────────────┬────────────────┬─────┬────────────────┐ @@ -1363,7 +1356,7 @@ func writeBitmapPostings(e *encoding.Encbuf, arr []uint32) { } var rbpMasks []byte -var rbpValueMask uint32 +var rbpValueMask uint64 var rbpValueSize int var rbpBitmapSize int @@ -1371,7 +1364,7 @@ func init() { for i := 7; i >= 0; i-- { rbpMasks = append(rbpMasks, byte(1<> 3 } @@ -1392,7 +1385,7 @@ type roaringBitmapPostings struct { idx1 int // The offset in the bitmap in current block in bytes. idx2 int // The offset in the current byte in the bitmap ([0,8)). footerAddr int - key uint32 + key uint64 numBlock int blockIdx int blockType byte @@ -1419,7 +1412,7 @@ func (it *roaringBitmapPostings) Next() bool { if it.inside { // Already entered the block. if it.blockType == 0 { // Type array. if it.idx < it.nextBlock { - it.cur = uint64(it.key) | uint64(it.bs[it.idx]) + it.cur = it.key | uint64(it.bs[it.idx]) it.idx += 1 return true } @@ -1429,7 +1422,7 @@ func (it *roaringBitmapPostings) Next() bool { } for it.idx1 < rbpBitmapSize { if it.bs[it.idx+it.idx1]&rbpMasks[it.idx2] != 0 { - it.cur = uint64(it.key) | uint64((it.idx1<<3)+it.idx2) + it.cur = it.key | uint64((it.idx1<<3)+it.idx2) it.idx2 += 1 if it.idx2 == 8 { it.idx1 += 1 @@ -1454,7 +1447,7 @@ func (it *roaringBitmapPostings) Next() bool { } else { // Not yet entered the block. if it.idx < it.footerAddr { val, size := binary.Uvarint(it.bs[it.idx:]) - it.key = uint32(val) << bitmapBits + it.key = val << bitmapBits it.idx += size it.blockType = it.bs[it.idx] it.idx += 1 @@ -1478,7 +1471,7 @@ func (it *roaringBitmapPostings) Next() bool { } func (it *roaringBitmapPostings) seekInBlock(x uint64) bool { - curVal := byte(uint32(x) & rbpValueMask) + curVal := byte(x & rbpValueMask) if it.blockType == 0 { // If encoding with array, binary search. num := (it.nextBlock - it.idx) @@ -1492,7 +1485,7 @@ func (it *roaringBitmapPostings) seekInBlock(x uint64) bool { return it.Next() } - it.cur = uint64(it.key) | uint64(it.bs[it.idx+j]) + it.cur = it.key | uint64(it.bs[it.idx+j]) it.idx += j + 1 return true } else { @@ -1500,7 +1493,7 @@ func (it *roaringBitmapPostings) seekInBlock(x uint64) bool { it.idx1 = int(curVal >> 3) it.idx2 = int(curVal % 8) if it.bs[it.idx+it.idx1]&rbpMasks[it.idx2] != 0 { // Found x. - it.cur = uint64(it.key) | uint64(it.idx1*8+it.idx2) + it.cur = it.key | uint64(it.idx1*8+it.idx2) it.idx2 += 1 if it.idx2 == 8 { it.idx1 += 1 @@ -1522,7 +1515,7 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { if it.cur >= x { return true } - curKey := uint32(x) >> bitmapBits + curKey := x >> bitmapBits if it.inside && it.key>>bitmapBits == curKey { // Fast path. return it.seekInBlock(x) @@ -1533,7 +1526,7 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { // off := it.readBytes(it.footerAddr+1+(it.blockIdx+i)*it.width) off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-4+it.width:]) & it.addrMask) k, _ := binary.Uvarint(it.bs[off:]) - return uint32(k) >= curKey + return k >= curKey // return binary.BigEndian.Uint32(it.bs[off:]) > curKey }) if i == it.numBlock-it.blockIdx { @@ -1553,7 +1546,7 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { val, size := binary.Uvarint(it.bs[it.idx:]) // If the key of current block doesn't match, directly go to the next block. - if uint32(val) != curKey { + if val != curKey { if it.blockIdx == it.numBlock-1 { it.idx = it.footerAddr return false @@ -1564,7 +1557,7 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-4+it.width:]) & it.addrMask) // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+it.blockIdx*4:])) val, size := binary.Uvarint(it.bs[it.idx:]) - it.key = uint32(val) << bitmapBits + it.key = val << bitmapBits it.idx += size it.blockType = it.bs[it.idx] it.idx += 1 @@ -1582,7 +1575,7 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { return it.Next() } } - it.key = uint32(val) << bitmapBits + it.key = val << bitmapBits it.idx += size it.blockType = it.bs[it.idx] it.idx += 1 @@ -1662,10 +1655,10 @@ func (it *roaringBitmapPostings) readBits(offset int) uint64 { return u } -func writeRoaringBitmapBlock(e *encoding.Encbuf, vals []int, c []byte, key uint32, thres int, bitmapSize int, valueSize int) { - var offset int // The starting offset of the bitmap of each block. - var idx1 int // The offset in the bitmap in current block in bytes. - var idx2 int // The offset in the current byte in the bitmap ([0,8)). +func writeRoaringBitmapBlock(e *encoding.Encbuf, vals []uint32, key uint32, thres int, bitmapSize int, valueSize int) { + var offset int // The starting offset of the bitmap of each block. + var idx1 uint32 // The offset in the bitmap in current block in bytes. + var idx2 uint32 // The offset in the current byte in the bitmap ([0,8)). e.PutUvarint32(key) if len(vals) > thres { e.PutByte(byte(1)) @@ -1676,12 +1669,13 @@ func writeRoaringBitmapBlock(e *encoding.Encbuf, vals []int, c []byte, key uint3 for _, val := range vals { idx1 = val >> 3 idx2 = val % 8 - e.B[offset+idx1] |= 1 << uint(7-idx2) + e.B[uint32(offset)+idx1] |= 1 << uint(7-idx2) } } else { + c := make([]byte, 4) e.PutByte(byte(0)) for _, val := range vals { - binary.BigEndian.PutUint32(c[:], uint32(val)) + binary.BigEndian.PutUint32(c[:], val) for i := 4 - valueSize; i < 4; i++ { e.PutByte(c[i]) } @@ -1689,6 +1683,34 @@ func writeRoaringBitmapBlock(e *encoding.Encbuf, vals []int, c []byte, key uint3 } } +func writeRoaringBitmapBlock64(e *encoding.Encbuf, vals []uint64, key uint64, thres int, bitmapSize int, valueSize int) { + var offset int // The starting offset of the bitmap of each block. + var idx1 uint64 // The offset in the bitmap in current block in bytes. + var idx2 uint64 // The offset in the current byte in the bitmap ([0,8)). + e.PutUvarint64(key) + if len(vals) > thres { + e.PutByte(byte(1)) + offset = len(e.Get()) + for i := 0; i < bitmapSize; i++ { + e.PutByte(byte(0)) + } + for _, val := range vals { + idx1 = val >> 3 + idx2 = val % 8 + e.B[uint64(offset)+idx1] |= 1 << uint(7-idx2) + } + } else { + c := make([]byte, 8) + e.PutByte(byte(0)) + for _, val := range vals { + binary.BigEndian.PutUint64(c[:], val) + for i := 8 - valueSize; i < 8; i++ { + e.PutByte(c[i]) + } + } + } +} + func putBytes(e *encoding.Encbuf, val uint32, width int) { for i := width - 1; i >= 0; i-- { e.PutByte(byte((val >> (8 * uint(i)) & 0xff))) @@ -1705,8 +1727,7 @@ func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { var curVal uint32 var idx int // Index of current element in arr. var startingOffs []uint32 // The starting offsets of each block. - var vals []int // The converted values in the current block. - c := make([]byte, 4) + var vals []uint32 // The converted values in the current block. startOff := len(e.Get()) e.PutBE32(0) // Footer starting offset. for idx < len(arr) { @@ -1716,22 +1737,22 @@ func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { // Move to next block. if idx != 0 { startingOffs = append(startingOffs, uint32(len(e.B))) - writeRoaringBitmapBlock(e, vals, c, key, thres, bitmapSize, valueSize) + writeRoaringBitmapBlock(e, vals, key, thres, bitmapSize, valueSize) vals = vals[:0] } key = curKey } - vals = append(vals, int(curVal)) + vals = append(vals, curVal) idx += 1 } startingOffs = append(startingOffs, uint32(len(e.B))) - writeRoaringBitmapBlock(e, vals, c, key, thres, bitmapSize, valueSize) + writeRoaringBitmapBlock(e, vals, key, thres, bitmapSize, valueSize) // Put footer starting offset. binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-4-startOff)) width := bits.Len32(startingOffs[len(startingOffs)-1] - 4 - uint32(startOff)) if width == 0 { - // key 0 will result in o width. + // key 0 will result in 0 width. width += 1 } // e.PutBE32(uint32(len(startingOffs))) // Number of blocks. @@ -1749,3 +1770,48 @@ func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { // e.PutBE32(off - 4 - uint32(startOff)) // } } + +func writeRoaringBitmapPostings64(e *encoding.Encbuf, arr []uint64) { + key := uint64(0xffffffffffffffff) // The initial key should be unique. + bitmapSize := 1 << (bitmapBits - 3) // Bitmap size in bytes. + valueSize := bitmapBits >> 3 // The size of the element in array in bytes. + thres := (1 << bitmapBits) / bitmapBits // Threshold of number of elements in the block for choosing encoding type. + mask := (uint64(1) << uint(bitmapBits)) - 1 // Mask for the elements in the block. + var curKey uint64 + var curVal uint64 + var idx int // Index of current element in arr. + var startingOffs []uint32 // The starting offsets of each block. + var vals []uint64 // The converted values in the current block. + startOff := len(e.Get()) + e.PutBE32(0) // Footer starting offset. + for idx < len(arr) { + curKey = arr[idx] >> bitmapBits // Key of block. + curVal = arr[idx] & mask // Value inside block. + if curKey != key { + // Move to next block. + if idx != 0 { + startingOffs = append(startingOffs, uint32(len(e.B))) + writeRoaringBitmapBlock64(e, vals, key, thres, bitmapSize, valueSize) + vals = vals[:0] + } + key = curKey + } + vals = append(vals, curVal) + idx += 1 + } + startingOffs = append(startingOffs, uint32(len(e.B))) + writeRoaringBitmapBlock64(e, vals, key, thres, bitmapSize, valueSize) + + // Put footer starting offset. + binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-4-startOff)) + width := bits.Len32(startingOffs[len(startingOffs)-1] - 4 - uint32(startOff)) + if width == 0 { + // key 0 will result in 0 width. + width += 1 + } + + e.PutByte(byte((width + 7) / 8)) + for _, off := range startingOffs { + putBytes(e, off-4-uint32(startOff), (width+7)/8) + } +} diff --git a/index/postings_test.go b/index/postings_test.go index 3fe7e5a2..6875bb11 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -731,6 +731,9 @@ func TestBaseDeltaPostings(t *testing.T) { width := (bits.Len32(ls[len(ls)-1] - ls[0]) + 7) >> 3 buf := encoding.Encbuf{} + for i := 0; i < 8 - width; i ++ { + buf.B = append(buf.B, 0) + } for i := 0; i < num; i++ { for j := width - 1; j >= 0; j-- { buf.B = append(buf.B, byte(((ls[i]-ls[0])>>(8*uint(j))&0xff))) @@ -1094,6 +1097,81 @@ func TestRoaringBitmapPostings(t *testing.T) { }) } +func TestRoaringBitmapPostings64(t *testing.T) { + num := 1000 + // mock a list as postings + ls := make([]uint64, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint64(rand.Int63n(15)) + 2 + // ls[i] = ls[i-1] + 10 + } + + buf := encoding.Encbuf{} + writeRoaringBitmapPostings64(&buf, ls) + // t.Log("len", len(buf.Get())) + + t.Run("Iteration", func(t *testing.T) { + rbp := newRoaringBitmapPostings(buf.Get()) + for i := 0; i < num; i++ { + testutil.Assert(t, rbp.Next() == true, "") + // t.Log("ls[i] =", ls[i], "rbp.At() =", rbp.At()) + testutil.Equals(t, ls[i], rbp.At()) + } + + testutil.Assert(t, rbp.Next() == false, "") + testutil.Assert(t, rbp.Err() == nil, "") + }) + + t.Run("Seek", func(t *testing.T) { + table := []struct { + seek uint64 + val uint64 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[4], ls[4], true, + }, + { + ls[500] - 1, ls[500], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[0], ls[601], true, + }, + { + ls[600], ls[601], true, + }, + { + ls[999], ls[999], true, + }, + { + ls[999] + 10, ls[999], false, + }, + } + + rbp := newRoaringBitmapPostings(buf.Get()) + + for _, v := range table { + // t.Log("i", i) + testutil.Equals(t, v.found, rbp.Seek(v.seek)) + testutil.Equals(t, v.val, rbp.At()) + testutil.Assert(t, rbp.Err() == nil, "") + } + }) +} + func BenchmarkPostings(b *testing.B) { num := 100000 // mock a list as postings @@ -1115,6 +1193,9 @@ func BenchmarkPostings(b *testing.B) { // baseDeltaPostings. width := (bits.Len32(ls[len(ls)-1] - ls[0]) + 7) >> 3 bufBD := encoding.Encbuf{} + for i := 0; i < 8 - width; i ++ { + bufBD.B = append(bufBD.B, 0) + } for i := 0; i < num; i++ { for j := width - 1; j >= 0; j-- { bufBD.B = append(bufBD.B, byte(((ls[i]-ls[0])>>(8*uint(j))&0xff))) From 55f5e6ff9d4f11ab00748193f8571bab7b205b6c Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Mon, 8 Jul 2019 23:56:11 +0800 Subject: [PATCH 14/18] add baseDeltaBlock8Postings & baseDeltaBlock16Postings(with detailed comments) Signed-off-by: naivewong <867245430@qq.com> --- index/index.go | 5 + index/postings.go | 432 ++++++++++++++++++++++++++++- index/postings_test.go | 602 +++++++++++++++-------------------------- 3 files changed, 644 insertions(+), 395 deletions(-) diff --git a/index/index.go b/index/index.go index 60aa9d74..1a93a195 100644 --- a/index/index.go +++ b/index/index.go @@ -566,6 +566,8 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { // w.buf2.PutByte(1) // writeRoaringBitmapPostings(&w.buf2, refs) // } + case 7: + writeBaseDeltaBlock16Postings(&w.buf2, refs) } w.uint32s = refs @@ -1099,6 +1101,9 @@ func (dec *Decoder) Postings(b []byte) (int, Postings, error) { // } else { // return n, newRoaringBitmapPostings(l), d.Err() // } + case 7: + l := d.Get() + return n, newBaseDeltaBlock16Postings(l), d.Err() default: return n, EmptyPostings(), d.Err() } diff --git a/index/postings.go b/index/postings.go index 4f8ef0a6..b73f2f8a 100644 --- a/index/postings.go +++ b/index/postings.go @@ -695,7 +695,7 @@ func (it *bigEndianPostings) Err() error { } // 1 is bigEndian, 2 is baseDelta, 3 is deltaBlock, 4 is baseDeltaBlock, 5 is bitmapPostings, 6 is roaringBitmapPostings. -const postingsType = 2 +const postingsType = 7 type bitSlice struct { bstream []byte @@ -1401,7 +1401,8 @@ func newRoaringBitmapPostings(bstream []byte) *roaringBitmapPostings { x := binary.BigEndian.Uint32(bstream) // return &roaringBitmapPostings{bs: bstream[4:], numBlock: int(binary.BigEndian.Uint32(bstream[4+int(x):])), footerAddr: int(x), width: int(bstream[8+int(x)])} // return &roaringBitmapPostings{bs: bstream[4:], numBlock: (len(bstream)-int(x))/4 - 1, footerAddr: int(x)} - return &roaringBitmapPostings{bs: bstream[4:], numBlock: (len(bstream) - int(x) - 5) / int(bstream[4+int(x)]), footerAddr: int(x), width: int(bstream[4+int(x)]), addrMask: uint32((1 << (8 * uint(bstream[4+int(x)]))) - 1)} + // return &roaringBitmapPostings{bs: bstream[4:], numBlock: (len(bstream) - int(x) - 5) / int(bstream[4+int(x)]), footerAddr: int(x), width: int(bstream[4+int(x)]), addrMask: uint32((1 << (8 * uint(bstream[4+int(x)]))) - 1)} + return &roaringBitmapPostings{bs: bstream[8:], numBlock: int(binary.BigEndian.Uint32(bstream[4:])), footerAddr: int(x), width: int(bstream[8+int(x)]), addrMask: uint32((1 << (8 * uint(bstream[8+int(x)]))) - 1)} } func (it *roaringBitmapPostings) At() uint64 { @@ -1730,6 +1731,7 @@ func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { var vals []uint32 // The converted values in the current block. startOff := len(e.Get()) e.PutBE32(0) // Footer starting offset. + e.PutBE32(0) // Number of blocks. for idx < len(arr) { curKey = arr[idx] >> bitmapBits // Key of block. curVal = arr[idx] & mask // Value inside block. @@ -1749,8 +1751,9 @@ func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { writeRoaringBitmapBlock(e, vals, key, thres, bitmapSize, valueSize) // Put footer starting offset. - binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-4-startOff)) - width := bits.Len32(startingOffs[len(startingOffs)-1] - 4 - uint32(startOff)) + binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-8-startOff)) + binary.BigEndian.PutUint32(e.B[startOff+4:], uint32(len(startingOffs))) + width := bits.Len32(startingOffs[len(startingOffs)-1] - 8 - uint32(startOff)) if width == 0 { // key 0 will result in 0 width. width += 1 @@ -1763,7 +1766,7 @@ func writeRoaringBitmapPostings(e *encoding.Encbuf, arr []uint32) { e.PutByte(byte((width + 7) / 8)) for _, off := range startingOffs { - putBytes(e, off-4-uint32(startOff), (width+7)/8) + putBytes(e, off-8-uint32(startOff), (width+7)/8) } // for _, off := range startingOffs { @@ -1784,6 +1787,7 @@ func writeRoaringBitmapPostings64(e *encoding.Encbuf, arr []uint64) { var vals []uint64 // The converted values in the current block. startOff := len(e.Get()) e.PutBE32(0) // Footer starting offset. + e.PutBE32(0) // Number of blocks. for idx < len(arr) { curKey = arr[idx] >> bitmapBits // Key of block. curVal = arr[idx] & mask // Value inside block. @@ -1803,8 +1807,9 @@ func writeRoaringBitmapPostings64(e *encoding.Encbuf, arr []uint64) { writeRoaringBitmapBlock64(e, vals, key, thres, bitmapSize, valueSize) // Put footer starting offset. - binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-4-startOff)) - width := bits.Len32(startingOffs[len(startingOffs)-1] - 4 - uint32(startOff)) + binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-8-startOff)) + binary.BigEndian.PutUint32(e.B[startOff+4:], uint32(len(startingOffs))) + width := bits.Len32(startingOffs[len(startingOffs)-1] - 8 - uint32(startOff)) if width == 0 { // key 0 will result in 0 width. width += 1 @@ -1812,6 +1817,417 @@ func writeRoaringBitmapPostings64(e *encoding.Encbuf, arr []uint64) { e.PutByte(byte((width + 7) / 8)) for _, off := range startingOffs { - putBytes(e, off-4-uint32(startOff), (width+7)/8) + putBytes(e, off-8-uint32(startOff), (width+7)/8) + } +} + +type baseDeltaBlock8Postings struct { + bs []byte + cur uint64 + inside bool + idx int // The current offset inside the bs. + footerAddr int + key uint64 + numBlock int + blockIdx int + nextBlock int + width int + prel int + addrMask uint32 +} + +func newBaseDeltaBlock8Postings(bstream []byte) *baseDeltaBlock8Postings { + if len(bstream) <= 4 { + return nil + } + x := binary.BigEndian.Uint32(bstream) + width := int(bstream[8+int(x)]) + return &baseDeltaBlock8Postings{bs: bstream[8:], numBlock: int(binary.BigEndian.Uint32(bstream[4:])), footerAddr: int(x), width: width, prel: 4 - width, addrMask: uint32((1 << (8 * uint(width))) - 1)} +} + +func (it *baseDeltaBlock8Postings) At() uint64 { + return it.cur +} + +func (it *baseDeltaBlock8Postings) Next() bool { + if it.inside { // Already entered the block. + if it.idx < it.nextBlock { + it.cur = it.key | uint64(it.bs[it.idx]) + it.idx += 1 + return true + } + it.blockIdx += 1 + it.inside = false + return it.Next() + } else { // Not yet entered the block. + if it.idx < it.footerAddr { + val, size := binary.Uvarint(it.bs[it.idx:]) + it.key = val << bitmapBits + it.idx += size + it.inside = true + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + it.cur = it.key | uint64(it.bs[it.idx]) + it.idx += 1 + return true + } else { + return false + } + } +} + +func (it *baseDeltaBlock8Postings) seekInBlock(x uint64) bool { + curVal := byte(x & rbpValueMask) + num := it.nextBlock - it.idx + j := sort.Search(num, func(i int) bool { + return it.bs[it.idx+i] >= curVal + }) + if j == num { + // Fast-path to the next block. + // The first element in next block should be >= x. + it.idx = it.nextBlock + it.blockIdx += 1 + if it.idx < it.footerAddr { + val, size := binary.Uvarint(it.bs[it.idx:]) + it.key = val << bitmapBits + it.idx += size + it.inside = true + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + it.cur = it.key | uint64(it.bs[it.idx]) + it.idx += 1 + return true + } else { + return false + } + } + it.cur = it.key | uint64(it.bs[it.idx+j]) + it.idx += j + 1 + return true +} + +func (it *baseDeltaBlock8Postings) Seek(x uint64) bool { + if it.cur >= x { + return true + } + curKey := x >> bitmapBits + if it.inside && it.key>>bitmapBits == curKey { + // Fast path. + return it.seekInBlock(x) + } else { + i := sort.Search(it.numBlock-it.blockIdx, func(i int) bool { + off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-it.prel:]) & it.addrMask) + k, _ := binary.Uvarint(it.bs[off:]) + return k >= curKey + }) + if i == it.numBlock-it.blockIdx { + return false + } + it.blockIdx += i + if i != 0 { // i > 0. + it.inside = false + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) + } + } + val, size := binary.Uvarint(it.bs[it.idx:]) + // If the key of current block doesn't match, directly go to the next block. + if val != curKey { + if it.blockIdx == it.numBlock-1 { + return false + } else { + it.blockIdx += 1 + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) + val, size := binary.Uvarint(it.bs[it.idx:]) + it.key = val << bitmapBits + it.idx += size + it.inside = true + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + it.cur = it.key | uint64(it.bs[it.idx]) + it.idx += 1 + return true + } + } + it.key = val << bitmapBits + it.idx += size + it.inside = true + + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + return it.seekInBlock(x) +} + +func (it *baseDeltaBlock8Postings) Err() error { + return nil +} + +func writeBaseDelta8Block(e *encoding.Encbuf, vals []uint32, key uint32, valueSize int) { + e.PutUvarint32(key) + c := make([]byte, 4) + for _, val := range vals { + binary.BigEndian.PutUint32(c[:], val) + for i := 4 - valueSize; i < 4; i++ { + e.PutByte(c[i]) + } + } +} + +func writeBaseDeltaBlock8Postings(e *encoding.Encbuf, arr []uint32) { + key := uint32(0xffffffff) // The initial key should be unique. + valueSize := bitmapBits >> 3 // The size of the element in array in bytes. + mask := uint32((1 << uint(bitmapBits)) - 1) // Mask for the elements in the block. + var curKey uint32 + var curVal uint32 + var idx int // Index of current element in arr. + var startingOffs []uint32 // The starting offsets of each block. + var vals []uint32 // The converted values in the current block. + startOff := len(e.Get()) + e.PutBE32(0) // Footer starting offset. + e.PutBE32(0) // Number of blocks. + for idx < len(arr) { + curKey = arr[idx] >> bitmapBits // Key of block. + curVal = arr[idx] & mask // Value inside block. + if curKey != key { + // Move to next block. + if idx != 0 { + startingOffs = append(startingOffs, uint32(len(e.B))) + writeBaseDelta8Block(e, vals, key, valueSize) + vals = vals[:0] + } + key = curKey + } + vals = append(vals, curVal) + idx += 1 + } + startingOffs = append(startingOffs, uint32(len(e.B))) + writeBaseDelta8Block(e, vals, key, valueSize) + + // Put footer starting offset. + binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-8-startOff)) + binary.BigEndian.PutUint32(e.B[startOff+4:], uint32(len(startingOffs))) + width := bits.Len32(startingOffs[len(startingOffs)-1] - 8 - uint32(startOff)) + if width == 0 { + // key 0 will result in 0 width. + width += 1 + } + + e.PutByte(byte((width + 7) / 8)) + for _, off := range startingOffs { + putBytes(e, off-8-uint32(startOff), (width+7)/8) + } +} + +type baseDeltaBlock16Postings struct { + bs []byte + cur uint64 + inside bool + idx int // The current offset inside the bs. + footerAddr int + key uint64 + numBlock int + blockIdx int // The current block idx. + nextBlock int + width int + prel int + addrMask uint32 +} + +func newBaseDeltaBlock16Postings(bstream []byte) *baseDeltaBlock16Postings { + x := binary.BigEndian.Uint32(bstream) // Read the footer address. + width := int(bstream[8+int(x)]) + return &baseDeltaBlock16Postings{bs: bstream[8:], numBlock: int(binary.BigEndian.Uint32(bstream[4:])), footerAddr: int(x), width: width, prel: 4 - width, addrMask: uint32((1 << (8 * uint(width))) - 1)} +} + +func (it *baseDeltaBlock16Postings) At() uint64 { + return it.cur +} + +func (it *baseDeltaBlock16Postings) Next() bool { + if it.inside { // Already entered the block. + if it.idx < it.nextBlock { + it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) + it.idx += 2 + return true + } + it.blockIdx += 1 // Go to the next block. + } + // Currently not entered any block. + if it.idx < it.footerAddr { + val, size := binary.Uvarint(it.bs[it.idx:]) // Read the key. + it.key = val << 16 + it.idx += size + it.inside = true + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) + it.idx += 2 + return true + } else { + return false + } +} + +func (it *baseDeltaBlock16Postings) seekInBlock(x uint64) bool { + curVal := x & 0xffff + num := (it.nextBlock - it.idx) >> 1 + j := sort.Search(num, func(i int) bool { + return uint64(binary.BigEndian.Uint16(it.bs[it.idx+(i<<1):])) >= curVal + }) + if j == num { + // Fast-path to the next block. + // The first element in next block should be >= x. + it.idx = it.nextBlock + it.blockIdx += 1 + if it.idx < it.footerAddr { + val, size := binary.Uvarint(it.bs[it.idx:]) + it.key = val << 16 + it.idx += size + it.inside = true + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) + it.idx += 2 + return true + } else { + return false + } + } + it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx+(j<<1):])) + it.idx += (j + 1) << 1 + return true +} + +func (it *baseDeltaBlock16Postings) Seek(x uint64) bool { + if it.cur >= x { + return true + } + curKey := x >> 16 + if it.inside && it.key>>16 == curKey { + // Fast path for x in current block. + return it.seekInBlock(x) + } else { + i := sort.Search(it.numBlock-it.blockIdx, func(i int) bool { + off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-it.prel:]) & it.addrMask) + k, _ := binary.Uvarint(it.bs[off:]) + return k >= curKey + }) + if i == it.numBlock-it.blockIdx { + return false + } + it.blockIdx += i + if i != 0 { // i > 0. + it.inside = false + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) + } + } + val, size := binary.Uvarint(it.bs[it.idx:]) + // If the key of current block doesn't match, directly go to the next block + // because the first value of the next block should be >= x. + if val != curKey { + if it.blockIdx == it.numBlock-1 { + return false + } else { + it.blockIdx += 1 + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) + val, size := binary.Uvarint(it.bs[it.idx:]) + it.key = val << 16 + it.idx += size + it.inside = true + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) + it.idx += 2 + return true + } + } + it.key = val << 16 + it.idx += size + it.inside = true + + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + return it.seekInBlock(x) +} + +func (it *baseDeltaBlock16Postings) Err() error { + return nil +} + +func writeBaseDelta16Block(e *encoding.Encbuf, vals []uint32, key uint32, valueSize int) { + e.PutUvarint32(key) + c := make([]byte, 4) + for _, val := range vals { + binary.BigEndian.PutUint32(c[:], val) + for i := 4 - valueSize; i < 4; i++ { + e.PutByte(c[i]) + } + } +} + +func writeBaseDeltaBlock16Postings(e *encoding.Encbuf, arr []uint32) { + key := uint32(0xffffffff) // The initial key should be unique. + valueSize := 16 >> 3 // The size of the element in array in bytes. + mask := uint32((1 << uint(16)) - 1) // Mask for the elements in the block. + var curKey uint32 + var curVal uint32 + var idx int // Index of current element in arr. + var startingOffs []uint32 // The starting offsets of each block. + var vals []uint32 // The converted values in the current block. + startOff := len(e.Get()) + e.PutBE32(0) // Footer starting offset. + e.PutBE32(0) // Number of blocks. + for idx < len(arr) { + curKey = arr[idx] >> 16 // Key of block. + curVal = arr[idx] & mask // Value inside block. + if curKey != key { + // Move to next block. + if idx != 0 { + startingOffs = append(startingOffs, uint32(len(e.B))) + writeBaseDelta16Block(e, vals, key, valueSize) + vals = vals[:0] + } + key = curKey + } + vals = append(vals, curVal) + idx += 1 + } + startingOffs = append(startingOffs, uint32(len(e.B))) + writeBaseDelta16Block(e, vals, key, valueSize) + + binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-8-startOff)) // Put footer starting offset. + binary.BigEndian.PutUint32(e.B[startOff+4:], uint32(len(startingOffs))) // Put number of blocks. + width := bits.Len32(startingOffs[len(startingOffs)-1] - 8 - uint32(startOff)) + if width == 0 { + // key 0 will result in 0 width. + width += 1 + } + + e.PutByte(byte((width + 7) / 8)) + for _, off := range startingOffs { + putBytes(e, off-8-uint32(startOff), (width+7)/8) } } diff --git a/index/postings_test.go b/index/postings_test.go index 6875bb11..63f35cf5 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -1040,7 +1040,9 @@ func TestRoaringBitmapPostings(t *testing.T) { rbp := newRoaringBitmapPostings(buf.Get()) for i := 0; i < num; i++ { testutil.Assert(t, rbp.Next() == true, "") - // t.Log("ls[i] =", ls[i], "rbp.At() =", rbp.At()) + if uint64(ls[i]) != rbp.At() { + t.Log("ls[i] =", ls[i], "rbp.At() =", rbp.At(), " i =", i) + } testutil.Equals(t, uint64(ls[i]), rbp.At()) } @@ -1097,6 +1099,160 @@ func TestRoaringBitmapPostings(t *testing.T) { }) } +func TestBaseDeltaBlock8Postings(t *testing.T) { + num := 1000 + // mock a list as postings + ls := make([]uint32, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint32(rand.Int31n(15)) + 2 + // ls[i] = ls[i-1] + 10 + } + + buf := encoding.Encbuf{} + writeBaseDeltaBlock8Postings(&buf, ls) + // t.Log("len", len(buf.Get())) + + t.Run("Iteration", func(t *testing.T) { + rbp := newBaseDeltaBlock8Postings(buf.Get()) + for i := 0; i < num; i++ { + testutil.Assert(t, rbp.Next() == true, "") + if uint64(ls[i]) != rbp.At() { + t.Log("ls[i] =", ls[i], "rbp.At() =", rbp.At(), " i =", i) + } + testutil.Equals(t, uint64(ls[i]), rbp.At()) + } + + testutil.Assert(t, rbp.Next() == false, "") + testutil.Assert(t, rbp.Err() == nil, "") + }) + + t.Run("Seek", func(t *testing.T) { + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[4], ls[4], true, + }, + { + ls[500] - 1, ls[500], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[0], ls[601], true, + }, + { + ls[600], ls[601], true, + }, + { + ls[999], ls[999], true, + }, + { + ls[999] + 10, ls[999], false, + }, + } + + rbp := newBaseDeltaBlock8Postings(buf.Get()) + + for _, v := range table { + // t.Log("i", i) + testutil.Equals(t, v.found, rbp.Seek(uint64(v.seek))) + testutil.Equals(t, uint64(v.val), rbp.At()) + testutil.Assert(t, rbp.Err() == nil, "") + } + }) +} + +func TestBaseDeltaBlock16Postings(t *testing.T) { + num := 1000 + // mock a list as postings + ls := make([]uint32, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint32(rand.Int31n(15)) + 2 + // ls[i] = ls[i-1] + 10 + } + + buf := encoding.Encbuf{} + writeBaseDeltaBlock16Postings(&buf, ls) + // t.Log("len", len(buf.Get())) + + t.Run("Iteration", func(t *testing.T) { + rbp := newBaseDeltaBlock16Postings(buf.Get()) + for i := 0; i < num; i++ { + testutil.Assert(t, rbp.Next() == true, "") + if uint64(ls[i]) != rbp.At() { + t.Log("ls[i] =", ls[i], "rbp.At() =", rbp.At(), " i =", i) + } + testutil.Equals(t, uint64(ls[i]), rbp.At()) + } + + testutil.Assert(t, rbp.Next() == false, "") + testutil.Assert(t, rbp.Err() == nil, "") + }) + + t.Run("Seek", func(t *testing.T) { + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[4], ls[4], true, + }, + { + ls[500] - 1, ls[500], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[0], ls[601], true, + }, + { + ls[600], ls[601], true, + }, + { + ls[999], ls[999], true, + }, + { + ls[999] + 10, ls[999], false, + }, + } + + rbp := newBaseDeltaBlock16Postings(buf.Get()) + + for _, v := range table { + // t.Log("i", i) + testutil.Equals(t, v.found, rbp.Seek(uint64(v.seek))) + testutil.Equals(t, uint64(v.val), rbp.At()) + testutil.Assert(t, rbp.Err() == nil, "") + } + }) +} + func TestRoaringBitmapPostings64(t *testing.T) { num := 1000 // mock a list as postings @@ -1224,6 +1380,14 @@ func BenchmarkPostings(b *testing.B) { writeRoaringBitmapPostings(&bufRBM, ls) b.Log("roaringBitmapPostings bits", bitmapBits, "size =", len(bufRBM.Get())) + bufRBM2 := encoding.Encbuf{} + writeBaseDeltaBlock8Postings(&bufRBM2, ls) + b.Log("baseDeltaBlock8Postings bits", bitmapBits, "size =", len(bufRBM2.Get())) + + bufRBM3 := encoding.Encbuf{} + writeBaseDeltaBlock16Postings(&bufRBM3, ls) + b.Log("baseDeltaBlock16Postings bits", bitmapBits, "size =", len(bufRBM3.Get())) + table := []struct { seek uint32 val uint32 @@ -1335,20 +1499,6 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, bdp.Err() == nil, "") } }) - // b.Run("deltaBlockIteration", func(bench *testing.B) { - // bench.ResetTimer() - // bench.ReportAllocs() - // for j := 0; j < bench.N; j++ { - // dbp := newDeltaBlockPostings(bufDB.Get(), len(ls)) - - // for i := 0; i < num; i++ { - // testutil.Assert(bench, dbp.Next() == true, "") - // testutil.Equals(bench, uint64(ls[i]), dbp.At()) - // } - // testutil.Assert(bench, dbp.Next() == false, "") - // testutil.Assert(bench, dbp.Err() == nil, "") - // } - // }) b.Run("baseDeltaBlockIteration", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() @@ -1365,20 +1515,6 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, bdbp.Err() == nil, "") } }) - // b.Run("bitmapPostingsIteration", func(bench *testing.B) { - // bench.ResetTimer() - // bench.ReportAllocs() - // for j := 0; j < bench.N; j++ { - // bm := newBitmapPostings(bufBM.Get()) - - // for i := 0; i < num; i++ { - // testutil.Assert(bench, bm.Next() == true, "") - // testutil.Equals(bench, uint64(ls[i]), bm.At()) - // } - // testutil.Assert(bench, bm.Next() == false, "") - // testutil.Assert(bench, bm.Err() == nil, "") - // } - // }) b.Run("roaringBitmapPostingsIteration", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() @@ -1395,6 +1531,38 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, rbm.Err() == nil, "") } }) + b.Run("baseDeltaBlock8PostingsIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + rbm := newBaseDeltaBlock8Postings(bufRBM2.Get()) + // bench.StartTimer() + + for i := 0; i < num; i++ { + testutil.Assert(bench, rbm.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), rbm.At()) + } + testutil.Assert(bench, rbm.Next() == false, "") + testutil.Assert(bench, rbm.Err() == nil, "") + } + }) + b.Run("baseDeltaBlock16PostingsIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + rbm := newBaseDeltaBlock16Postings(bufRBM3.Get()) + // bench.StartTimer() + + for i := 0; i < num; i++ { + testutil.Assert(bench, rbm.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), rbm.At()) + } + testutil.Assert(bench, rbm.Next() == false, "") + testutil.Assert(bench, rbm.Err() == nil, "") + } + }) b.Run("bigEndianSeek", func(bench *testing.B) { bench.ResetTimer() @@ -1426,19 +1594,6 @@ func BenchmarkPostings(b *testing.B) { } } }) - // b.Run("deltaBlockSeek", func(bench *testing.B) { - // bench.ResetTimer() - // bench.ReportAllocs() - // for j := 0; j < bench.N; j++ { - // dbp := newDeltaBlockPostings(bufDB.Get(), len(ls)) - - // for _, v := range table { - // testutil.Equals(bench, v.found, dbp.Seek(uint64(v.seek))) - // testutil.Equals(bench, uint64(v.val), dbp.At()) - // testutil.Assert(bench, dbp.Err() == nil, "") - // } - // } - // }) b.Run("baseDeltaBlockSeek", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() @@ -1454,19 +1609,6 @@ func BenchmarkPostings(b *testing.B) { } } }) - // b.Run("bitmapPostingsSeek", func(bench *testing.B) { - // bench.ResetTimer() - // bench.ReportAllocs() - // for j := 0; j < bench.N; j++ { - // bm := newBitmapPostings(bufBM.Get()) - - // for _, v := range table { - // testutil.Equals(bench, v.found, bm.Seek(uint64(v.seek))) - // testutil.Equals(bench, uint64(v.val), bm.At()) - // testutil.Assert(bench, bm.Err() == nil, "") - // } - // } - // }) b.Run("roaringBitmapPostingsSeek", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() @@ -1482,347 +1624,33 @@ func BenchmarkPostings(b *testing.B) { } } }) -} - -func BenchmarkPostingsIntersect(t *testing.B) { - // bigEndianPostings. - t.Run("BELongPostings1", func(bench *testing.B) { - var a, b, c, d []uint32 - - for i := 0; i < 10000000; i += 2 { - a = append(a, uint32(i)) - } - for i := 5000000; i < 5000100; i += 4 { - b = append(b, uint32(i)) - } - for i := 5090000; i < 5090600; i += 4 { - b = append(b, uint32(i)) - } - for i := 4990000; i < 5100000; i++ { - c = append(c, uint32(i)) - } - for i := 4000000; i < 6000000; i++ { - d = append(d, uint32(i)) - } - - bufBE1 := make([]byte, len(a)*4) - for i := 0; i < len(a); i++ { - bs := bufBE1[i*4 : i*4+4] - binary.BigEndian.PutUint32(bs, a[i]) - } - bufBE2 := make([]byte, len(b)*4) - for i := 0; i < len(b); i++ { - bs := bufBE2[i*4 : i*4+4] - binary.BigEndian.PutUint32(bs, b[i]) - } - bufBE3 := make([]byte, len(c)*4) - for i := 0; i < len(c); i++ { - bs := bufBE3[i*4 : i*4+4] - binary.BigEndian.PutUint32(bs, c[i]) - } - bufBE4 := make([]byte, len(d)*4) - for i := 0; i < len(d); i++ { - bs := bufBE4[i*4 : i*4+4] - binary.BigEndian.PutUint32(bs, d[i]) - } - - i1 := newBigEndianPostings(bufBE1) - i2 := newBigEndianPostings(bufBE2) - i3 := newBigEndianPostings(bufBE3) - i4 := newBigEndianPostings(bufBE4) - - bench.ResetTimer() - bench.ReportAllocs() - for i := 0; i < bench.N; i++ { - if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { - bench.Fatal(err) - } - } - }) - // bigEndianPostings. - t.Run("BELongPostings2", func(bench *testing.B) { - var a, b, c, d []uint32 - - for i := 0; i < 12500000; i++ { - a = append(a, uint32(i)) - } - for i := 7500000; i < 12500000; i++ { - b = append(b, uint32(i)) - } - for i := 9000000; i < 20000000; i++ { - c = append(c, uint32(i)) - } - for i := 10000000; i < 12000000; i++ { - d = append(d, uint32(i)) - } - - bufBE1 := make([]byte, len(a)*4) - for i := 0; i < len(a); i++ { - bs := bufBE1[i*4 : i*4+4] - binary.BigEndian.PutUint32(bs, a[i]) - } - bufBE2 := make([]byte, len(b)*4) - for i := 0; i < len(b); i++ { - bs := bufBE2[i*4 : i*4+4] - binary.BigEndian.PutUint32(bs, b[i]) - } - bufBE3 := make([]byte, len(c)*4) - for i := 0; i < len(c); i++ { - bs := bufBE3[i*4 : i*4+4] - binary.BigEndian.PutUint32(bs, c[i]) - } - bufBE4 := make([]byte, len(d)*4) - for i := 0; i < len(d); i++ { - bs := bufBE4[i*4 : i*4+4] - binary.BigEndian.PutUint32(bs, d[i]) - } - - i1 := newBigEndianPostings(bufBE1) - i2 := newBigEndianPostings(bufBE2) - i3 := newBigEndianPostings(bufBE3) - i4 := newBigEndianPostings(bufBE4) - - bench.ResetTimer() - bench.ReportAllocs() - for i := 0; i < bench.N; i++ { - if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { - bench.Fatal(err) - } - } - }) - // Many matchers(k >> n). - t.Run("BEManyPostings", func(bench *testing.B) { - var its []Postings - - // 100000 matchers(k=100000). - for i := 0; i < 100000; i++ { - var temp []uint32 - for j := 1; j < 100; j++ { - temp = append(temp, uint32(j)) - } - bufBE := make([]byte, len(temp)*4) - for i := 0; i < len(temp); i++ { - bs := bufBE[i*4 : i*4+4] - binary.BigEndian.PutUint32(bs, temp[i]) - } - its = append(its, newBigEndianPostings(bufBE)) - } - - bench.ResetTimer() - bench.ReportAllocs() - for i := 0; i < bench.N; i++ { - if _, err := ExpandPostings(Intersect(its...)); err != nil { - bench.Fatal(err) - } - } - }) - - // baseDeltaBlockPostings. - t.Run("BDBLongPostings1", func(bench *testing.B) { - var a, b, c, d []uint32 - - for i := 0; i < 10000000; i += 2 { - a = append(a, uint32(i)) - } - for i := 5000000; i < 5000100; i += 4 { - b = append(b, uint32(i)) - } - for i := 5090000; i < 5090600; i += 4 { - b = append(b, uint32(i)) - } - for i := 4990000; i < 5100000; i++ { - c = append(c, uint32(i)) - } - for i := 4000000; i < 6000000; i++ { - d = append(d, uint32(i)) - } - - bufBDB1 := encoding.Encbuf{} - writeBaseDeltaBlockPostings(&bufBDB1, a) - bufBDB2 := encoding.Encbuf{} - writeBaseDeltaBlockPostings(&bufBDB2, b) - bufBDB3 := encoding.Encbuf{} - writeBaseDeltaBlockPostings(&bufBDB3, c) - bufBDB4 := encoding.Encbuf{} - writeBaseDeltaBlockPostings(&bufBDB4, d) - - i1 := newBaseDeltaBlockPostings(bufBDB1.Get()) - i2 := newBaseDeltaBlockPostings(bufBDB2.Get()) - i3 := newBaseDeltaBlockPostings(bufBDB3.Get()) - i4 := newBaseDeltaBlockPostings(bufBDB4.Get()) - - bench.ResetTimer() - bench.ReportAllocs() - for i := 0; i < bench.N; i++ { - if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { - bench.Fatal(err) - } - } - }) - // baseDeltaBlockPostings. - t.Run("BDBLongPostings2", func(bench *testing.B) { - var a, b, c, d []uint32 - - for i := 0; i < 12500000; i++ { - a = append(a, uint32(i)) - } - for i := 7500000; i < 12500000; i++ { - b = append(b, uint32(i)) - } - for i := 9000000; i < 20000000; i++ { - c = append(c, uint32(i)) - } - for i := 10000000; i < 12000000; i++ { - d = append(d, uint32(i)) - } - - bufBDB1 := encoding.Encbuf{} - writeBaseDeltaBlockPostings(&bufBDB1, a) - bufBDB2 := encoding.Encbuf{} - writeBaseDeltaBlockPostings(&bufBDB2, b) - bufBDB3 := encoding.Encbuf{} - writeBaseDeltaBlockPostings(&bufBDB3, c) - bufBDB4 := encoding.Encbuf{} - writeBaseDeltaBlockPostings(&bufBDB4, d) - - i1 := newBaseDeltaBlockPostings(bufBDB1.Get()) - i2 := newBaseDeltaBlockPostings(bufBDB2.Get()) - i3 := newBaseDeltaBlockPostings(bufBDB3.Get()) - i4 := newBaseDeltaBlockPostings(bufBDB4.Get()) - - bench.ResetTimer() - bench.ReportAllocs() - for i := 0; i < bench.N; i++ { - if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { - bench.Fatal(err) - } - } - }) - // Many matchers(k >> n). - t.Run("BDBManyPostings", func(bench *testing.B) { - var its []Postings - - // 100000 matchers(k=100000). - for i := 0; i < 100000; i++ { - var temp []uint32 - for j := 1; j < 100; j++ { - temp = append(temp, uint32(j)) - } - bufBDB := encoding.Encbuf{} - writeBaseDeltaBlockPostings(&bufBDB, temp) - its = append(its, newBaseDeltaBlockPostings(bufBDB.Get())) - } - + b.Run("baseDeltaBlock8PostingsSeek", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() - for i := 0; i < bench.N; i++ { - if _, err := ExpandPostings(Intersect(its...)); err != nil { - bench.Fatal(err) - } - } - }) - - // roaringBitmapPostings. - t.Run("RBMLongPostings1", func(bench *testing.B) { - var a, b, c, d []uint32 - - for i := 0; i < 10000000; i += 2 { - a = append(a, uint32(i)) - } - for i := 5000000; i < 5000100; i += 4 { - b = append(b, uint32(i)) - } - for i := 5090000; i < 5090600; i += 4 { - b = append(b, uint32(i)) - } - for i := 4990000; i < 5100000; i++ { - c = append(c, uint32(i)) - } - for i := 4000000; i < 6000000; i++ { - d = append(d, uint32(i)) - } - - bufRBM1 := encoding.Encbuf{} - writeRoaringBitmapPostings(&bufRBM1, a) - bufRBM2 := encoding.Encbuf{} - writeRoaringBitmapPostings(&bufRBM2, b) - bufRBM3 := encoding.Encbuf{} - writeRoaringBitmapPostings(&bufRBM3, c) - bufRBM4 := encoding.Encbuf{} - writeRoaringBitmapPostings(&bufRBM4, d) - - i1 := newRoaringBitmapPostings(bufRBM1.Get()) - i2 := newRoaringBitmapPostings(bufRBM2.Get()) - i3 := newRoaringBitmapPostings(bufRBM3.Get()) - i4 := newRoaringBitmapPostings(bufRBM4.Get()) + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + rbm := newBaseDeltaBlock8Postings(bufRBM2.Get()) + // bench.StartTimer() - bench.ResetTimer() - bench.ReportAllocs() - for i := 0; i < bench.N; i++ { - if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { - bench.Fatal(err) + for _, v := range table { + testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), rbm.At()) + testutil.Assert(bench, rbm.Err() == nil, "") } } }) - // roaringBitmapPostings. - t.Run("RBMLongPostings2", func(bench *testing.B) { - var a, b, c, d []uint32 - - for i := 0; i < 12500000; i++ { - a = append(a, uint32(i)) - } - for i := 7500000; i < 12500000; i++ { - b = append(b, uint32(i)) - } - for i := 9000000; i < 20000000; i++ { - c = append(c, uint32(i)) - } - for i := 10000000; i < 12000000; i++ { - d = append(d, uint32(i)) - } - - bufRBM1 := encoding.Encbuf{} - writeRoaringBitmapPostings(&bufRBM1, a) - bufRBM2 := encoding.Encbuf{} - writeRoaringBitmapPostings(&bufRBM2, b) - bufRBM3 := encoding.Encbuf{} - writeRoaringBitmapPostings(&bufRBM3, c) - bufRBM4 := encoding.Encbuf{} - writeRoaringBitmapPostings(&bufRBM4, d) - - i1 := newRoaringBitmapPostings(bufRBM1.Get()) - i2 := newRoaringBitmapPostings(bufRBM2.Get()) - i3 := newRoaringBitmapPostings(bufRBM3.Get()) - i4 := newRoaringBitmapPostings(bufRBM4.Get()) - + b.Run("baseDeltaBlock16PostingsSeek", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() - for i := 0; i < bench.N; i++ { - if _, err := ExpandPostings(Intersect(i1, i2, i3, i4)); err != nil { - bench.Fatal(err) - } - } - }) - // Many matchers(k >> n). - t.Run("RBMManyPostings", func(bench *testing.B) { - var its []Postings - - // 100000 matchers(k=100000). - for i := 0; i < 100000; i++ { - var temp []uint32 - for j := 1; j < 100; j++ { - temp = append(temp, uint32(j)) - } - bufRBM := encoding.Encbuf{} - writeRoaringBitmapPostings(&bufRBM, temp) - its = append(its, newRoaringBitmapPostings(bufRBM.Get())) - } + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + rbm := newBaseDeltaBlock16Postings(bufRBM3.Get()) + // bench.StartTimer() - bench.ResetTimer() - bench.ReportAllocs() - for i := 0; i < bench.N; i++ { - if _, err := ExpandPostings(Intersect(its...)); err != nil { - bench.Fatal(err) + for _, v := range table { + testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), rbm.At()) + testutil.Assert(bench, rbm.Err() == nil, "") } } }) From 51daf59de523c7cf1a60c11366b1e06008fe5e6b Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Thu, 11 Jul 2019 10:33:49 +0800 Subject: [PATCH 15/18] improve baseDeltaBlock16Postings and add baseDeltaBlock16PostingsV2 Signed-off-by: naivewong <867245430@qq.com> --- index/index.go | 5 + index/postings.go | 270 ++++++++++++++++++++++++++++++++++++----- index/postings_test.go | 112 +++++++++++++++++ 3 files changed, 359 insertions(+), 28 deletions(-) diff --git a/index/index.go b/index/index.go index 1a93a195..08591e3a 100644 --- a/index/index.go +++ b/index/index.go @@ -568,6 +568,8 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { // } case 7: writeBaseDeltaBlock16Postings(&w.buf2, refs) + case 8: + writeBaseDeltaBlock16PostingsV2(&w.buf2, refs) } w.uint32s = refs @@ -1104,6 +1106,9 @@ func (dec *Decoder) Postings(b []byte) (int, Postings, error) { case 7: l := d.Get() return n, newBaseDeltaBlock16Postings(l), d.Err() + case 8: + l := d.Get() + return n, newBaseDeltaBlock16PostingsV2(l), d.Err() default: return n, EmptyPostings(), d.Err() } diff --git a/index/postings.go b/index/postings.go index b73f2f8a..8cc530eb 100644 --- a/index/postings.go +++ b/index/postings.go @@ -780,6 +780,9 @@ func (it *baseDeltaPostings) Next() bool { func (it *baseDeltaPostings) Seek(x uint64) bool { if it.cur >= x { + if it.cur == it.base { + it.idx += it.width + } return true } @@ -2039,15 +2042,16 @@ type baseDeltaBlock16Postings struct { numBlock int blockIdx int // The current block idx. nextBlock int - width int - prel int - addrMask uint32 + // width int + // prel int + // addrMask uint32 } func newBaseDeltaBlock16Postings(bstream []byte) *baseDeltaBlock16Postings { x := binary.BigEndian.Uint32(bstream) // Read the footer address. - width := int(bstream[8+int(x)]) - return &baseDeltaBlock16Postings{bs: bstream[8:], numBlock: int(binary.BigEndian.Uint32(bstream[4:])), footerAddr: int(x), width: width, prel: 4 - width, addrMask: uint32((1 << (8 * uint(width))) - 1)} + // width := int(bstream[8+int(x)]) + // return &baseDeltaBlock16Postings{bs: bstream[8:], numBlock: int(binary.BigEndian.Uint32(bstream[4:])), footerAddr: int(x), width: width, prel: 4 - width, addrMask: uint32((1 << (8 * uint(width))) - 1)} + return &baseDeltaBlock16Postings{bs: bstream[8:], numBlock: int(binary.BigEndian.Uint32(bstream[4:])), footerAddr: int(x)} } func (it *baseDeltaBlock16Postings) At() uint64 { @@ -2069,11 +2073,12 @@ func (it *baseDeltaBlock16Postings) Next() bool { it.key = val << 16 it.idx += size it.inside = true - if it.blockIdx != it.numBlock-1 { - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) - } else { - it.nextBlock = it.footerAddr - } + // if it.blockIdx != it.numBlock-1 { + // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) + // } else { + // it.nextBlock = it.footerAddr + // } it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) it.idx += 2 return true @@ -2098,11 +2103,12 @@ func (it *baseDeltaBlock16Postings) seekInBlock(x uint64) bool { it.key = val << 16 it.idx += size it.inside = true - if it.blockIdx != it.numBlock-1 { - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) - } else { - it.nextBlock = it.footerAddr - } + // if it.blockIdx != it.numBlock-1 { + // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) + // } else { + // it.nextBlock = it.footerAddr + // } it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) it.idx += 2 return true @@ -2125,7 +2131,8 @@ func (it *baseDeltaBlock16Postings) Seek(x uint64) bool { return it.seekInBlock(x) } else { i := sort.Search(it.numBlock-it.blockIdx, func(i int) bool { - off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-it.prel:]) & it.addrMask) + // off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-it.prel:]) & it.addrMask) + off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+i)<<2):])) k, _ := binary.Uvarint(it.bs[off:]) return k >= curKey }) @@ -2135,7 +2142,8 @@ func (it *baseDeltaBlock16Postings) Seek(x uint64) bool { it.blockIdx += i if i != 0 { // i > 0. it.inside = false - it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) + // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx)<<2):])) } } val, size := binary.Uvarint(it.bs[it.idx:]) @@ -2146,16 +2154,18 @@ func (it *baseDeltaBlock16Postings) Seek(x uint64) bool { return false } else { it.blockIdx += 1 - it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) + // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx)<<2):])) val, size := binary.Uvarint(it.bs[it.idx:]) it.key = val << 16 it.idx += size it.inside = true - if it.blockIdx != it.numBlock-1 { - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) - } else { - it.nextBlock = it.footerAddr - } + // if it.blockIdx != it.numBlock-1 { + // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) + // } else { + // it.nextBlock = it.footerAddr + // } it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) it.idx += 2 return true @@ -2165,11 +2175,12 @@ func (it *baseDeltaBlock16Postings) Seek(x uint64) bool { it.idx += size it.inside = true - if it.blockIdx != it.numBlock-1 { - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) - } else { - it.nextBlock = it.footerAddr - } + // if it.blockIdx != it.numBlock-1 { + // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) + // } else { + // it.nextBlock = it.footerAddr + // } return it.seekInBlock(x) } @@ -2217,6 +2228,209 @@ func writeBaseDeltaBlock16Postings(e *encoding.Encbuf, arr []uint32) { } startingOffs = append(startingOffs, uint32(len(e.B))) writeBaseDelta16Block(e, vals, key, valueSize) + startingOffs = append(startingOffs, uint32(len(e.B))) + + binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-8-startOff)) // Put footer starting offset. + binary.BigEndian.PutUint32(e.B[startOff+4:], uint32(len(startingOffs)-1)) // Put number of blocks. + // width := bits.Len32(startingOffs[len(startingOffs)-1] - 8 - uint32(startOff)) + // if width == 0 { + // // key 0 will result in 0 width. + // width += 1 + // } + + // e.PutByte(byte((width + 7) / 8)) + // for _, off := range startingOffs { + // putBytes(e, off-8-uint32(startOff), (width+7)/8) + // } + for _, off := range startingOffs { + e.PutBE32(off-8-uint32(startOff)) + } +} + +type baseDeltaBlock16PostingsV2 struct { + bs []byte + cur uint64 + inside bool + idx int // The current offset inside the bs. + footerAddr int + base uint64 + numBlock int + blockIdx int // The current block idx. + nextBlock int + width int + prel int + addrMask uint32 +} + +func newBaseDeltaBlock16PostingsV2(bstream []byte) *baseDeltaBlock16PostingsV2 { + x := binary.BigEndian.Uint32(bstream) // Read the footer address. + width := int(bstream[8+int(x)]) + return &baseDeltaBlock16PostingsV2{bs: bstream[8:], numBlock: int(binary.BigEndian.Uint32(bstream[4:])), footerAddr: int(x), width: width, prel: 4 - width, addrMask: uint32((1 << (8 * uint(width))) - 1)} +} + +func (it *baseDeltaBlock16PostingsV2) At() uint64 { + return it.cur +} + +func (it *baseDeltaBlock16PostingsV2) Next() bool { + if it.inside { // Already entered the block. + if it.idx < it.nextBlock { + it.cur = it.base + uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) + it.idx += 2 + return true + } + it.blockIdx += 1 // Go to the next block. + } + // Currently not entered any block. + if it.idx < it.footerAddr { + val, size := binary.Uvarint(it.bs[it.idx:]) // Read the base. + it.base = val + it.idx += size + it.inside = true + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + it.cur = it.base + return true + } else { + return false + } +} + +func (it *baseDeltaBlock16PostingsV2) seekInBlock(x uint64) bool { + temp := x - it.base + num := (it.nextBlock - it.idx) >> 1 + j := sort.Search(num, func(i int) bool { + return uint64(binary.BigEndian.Uint16(it.bs[it.idx+(i<<1):])) >= temp + }) + if j == num { + // Fast-path to the next block. + // The first element in next block should be >= x. + it.idx = it.nextBlock + it.blockIdx += 1 + if it.idx < it.footerAddr { + val, size := binary.Uvarint(it.bs[it.idx:]) + it.base = val + it.idx += size + it.inside = true + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + it.cur = it.base + return true + } else { + return false + } + } + it.cur = it.base + uint64(binary.BigEndian.Uint16(it.bs[it.idx+(j<<1):])) + it.idx += (j + 1) << 1 + return true +} + +func (it *baseDeltaBlock16PostingsV2) Seek(x uint64) bool { + if it.cur >= x { + return true + } + if it.inside && bits.Len64(x - it.base) <= 16 { + // Fast path for x in current block. + return it.seekInBlock(x) + } else { + i := sort.Search(it.numBlock-it.blockIdx, func(i int) bool { + off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-it.prel:]) & it.addrMask) + k, _ := binary.Uvarint(it.bs[off:]) + return k > x + }) + if i > 0 { + i -= 1 + } + it.blockIdx += i + if i != 0 { // i > 0. + it.inside = false + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) + } + } + val, size := binary.Uvarint(it.bs[it.idx:]) + it.base = val + it.idx += size + it.inside = true + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + if it.base >= x { + it.cur = it.base + return true + } + + // If the length of the diff larger than 16, directly go to the next block + // because the first value of the next block should be >= x. + if bits.Len64(x - val) > 16 { + if it.blockIdx == it.numBlock-1 { + return false + } else { + it.blockIdx += 1 + it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) + val, size := binary.Uvarint(it.bs[it.idx:]) + it.base = val + it.idx += size + it.inside = true + if it.blockIdx != it.numBlock-1 { + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) + } else { + it.nextBlock = it.footerAddr + } + it.cur = it.base + uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) + it.idx += 2 + return true + } + } + return it.seekInBlock(x) +} + +func (it *baseDeltaBlock16PostingsV2) Err() error { + return nil +} + +func writeBaseDelta16BlockV2(e *encoding.Encbuf, vals []uint32, base uint32) { + e.PutUvarint32(base) + c := make([]byte, 2) + for _, val := range vals { + binary.BigEndian.PutUint16(c[:], uint16(val)) + e.PutByte(c[0]) + e.PutByte(c[1]) + } +} + +func writeBaseDeltaBlock16PostingsV2(e *encoding.Encbuf, arr []uint32) { + var base uint32 + var idx int // Index of current element in arr. + var startingOffs []uint32 // The starting offsets of each block. + var vals []uint32 // The converted values in the current block. + startOff := len(e.Get()) + e.PutBE32(0) // Footer starting offset. + e.PutBE32(0) // Number of blocks. + base = arr[idx] + idx += 1 + for idx < len(arr) { + delta := arr[idx] - base + if bits.Len32(delta) > 16 { + startingOffs = append(startingOffs, uint32(len(e.B))) + writeBaseDelta16BlockV2(e, vals, base) + base = arr[idx] + idx += 1 + vals = vals[:0] + continue + } + vals = append(vals, delta) + idx += 1 + } + startingOffs = append(startingOffs, uint32(len(e.B))) + writeBaseDelta16BlockV2(e, vals, base) binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-8-startOff)) // Put footer starting offset. binary.BigEndian.PutUint32(e.B[startOff+4:], uint32(len(startingOffs))) // Put number of blocks. diff --git a/index/postings_test.go b/index/postings_test.go index 63f35cf5..366a6837 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -1253,6 +1253,83 @@ func TestBaseDeltaBlock16Postings(t *testing.T) { }) } +func TestBaseDeltaBlock16PostingsV2(t *testing.T) { + num := 1000 + // mock a list as postings + ls := make([]uint32, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint32(rand.Int31n(15)) + 2 + // ls[i] = ls[i-1] + 10 + } + + buf := encoding.Encbuf{} + writeBaseDeltaBlock16PostingsV2(&buf, ls) + // t.Log("len", len(buf.Get())) + + t.Run("Iteration", func(t *testing.T) { + rbp := newBaseDeltaBlock16PostingsV2(buf.Get()) + for i := 0; i < num; i++ { + testutil.Assert(t, rbp.Next() == true, "") + if uint64(ls[i]) != rbp.At() { + t.Log("ls[i] =", ls[i], "rbp.At() =", rbp.At(), " i =", i) + } + testutil.Equals(t, uint64(ls[i]), rbp.At()) + } + + testutil.Assert(t, rbp.Next() == false, "") + testutil.Assert(t, rbp.Err() == nil, "") + }) + + t.Run("Seek", func(t *testing.T) { + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[4], ls[4], true, + }, + { + ls[500] - 1, ls[500], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[600] + 1, ls[601], true, + }, + { + ls[0], ls[601], true, + }, + { + ls[600], ls[601], true, + }, + { + ls[999], ls[999], true, + }, + { + ls[999] + 10, ls[999], false, + }, + } + + rbp := newBaseDeltaBlock16PostingsV2(buf.Get()) + + for _, v := range table { + // t.Log("i", i) + testutil.Equals(t, v.found, rbp.Seek(uint64(v.seek))) + testutil.Equals(t, uint64(v.val), rbp.At()) + testutil.Assert(t, rbp.Err() == nil, "") + } + }) +} + func TestRoaringBitmapPostings64(t *testing.T) { num := 1000 // mock a list as postings @@ -1388,6 +1465,10 @@ func BenchmarkPostings(b *testing.B) { writeBaseDeltaBlock16Postings(&bufRBM3, ls) b.Log("baseDeltaBlock16Postings bits", bitmapBits, "size =", len(bufRBM3.Get())) + bufRBM4 := encoding.Encbuf{} + writeBaseDeltaBlock16PostingsV2(&bufRBM4, ls) + b.Log("baseDeltaBlock16PostingsV2 bits", bitmapBits, "size =", len(bufRBM4.Get())) + table := []struct { seek uint32 val uint32 @@ -1563,6 +1644,22 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, rbm.Err() == nil, "") } }) + b.Run("baseDeltaBlock16PostingsV2Iteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + rbm := newBaseDeltaBlock16PostingsV2(bufRBM4.Get()) + // bench.StartTimer() + + for i := 0; i < num; i++ { + testutil.Assert(bench, rbm.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), rbm.At()) + } + testutil.Assert(bench, rbm.Next() == false, "") + testutil.Assert(bench, rbm.Err() == nil, "") + } + }) b.Run("bigEndianSeek", func(bench *testing.B) { bench.ResetTimer() @@ -1654,6 +1751,21 @@ func BenchmarkPostings(b *testing.B) { } } }) + // b.Run("baseDeltaBlock16PostingsV2Seek", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // rbm := newBaseDeltaBlock16PostingsV2(bufRBM4.Get()) + // // bench.StartTimer() + + // for _, v := range table { + // testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + // testutil.Equals(bench, uint64(v.val), rbm.At()) + // testutil.Assert(bench, rbm.Err() == nil, "") + // } + // } + // }) } func TestIntersectWithMerge(t *testing.T) { From a4c94307420eaa85b055ae0167c972e8010c7179 Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Sat, 13 Jul 2019 11:58:59 +0800 Subject: [PATCH 16/18] improve baseDeltaBlock16Postings Signed-off-by: naivewong <867245430@qq.com> --- index/postings.go | 163 +++++++++++++---------------------------- index/postings_test.go | 75 ++++++++++++++----- 2 files changed, 108 insertions(+), 130 deletions(-) diff --git a/index/postings.go b/index/postings.go index 8cc530eb..cdb244ce 100644 --- a/index/postings.go +++ b/index/postings.go @@ -780,9 +780,6 @@ func (it *baseDeltaPostings) Next() bool { func (it *baseDeltaPostings) Seek(x uint64) bool { if it.cur >= x { - if it.cur == it.base { - it.idx += it.width - } return true } @@ -1549,36 +1546,6 @@ func (it *roaringBitmapPostings) Seek(x uint64) bool { } val, size := binary.Uvarint(it.bs[it.idx:]) - // If the key of current block doesn't match, directly go to the next block. - if val != curKey { - if it.blockIdx == it.numBlock-1 { - it.idx = it.footerAddr - return false - } else { - it.blockIdx += 1 - // it.idx = int(it.readBits((it.footerAddr+5)*8+it.blockIdx*it.width)) - // it.idx = it.readBytes(it.footerAddr+1+it.blockIdx*it.width) - it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-4+it.width:]) & it.addrMask) - // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+it.blockIdx*4:])) - val, size := binary.Uvarint(it.bs[it.idx:]) - it.key = val << bitmapBits - it.idx += size - it.blockType = it.bs[it.idx] - it.idx += 1 - it.inside = true - if it.blockType == 0 { - if it.blockIdx != it.numBlock-1 { - // it.nextBlock = int(it.readBits((it.footerAddr+5)*8+(it.blockIdx+1)*it.width)) - // it.nextBlock = it.readBytes(it.footerAddr+1+(it.blockIdx+1)*it.width) - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-4+it.width:]) & it.addrMask) - // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+(it.blockIdx+1)*4:])) - } else { - it.nextBlock = it.footerAddr - } - } - return it.Next() - } - } it.key = val << bitmapBits it.idx += size it.blockType = it.bs[it.idx] @@ -1939,27 +1906,6 @@ func (it *baseDeltaBlock8Postings) Seek(x uint64) bool { } } val, size := binary.Uvarint(it.bs[it.idx:]) - // If the key of current block doesn't match, directly go to the next block. - if val != curKey { - if it.blockIdx == it.numBlock-1 { - return false - } else { - it.blockIdx += 1 - it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) - val, size := binary.Uvarint(it.bs[it.idx:]) - it.key = val << bitmapBits - it.idx += size - it.inside = true - if it.blockIdx != it.numBlock-1 { - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) - } else { - it.nextBlock = it.footerAddr - } - it.cur = it.key | uint64(it.bs[it.idx]) - it.idx += 1 - return true - } - } it.key = val << bitmapBits it.idx += size it.inside = true @@ -2042,15 +1988,10 @@ type baseDeltaBlock16Postings struct { numBlock int blockIdx int // The current block idx. nextBlock int - // width int - // prel int - // addrMask uint32 } func newBaseDeltaBlock16Postings(bstream []byte) *baseDeltaBlock16Postings { x := binary.BigEndian.Uint32(bstream) // Read the footer address. - // width := int(bstream[8+int(x)]) - // return &baseDeltaBlock16Postings{bs: bstream[8:], numBlock: int(binary.BigEndian.Uint32(bstream[4:])), footerAddr: int(x), width: width, prel: 4 - width, addrMask: uint32((1 << (8 * uint(width))) - 1)} return &baseDeltaBlock16Postings{bs: bstream[8:], numBlock: int(binary.BigEndian.Uint32(bstream[4:])), footerAddr: int(x)} } @@ -2073,12 +2014,7 @@ func (it *baseDeltaBlock16Postings) Next() bool { it.key = val << 16 it.idx += size it.inside = true - // if it.blockIdx != it.numBlock-1 { - // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) - // } else { - // it.nextBlock = it.footerAddr - // } + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) it.idx += 2 return true @@ -2103,12 +2039,7 @@ func (it *baseDeltaBlock16Postings) seekInBlock(x uint64) bool { it.key = val << 16 it.idx += size it.inside = true - // if it.blockIdx != it.numBlock-1 { - // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) - // } else { - // it.nextBlock = it.footerAddr - // } + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) it.idx += 2 return true @@ -2131,7 +2062,6 @@ func (it *baseDeltaBlock16Postings) Seek(x uint64) bool { return it.seekInBlock(x) } else { i := sort.Search(it.numBlock-it.blockIdx, func(i int) bool { - // off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+i)*it.width-it.prel:]) & it.addrMask) off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+i)<<2):])) k, _ := binary.Uvarint(it.bs[off:]) return k >= curKey @@ -2142,45 +2072,15 @@ func (it *baseDeltaBlock16Postings) Seek(x uint64) bool { it.blockIdx += i if i != 0 { // i > 0. it.inside = false - // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx)<<2):])) } } val, size := binary.Uvarint(it.bs[it.idx:]) - // If the key of current block doesn't match, directly go to the next block - // because the first value of the next block should be >= x. - if val != curKey { - if it.blockIdx == it.numBlock-1 { - return false - } else { - it.blockIdx += 1 - // it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+it.blockIdx*it.width-it.prel:]) & it.addrMask) - it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx)<<2):])) - val, size := binary.Uvarint(it.bs[it.idx:]) - it.key = val << 16 - it.idx += size - it.inside = true - // if it.blockIdx != it.numBlock-1 { - // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) - // } else { - // it.nextBlock = it.footerAddr - // } - it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) - it.idx += 2 - return true - } - } it.key = val << 16 it.idx += size it.inside = true - // if it.blockIdx != it.numBlock-1 { - // it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+1+(it.blockIdx+1)*it.width-it.prel:]) & it.addrMask) - it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) - // } else { - // it.nextBlock = it.footerAddr - // } + it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) return it.seekInBlock(x) } @@ -2199,6 +2099,17 @@ func writeBaseDelta16Block(e *encoding.Encbuf, vals []uint32, key uint32, valueS } } +func writeBaseDelta16Block64(e *encoding.Encbuf, vals []uint64, key uint64, valueSize int) { + e.PutUvarint64(key) + c := make([]byte, 8) + for _, val := range vals { + binary.BigEndian.PutUint64(c[:], val) + for i := 8 - valueSize; i < 8; i++ { + e.PutByte(c[i]) + } + } +} + func writeBaseDeltaBlock16Postings(e *encoding.Encbuf, arr []uint32) { key := uint32(0xffffffff) // The initial key should be unique. valueSize := 16 >> 3 // The size of the element in array in bytes. @@ -2232,16 +2143,44 @@ func writeBaseDeltaBlock16Postings(e *encoding.Encbuf, arr []uint32) { binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-8-startOff)) // Put footer starting offset. binary.BigEndian.PutUint32(e.B[startOff+4:], uint32(len(startingOffs)-1)) // Put number of blocks. - // width := bits.Len32(startingOffs[len(startingOffs)-1] - 8 - uint32(startOff)) - // if width == 0 { - // // key 0 will result in 0 width. - // width += 1 - // } + for _, off := range startingOffs { + e.PutBE32(off-8-uint32(startOff)) + } +} - // e.PutByte(byte((width + 7) / 8)) - // for _, off := range startingOffs { - // putBytes(e, off-8-uint32(startOff), (width+7)/8) - // } +func writeBaseDeltaBlock16Postings64(e *encoding.Encbuf, arr []uint64) { + key := uint64(0xffffffff) // The initial key should be unique. + valueSize := 16 >> 3 // The size of the element in array in bytes. + mask := uint64((1 << uint(16)) - 1) // Mask for the elements in the block. + var curKey uint64 + var curVal uint64 + var idx int // Index of current element in arr. + var startingOffs []uint32 // The starting offsets of each block. + var vals []uint64 // The converted values in the current block. + startOff := len(e.Get()) + e.PutBE32(0) // Footer starting offset. + e.PutBE32(0) // Number of blocks. + for idx < len(arr) { + curKey = arr[idx] >> 16 // Key of block. + curVal = arr[idx] & mask // Value inside block. + if curKey != key { + // Move to next block. + if idx != 0 { + startingOffs = append(startingOffs, uint32(len(e.B))) + writeBaseDelta16Block64(e, vals, key, valueSize) + vals = vals[:0] + } + key = curKey + } + vals = append(vals, curVal) + idx += 1 + } + startingOffs = append(startingOffs, uint32(len(e.B))) + writeBaseDelta16Block64(e, vals, key, valueSize) + startingOffs = append(startingOffs, uint32(len(e.B))) + + binary.BigEndian.PutUint32(e.B[startOff:], uint32(len(e.B)-8-startOff)) // Put footer starting offset. + binary.BigEndian.PutUint32(e.B[startOff+4:], uint32(len(startingOffs)-1)) // Put number of blocks. for _, off := range startingOffs { e.PutBE32(off-8-uint32(startOff)) } diff --git a/index/postings_test.go b/index/postings_test.go index 366a6837..86c947e2 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -1459,15 +1459,23 @@ func BenchmarkPostings(b *testing.B) { bufRBM2 := encoding.Encbuf{} writeBaseDeltaBlock8Postings(&bufRBM2, ls) - b.Log("baseDeltaBlock8Postings bits", bitmapBits, "size =", len(bufRBM2.Get())) + b.Log("baseDeltaBlock8Postings", len(bufRBM2.Get())) bufRBM3 := encoding.Encbuf{} writeBaseDeltaBlock16Postings(&bufRBM3, ls) - b.Log("baseDeltaBlock16Postings bits", bitmapBits, "size =", len(bufRBM3.Get())) + b.Log("baseDeltaBlock16Postings", len(bufRBM3.Get())) + + bufBDB16 := encoding.Encbuf{} + temp := make([]uint64, 0, len(ls)) + for _, x := range ls { + temp = append(temp, uint64(x)) + } + writeBaseDeltaBlock16Postings64(&bufBDB16, temp) + b.Log("baseDeltaBlock16Postings (64bit)", len(bufBDB16.Get())) bufRBM4 := encoding.Encbuf{} writeBaseDeltaBlock16PostingsV2(&bufRBM4, ls) - b.Log("baseDeltaBlock16PostingsV2 bits", bitmapBits, "size =", len(bufRBM4.Get())) + b.Log("baseDeltaBlock16PostingsV2", len(bufRBM4.Get())) table := []struct { seek uint32 @@ -1644,6 +1652,22 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, rbm.Err() == nil, "") } }) + b.Run("baseDeltaBlock16PostingsIteration (64bit)", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + rbm := newBaseDeltaBlock16Postings(bufBDB16.Get()) + // bench.StartTimer() + + for i := 0; i < num; i++ { + testutil.Assert(bench, rbm.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), rbm.At()) + } + testutil.Assert(bench, rbm.Next() == false, "") + testutil.Assert(bench, rbm.Err() == nil, "") + } + }) b.Run("baseDeltaBlock16PostingsV2Iteration", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() @@ -1751,21 +1775,36 @@ func BenchmarkPostings(b *testing.B) { } } }) - // b.Run("baseDeltaBlock16PostingsV2Seek", func(bench *testing.B) { - // bench.ResetTimer() - // bench.ReportAllocs() - // for j := 0; j < bench.N; j++ { - // // bench.StopTimer() - // rbm := newBaseDeltaBlock16PostingsV2(bufRBM4.Get()) - // // bench.StartTimer() - - // for _, v := range table { - // testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) - // testutil.Equals(bench, uint64(v.val), rbm.At()) - // testutil.Assert(bench, rbm.Err() == nil, "") - // } - // } - // }) + b.Run("baseDeltaBlock16PostingsSeek (64bit)", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + rbm := newBaseDeltaBlock16Postings(bufBDB16.Get()) + // bench.StartTimer() + + for _, v := range table { + testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), rbm.At()) + testutil.Assert(bench, rbm.Err() == nil, "") + } + } + }) + b.Run("baseDeltaBlock16PostingsV2Seek", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + rbm := newBaseDeltaBlock16PostingsV2(bufRBM4.Get()) + // bench.StartTimer() + + for _, v := range table { + testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), rbm.At()) + testutil.Assert(bench, rbm.Err() == nil, "") + } + } + }) } func TestIntersectWithMerge(t *testing.T) { From 6af1ed053bd3cf62a1bce8ce1a047f26fcc0d4fe Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Mon, 15 Jul 2019 19:03:37 +0800 Subject: [PATCH 17/18] improve baseDeltaBlock16Postings by using uint64 as block key Signed-off-by: naivewong <867245430@qq.com> --- index/postings.go | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/index/postings.go b/index/postings.go index cdb244ce..9796642a 100644 --- a/index/postings.go +++ b/index/postings.go @@ -2010,9 +2010,8 @@ func (it *baseDeltaBlock16Postings) Next() bool { } // Currently not entered any block. if it.idx < it.footerAddr { - val, size := binary.Uvarint(it.bs[it.idx:]) // Read the key. - it.key = val << 16 - it.idx += size + it.key = binary.BigEndian.Uint64(it.bs[it.idx:]) + it.idx += 8 it.inside = true it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) @@ -2035,9 +2034,8 @@ func (it *baseDeltaBlock16Postings) seekInBlock(x uint64) bool { it.idx = it.nextBlock it.blockIdx += 1 if it.idx < it.footerAddr { - val, size := binary.Uvarint(it.bs[it.idx:]) - it.key = val << 16 - it.idx += size + it.key = binary.BigEndian.Uint64(it.bs[it.idx:]) + it.idx += 8 it.inside = true it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) it.cur = it.key | uint64(binary.BigEndian.Uint16(it.bs[it.idx:])) @@ -2056,14 +2054,15 @@ func (it *baseDeltaBlock16Postings) Seek(x uint64) bool { if it.cur >= x { return true } - curKey := x >> 16 - if it.inside && it.key>>16 == curKey { + curKey := (x >> 16) << 16 + if it.inside && it.key == curKey { // Fast path for x in current block. return it.seekInBlock(x) } else { i := sort.Search(it.numBlock-it.blockIdx, func(i int) bool { off := int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+i)<<2):])) - k, _ := binary.Uvarint(it.bs[off:]) + // k, _ := binary.Uvarint(it.bs[off:]) + k := binary.BigEndian.Uint64(it.bs[off:]) return k >= curKey }) if i == it.numBlock-it.blockIdx { @@ -2075,9 +2074,9 @@ func (it *baseDeltaBlock16Postings) Seek(x uint64) bool { it.idx = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx)<<2):])) } } - val, size := binary.Uvarint(it.bs[it.idx:]) - it.key = val << 16 - it.idx += size + it.key = binary.BigEndian.Uint64(it.bs[it.idx:]) + it.idx += 8 + it.inside = true it.nextBlock = int(binary.BigEndian.Uint32(it.bs[it.footerAddr+((it.blockIdx+1)<<2):])) @@ -2089,7 +2088,7 @@ func (it *baseDeltaBlock16Postings) Err() error { } func writeBaseDelta16Block(e *encoding.Encbuf, vals []uint32, key uint32, valueSize int) { - e.PutUvarint32(key) + e.PutBE64(uint64(key)) c := make([]byte, 4) for _, val := range vals { binary.BigEndian.PutUint32(c[:], val) @@ -2100,7 +2099,7 @@ func writeBaseDelta16Block(e *encoding.Encbuf, vals []uint32, key uint32, valueS } func writeBaseDelta16Block64(e *encoding.Encbuf, vals []uint64, key uint64, valueSize int) { - e.PutUvarint64(key) + e.PutBE64(key) c := make([]byte, 8) for _, val := range vals { binary.BigEndian.PutUint64(c[:], val) @@ -2114,6 +2113,7 @@ func writeBaseDeltaBlock16Postings(e *encoding.Encbuf, arr []uint32) { key := uint32(0xffffffff) // The initial key should be unique. valueSize := 16 >> 3 // The size of the element in array in bytes. mask := uint32((1 << uint(16)) - 1) // Mask for the elements in the block. + invertedMask := ^mask var curKey uint32 var curVal uint32 var idx int // Index of current element in arr. @@ -2123,8 +2123,8 @@ func writeBaseDeltaBlock16Postings(e *encoding.Encbuf, arr []uint32) { e.PutBE32(0) // Footer starting offset. e.PutBE32(0) // Number of blocks. for idx < len(arr) { - curKey = arr[idx] >> 16 // Key of block. - curVal = arr[idx] & mask // Value inside block. + curKey = arr[idx] & invertedMask // Key of block. + curVal = arr[idx] & mask // Value inside block. if curKey != key { // Move to next block. if idx != 0 { @@ -2152,6 +2152,7 @@ func writeBaseDeltaBlock16Postings64(e *encoding.Encbuf, arr []uint64) { key := uint64(0xffffffff) // The initial key should be unique. valueSize := 16 >> 3 // The size of the element in array in bytes. mask := uint64((1 << uint(16)) - 1) // Mask for the elements in the block. + invertedMask := ^mask var curKey uint64 var curVal uint64 var idx int // Index of current element in arr. @@ -2161,8 +2162,8 @@ func writeBaseDeltaBlock16Postings64(e *encoding.Encbuf, arr []uint64) { e.PutBE32(0) // Footer starting offset. e.PutBE32(0) // Number of blocks. for idx < len(arr) { - curKey = arr[idx] >> 16 // Key of block. - curVal = arr[idx] & mask // Value inside block. + curKey = arr[idx] & invertedMask // Key of block. + curVal = arr[idx] & mask // Value inside block. if curKey != key { // Move to next block. if idx != 0 { From eaf32deb4742cdeb68dac366d3130088d5b81d12 Mon Sep 17 00:00:00 2001 From: naivewong <867245430@qq.com> Date: Sat, 3 Aug 2019 15:50:19 +0800 Subject: [PATCH 18/18] add remaining test codes for later reference Signed-off-by: naivewong <867245430@qq.com> --- index/index.go | 261 ++++++++++- index/index_test.go | 252 +++++++++++ index/postings.go | 13 +- index/postings_test.go | 953 +++++++++++++++++++++++++++++++++++------ 4 files changed, 1337 insertions(+), 142 deletions(-) diff --git a/index/index.go b/index/index.go index 08591e3a..aab9c37f 100644 --- a/index/index.go +++ b/index/index.go @@ -583,6 +583,182 @@ func (w *Writer) WritePostings(name, value string, it Postings) error { return errors.Wrap(err, "write postings") } +func (w *Writer) WritePostings1(name, value string, it Postings) (uint64, error) { + if err := w.ensureStage(idxStagePostings); err != nil { + return 0, errors.Wrap(err, "ensure stage") + } + + start := w.pos + + // Align beginning to 4 bytes for more efficient postings list scans. + if err := w.addPadding(4); err != nil { + return 0, err + } + + w.postings = append(w.postings, hashEntry{ + keys: []string{name, value}, + offset: w.pos, + }) + + // Order of the references in the postings list does not imply order + // of the series references within the persisted block they are mapped to. + // We have to sort the new references again. + refs := w.uint32s[:0] + + for it.Next() { + offset, ok := w.seriesOffsets[it.At()] + if !ok { + return 0, errors.Errorf("%p series for reference %d not found", w, it.At()) + } + if offset > (1<<32)-1 { + return 0, errors.Errorf("series offset %d exceeds 4 bytes", offset) + } + refs = append(refs, uint32(offset)) + } + if err := it.Err(); err != nil { + return 0, err + } + sort.Sort(uint32slice(refs)) + + w.buf2.Reset() + w.buf2.PutBE32int(len(refs)) + + for _, r := range refs { + w.buf2.PutBE32(r) + } + + w.uint32s = refs + + w.buf1.Reset() + w.buf1.PutBE32int(w.buf2.Len()) + + w.buf2.PutHash(w.crc32) + + err := w.write(w.buf1.Get(), w.buf2.Get()) + return w.pos - start, errors.Wrap(err, "write postings") +} + +func (w *Writer) WritePostings2(name, value string, it Postings) (uint64, int, error) { + if err := w.ensureStage(idxStagePostings); err != nil { + return 0, 0, errors.Wrap(err, "ensure stage") + } + + // Align beginning to 4 bytes for more efficient postings list scans. + // if err := w.addPadding(4); err != nil { + // return err + // } + + start := w.pos + + w.postings = append(w.postings, hashEntry{ + keys: []string{name, value}, + offset: w.pos, + }) + + // Order of the references in the postings list does not imply order + // of the series references within the persisted block they are mapped to. + // We have to sort the new references again. + refs := w.uint32s[:0] + + for it.Next() { + offset, ok := w.seriesOffsets[it.At()] + if !ok { + return 0, 0, errors.Errorf("%p series for reference %d not found", w, it.At()) + } + if offset > (1<<32)-1 { + return 0, 0, errors.Errorf("series offset %d exceeds 4 bytes", offset) + } + refs = append(refs, uint32(offset)) + } + if err := it.Err(); err != nil { + return 0, 0, err + } + sort.Sort(uint32slice(refs)) + + w.buf2.Reset() + w.buf2.PutBE32int(len(refs)) + + n := writeBaseDeltaBlock16Postings(&w.buf2, refs) + + w.uint32s = refs + + w.buf1.Reset() + w.buf1.PutBE32int(w.buf2.Len()) + + w.buf2.PutHash(w.crc32) + + err := w.write(w.buf1.Get(), w.buf2.Get()) + return w.pos - start, n, errors.Wrap(err, "write postings") +} + +func (w *Writer) WritePostings3(name, value string, it Postings) (uint64, error) { + if err := w.ensureStage(idxStagePostings); err != nil { + return 0, errors.Wrap(err, "ensure stage") + } + + // Align beginning to 4 bytes for more efficient postings list scans. + // if err := w.addPadding(4); err != nil { + // return err + // } + + start := w.pos + + w.postings = append(w.postings, hashEntry{ + keys: []string{name, value}, + offset: w.pos, + }) + + // Order of the references in the postings list does not imply order + // of the series references within the persisted block they are mapped to. + // We have to sort the new references again. + refs := w.uint32s[:0] + + for it.Next() { + offset, ok := w.seriesOffsets[it.At()] + if !ok { + return 0, errors.Errorf("%p series for reference %d not found", w, it.At()) + } + if offset > (1<<32)-1 { + return 0, errors.Errorf("series offset %d exceeds 4 bytes", offset) + } + refs = append(refs, uint32(offset)) + } + if err := it.Err(); err != nil { + return 0, err + } + sort.Sort(uint32slice(refs)) + + w.buf2.Reset() + w.buf2.PutBE32int(len(refs)) + + // The base. + w.buf2.PutUvarint32(refs[0]) + // The width. + width := (bits.Len32(refs[len(refs)-1] - refs[0]) + 7) >> 3 + if width == 0 { + width = 1 + } + w.buf2.PutByte(byte(width)) + for i := 0; i < 8 - width; i++ { + w.buf2.PutByte(0) + } + for i := 0; i < len(refs); i++ { + for j := width - 1; j >= 0; j-- { + w.buf2.B = append(w.buf2.B, byte(((refs[i]-refs[0])>>(8*uint(j))&0xff))) + } + } + + w.uint32s = refs + + w.buf1.Reset() + w.buf1.PutBE32int(w.buf2.Len()) + + w.buf2.PutHash(w.crc32) + + err := w.write(w.buf1.Get(), w.buf2.Get()) + return w.pos - start, errors.Wrap(err, "write postings") +} + type uint32slice []uint32 func (s uint32slice) Len() int { return len(s) } @@ -941,6 +1117,26 @@ func (r *Reader) Series(id uint64, lbls *labels.Labels, chks *[]chunks.Meta) err // Postings returns a postings list for the given label pair. func (r *Reader) Postings(name, value string) (Postings, error) { + e, ok := r.postings[name] + if !ok { + return EmptyPostings(), errors.Errorf("cannot find name") + } + off, ok := e[value] + if !ok { + return EmptyPostings(), errors.Errorf("cannot find value") + } + d := encoding.NewDecbufAt(r.b, int(off), castagnoliTable) + if d.Err() != nil { + return nil, errors.Wrap(d.Err(), "get postings entry") + } + _, p, err := r.dec.Postings(d.Get()) + if err != nil { + return nil, errors.Wrap(err, "decode postings") + } + return p, nil +} + +func (r *Reader) Postings1(name, value string) (Postings, error) { e, ok := r.postings[name] if !ok { return EmptyPostings(), nil @@ -953,7 +1149,47 @@ func (r *Reader) Postings(name, value string) (Postings, error) { if d.Err() != nil { return nil, errors.Wrap(d.Err(), "get postings entry") } - _, p, err := r.dec.Postings(d.Get()) + _, p, err := r.dec.Postings1(d.Get()) + if err != nil { + return nil, errors.Wrap(err, "decode postings") + } + return p, nil +} + +func (r *Reader) Postings2(name, value string) (Postings, error) { + e, ok := r.postings[name] + if !ok { + return EmptyPostings(), nil + } + off, ok := e[value] + if !ok { + return EmptyPostings(), nil + } + d := encoding.NewDecbufAt(r.b, int(off), castagnoliTable) + if d.Err() != nil { + return nil, errors.Wrap(d.Err(), "get postings entry") + } + _, p, err := r.dec.Postings2(d.Get()) + if err != nil { + return nil, errors.Wrap(err, "decode postings") + } + return p, nil +} + +func (r *Reader) Postings3(name, value string) (Postings, error) { + e, ok := r.postings[name] + if !ok { + return EmptyPostings(), nil + } + off, ok := e[value] + if !ok { + return EmptyPostings(), nil + } + d := encoding.NewDecbufAt(r.b, int(off), castagnoliTable) + if d.Err() != nil { + return nil, errors.Wrap(d.Err(), "get postings entry") + } + _, p, err := r.dec.Postings3(d.Get()) if err != nil { return nil, errors.Wrap(err, "decode postings") } @@ -1114,6 +1350,29 @@ func (dec *Decoder) Postings(b []byte) (int, Postings, error) { } } +func (dec *Decoder) Postings1(b []byte) (int, Postings, error) { + d := encoding.Decbuf{B: b} + n := d.Be32int() + l := d.Get() + return n, newBigEndianPostings(l), d.Err() +} + +func (dec *Decoder) Postings2(b []byte) (int, Postings, error) { + d := encoding.Decbuf{B: b} + n := d.Be32int() + l := d.Get() + return n, newBaseDeltaBlock16Postings(l), d.Err() +} + +func (dec *Decoder) Postings3(b []byte) (int, Postings, error) { + d := encoding.Decbuf{B: b} + n := d.Be32int() + base := uint64(d.Uvarint()) + width := int(d.Byte()) + l := d.Get() + return n, newBaseDeltaPostings(l, base, width, n), d.Err() +} + // Series decodes a series entry from the given byte slice into lset and chks. func (dec *Decoder) Series(b []byte, lbls *labels.Labels, chks *[]chunks.Meta) error { *lbls = (*lbls)[:0] diff --git a/index/index_test.go b/index/index_test.go index fb1f0405..682ed535 100644 --- a/index/index_test.go +++ b/index/index_test.go @@ -14,6 +14,7 @@ package index import ( + "fmt" "io/ioutil" "math/rand" "os" @@ -239,6 +240,257 @@ func TestIndexRW_Postings(t *testing.T) { testutil.Ok(t, ir.Close()) } +func rewriteIndex(inputFilePath, outputFilePath string) int { + var ( + labelsBuf labels.Labels + chunksBuf []chunks.Meta + err error + apkName, apkValue = AllPostingsKey() + values = map[string]map[string]struct{}{} + lenCount = map[int]int{} + larger1 = map[int]map[int]int{} + // larger2 = map[int]map[int]int{} + // count = uint64(0) + ) + + indexr, err := NewFileReader(inputFilePath) + if err != nil { + fmt.Println("cannot create index reader") + fmt.Fprintln(os.Stderr, err) + return 1 + } + defer indexr.Close() + + // Rename the symbols. + originalSymbols, err := indexr.Symbols() + if err != nil { + fmt.Println("index reader symbols") + fmt.Fprintln(os.Stderr, err) + return 1 + } + + indexw, err := NewWriter(outputFilePath) + if err != nil { + fmt.Println("index writer") + fmt.Fprintln(os.Stderr, err) + return 1 + } + defer indexw.Close() + + // Write symbols. + if err := indexw.AddSymbols(originalSymbols); err != nil { + fmt.Println("index writer symbols") + fmt.Fprintln(os.Stderr, err) + return 1 + } + + // Write Series. + posts, err := indexr.Postings1(apkName, apkValue) + if err != nil { + fmt.Println("index reader postings") + fmt.Fprintln(os.Stderr, err) + return 1 + } + + for posts.Next() { + p := posts.At() + labelsBuf = labelsBuf[:0] + chunksBuf = chunksBuf[:0] + + if err := indexr.Series(p, &labelsBuf, &chunksBuf); err != nil { + fmt.Println("index reader series") + fmt.Fprintln(os.Stderr, err) + return 1 + } + + // Recording the original labels values which is needed + // to fetch and write the postings. + for _, l := range labelsBuf { + valset, ok := values[l.Name] + if !ok { + valset = map[string]struct{}{} + values[l.Name] = valset + } + valset[l.Value] = struct{}{} + } + + if err := indexw.AddSeries(p, labelsBuf, chunksBuf...); err != nil { + fmt.Println("index writer series") + fmt.Fprintln(os.Stderr, err) + return 1 + } + } + + names := []string{} + labelValuesBuf := []string{} + for n, v := range values { + labelValuesBuf = labelValuesBuf[:0] + names = append(names, n) + + for val := range v { + labelValuesBuf = append(labelValuesBuf, val) + } + if err := indexw.WriteLabelIndex([]string{n}, labelValuesBuf); err != nil { + return 1 + } + } + names = append(names, apkName) + values[apkName] = map[string]struct{}{apkValue: struct{}{}} + sort.Strings(names) + + for _, n := range names { + labelValuesBuf = labelValuesBuf[:0] + for v := range values[n] { + labelValuesBuf = append(labelValuesBuf, v) + } + sort.Strings(labelValuesBuf) + + for _, v := range labelValuesBuf { + posts, err := indexr.Postings1(n, v) + if err != nil { + return 1 + } + arr, _ := ExpandPostings(posts) + if _, ok := lenCount[len(arr)]; ok { + lenCount[len(arr)] += 1 + } else { + lenCount[len(arr)] = 1 + } + posts, _ = indexr.Postings1(n, v) + + // if len(arr) < 512 { + l, err := indexw.WritePostings3(n, v, posts) + if err != nil { + return 1 + } + if l > uint64(len(arr) * 4 + 12) { + if _, ok := larger1[len(arr)]; !ok { + larger1[len(arr)] = map[int]int{} + } + if _, ok := larger1[len(arr)][int(l) - (len(arr) * 4 + 12)]; ok { + larger1[len(arr)][int(l) - (len(arr) * 4 + 12)] += 1 + } else { + larger1[len(arr)][int(l) - (len(arr) * 4 + 12)] = 1 + } + } + // } else { + // if _, _, err := indexw.WritePostings2(n, v, posts); err != nil { + // return 1 + // } + // } + // l, n, _ := indexw.WritePostings2(n, v, posts) + // if len(arr) > 11 { + // if l > uint64(len(arr) * 4 + 12) { + // if _, ok := larger1[len(arr)]; !ok { + // larger1[len(arr)] = map[int]int{} + // } + // if _, ok := larger1[len(arr)][int(l) - (len(arr) * 4 + 12)]; ok { + // larger1[len(arr)][int(l) - (len(arr) * 4 + 12)] += 1 + // } else { + // larger1[len(arr)][int(l) - (len(arr) * 4 + 12)] = 1 + // } + // if _, ok := larger2[len(arr)]; !ok { + // larger2[len(arr)] = map[int]int{} + // } + // if _, ok := larger2[len(arr)][n]; ok { + // larger2[len(arr)][n] += 1 + // } else { + // larger2[len(arr)][n] = 1 + // } + // } + // } + // if len(arr) == 300 { + // fmt.Println(n) + // for _, i := range arr { + // fmt.Printf("%d,", i) + // } + // fmt.Println() + // } + } + } + // fmt.Println(lenCount) + // fmt.Println(count) + fmt.Println(larger1) + // fmt.Println() + // fmt.Println(larger2) + + return 0 +} + +func TestIndexSizeComparison(t *testing.T) { + f, err := fileutil.OpenMmapFile("../../remappedindex_corrected") + testutil.Ok(t, err) + toc, err := NewTOCFromByteSlice(realByteSlice(f.Bytes())) + testutil.Ok(t, err) + t.Log("size of postings =", toc.LabelIndicesTable-toc.Postings) + t.Log(toc) + f.Close() + + + // ir, err := NewFileReader("../../remappedindex") + // testutil.Ok(t, err) + // labelNames, _ := ir.LabelNames() + // // labelValues := make(map[string][]string) + // // for _, name := range labelNames { + // // vals, _ := ir.LabelValues(name) + // // arr := make([]string, vals.Len()) + // // for i := 0; i < vals.Len(); i++ { + // // arr[i], _ = vals.At(i) + // // } + // // labelValues[name] = arr + // // } + // // iw, err := NewWriter("../../remappedindex_r16") + // // testutil.Ok(t, err) + // all := []uint64{} + // lenCount := map[int]int{} + // t.Log("labelNames size =", len(labelNames)) + // for _, name := range labelNames { + // t.Log(name) + // vals, _ := ir.LabelValues(name) + // for i := 0; i < vals.Len(); i++ { + // v, _ := vals.At(i) + // p, err := ir.Postings(name, v[0]) + // testutil.Ok(t, err) + // count := 0 + // for p.Next() { + // all = append(all, p.At()) + // count += 1 + // } + // if _, ok := lenCount[count]; ok { + // lenCount[count] += 1 + // } else { + // lenCount[count] = 1 + // } + // p, err = ir.Postings(name, v[0]) + // testutil.Ok(t, err) + // // err = iw.WritePostings2(name, v[0], p) + // // testutil.Ok(t, err) + // } + // } + // sort.Slice(all, func(i, j int) bool { return all[i] < all[j] }) + // t.Log(lenCount) + // t.Log("AllPostings len =", len(all)) + // // err = iw.WritePostings2("", "", newListPostings(all...)) + // // testutil.Ok(t, err) + // ir.Close() + // // iw.Close() + + // f, err = fileutil.OpenMmapFile("../../remappedindex_r16") + // testutil.Ok(t, err) + // toc, err = NewTOCFromByteSlice(realByteSlice(f.Bytes())) + // testutil.Ok(t, err) + // t.Log("size of postings (r16) =", toc.LabelIndicesTable-toc.Postings) + // f.Close() + rewriteIndex("../../remappedindex_corrected", "../../remappedindex_corrected_1") + f, err = fileutil.OpenMmapFile("../../remappedindex_corrected_1") + testutil.Ok(t, err) + toc, err = NewTOCFromByteSlice(realByteSlice(f.Bytes())) + testutil.Ok(t, err) + t.Log("size of postings =", toc.LabelIndicesTable-toc.Postings) + t.Log(toc) + f.Close() +} + func TestPersistence_index_e2e(t *testing.T) { dir, err := ioutil.TempDir("", "test_persistence_e2e") testutil.Ok(t, err) diff --git a/index/postings.go b/index/postings.go index 9796642a..88a0e050 100644 --- a/index/postings.go +++ b/index/postings.go @@ -2109,7 +2109,7 @@ func writeBaseDelta16Block64(e *encoding.Encbuf, vals []uint64, key uint64, valu } } -func writeBaseDeltaBlock16Postings(e *encoding.Encbuf, arr []uint32) { +func writeBaseDeltaBlock16Postings(e *encoding.Encbuf, arr []uint32) int { key := uint32(0xffffffff) // The initial key should be unique. valueSize := 16 >> 3 // The size of the element in array in bytes. mask := uint32((1 << uint(16)) - 1) // Mask for the elements in the block. @@ -2146,6 +2146,17 @@ func writeBaseDeltaBlock16Postings(e *encoding.Encbuf, arr []uint32) { for _, off := range startingOffs { e.PutBE32(off-8-uint32(startOff)) } + // e.PutUvarint32(startingOffs[0]-8-uint32(startOff)) + // width := bits.Len32(startingOffs[len(startingOffs)-1] - 4 - uint32(startOff)) + // if width == 0 { + // // key 0 will result in 0 width. + // width += 1 + // } + // e.PutByte(byte((width + 7) / 8)) + // for _, off := range startingOffs { + // putBytes(e, off - (startingOffs[len(startingOffs)-1] - 4 - uint32(startOff)), (width + 7) / 8) + // } + return len(startingOffs) - 1 } func writeBaseDeltaBlock16Postings64(e *encoding.Encbuf, arr []uint64) { diff --git a/index/postings_test.go b/index/postings_test.go index 86c947e2..4990bc8d 100644 --- a/index/postings_test.go +++ b/index/postings_test.go @@ -14,11 +14,14 @@ package index import ( + "bufio" "encoding/binary" "fmt" "math/bits" "math/rand" + "os" "sort" + "strconv" "testing" "github.com/prometheus/tsdb/encoding" @@ -1405,15 +1408,21 @@ func TestRoaringBitmapPostings64(t *testing.T) { }) } -func BenchmarkPostings(b *testing.B) { +func BenchmarkRandomPostings(b *testing.B) { num := 100000 - // mock a list as postings ls := make([]uint32, num) - ls[0] = 2 - for i := 1; i < num; i++ { - ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 - // ls[i] = ls[i-1] + 2 + existedNum := make(map[uint32]struct{}) + for i := 0; i < num; i++ { + for { + x := uint32(rand.Int31n(1000000)) + if _, ok := existedNum[x]; !ok { + ls[i] = x + existedNum[x] = struct{}{} + break + } + } } + sort.Sort(uint32slice(ls)) // bigEndianPostings. bufBE := make([]byte, num*4) @@ -1423,48 +1432,6 @@ func BenchmarkPostings(b *testing.B) { } b.Log("bigEndianPostings size =", len(bufBE)) - // baseDeltaPostings. - width := (bits.Len32(ls[len(ls)-1] - ls[0]) + 7) >> 3 - bufBD := encoding.Encbuf{} - for i := 0; i < 8 - width; i ++ { - bufBD.B = append(bufBD.B, 0) - } - for i := 0; i < num; i++ { - for j := width - 1; j >= 0; j-- { - bufBD.B = append(bufBD.B, byte(((ls[i]-ls[0])>>(8*uint(j))&0xff))) - } - // bufBD.PutBits(uint64(ls[i]-ls[0]), width) - } - b.Log("baseDeltaPostings size =", len(bufBD.Get())) - - // deltaBlockPostings. - bufDB := encoding.Encbuf{} - writeDeltaBlockPostings(&bufDB, ls) - b.Log("deltaBlockPostings size =", len(bufDB.Get())) - - // baseDeltaBlockPostings. - bufBDB := encoding.Encbuf{} - writeBaseDeltaBlockPostings(&bufBDB, ls) - b.Log("baseDeltaBlockPostings size =", len(bufBDB.Get())) - - // bitmapPostings. - bufBM := encoding.Encbuf{} - writeBitmapPostings(&bufBM, ls) - b.Log("bitmapPostings bits", bitmapBits, "size =", len(bufBM.Get())) - - // roaringBitmapPostings. - bufRBM := encoding.Encbuf{} - writeRoaringBitmapPostings(&bufRBM, ls) - b.Log("roaringBitmapPostings bits", bitmapBits, "size =", len(bufRBM.Get())) - - bufRBM2 := encoding.Encbuf{} - writeBaseDeltaBlock8Postings(&bufRBM2, ls) - b.Log("baseDeltaBlock8Postings", len(bufRBM2.Get())) - - bufRBM3 := encoding.Encbuf{} - writeBaseDeltaBlock16Postings(&bufRBM3, ls) - b.Log("baseDeltaBlock16Postings", len(bufRBM3.Get())) - bufBDB16 := encoding.Encbuf{} temp := make([]uint64, 0, len(ls)) for _, x := range ls { @@ -1473,17 +1440,13 @@ func BenchmarkPostings(b *testing.B) { writeBaseDeltaBlock16Postings64(&bufBDB16, temp) b.Log("baseDeltaBlock16Postings (64bit)", len(bufBDB16.Get())) - bufRBM4 := encoding.Encbuf{} - writeBaseDeltaBlock16PostingsV2(&bufRBM4, ls) - b.Log("baseDeltaBlock16PostingsV2", len(bufRBM4.Get())) - table := []struct { seek uint32 val uint32 found bool }{ { - ls[0] - 1, ls[0], true, + ls[0] + 1, ls[1], true, }, { ls[1000], ls[1000], true, @@ -1572,111 +1535,232 @@ func BenchmarkPostings(b *testing.B) { testutil.Assert(bench, bep.Err() == nil, "") } }) - b.Run("baseDeltaIteration", func(bench *testing.B) { + b.Run("baseDeltaBlock16PostingsIteration (64bit)", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - bdp := newBaseDeltaPostings(bufBD.Get(), uint64(ls[0]), width, len(ls)) + rbm := newBaseDeltaBlock16Postings(bufBDB16.Get()) // bench.StartTimer() for i := 0; i < num; i++ { - testutil.Assert(bench, bdp.Next() == true, "") - testutil.Equals(bench, uint64(ls[i]), bdp.At()) + testutil.Assert(bench, rbm.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), rbm.At()) } - testutil.Assert(bench, bdp.Next() == false, "") - testutil.Assert(bench, bdp.Err() == nil, "") + testutil.Assert(bench, rbm.Next() == false, "") + testutil.Assert(bench, rbm.Err() == nil, "") } }) - b.Run("baseDeltaBlockIteration", func(bench *testing.B) { + + b.Run("bigEndianSeek", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - bdbp := newBaseDeltaBlockPostings(bufBDB.Get()) + bep := newBigEndianPostings(bufBE) // bench.StartTimer() - for i := 0; i < num; i++ { - testutil.Assert(bench, bdbp.Next() == true, "") - testutil.Equals(bench, uint64(ls[i]), bdbp.At()) + for _, v := range table { + testutil.Equals(bench, v.found, bep.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), bep.At()) + testutil.Assert(bench, bep.Err() == nil, "") } - testutil.Assert(bench, bdbp.Next() == false, "") - testutil.Assert(bench, bdbp.Err() == nil, "") } }) - b.Run("roaringBitmapPostingsIteration", func(bench *testing.B) { + b.Run("baseDeltaBlock16PostingsSeek (64bit)", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - rbm := newRoaringBitmapPostings(bufRBM.Get()) + rbm := newBaseDeltaBlock16Postings(bufBDB16.Get()) // bench.StartTimer() - for i := 0; i < num; i++ { - testutil.Assert(bench, rbm.Next() == true, "") - testutil.Equals(bench, uint64(ls[i]), rbm.At()) + for _, v := range table { + testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), rbm.At()) + testutil.Assert(bench, rbm.Err() == nil, "") } - testutil.Assert(bench, rbm.Next() == false, "") - testutil.Assert(bench, rbm.Err() == nil, "") } }) - b.Run("baseDeltaBlock8PostingsIteration", func(bench *testing.B) { - bench.ResetTimer() - bench.ReportAllocs() - for j := 0; j < bench.N; j++ { - // bench.StopTimer() - rbm := newBaseDeltaBlock8Postings(bufRBM2.Get()) - // bench.StartTimer() +} - for i := 0; i < num; i++ { - testutil.Assert(bench, rbm.Next() == true, "") - testutil.Equals(bench, uint64(ls[i]), rbm.At()) - } - testutil.Assert(bench, rbm.Next() == false, "") - testutil.Assert(bench, rbm.Err() == nil, "") +func BenchmarkRealPostings(b *testing.B) { + file, err := os.Open("../../realWorldPostings.txt") + if err != nil { + panic(err) + } + defer file.Close() + + var ls []uint32 + scanner := bufio.NewScanner(file) + for scanner.Scan() { + x, err := strconv.Atoi(scanner.Text()) + if err != nil { + panic(err) } - }) - b.Run("baseDeltaBlock16PostingsIteration", func(bench *testing.B) { + ls = append(ls, uint32(x)) + } + if err := scanner.Err(); err != nil { + panic(err) + } + + // bigEndianPostings. + bufBE := make([]byte, len(ls)*4) + for i := 0; i < len(ls); i++ { + b := bufBE[i*4 : i*4+4] + binary.BigEndian.PutUint32(b, ls[i]) + } + b.Log("bigEndianPostings size =", len(bufBE)) + + width := (bits.Len32(ls[len(ls)-1] - ls[0]) + 7) >> 3 + bufBD := encoding.Encbuf{} + for i := 0; i < 8 - width; i ++ { + bufBD.B = append(bufBD.B, 0) + } + for i := 0; i < len(ls); i++ { + for j := width - 1; j >= 0; j-- { + bufBD.B = append(bufBD.B, byte(((ls[i]-ls[0])>>(8*uint(j))&0xff))) + } + // bufBD.PutBits(uint64(ls[i]-ls[0]), width) + } + b.Log("baseDeltaPostings size =", len(bufBD.Get())) + + bufBDB16 := encoding.Encbuf{} + temp := make([]uint64, 0, len(ls)) + for _, x := range ls { + temp = append(temp, uint64(x)) + } + writeBaseDeltaBlock16Postings64(&bufBDB16, temp) + b.Log("baseDeltaBlock16Postings (64bit)", len(bufBDB16.Get())) + + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] + 1, ls[1], true, + }, + { + ls[1000], ls[1000], true, + }, + { + ls[1001], ls[1001], true, + }, + { + ls[2000]+1, ls[2001], true, + }, + { + ls[3000], ls[3000], true, + }, + { + ls[3001], ls[3001], true, + }, + { + ls[4000]+1, ls[4001], true, + }, + { + ls[5000], ls[5000], true, + }, + { + ls[5001], ls[5001], true, + }, + { + ls[6000]+1, ls[6001], true, + }, + { + ls[10000], ls[10000], true, + }, + { + ls[10001], ls[10001], true, + }, + { + ls[20000]+1, ls[20001], true, + }, + { + ls[30000], ls[30000], true, + }, + { + ls[30001], ls[30001], true, + }, + { + ls[40000]+1, ls[40001], true, + }, + { + ls[50000], ls[50000], true, + }, + { + ls[50001], ls[50001], true, + }, + { + ls[60000]+1, ls[60001], true, + }, + { + ls[70000], ls[70000], true, + }, + { + ls[70001], ls[70001], true, + }, + { + ls[80000]+1, ls[80001], true, + }, + { + ls[100000], ls[100000], true, + }, + { + ls[150000]+1, ls[150001], true, + }, + { + ls[200000], ls[200000], true, + }, + { + ls[250000]+1, ls[250001], true, + }, + { + ls[300000], ls[300000], true, + }, + } + b.Run("bigEndianIteration", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - rbm := newBaseDeltaBlock16Postings(bufRBM3.Get()) + bep := newBigEndianPostings(bufBE) // bench.StartTimer() - for i := 0; i < num; i++ { - testutil.Assert(bench, rbm.Next() == true, "") - testutil.Equals(bench, uint64(ls[i]), rbm.At()) + for i := 0; i < len(ls); i++ { + testutil.Assert(bench, bep.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), bep.At()) } - testutil.Assert(bench, rbm.Next() == false, "") - testutil.Assert(bench, rbm.Err() == nil, "") + testutil.Assert(bench, bep.Next() == false, "") + testutil.Assert(bench, bep.Err() == nil, "") } }) - b.Run("baseDeltaBlock16PostingsIteration (64bit)", func(bench *testing.B) { + b.Run("baseDeltaIteration", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - rbm := newBaseDeltaBlock16Postings(bufBDB16.Get()) + bdp := newBaseDeltaPostings(bufBD.Get(), uint64(ls[0]), width, len(ls)) // bench.StartTimer() - for i := 0; i < num; i++ { - testutil.Assert(bench, rbm.Next() == true, "") - testutil.Equals(bench, uint64(ls[i]), rbm.At()) + for i := 0; i < len(ls); i++ { + testutil.Assert(bench, bdp.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), bdp.At()) } - testutil.Assert(bench, rbm.Next() == false, "") - testutil.Assert(bench, rbm.Err() == nil, "") + testutil.Assert(bench, bdp.Next() == false, "") + testutil.Assert(bench, bdp.Err() == nil, "") } }) - b.Run("baseDeltaBlock16PostingsV2Iteration", func(bench *testing.B) { + b.Run("baseDeltaBlock16PostingsIteration (64bit)", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - rbm := newBaseDeltaBlock16PostingsV2(bufRBM4.Get()) + rbm := newBaseDeltaBlock16Postings(bufBDB16.Get()) // bench.StartTimer() - for i := 0; i < num; i++ { + for i := 0; i < len(ls); i++ { testutil.Assert(bench, rbm.Next() == true, "") testutil.Equals(bench, uint64(ls[i]), rbm.At()) } @@ -1715,27 +1799,12 @@ func BenchmarkPostings(b *testing.B) { } } }) - b.Run("baseDeltaBlockSeek", func(bench *testing.B) { - bench.ResetTimer() - bench.ReportAllocs() - for j := 0; j < bench.N; j++ { - // bench.StopTimer() - bdbp := newBaseDeltaBlockPostings(bufBDB.Get()) - // bench.StartTimer() - - for _, v := range table { - testutil.Equals(bench, v.found, bdbp.Seek(uint64(v.seek))) - testutil.Equals(bench, uint64(v.val), bdbp.At()) - testutil.Assert(bench, bdbp.Err() == nil, "") - } - } - }) - b.Run("roaringBitmapPostingsSeek", func(bench *testing.B) { + b.Run("baseDeltaBlock16PostingsSeek (64bit)", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - rbm := newRoaringBitmapPostings(bufRBM.Get()) + rbm := newBaseDeltaBlock16Postings(bufBDB16.Get()) // bench.StartTimer() for _, v := range table { @@ -1745,33 +1814,250 @@ func BenchmarkPostings(b *testing.B) { } } }) - b.Run("baseDeltaBlock8PostingsSeek", func(bench *testing.B) { - bench.ResetTimer() - bench.ReportAllocs() - for j := 0; j < bench.N; j++ { - // bench.StopTimer() - rbm := newBaseDeltaBlock8Postings(bufRBM2.Get()) - // bench.StartTimer() +} + +func BenchmarkRealShortPostings(b *testing.B) { + ls := []uint64{12825376,12825699,12826041,12826364,12826706,12826880,12827211,12827553,12827885,12828225,12828529,12828852,12829194,12829555,12829878,12830239,12830581,12830904,12831265,12831569,12831892,12832234,12832557,12832937,12833351,12833672,12834014,12834299,12834641,12834983,12835306,12835648,12835971,12836313,12836655,12837006,12837346,12837650,12838011,12838334,12838695,12839009,12839330,12839653,12839995,12840308,12840629,12840971,12841313,12841655,12845998,12846017,12846036,12846967,12846986,12847005,12847993,12848012,12848031,12848962,12848981,12849000,12849988,12850007,12850026,12850510,12850519,12850528,12851503,12851522,12851541,12852529,12852548,12852567,12853555,12853574,12853593,12854581,12854600,12854619,12855493,12855512,12855531,12856462,12856481,12856500,12857488,12857507,12857526,12858571,12858590,12858609,12859540,12859559,12859578,12860623,12860642,12860661,12861649,12861668,12861687,12862618,12862637,12862656,12863701,12863720,12863739,12864613,12864632,12864651,12865582,12865601,12865620,12866608,12866627,12866646,12867577,12867596,12867615,12868717,12868736,12868755,12869980,12869999,12870018,12870949,12870968,12870987,12871975,12871994,12872013,12872830,12872849,12872868,12873856,12873875,12873894,12874882,12874901,12874920,12875851,12875870,12875889,12876877,12876896,12876915,12877846,12877865,12877884,12878872,12878891,12878910,12879898,12879917,12879936,12880981,12881000,12881019,12882007,12882026,12882045,12882919,12882938,12882957,12884002,12884021,12884040,12884971,12884990,12885009,12886054,12886073,12886092,12887023,12887042,12887061,12887992,12888011,12888030,12888961,12888980,12888999,12889987,12890006,12890025,12890929,12890947,12890965,12891892,12891911,12891930,12892918,12892937,12892956,12893944,12893963,12893982,12894970,12894989,12895008,12895445,12895768,12896110,12896433,12896775,12896949,12897280,12897622,12897954,12898294,12898598,12898921,12899263,12899624,12899947,12900308,12900650,12900973,12901334,12901638,12901961,12902303,12902626,12903006,12903420,12903741,12904083,12904368,12904710,12905052,12905375,12905717,12906040,12906382,12906724,12907075,12907415,12907719,12908080,12908403,12908764,12909078,12909399,12909722,12910064,12910377,12910698,12911040,12911382,12911724,12912085,12912408,12912750,12913073,12913415,12913589,12913920,12914262,12914594,12914934,12915238,12915561,12915903,12916264,12916587,12916948,12917290,12917613,12917974,12918278,12918601,12918943,12919266,12919646,12920060,12920381,12920723,12921008,12921350,12921692,12922015,12922357,12922680,12923022,12923364,12923715,12924055,12924359,12924720,12925043,12925404,12925718,12926039,12926362,12926704,12927018,12927339,12927681,12928023,12928365} + + bufBE := make([]byte, len(ls)*4) + for i := 0; i < len(ls); i++ { + b := bufBE[i*4 : i*4+4] + binary.BigEndian.PutUint32(b, uint32(ls[i])) + } + b.Log("bigEndianPostings size =", len(bufBE)) + + width := (bits.Len64(ls[len(ls)-1] - ls[0]) + 7) >> 3 + bufBD := encoding.Encbuf{} + for i := 0; i < 8 - width; i ++ { + bufBD.B = append(bufBD.B, 0) + } + for i := 0; i < len(ls); i++ { + for j := width - 1; j >= 0; j-- { + bufBD.B = append(bufBD.B, byte(((ls[i]-ls[0])>>(8*uint(j))&0xff))) + } + // bufBD.PutBits(uint64(ls[i]-ls[0]), width) + } + b.Log("baseDeltaPostings size =", len(bufBD.Get())) + + bufBDB16 := encoding.Encbuf{} + temp := make([]uint64, 0, len(ls)) + for _, x := range ls { + temp = append(temp, uint64(x)) + } + writeBaseDeltaBlock16Postings64(&bufBDB16, temp) + b.Log("baseDeltaBlock16Postings (64bit)", len(bufBDB16.Get())) + + table := []struct { + seek uint64 + val uint64 + found bool + }{ + { + ls[0], ls[0], true, + }, + { + ls[5], ls[5], true, + }, + { + ls[10], ls[10], true, + }, + { + ls[15], ls[15], true, + }, + { + ls[20], ls[20], true, + }, + { + ls[25], ls[25], true, + }, + { + ls[30], ls[30], true, + }, + { + ls[35], ls[35], true, + }, + { + ls[40], ls[40], true, + }, + { + ls[45], ls[45], true, + }, + { + ls[50], ls[50], true, + }, + { + ls[55], ls[55], true, + }, + { + ls[60], ls[60], true, + }, + { + ls[65], ls[65], true, + }, + { + ls[70], ls[70], true, + }, + { + ls[75], ls[75], true, + }, + { + ls[80], ls[80], true, + }, + { + ls[85], ls[85], true, + }, + { + ls[90], ls[90], true, + }, + { + ls[95], ls[95], true, + }, + { + ls[100], ls[100], true, + }, + { + ls[105], ls[105], true, + }, + { + ls[110], ls[110], true, + }, + { + ls[115], ls[115], true, + }, + { + ls[120], ls[120], true, + }, + { + ls[125], ls[125], true, + }, + { + ls[130], ls[130], true, + }, + { + ls[135], ls[135], true, + }, + { + ls[140], ls[140], true, + }, + { + ls[145], ls[145], true, + }, + { + ls[150], ls[150], true, + }, + { + ls[155], ls[155], true, + }, + { + ls[160], ls[160], true, + }, + { + ls[165], ls[165], true, + }, + { + ls[170], ls[170], true, + }, + { + ls[175], ls[175], true, + }, + { + ls[180], ls[180], true, + }, + { + ls[185], ls[185], true, + }, + { + ls[190], ls[190], true, + }, + { + ls[195], ls[195], true, + }, + { + ls[200], ls[200], true, + }, + { + ls[205], ls[205], true, + }, + { + ls[210], ls[210], true, + }, + } + b.Run("bigEndianIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + bep := newBigEndianPostings(bufBE) + // bench.StartTimer() + + for i := 0; i < len(ls); i++ { + testutil.Assert(bench, bep.Next() == true, "") + testutil.Equals(bench, ls[i], bep.At()) + } + testutil.Assert(bench, bep.Next() == false, "") + testutil.Assert(bench, bep.Err() == nil, "") + } + }) + b.Run("baseDeltaIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + bdp := newBaseDeltaPostings(bufBD.Get(), uint64(ls[0]), width, len(ls)) + // bench.StartTimer() + + for i := 0; i < len(ls); i++ { + testutil.Assert(bench, bdp.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), bdp.At()) + } + testutil.Assert(bench, bdp.Next() == false, "") + testutil.Assert(bench, bdp.Err() == nil, "") + } + }) + b.Run("baseDeltaBlock16PostingsIteration (64bit)", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + rbm := newBaseDeltaBlock16Postings(bufBDB16.Get()) + // bench.StartTimer() + + for i := 0; i < len(ls); i++ { + testutil.Assert(bench, rbm.Next() == true, "") + testutil.Equals(bench, ls[i], rbm.At()) + } + testutil.Assert(bench, rbm.Next() == false, "") + testutil.Assert(bench, rbm.Err() == nil, "") + } + }) + + b.Run("bigEndianSeek", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + bep := newBigEndianPostings(bufBE) + // bench.StartTimer() for _, v := range table { - testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) - testutil.Equals(bench, uint64(v.val), rbm.At()) - testutil.Assert(bench, rbm.Err() == nil, "") + testutil.Equals(bench, v.found, bep.Seek(v.seek)) + testutil.Equals(bench, v.val, bep.At()) + testutil.Assert(bench, bep.Err() == nil, "") } } }) - b.Run("baseDeltaBlock16PostingsSeek", func(bench *testing.B) { + b.Run("baseDeltaSeek", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - rbm := newBaseDeltaBlock16Postings(bufRBM3.Get()) + bdp := newBaseDeltaPostings(bufBD.Get(), uint64(ls[0]), width, len(ls)) // bench.StartTimer() for _, v := range table { - testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) - testutil.Equals(bench, uint64(v.val), rbm.At()) - testutil.Assert(bench, rbm.Err() == nil, "") + testutil.Equals(bench, v.found, bdp.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), bdp.At()) + testutil.Assert(bench, bdp.Err() == nil, "") } } }) @@ -1784,18 +2070,390 @@ func BenchmarkPostings(b *testing.B) { // bench.StartTimer() for _, v := range table { - testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) - testutil.Equals(bench, uint64(v.val), rbm.At()) + testutil.Equals(bench, v.found, rbm.Seek(v.seek)) + testutil.Equals(bench, v.val, rbm.At()) testutil.Assert(bench, rbm.Err() == nil, "") } } }) - b.Run("baseDeltaBlock16PostingsV2Seek", func(bench *testing.B) { +} + +func BenchmarkPostings(b *testing.B) { + num := 100000 + // mock a list as postings + ls := make([]uint32, num) + ls[0] = 2 + for i := 1; i < num; i++ { + ls[i] = ls[i-1] + uint32(rand.Int31n(25)) + 2 + // ls[i] = ls[i-1] + 2 + } + + // bigEndianPostings. + bufBE := make([]byte, num*4) + for i := 0; i < num; i++ { + b := bufBE[i*4 : i*4+4] + binary.BigEndian.PutUint32(b, ls[i]) + } + b.Log("bigEndianPostings size =", len(bufBE)) + + // baseDeltaPostings. + width := (bits.Len32(ls[len(ls)-1] - ls[0]) + 7) >> 3 + bufBD := encoding.Encbuf{} + for i := 0; i < 8 - width; i ++ { + bufBD.B = append(bufBD.B, 0) + } + for i := 0; i < num; i++ { + for j := width - 1; j >= 0; j-- { + bufBD.B = append(bufBD.B, byte(((ls[i]-ls[0])>>(8*uint(j))&0xff))) + } + // bufBD.PutBits(uint64(ls[i]-ls[0]), width) + } + b.Log("baseDeltaPostings size =", len(bufBD.Get())) + + // deltaBlockPostings. + bufDB := encoding.Encbuf{} + writeDeltaBlockPostings(&bufDB, ls) + b.Log("deltaBlockPostings size =", len(bufDB.Get())) + + // baseDeltaBlockPostings. + bufBDB := encoding.Encbuf{} + writeBaseDeltaBlockPostings(&bufBDB, ls) + b.Log("baseDeltaBlockPostings size =", len(bufBDB.Get())) + + // bitmapPostings. + bufBM := encoding.Encbuf{} + writeBitmapPostings(&bufBM, ls) + b.Log("bitmapPostings bits", bitmapBits, "size =", len(bufBM.Get())) + + // roaringBitmapPostings. + bufRBM := encoding.Encbuf{} + writeRoaringBitmapPostings(&bufRBM, ls) + b.Log("roaringBitmapPostings bits", bitmapBits, "size =", len(bufRBM.Get())) + + bufRBM2 := encoding.Encbuf{} + writeBaseDeltaBlock8Postings(&bufRBM2, ls) + b.Log("baseDeltaBlock8Postings", len(bufRBM2.Get())) + + bufRBM3 := encoding.Encbuf{} + writeBaseDeltaBlock16Postings(&bufRBM3, ls) + b.Log("baseDeltaBlock16Postings", len(bufRBM3.Get())) + + bufBDB16 := encoding.Encbuf{} + temp := make([]uint64, 0, len(ls)) + for _, x := range ls { + temp = append(temp, uint64(x)) + } + writeBaseDeltaBlock16Postings64(&bufBDB16, temp) + b.Log("baseDeltaBlock16Postings (64bit)", len(bufBDB16.Get())) + + bufRBM4 := encoding.Encbuf{} + writeBaseDeltaBlock16PostingsV2(&bufRBM4, ls) + b.Log("baseDeltaBlock16PostingsV2", len(bufRBM4.Get())) + + table := []struct { + seek uint32 + val uint32 + found bool + }{ + { + ls[0] - 1, ls[0], true, + }, + { + ls[1000], ls[1000], true, + }, + { + ls[1001], ls[1001], true, + }, + { + ls[2000]+1, ls[2001], true, + }, + { + ls[3000], ls[3000], true, + }, + { + ls[3001], ls[3001], true, + }, + { + ls[4000]+1, ls[4001], true, + }, + { + ls[5000], ls[5000], true, + }, + { + ls[5001], ls[5001], true, + }, + { + ls[6000]+1, ls[6001], true, + }, + { + ls[10000], ls[10000], true, + }, + { + ls[10001], ls[10001], true, + }, + { + ls[20000]+1, ls[20001], true, + }, + { + ls[30000], ls[30000], true, + }, + { + ls[30001], ls[30001], true, + }, + { + ls[40000]+1, ls[40001], true, + }, + { + ls[50000], ls[50000], true, + }, + { + ls[50001], ls[50001], true, + }, + { + ls[60000]+1, ls[60001], true, + }, + { + ls[70000], ls[70000], true, + }, + { + ls[70001], ls[70001], true, + }, + { + ls[80000]+1, ls[80001], true, + }, + { + ls[99999], ls[99999], true, + }, + { + ls[99999] + 10, ls[99999], false, + }, + } + + b.Run("bigEndianIteration", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + bep := newBigEndianPostings(bufBE) + // bench.StartTimer() + + for i := 0; i < num; i++ { + testutil.Assert(bench, bep.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), bep.At()) + } + testutil.Assert(bench, bep.Next() == false, "") + testutil.Assert(bench, bep.Err() == nil, "") + } + }) + // b.Run("baseDeltaIteration", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // bdp := newBaseDeltaPostings(bufBD.Get(), uint64(ls[0]), width, len(ls)) + // // bench.StartTimer() + + // for i := 0; i < num; i++ { + // testutil.Assert(bench, bdp.Next() == true, "") + // testutil.Equals(bench, uint64(ls[i]), bdp.At()) + // } + // testutil.Assert(bench, bdp.Next() == false, "") + // testutil.Assert(bench, bdp.Err() == nil, "") + // } + // }) + // b.Run("baseDeltaBlockIteration", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // bdbp := newBaseDeltaBlockPostings(bufBDB.Get()) + // // bench.StartTimer() + + // for i := 0; i < num; i++ { + // testutil.Assert(bench, bdbp.Next() == true, "") + // testutil.Equals(bench, uint64(ls[i]), bdbp.At()) + // } + // testutil.Assert(bench, bdbp.Next() == false, "") + // testutil.Assert(bench, bdbp.Err() == nil, "") + // } + // }) + // b.Run("roaringBitmapPostingsIteration", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // rbm := newRoaringBitmapPostings(bufRBM.Get()) + // // bench.StartTimer() + + // for i := 0; i < num; i++ { + // testutil.Assert(bench, rbm.Next() == true, "") + // testutil.Equals(bench, uint64(ls[i]), rbm.At()) + // } + // testutil.Assert(bench, rbm.Next() == false, "") + // testutil.Assert(bench, rbm.Err() == nil, "") + // } + // }) + // b.Run("baseDeltaBlock8PostingsIteration", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // rbm := newBaseDeltaBlock8Postings(bufRBM2.Get()) + // // bench.StartTimer() + + // for i := 0; i < num; i++ { + // testutil.Assert(bench, rbm.Next() == true, "") + // testutil.Equals(bench, uint64(ls[i]), rbm.At()) + // } + // testutil.Assert(bench, rbm.Next() == false, "") + // testutil.Assert(bench, rbm.Err() == nil, "") + // } + // }) + // b.Run("baseDeltaBlock16PostingsIteration", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // rbm := newBaseDeltaBlock16Postings(bufRBM3.Get()) + // // bench.StartTimer() + + // for i := 0; i < num; i++ { + // testutil.Assert(bench, rbm.Next() == true, "") + // testutil.Equals(bench, uint64(ls[i]), rbm.At()) + // } + // testutil.Assert(bench, rbm.Next() == false, "") + // testutil.Assert(bench, rbm.Err() == nil, "") + // } + // }) + b.Run("baseDeltaBlock16PostingsIteration (64bit)", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + rbm := newBaseDeltaBlock16Postings(bufBDB16.Get()) + // bench.StartTimer() + + for i := 0; i < num; i++ { + testutil.Assert(bench, rbm.Next() == true, "") + testutil.Equals(bench, uint64(ls[i]), rbm.At()) + } + testutil.Assert(bench, rbm.Next() == false, "") + testutil.Assert(bench, rbm.Err() == nil, "") + } + }) + // b.Run("baseDeltaBlock16PostingsV2Iteration", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // rbm := newBaseDeltaBlock16PostingsV2(bufRBM4.Get()) + // // bench.StartTimer() + + // for i := 0; i < num; i++ { + // testutil.Assert(bench, rbm.Next() == true, "") + // testutil.Equals(bench, uint64(ls[i]), rbm.At()) + // } + // testutil.Assert(bench, rbm.Next() == false, "") + // testutil.Assert(bench, rbm.Err() == nil, "") + // } + // }) + + b.Run("bigEndianSeek", func(bench *testing.B) { + bench.ResetTimer() + bench.ReportAllocs() + for j := 0; j < bench.N; j++ { + // bench.StopTimer() + bep := newBigEndianPostings(bufBE) + // bench.StartTimer() + + for _, v := range table { + testutil.Equals(bench, v.found, bep.Seek(uint64(v.seek))) + testutil.Equals(bench, uint64(v.val), bep.At()) + testutil.Assert(bench, bep.Err() == nil, "") + } + } + }) + // b.Run("baseDeltaSeek", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // bdp := newBaseDeltaPostings(bufBD.Get(), uint64(ls[0]), width, len(ls)) + // // bench.StartTimer() + + // for _, v := range table { + // testutil.Equals(bench, v.found, bdp.Seek(uint64(v.seek))) + // testutil.Equals(bench, uint64(v.val), bdp.At()) + // testutil.Assert(bench, bdp.Err() == nil, "") + // } + // } + // }) + // b.Run("baseDeltaBlockSeek", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // bdbp := newBaseDeltaBlockPostings(bufBDB.Get()) + // // bench.StartTimer() + + // for _, v := range table { + // testutil.Equals(bench, v.found, bdbp.Seek(uint64(v.seek))) + // testutil.Equals(bench, uint64(v.val), bdbp.At()) + // testutil.Assert(bench, bdbp.Err() == nil, "") + // } + // } + // }) + // b.Run("roaringBitmapPostingsSeek", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // rbm := newRoaringBitmapPostings(bufRBM.Get()) + // // bench.StartTimer() + + // for _, v := range table { + // testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + // testutil.Equals(bench, uint64(v.val), rbm.At()) + // testutil.Assert(bench, rbm.Err() == nil, "") + // } + // } + // }) + // b.Run("baseDeltaBlock8PostingsSeek", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // rbm := newBaseDeltaBlock8Postings(bufRBM2.Get()) + // // bench.StartTimer() + + // for _, v := range table { + // testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + // testutil.Equals(bench, uint64(v.val), rbm.At()) + // testutil.Assert(bench, rbm.Err() == nil, "") + // } + // } + // }) + // b.Run("baseDeltaBlock16PostingsSeek", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // rbm := newBaseDeltaBlock16Postings(bufRBM3.Get()) + // // bench.StartTimer() + + // for _, v := range table { + // testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + // testutil.Equals(bench, uint64(v.val), rbm.At()) + // testutil.Assert(bench, rbm.Err() == nil, "") + // } + // } + // }) + b.Run("baseDeltaBlock16PostingsSeek (64bit)", func(bench *testing.B) { bench.ResetTimer() bench.ReportAllocs() for j := 0; j < bench.N; j++ { // bench.StopTimer() - rbm := newBaseDeltaBlock16PostingsV2(bufRBM4.Get()) + rbm := newBaseDeltaBlock16Postings(bufBDB16.Get()) // bench.StartTimer() for _, v := range table { @@ -1805,6 +2463,21 @@ func BenchmarkPostings(b *testing.B) { } } }) + // b.Run("baseDeltaBlock16PostingsV2Seek", func(bench *testing.B) { + // bench.ResetTimer() + // bench.ReportAllocs() + // for j := 0; j < bench.N; j++ { + // // bench.StopTimer() + // rbm := newBaseDeltaBlock16PostingsV2(bufRBM4.Get()) + // // bench.StartTimer() + + // for _, v := range table { + // testutil.Equals(bench, v.found, rbm.Seek(uint64(v.seek))) + // testutil.Equals(bench, uint64(v.val), rbm.At()) + // testutil.Assert(bench, rbm.Err() == nil, "") + // } + // } + // }) } func TestIntersectWithMerge(t *testing.T) {