Commit 0a6058b
committed
perf: optimize batch execution path for reduced allocations
Motivation
----------
The BATCH execution path (executeBatch / writeBatchFrame) in the gocql
driver performed several avoidable allocations per batch execution:
individual make() calls for each statement's queryValues slice, a
separate stmts map[string]string to track prepared-ID-to-statement
mappings, sequential preparation of all statements, and unbounded
recursive retries on RequestErrUnprepared errors.
Changes
-------
1. Concurrent statement preparation (Item 1):
Split executeBatch into a retry loop (executeBatch) and single-attempt
logic (executeBatchOnce). Unique statements are collected into a map
and prepared concurrently via goroutines + sync.WaitGroup. The
existing prepareStatement coalesces concurrent calls for the same
statement via execIfMissing on the LRU cache, so this is safe.
2. Bulk-allocate queryValues (Item 2):
Two-pass approach: first pass computes totalValues across all entries,
second pass slices from a single []queryValues allocation. Replaces N
individual make([]queryValues, colCount) calls with one.
3. Eliminate stmts map (Item 3):
Added sourceStmt field to batchStatment struct (frame.go). On
RequestErrUnprepared, a linear scan of req.statements replaces the
map lookup. This is acceptable because it only runs on the error
recovery path, not the hot path. Added unexported stmt field to
RequestErrUnprepared (errors.go) to carry the source statement text
from executeBatchOnce back to the retry loop.
4. Reserve(n int) API (Item 4):
Added Batch.Reserve(n int) *Batch to session.go, which pre-allocates
the Entries slice capacity. Named Reserve (not Size) because Size()
already exists and returns len(b.Entries).
5. Bounded retry on RequestErrUnprepared (Item 9):
Converted the unbounded recursive executeBatch call to an iterative
loop with maxBatchPrepareRetries = 1. If re-preparation fails twice,
the error is returned rather than retrying indefinitely.
Benchmark results (synthetic, 8 samples, not end-to-end latency)
----------------------------------------------------------------
These benchmarks measure allocation patterns only. They do NOT measure
real Cassandra/Scylla round-trip latency or throughput. The numbers
below reflect the cost of building batch data structures in isolation.
Reserve() — Batch.Query append:
entries=10: 2407 B/op 35 allocs -> 1110 B/op 31 allocs (-54% B, -11% allocs)
entries=100: 21791 B/op 308 allocs -> 11720 B/op 301 allocs (-46% B, -2% allocs)
Bulk queryValues allocation — writeBatchFrame construction:
entries=10: 3434 B/op 74 allocs -> 2294 B/op 42 allocs (-33% B, -43% allocs)
entries=100: 32319 B/op 704 allocs -> 21668 B/op 402 allocs (-33% B, -43% allocs)
Files changed: conn.go, frame.go, errors.go, session.go
Files added: batch_bench_test.go1 parent 0952897 commit 0a6058b
5 files changed
+332
-41
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
0 commit comments