Skip to content

Commit 0eff368

Browse files
committed
fix: improve batch benchmarks to avoid polluting allocation measurements
- Pre-compute fmt.Sprintf values outside inner benchmark loops - Replace fmt.Sprintf with strconv.Itoa + string concat for prepared IDs - Check Marshal errors instead of discarding with blank identifier - Add b.ResetTimer() after setup in BenchmarkBatchBuildWriteFrame variants
1 parent b4a28e4 commit 0eff368

File tree

1 file changed

+57
-13
lines changed

1 file changed

+57
-13
lines changed

batch_bench_test.go

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package gocql
2020

2121
import (
2222
"fmt"
23+
"strconv"
2324
"testing"
2425
)
2526

@@ -33,13 +34,18 @@ func BenchmarkBatchQueryAppend(b *testing.B) {
3334
for _, size := range []int{10, 100} {
3435
b.Run(fmt.Sprintf("entries=%d", size), func(b *testing.B) {
3536
b.ReportAllocs()
37+
// Pre-compute value strings so fmt.Sprintf doesn't dominate allocations.
38+
vals := make([]string, size)
39+
for j := 0; j < size; j++ {
40+
vals[j] = "val_" + strconv.Itoa(j)
41+
}
3642
var batch *Batch
3743
for i := 0; i < b.N; i++ {
3844
batch = &Batch{
3945
Type: LoggedBatch,
4046
}
4147
for j := 0; j < size; j++ {
42-
batch.Query("INSERT INTO ks.tbl (pk, v) VALUES (?, ?)", j, fmt.Sprintf("val_%d", j))
48+
batch.Query("INSERT INTO ks.tbl (pk, v) VALUES (?, ?)", j, vals[j])
4349
}
4450
}
4551
benchSink = batch
@@ -54,13 +60,18 @@ func BenchmarkBatchQueryAppendPreallocated(b *testing.B) {
5460
for _, size := range []int{10, 100} {
5561
b.Run(fmt.Sprintf("entries=%d", size), func(b *testing.B) {
5662
b.ReportAllocs()
63+
// Pre-compute value strings so fmt.Sprintf doesn't dominate allocations.
64+
vals := make([]string, size)
65+
for j := 0; j < size; j++ {
66+
vals[j] = "val_" + strconv.Itoa(j)
67+
}
5768
var batch *Batch
5869
for i := 0; i < b.N; i++ {
5970
batch = (&Batch{
6071
Type: LoggedBatch,
6172
}).Reserve(size)
6273
for j := 0; j < size; j++ {
63-
batch.Query("INSERT INTO ks.tbl (pk, v) VALUES (?, ?)", j, fmt.Sprintf("val_%d", j))
74+
batch.Query("INSERT INTO ks.tbl (pk, v) VALUES (?, ?)", j, vals[j])
6475
}
6576
}
6677
benchSink = batch
@@ -76,10 +87,26 @@ func BenchmarkBatchBuildWriteFrame(b *testing.B) {
7687
b.Run(fmt.Sprintf("entries=%d", size), func(b *testing.B) {
7788
b.ReportAllocs()
7889

79-
// Pre-build mock column types and values
8090
colCount := 2
8191
typ := NativeType{proto: protoVersion4, typ: TypeInt}
8292

93+
// Pre-compute prepared IDs and marshaled values outside the benchmark loop
94+
// so fmt.Sprintf and Marshal don't pollute allocation measurements.
95+
prepIDs := make([][]byte, size)
96+
marshaledVals := make([][]byte, size*colCount)
97+
for j := 0; j < size; j++ {
98+
prepIDs[j] = []byte("prepared_" + strconv.Itoa(j%5))
99+
for k := 0; k < colCount; k++ {
100+
val, err := Marshal(typ, j+k)
101+
if err != nil {
102+
b.Fatalf("Marshal(%d): %v", j+k, err)
103+
}
104+
marshaledVals[j*colCount+k] = val
105+
}
106+
}
107+
108+
b.ResetTimer()
109+
83110
var req *writeBatchFrame
84111
for i := 0; i < b.N; i++ {
85112
req = &writeBatchFrame{
@@ -91,16 +118,15 @@ func BenchmarkBatchBuildWriteFrame(b *testing.B) {
91118

92119
stmts := make(map[string]string, size)
93120

94-
// Simulate the allocation pattern from executeBatch
121+
// Simulate the per-statement allocation pattern from executeBatch
95122
for j := 0; j < size; j++ {
96123
bs := &req.statements[j]
97-
bs.preparedID = []byte(fmt.Sprintf("prepared_%d", j%5))
98-
stmts[string(bs.preparedID)] = fmt.Sprintf("INSERT INTO ks.tbl (pk, v) VALUES (?, ?)")
124+
bs.preparedID = prepIDs[j]
125+
stmts[string(bs.preparedID)] = "INSERT INTO ks.tbl (pk, v) VALUES (?, ?)"
99126

100127
bs.values = make([]queryValues, colCount)
101128
for k := 0; k < colCount; k++ {
102-
val, _ := Marshal(typ, j+k)
103-
bs.values[k] = queryValues{value: val}
129+
bs.values[k] = queryValues{value: marshaledVals[j*colCount+k]}
104130
}
105131
}
106132
// Prevent the compiler from eliminating the stmts allocation.
@@ -123,6 +149,22 @@ func BenchmarkBatchBuildWriteFrameBulkAlloc(b *testing.B) {
123149
colCount := 2
124150
typ := NativeType{proto: protoVersion4, typ: TypeInt}
125151

152+
// Pre-compute prepared IDs and marshaled values outside the benchmark loop.
153+
prepIDs := make([][]byte, size)
154+
marshaledVals := make([][]byte, size*colCount)
155+
for j := 0; j < size; j++ {
156+
prepIDs[j] = []byte("prepared_" + strconv.Itoa(j%5))
157+
for k := 0; k < colCount; k++ {
158+
val, err := Marshal(typ, j+k)
159+
if err != nil {
160+
b.Fatalf("Marshal(%d): %v", j+k, err)
161+
}
162+
marshaledVals[j*colCount+k] = val
163+
}
164+
}
165+
166+
b.ResetTimer()
167+
126168
var req *writeBatchFrame
127169
for i := 0; i < b.N; i++ {
128170
req = &writeBatchFrame{
@@ -137,12 +179,11 @@ func BenchmarkBatchBuildWriteFrameBulkAlloc(b *testing.B) {
137179

138180
for j := 0; j < size; j++ {
139181
bs := &req.statements[j]
140-
bs.preparedID = []byte(fmt.Sprintf("prepared_%d", j%5))
182+
bs.preparedID = prepIDs[j]
141183

142184
bs.values = allValues[j*colCount : (j+1)*colCount]
143185
for k := 0; k < colCount; k++ {
144-
val, _ := Marshal(typ, j+k)
145-
bs.values[k] = queryValues{value: val}
186+
bs.values[k] = queryValues{value: marshaledVals[j*colCount+k]}
146187
}
147188
}
148189
}
@@ -171,10 +212,13 @@ func BenchmarkBatchWriteFrameSerialization(b *testing.B) {
171212

172213
for j := 0; j < size; j++ {
173214
bs := &frame.statements[j]
174-
bs.preparedID = []byte(fmt.Sprintf("prepared_%d", j%5))
215+
bs.preparedID = []byte("prepared_" + strconv.Itoa(j%5))
175216
bs.values = make([]queryValues, colCount)
176217
for k := 0; k < colCount; k++ {
177-
val, _ := Marshal(typ, j+k)
218+
val, err := Marshal(typ, j+k)
219+
if err != nil {
220+
b.Fatalf("Marshal(%d): %v", j+k, err)
221+
}
178222
bs.values[k] = queryValues{value: val}
179223
}
180224
}

0 commit comments

Comments
 (0)