Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 10 additions & 16 deletions frequencies/items_sketch.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ func NewFrequencyItemsSketchFromSlice[C comparable](slc []byte, hasher common.It
}

pre0, err := checkPreambleSize(slc) //make sure preamble will fit
if err != nil {
return nil, err
}
maxPreLongs := internal.FamilyEnum.Frequency.MaxPreLongs

preLongs := extractPreLongs(pre0) //Byte 0
Expand Down Expand Up @@ -143,45 +146,36 @@ func NewFrequencyItemsSketchFromSlice[C comparable](slc []byte, hasher common.It
if empty {
return NewFrequencyItemsSketchWithMaxMapSize[C](1<<_LG_MIN_MAP_SIZE, hasher, serde)
}
// Get full preamble
preArr := make([]int64, preLongs)
for j := 0; j < preLongs; j++ {
preArr[j] = int64(binary.LittleEndian.Uint64(slc[j<<3:]))
}

fis, err := NewFrequencyItemsSketch[C](lgMaxMapSize, lgCurMapSize, hasher, serde)
if err != nil {
return nil, err
}
fis.streamWeight = 0 // update after
fis.offset = preArr[3]
fis.offset = int64(binary.LittleEndian.Uint64(slc[24:]))

preBytes := preLongs << 3
activeItems := extractActiveItems(preArr[1])
activeItems := extractActiveItems(int64(binary.LittleEndian.Uint64(slc[8:])))

// Get countArray
countArray := make([]int64, activeItems)
reqBytes := preBytes + activeItems*8 // count Arr only
if len(slc) < reqBytes {
return nil, fmt.Errorf("possible Corruption: Insufficient bytes in array: %d, %d", len(slc), reqBytes)
}
for j := 0; j < activeItems; j++ {
countArray[j] = int64(binary.LittleEndian.Uint64(slc[preBytes+j<<3:]))
}
// Get itemArray
itemsOffset := preBytes + (8 * activeItems)
itemArray, err := serde.DeserializeManyFromSlice(slc[itemsOffset:], 0, activeItems)
if err != nil {
return nil, err
}
// update the sketch
// update the sketch, decoding counts in place
counts := slc[preBytes:itemsOffset]
for j := 0; j < activeItems; j++ {
err := fis.UpdateMany(itemArray[j], countArray[j])
if err != nil {
count := int64(binary.LittleEndian.Uint64(counts[j<<3:]))
if err := fis.UpdateMany(itemArray[j], count); err != nil {
return nil, err
}
}
fis.streamWeight = preArr[2] // override streamWeight due to updating
fis.streamWeight = int64(binary.LittleEndian.Uint64(slc[16:])) // override streamWeight due to updating
return fis, nil
}

Expand Down
61 changes: 60 additions & 1 deletion frequencies/items_sketch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ import (
"strconv"
"testing"

"github.com/apache/datasketches-go/common"
"github.com/stretchr/testify/assert"

"github.com/apache/datasketches-go/common"
)

func TestEmpty(t *testing.T) {
Expand Down Expand Up @@ -671,3 +672,61 @@ func BenchmarkSlicesSortFuncRow(b *testing.B) {
})
}
}

var benchmarkItemsSketchFromSliceSink any

func BenchmarkItemsSketchFromSlice(b *testing.B) {
benchmarkItemsSketchFromSlice(
b,
"int64",
common.ItemSketchLongHasher{},
common.ItemSketchLongSerDe{},
func(index int) int64 { return int64(index) },
)
benchmarkItemsSketchFromSlice(
b,
"string",
common.ItemSketchStringHasher{},
common.ItemSketchStringSerDe{},
func(index int) string { return "item-" + strconv.Itoa(index) },
)
}

func benchmarkItemsSketchFromSlice[C comparable](
b *testing.B,
itemType string,
hasher common.ItemSketchHasher[C],
serde common.ItemSketchSerde[C],
itemAt func(int) C,
) {
for _, mapSize := range []int{64, 256, 1024} {
activeItems := mapSize * 3 / 4
b.Run(itemType+"/items="+strconv.Itoa(activeItems), func(b *testing.B) {
sketch, err := NewFrequencyItemsSketchWithMaxMapSize(mapSize, hasher, serde)
if err != nil {
b.Fatal(err)
}
for index := 0; index < activeItems; index++ {
if err := sketch.UpdateMany(itemAt(index), int64(index+1)); err != nil {
b.Fatal(err)
}
}

serialized, err := sketch.ToSlice()
if err != nil {
b.Fatal(err)
}
b.SetBytes(int64(len(serialized)))
b.ReportAllocs()
b.ResetTimer()

for iteration := 0; iteration < b.N; iteration++ {
restored, err := NewFrequencyItemsSketchFromSlice(serialized, hasher, serde)
if err != nil {
b.Fatal(err)
}
benchmarkItemsSketchFromSliceSink = restored
}
})
}
}
38 changes: 14 additions & 24 deletions frequencies/longs_sketch.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,46 +130,36 @@ func NewLongsSketchFromSlice(slc []byte) (*LongsSketch, error) {
if empty {
return NewLongsSketch(lgMaxMapSize, _LG_MIN_MAP_SIZE)
}
// get full preamble
preArr := make([]int64, preLongs)
for i := 0; i < preLongs; i++ {
preArr[i] = int64(binary.LittleEndian.Uint64(slc[i<<3:]))
}

fls, err := NewLongsSketch(lgMaxMapSize, lgCurMapSize)
if err != nil {
return nil, err
}
fls.streamWeight = 0 //update after
fls.offset = preArr[3]
fls.offset = int64(binary.LittleEndian.Uint64(slc[24:]))

preBytes := preLongs << 3
activeItems := extractActiveItems(preArr[1])
activeItems := extractActiveItems(int64(binary.LittleEndian.Uint64(slc[8:])))

// Get countArray
countArray := make([]int64, activeItems)
reqBytes := preBytes + 2*activeItems*8 //count Arr + Items Arr
if len(slc) < reqBytes {
return nil, fmt.Errorf("possible Corruption: Insufficient bytes in array: %d, %d", len(slc), reqBytes)
}
for i := 0; i < activeItems; i++ {
countArray[i] = int64(binary.LittleEndian.Uint64(slc[preBytes+(i<<3):]))
}

// Get itemArray
itemsOffset := preBytes + (8 * activeItems)
itemArray := make([]int64, activeItems)
// UpdateMany the sketch, decoding counts and items straight out of slc.
// Counts occupy the activeItems longs starting at preBytes; items follow them.
itemsOffset := preBytes + (activeItems << 3)
counts := slc[preBytes:itemsOffset]
items := slc[itemsOffset:reqBytes]
for i := 0; i < activeItems; i++ {
itemArray[i] = int64(binary.LittleEndian.Uint64(slc[itemsOffset+(i<<3):]))
count := int64(binary.LittleEndian.Uint64(counts[i<<3:]))
item := int64(binary.LittleEndian.Uint64(items[i<<3:]))
if err := fls.UpdateMany(item, count); err != nil {
return nil, err
}
}

// UpdateMany the sketch
for i := 0; i < activeItems && err == nil; i++ {
err = fls.UpdateMany(itemArray[i], countArray[i])
}
if err != nil {
return nil, err
}
fls.streamWeight = preArr[2] //override streamWeight due to updating
fls.streamWeight = int64(binary.LittleEndian.Uint64(slc[16:])) //override streamWeight due to updating
return fls, nil
}

Expand Down
33 changes: 33 additions & 0 deletions frequencies/longs_sketch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package frequencies
import (
"encoding/binary"
"fmt"
"strconv"
"strings"
"testing"

Expand Down Expand Up @@ -678,3 +679,35 @@ func BenchmarkLongsSketchToSlice(b *testing.B) {
})
}
}

var benchmarkLongsSketchFromSliceSink *LongsSketch

func BenchmarkLongsSketchFromSlice(b *testing.B) {
for _, mapSize := range []int{64, 256, 1024} {
activeItems := mapSize * 3 / 4
b.Run("items="+strconv.Itoa(activeItems), func(b *testing.B) {
sketch, err := NewLongsSketchWithMaxMapSize(mapSize)
if err != nil {
b.Fatal(err)
}
for index := 0; index < activeItems; index++ {
if err := sketch.UpdateMany(int64(index), int64(index+1)); err != nil {
b.Fatal(err)
}
}

serialized := sketch.ToSlice()
b.SetBytes(int64(len(serialized)))
b.ReportAllocs()
b.ResetTimer()

for iteration := 0; iteration < b.N; iteration++ {
restored, err := NewLongsSketchFromSlice(serialized)
if err != nil {
b.Fatal(err)
}
benchmarkLongsSketchFromSliceSink = restored
}
})
}
}
Loading