diff --git a/tsdb/tsm1/reader_prefix_tree.go b/tsdb/tsm1/reader_prefix_tree.go index b03f08a53e..059de6c04d 100644 --- a/tsdb/tsm1/reader_prefix_tree.go +++ b/tsdb/tsm1/reader_prefix_tree.go @@ -6,6 +6,12 @@ type prefixTreeKey [8]byte const prefixTreeKeySize = len(prefixTreeKey{}) +// prefixTree is a type that keeps track of a slice of time ranges for prefixes and allows +// querying for all of the time ranges for prefixes that match a provided key. It chunks +// added prefixes by 8 bytes and then by 1 byte because typical prefixes will be 8 or 16 +// bytes. This allows for effectively O(1) searches, but degrades to O(len(key)) in the +// worst case when there is a matching prefix for every byte of the key. Appending a prefix +// is similar. type prefixTree struct { values []TimeRange short map[byte]*prefixTree diff --git a/tsdb/tsm1/reader_prefix_tree_test.go b/tsdb/tsm1/reader_prefix_tree_test.go index 955901dadd..392523639b 100644 --- a/tsdb/tsm1/reader_prefix_tree_test.go +++ b/tsdb/tsm1/reader_prefix_tree_test.go @@ -45,6 +45,36 @@ func TestPrefixTree(t *testing.T) { }) } +// Typical results on a 2018 MPB. Pay special attention to the +// 8 and 16 results as they are the most likely. +// +// BenchmarkPrefixTree/Append/0-8 300000000 5.93 ns/op +// BenchmarkPrefixTree/Append/4-8 20000000 93.7 ns/op +// BenchmarkPrefixTree/Append/8-8 100000000 12.9 ns/op +// BenchmarkPrefixTree/Append/12-8 20000000 100.0 ns/op +// BenchmarkPrefixTree/Append/16-8 100000000 20.4 ns/op +// BenchmarkPrefixTree/Append/20-8 20000000 111.0 ns/op +// BenchmarkPrefixTree/Append/24-8 50000000 28.5 ns/op +// BenchmarkPrefixTree/Append/28-8 20000000 118.0 ns/op +// BenchmarkPrefixTree/Append/32-8 50000000 35.8 ns/op +// BenchmarkPrefixTree/Search/Best/0-8 300000000 5.76 ns/op +// BenchmarkPrefixTree/Search/Best/4-8 20000000 102.0 ns/op +// BenchmarkPrefixTree/Search/Best/8-8 100000000 18.5 ns/op +// BenchmarkPrefixTree/Search/Best/12-8 20000000 116.0 ns/op +// BenchmarkPrefixTree/Search/Best/16-8 50000000 31.9 ns/op +// BenchmarkPrefixTree/Search/Best/20-8 10000000 131.0 ns/op +// BenchmarkPrefixTree/Search/Best/24-8 30000000 45.3 ns/op +// BenchmarkPrefixTree/Search/Best/28-8 10000000 142.0 ns/op +// BenchmarkPrefixTree/Search/Best/32-8 20000000 58.0 ns/op +// BenchmarkPrefixTree/Search/Worst/0-8 300000000 5.79 ns/op +// BenchmarkPrefixTree/Search/Worst/4-8 20000000 79.2 ns/op +// BenchmarkPrefixTree/Search/Worst/8-8 10000000 199.0 ns/op +// BenchmarkPrefixTree/Search/Worst/12-8 5000000 301.0 ns/op +// BenchmarkPrefixTree/Search/Worst/16-8 3000000 422.0 ns/op +// BenchmarkPrefixTree/Search/Worst/20-8 3000000 560.0 ns/op +// BenchmarkPrefixTree/Search/Worst/24-8 2000000 683.0 ns/op +// BenchmarkPrefixTree/Search/Worst/28-8 2000000 772.0 ns/op +// BenchmarkPrefixTree/Search/Worst/32-8 2000000 875.0 ns/op func BenchmarkPrefixTree(b *testing.B) { b.Run("Append", func(b *testing.B) { run := func(b *testing.B, prefix []byte) { diff --git a/tsdb/tsm1/reader_time_range.go b/tsdb/tsm1/reader_time_range.go index a588799de7..a800df5005 100644 --- a/tsdb/tsm1/reader_time_range.go +++ b/tsdb/tsm1/reader_time_range.go @@ -51,7 +51,7 @@ func timeRangesCoverEntries(merger timeRangeMerger, entries []IndexEntry) (cover return len(entries) == 0 } -// timeRangeMerger is a special purpose data structure to merge three sources off +// timeRangeMerger is a special purpose data structure to merge three sources of // TimeRanges so that we can check if they cover a slice of index entries. type timeRangeMerger struct { sorted []TimeRange