From c0af52228a94a526b6e2efa14783d5ac55099bb5 Mon Sep 17 00:00:00 2001
From: Aliaksandr Valialkin <valyala@victoriametrics.com>
Date: Mon, 11 Jul 2022 11:57:31 +0300
Subject: [PATCH] app/vmselect/netstorage: add benchmarks for mergeSortBlocks

This is a follow-up for 743ff84863a3ce6087544ca85241f1f0b60c3f37
---
 app/vmselect/netstorage/netstorage.go         |  4 +-
 .../netstorage/netstorage_timing_test.go      | 78 +++++++++++++++++++
 2 files changed, 80 insertions(+), 2 deletions(-)
 create mode 100644 app/vmselect/netstorage/netstorage_timing_test.go

diff --git a/app/vmselect/netstorage/netstorage.go b/app/vmselect/netstorage/netstorage.go
index 01755db7c4..0b45386e56 100644
--- a/app/vmselect/netstorage/netstorage.go
+++ b/app/vmselect/netstorage/netstorage.go
@@ -561,8 +561,8 @@ func equalTimestampsPrefix(a, b []int64) int {
 func binarySearchTimestamps(timestamps []int64, ts int64) int {
 	// The code has been adapted from sort.Search.
 	n := len(timestamps)
-	if n > 0 && timestamps[n-1] < ts {
-		// Fast path for values scanned in ascending order.
+	if n > 0 && timestamps[n-1] <= ts {
+		// Fast path for timestamps scanned in ascending order.
 		return n
 	}
 	i, j := 0, n
diff --git a/app/vmselect/netstorage/netstorage_timing_test.go b/app/vmselect/netstorage/netstorage_timing_test.go
new file mode 100644
index 0000000000..b3312b514c
--- /dev/null
+++ b/app/vmselect/netstorage/netstorage_timing_test.go
@@ -0,0 +1,78 @@
+package netstorage
+
+import (
+	"fmt"
+	"testing"
+)
+
+func BenchmarkMergeSortBlocks(b *testing.B) {
+	for _, replicationFactor := range []int{1, 2, 3, 4, 5} {
+		b.Run(fmt.Sprintf("replicationFactor-%d", replicationFactor), func(b *testing.B) {
+			const samplesPerBlock = 8192
+			var blocks []*sortBlock
+			for j := 0; j < 10; j++ {
+				timestamps := make([]int64, samplesPerBlock)
+				values := make([]float64, samplesPerBlock)
+				for i := range timestamps {
+					timestamps[i] = int64(j*samplesPerBlock + i)
+					values[i] = float64(j*samplesPerBlock + i)
+				}
+				for i := 0; i < replicationFactor; i++ {
+					blocks = append(blocks, &sortBlock{
+						Timestamps: timestamps,
+						Values:     values,
+					})
+				}
+			}
+			benchmarkMergeSortBlocks(b, blocks)
+		})
+	}
+	b.Run("overlapped-blocks", func(b *testing.B) {
+		const samplesPerBlock = 8192
+		var blocks []*sortBlock
+		for j := 0; j < 10; j++ {
+			timestamps := make([]int64, samplesPerBlock)
+			values := make([]float64, samplesPerBlock)
+			for i := range timestamps {
+				timestamps[i] = int64(j*samplesPerBlock + i)
+				values[i] = float64(j*samplesPerBlock + i)
+			}
+			blocks = append(blocks, &sortBlock{
+				Timestamps: timestamps,
+				Values:     values,
+			})
+		}
+		for j := 1; j < len(blocks); j++ {
+			prev := blocks[j-1].Timestamps
+			curr := blocks[j].Timestamps
+			for i := 0; i < samplesPerBlock/2; i++ {
+				prev[i+samplesPerBlock/2], curr[i] = curr[i], prev[i+samplesPerBlock/2]
+			}
+		}
+		benchmarkMergeSortBlocks(b, blocks)
+	})
+}
+
+func benchmarkMergeSortBlocks(b *testing.B, blocks []*sortBlock) {
+	dedupInterval := int64(1)
+	samples := 0
+	for _, b := range blocks {
+		samples += len(b.Timestamps)
+	}
+	b.SetBytes(int64(samples))
+	b.ReportAllocs()
+	b.RunParallel(func(pb *testing.PB) {
+		var result Result
+		sbs := make(sortBlocksHeap, len(blocks))
+		for pb.Next() {
+			result.reset()
+			for i, b := range blocks {
+				sb := getSortBlock()
+				sb.Timestamps = b.Timestamps
+				sb.Values = b.Values
+				sbs[i] = sb
+			}
+			mergeSortBlocks(&result, sbs, dedupInterval)
+		}
+	})
+}