Skip to content
Open
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
12 changes: 6 additions & 6 deletions frequencies/items_sketch.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,20 +283,20 @@ func (i *ItemsSketch[C]) frequencies(item C) (est, lower, upper int64, err error
// threshold to include items in the result list
// errorType determines whether no false positives or no false negatives are desired.
// an array of frequent items
func (i *ItemsSketch[C]) GetFrequentItemsWithThreshold(threshold int64, errorType errorType) ([]*RowItem[C], error) {
func (i *ItemsSketch[C]) GetFrequentItemsWithThreshold(threshold int64, errorType errorType) ([]RowItem[C], error) {
finalThreshold := i.GetMaximumError()
if threshold > finalThreshold {
finalThreshold = threshold
}
return i.sortItems(finalThreshold, errorType)
}

// GetFrequentItems returns an array of Row that include frequent items, estimates, upper and
// GetFrequentItems returns an array of RowItem that include frequent items, estimates, upper and
// lower bounds given an ErrorCondition and the default threshold.
// This is the same as GetFrequentItemsWithThreshold(getMaximumError(), errorType)
//
// errorType determines whether no false positives or no false negatives are desired.
func (i *ItemsSketch[C]) GetFrequentItems(errorType errorType) ([]*RowItem[C], error) {
func (i *ItemsSketch[C]) GetFrequentItems(errorType errorType) ([]RowItem[C], error) {
return i.sortItems(i.GetMaximumError(), errorType)
}

Expand Down Expand Up @@ -514,8 +514,8 @@ func (i *ItemsSketch[C]) String() string {
return sb.String()
}

func (i *ItemsSketch[C]) sortItems(threshold int64, errorType errorType) ([]*RowItem[C], error) {
rowList := make([]*RowItem[C], 0)
func (i *ItemsSketch[C]) sortItems(threshold int64, errorType errorType) ([]RowItem[C], error) {
rowList := make([]RowItem[C], 0, i.hashMap.numActive)
iter := i.hashMap.iterator()
if errorType == ErrorTypeEnum.NoFalseNegatives {
for iter.next() {
Expand All @@ -541,7 +541,7 @@ func (i *ItemsSketch[C]) sortItems(threshold int64, errorType errorType) ([]*Row
}
}

slices.SortFunc(rowList, func(a, b *RowItem[C]) int {
slices.SortFunc(rowList, func(a, b RowItem[C]) int {
if a.est > b.est {
return -1
}
Expand Down
15 changes: 7 additions & 8 deletions frequencies/items_sketch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,7 @@ func TestMisc(t *testing.T) {
assert.Equal(t, row.GetUpperBound(), int64(1))
s := row.String()
t.Log(s)
var nullRow *RowItem[int64]
assert.NotEqual(t, row, nullRow)
assert.NotEqual(t, RowItem[int64]{}, row)
}

func TestToString(t *testing.T) {
Expand All @@ -417,7 +416,7 @@ func TestFrequentItems1(t *testing.T) {
rows, err := fis.GetFrequentItems(ErrorTypeEnum.NoFalsePositives)
assert.NoError(t, err)
row := rows[0]
assert.NotNil(t, row)
assert.NotEqual(t, RowItem[int64]{}, row)
assert.Equal(t, row.GetItem(), int64(1))
assert.Equal(t, row.GetEstimate(), int64(1))
assert.Equal(t, row.GetUpperBound(), int64(1))
Expand Down Expand Up @@ -610,8 +609,8 @@ func benchmarkItemsSketchToSlice[C comparable](
}
}

func generateTestRowItems(n int) []*RowItem[string] {
items := make([]*RowItem[string], n)
func generateTestRowItems(n int) []RowItem[string] {
items := make([]RowItem[string], n)
for i := 0; i < n; i++ {
est := rand.Int63n(10000)
items[i] = newRowItem(
Expand All @@ -634,7 +633,7 @@ func BenchmarkSortSliceRow(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
rowList := make([]*RowItem[string], len(original))
rowList := make([]RowItem[string], len(original))
copy(rowList, original)

sort.Slice(rowList, func(i, j int) bool {
Expand All @@ -655,10 +654,10 @@ func BenchmarkSlicesSortFuncRow(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
rowList := make([]*RowItem[string], len(original))
rowList := make([]RowItem[string], len(original))
copy(rowList, original)

slices.SortFunc(rowList, func(a, b *RowItem[string]) int {
slices.SortFunc(rowList, func(a, b RowItem[string]) int {
if a.est > b.est {
return -1
}
Expand Down
10 changes: 5 additions & 5 deletions frequencies/longs_sketch.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ func (s *LongsSketch) GetUpperBound(item int64) (int64, error) {
// threshold to include items in the result list
// errorType determines whether no false positives or no false negatives are desired.
// an array of frequent items
func (s *LongsSketch) GetFrequentItemsWithThreshold(threshold int64, errorType errorType) ([]*Row, error) {
func (s *LongsSketch) GetFrequentItemsWithThreshold(threshold int64, errorType errorType) ([]Row, error) {
finalThreshold := s.GetMaximumError()
if threshold > finalThreshold {
finalThreshold = threshold
Expand All @@ -355,7 +355,7 @@ func (s *LongsSketch) GetFrequentItemsWithThreshold(threshold int64, errorType e
// This is the same as GetFrequentItemsWithThreshold(getMaximumError(), errorType)
//
// errorType determines whether no false positives or no false negatives are desired.
func (s *LongsSketch) GetFrequentItems(errorType errorType) ([]*Row, error) {
func (s *LongsSketch) GetFrequentItems(errorType errorType) ([]Row, error) {
return s.sortItems(s.GetMaximumError(), errorType)
}

Expand Down Expand Up @@ -555,8 +555,8 @@ func (s *LongsSketch) String() string {
return sb.String()
}

func (s *LongsSketch) sortItems(threshold int64, errorType errorType) ([]*Row, error) {
rowList := make([]*Row, 0)
func (s *LongsSketch) sortItems(threshold int64, errorType errorType) ([]Row, error) {
rowList := make([]Row, 0, s.hashMap.numActive)
iter := s.hashMap.iterator()
if errorType == ErrorTypeEnum.NoFalseNegatives {
for iter.next() {
Expand All @@ -582,7 +582,7 @@ func (s *LongsSketch) sortItems(threshold int64, errorType errorType) ([]*Row, e
}
}

slices.SortFunc(rowList, func(a, b *Row) int {
slices.SortFunc(rowList, func(a, b Row) int {
if a.est > b.est {
return -1
}
Expand Down
8 changes: 4 additions & 4 deletions frequencies/longs_sketch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import (
"strings"
"testing"

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

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

func TestFrequentItemsStringSerial(t *testing.T) {
Expand Down Expand Up @@ -533,9 +534,8 @@ func printRows(t *testing.T, fls *LongsSketch, errorType errorType) {
s2 := row.String()
fmt.Println(s2)
}
if len(rows) > 0 { //check equals null case
var nullRow *Row
assert.NotEqual(t, rows[0], nullRow)
if len(rows) > 0 {
assert.NotEqual(t, Row{}, rows[0])
}
}

Expand Down
8 changes: 4 additions & 4 deletions frequencies/row.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@ type RowItem[C comparable] struct {
lb int64
}

func newRow(item int64, estimate int64, ub int64, lb int64) *Row {
return &Row{
func newRow(item int64, estimate int64, ub int64, lb int64) Row {
return Row{
item: item,
est: estimate,
ub: ub,
lb: lb,
}
}

func newRowItem[C comparable](item C, estimate int64, ub int64, lb int64) *RowItem[C] {
return &RowItem[C]{
func newRowItem[C comparable](item C, estimate int64, ub int64, lb int64) RowItem[C] {
return RowItem[C]{
item: item,
est: estimate,
ub: ub,
Expand Down
199 changes: 199 additions & 0 deletions frequencies/row_benchmark_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package frequencies

import (
"math/bits"
"slices"
"strconv"
"testing"

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

// Sinks, to keep the compiler from eliding the benchmarked work.
var (
benchRowPointerSink []*Row
benchRowValueSink []Row
benchRowItemPointerSink []*RowItem[string]
benchRowItemValueSink []RowItem[string]
)

func benchMapSizeFor(n int) int {
need := (n*4 + 2) / 3
if need < 1<<_LG_MIN_MAP_SIZE {
return 1 << _LG_MIN_MAP_SIZE
}
return 1 << bits.Len(uint(need-1))
}

func newBenchLongsSketch(b *testing.B, n int) *LongsSketch {
b.Helper()
sk, err := NewLongsSketchWithMaxMapSize(benchMapSizeFor(n))
if err != nil {
b.Fatal(err)
}
for i := 0; i < n; i++ {
if err := sk.UpdateMany(int64(i), int64(i%97)+1); err != nil {
b.Fatal(err)
}
}
if got := sk.GetNumActiveItems(); got != n {
b.Fatalf("want %d active items, got %d (sketch purged)", n, got)
}
return sk
}

func newBenchItemsSketch(b *testing.B, n int) *ItemsSketch[string] {
b.Helper()
sk, err := NewFrequencyItemsSketchWithMaxMapSize[string](
benchMapSizeFor(n), common.ItemSketchStringHasher{}, common.ItemSketchStringSerDe{})
if err != nil {
b.Fatal(err)
}
for i := 0; i < n; i++ {
if err := sk.UpdateMany("item"+strconv.Itoa(i), int64(i%97)+1); err != nil {
b.Fatal(err)
}
}
if got := sk.GetNumActiveItems(); got != n {
b.Fatalf("want %d active items, got %d (sketch purged)", n, got)
}
return sk
}

func keep(errorType errorType, lb, ub, threshold int64) bool {
if errorType == ErrorTypeEnum.NoFalseNegatives {
return ub >= threshold
}
return lb >= threshold
}

// sortItemsPointerLong is the pre-change LongsSketch.sortItems, kept for comparison.
func sortItemsPointerLong(s *LongsSketch, threshold int64, errorType errorType) ([]*Row, error) {
rowList := make([]*Row, 0)
iter := s.hashMap.iterator()
for iter.next() {
est, lb, ub, err := s.frequencies(iter.getKey())
if err != nil {
return nil, err
}
if keep(errorType, lb, ub, threshold) {
rowList = append(rowList, &Row{item: iter.getKey(), est: est, ub: ub, lb: lb})
}
}
slices.SortFunc(rowList, func(a, b *Row) int {
if a.est > b.est {
return -1
}
if a.est < b.est {
return 1
}
return 0
})
return rowList, nil
}

// sortItemsPointerItem is the pre-change ItemsSketch.sortItems, kept for comparison.
func sortItemsPointerItem[C comparable](i *ItemsSketch[C], threshold int64, errorType errorType) ([]*RowItem[C], error) {
rowList := make([]*RowItem[C], 0)
iter := i.hashMap.iterator()
for iter.next() {
est, lb, ub, err := i.frequencies(iter.getKey())
if err != nil {
return nil, err
}
if keep(errorType, lb, ub, threshold) {
rowList = append(rowList, &RowItem[C]{item: iter.getKey(), est: est, ub: ub, lb: lb})
}
}
slices.SortFunc(rowList, func(a, b *RowItem[C]) int {
if a.est > b.est {
return -1
}
if a.est < b.est {
return 1
}
return 0
})
return rowList, nil
}

var benchRowSizes = []int{10, 100, 1000, 10000}

func benchLongs(b *testing.B, run func(*LongsSketch, int64) error) {
for _, size := range benchRowSizes {
b.Run("size="+strconv.Itoa(size), func(b *testing.B) {
sk := newBenchLongsSketch(b, size)
threshold := sk.GetMaximumError()
b.ReportAllocs()
for b.Loop() {
if err := run(sk, threshold); err != nil {
b.Fatal(err)
}
}
})
}
}

func benchItems(b *testing.B, run func(*ItemsSketch[string], int64) error) {
for _, size := range benchRowSizes {
b.Run("size="+strconv.Itoa(size), func(b *testing.B) {
sk := newBenchItemsSketch(b, size)
threshold := sk.GetMaximumError()
b.ReportAllocs()
for b.Loop() {
if err := run(sk, threshold); err != nil {
b.Fatal(err)
}
}
})
}
}

func BenchmarkSortItems_Row_Pointer(b *testing.B) {
benchLongs(b, func(sk *LongsSketch, th int64) error {
rows, err := sortItemsPointerLong(sk, th, ErrorTypeEnum.NoFalseNegatives)
benchRowPointerSink = rows
return err
})
}

func BenchmarkSortItems_Row_Value(b *testing.B) {
benchLongs(b, func(sk *LongsSketch, th int64) error {
rows, err := sk.sortItems(th, ErrorTypeEnum.NoFalseNegatives)
benchRowValueSink = rows
return err
})
}

func BenchmarkSortItems_RowItem_Pointer(b *testing.B) {
benchItems(b, func(sk *ItemsSketch[string], th int64) error {
rows, err := sortItemsPointerItem(sk, th, ErrorTypeEnum.NoFalseNegatives)
benchRowItemPointerSink = rows
return err
})
}

func BenchmarkSortItems_RowItem_Value(b *testing.B) {
benchItems(b, func(sk *ItemsSketch[string], th int64) error {
rows, err := sk.sortItems(th, ErrorTypeEnum.NoFalseNegatives)
benchRowItemValueSink = rows
return err
})
}
Loading