diff --git a/badger/helpers.go b/badger/helpers.go index 671c89e..cc78b6a 100644 --- a/badger/helpers.go +++ b/badger/helpers.go @@ -147,17 +147,29 @@ func getAddrTagElements(tagValue string) (kind uint16, pkb []byte, d string) { // mergeSortMultipleBatches takes the results of multiple iterators, which are already sorted, // and merges them into a single big sorted slice func mergeSortMultiple(batches [][]*nostr.Event, limit int) []*nostr.Event { + // clear up empty lists here while simultaneously computing the total count. + // this helps because if there are a bunch of empty lists then this pre-clean + // step will get us in the faster 'merge' branch otherwise we would go to the other. + // we would have to do the cleaning anyway inside it. + // and even if we still go on the other we save one iteration by already computing the + // total count. + total := 0 + for i := len(batches) - 1; i >= 0; i-- { + if len(batches[i]) == 0 { + batches = swapDelete(batches, i) + } else { + total += len(batches[i]) + } + } + + // this amazing equation will ensure that if one of the two sides goes very small (like 1 or 2) + // the other can go very high (like 500) and we're still in the 'merge' branch. + // if values go somewhere in the middle then they may match the 'merge' branch (batches=20,limit=70) + // or not (batches=25, limit=60) if math.Log(float64(len(batches)*2))+math.Log(float64(limit)) < 8 { - // this amazing function will ensure that if one of the two sides goes very small (like 1 or 2) - // the other can go very high (like 500) and we're still here. if values go somewhere in the middle - // then they may match here (batches=20,limit=70) or not (batches=25, limit=60) - return mergesortedslices.MergeFuncLimit(batches, nostr.CompareEventPtr, limit) + return mergesortedslices.MergeFuncLimitNoEmptyLists(batches, nostr.CompareEventPtr, limit) } else { // use quicksort in a dumb way that will still be fast because it's cheated - total := 0 - for _, one := range batches { - total += len(one) - } merged := make([]*nostr.Event, total) lastIndex := 0 @@ -201,3 +213,8 @@ func filterMatchesTags(ef *nostr.Filter, event *nostr.Event) bool { } return true } + +func swapDelete[A any](arr []A, i int) []A { + arr[i] = arr[len(arr)-1] + return arr[:len(arr)-1] +} diff --git a/go.mod b/go.mod index 75727e5..236a5e2 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/elastic/go-elasticsearch/v8 v8.10.1 github.com/fergusstrange/embedded-postgres v1.28.0 github.com/fiatjaf/cli/v3 v3.0.0-20240723181502-e7dd498b16ae - github.com/fiatjaf/merge-sorted-slices v0.0.5 + github.com/fiatjaf/merge-sorted-slices v0.0.6 github.com/go-sql-driver/mysql v1.7.1 github.com/jmoiron/sqlx v1.3.5 github.com/lib/pq v1.10.9 diff --git a/go.sum b/go.sum index df23014..d4f70e2 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,8 @@ github.com/fergusstrange/embedded-postgres v1.28.0 h1:Atixd24HCuBHBavnG4eiZAjRiz github.com/fergusstrange/embedded-postgres v1.28.0/go.mod h1:t/MLs0h9ukYM6FSt99R7InCHs1nW0ordoVCcnzmpTYw= github.com/fiatjaf/cli/v3 v3.0.0-20240723181502-e7dd498b16ae h1:0B/1dU3YECIbPoBIRTQ4c0scZCNz9TVHtQpiODGrTTo= github.com/fiatjaf/cli/v3 v3.0.0-20240723181502-e7dd498b16ae/go.mod h1:aAWPO4bixZZxPtOnH6K3q4GbQ0jftUNDW9Oa861IRew= -github.com/fiatjaf/merge-sorted-slices v0.0.5 h1:Mmt3TdONTnhp3F4YHN/Uta8tFOm5RhBhdzuLEucStAw= -github.com/fiatjaf/merge-sorted-slices v0.0.5/go.mod h1:k7H3l+OhO2m/PA99FdBjF29Z0TAwRClgKA9PvoT6obY= +github.com/fiatjaf/merge-sorted-slices v0.0.6 h1:6jawP9Zx5RTKEHvQt6z+8zvmtCkb1JTz1498N3OE70A= +github.com/fiatjaf/merge-sorted-slices v0.0.6/go.mod h1:k7H3l+OhO2m/PA99FdBjF29Z0TAwRClgKA9PvoT6obY= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=