Skip to content

Commit f97f966

Browse files
committed
feat: add multi-threaded and percentile latency benchmarks
1 parent 0fcb5b4 commit f97f966

1 file changed

Lines changed: 218 additions & 0 deletions

File tree

bench/bench_parser.cpp

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
#include "sql_parser/parser.h"
33
#include "sql_parser/emitter.h"
44

5+
#include <chrono>
6+
#include <vector>
7+
#include <algorithm>
8+
#include <cmath>
9+
510
using namespace sql_parser;
611

712
// ========== Tier 2: Classification ==========
@@ -237,3 +242,216 @@ static void BM_PgSQL_Set_Simple(benchmark::State& state) {
237242
}
238243
}
239244
BENCHMARK(BM_PgSQL_Set_Simple);
245+
246+
// ========== Multi-threaded benchmarks ==========
247+
// Parser is per-thread — each thread creates its own instance
248+
249+
static void BM_MT_Set_Simple(benchmark::State& state) {
250+
Parser<Dialect::MySQL> parser; // one per thread
251+
const char* sql = "SET @@session.wait_timeout = 600";
252+
size_t len = strlen(sql);
253+
for (auto _ : state) {
254+
auto r = parser.parse(sql, len);
255+
benchmark::DoNotOptimize(r.ast);
256+
}
257+
}
258+
BENCHMARK(BM_MT_Set_Simple)->Threads(1)->Threads(2)->Threads(4)->Threads(8);
259+
260+
static void BM_MT_Select_Simple(benchmark::State& state) {
261+
Parser<Dialect::MySQL> parser;
262+
const char* sql = "SELECT col FROM t WHERE id = 1";
263+
size_t len = strlen(sql);
264+
for (auto _ : state) {
265+
auto r = parser.parse(sql, len);
266+
benchmark::DoNotOptimize(r.ast);
267+
}
268+
}
269+
BENCHMARK(BM_MT_Select_Simple)->Threads(1)->Threads(2)->Threads(4)->Threads(8);
270+
271+
static void BM_MT_Select_Complex(benchmark::State& state) {
272+
Parser<Dialect::MySQL> parser;
273+
const char* sql =
274+
"SELECT u.id, u.name, COUNT(o.id) AS order_count "
275+
"FROM users u "
276+
"LEFT JOIN orders o ON u.id = o.user_id "
277+
"WHERE u.status = 'active' AND u.created_at > '2024-01-01' "
278+
"GROUP BY u.id, u.name "
279+
"HAVING COUNT(o.id) > 5 "
280+
"ORDER BY order_count DESC "
281+
"LIMIT 50 OFFSET 10";
282+
size_t len = strlen(sql);
283+
for (auto _ : state) {
284+
auto r = parser.parse(sql, len);
285+
benchmark::DoNotOptimize(r.ast);
286+
}
287+
}
288+
BENCHMARK(BM_MT_Select_Complex)->Threads(1)->Threads(2)->Threads(4)->Threads(8);
289+
290+
static void BM_MT_Classify_Begin(benchmark::State& state) {
291+
Parser<Dialect::MySQL> parser;
292+
const char* sql = "BEGIN";
293+
size_t len = strlen(sql);
294+
for (auto _ : state) {
295+
auto r = parser.parse(sql, len);
296+
benchmark::DoNotOptimize(r.stmt_type);
297+
}
298+
}
299+
BENCHMARK(BM_MT_Classify_Begin)->Threads(1)->Threads(2)->Threads(4)->Threads(8);
300+
301+
// ========== Percentile latency benchmarks ==========
302+
// Custom benchmarks that collect per-iteration timing for percentile analysis.
303+
// Collects individual latencies inside the benchmark loop, then computes
304+
// percentiles after the loop completes. Only the parse call is timed;
305+
// timestamp collection overhead is excluded via PauseTiming/ResumeTiming.
306+
307+
static void BM_Percentile_Set_Simple(benchmark::State& state) {
308+
Parser<Dialect::MySQL> parser;
309+
const char* sql = "SET @@session.wait_timeout = 600";
310+
size_t len = strlen(sql);
311+
312+
std::vector<double> latencies;
313+
latencies.reserve(1 << 20);
314+
for (auto _ : state) {
315+
state.PauseTiming();
316+
auto start = std::chrono::high_resolution_clock::now();
317+
state.ResumeTiming();
318+
319+
auto r = parser.parse(sql, len);
320+
benchmark::DoNotOptimize(r.ast);
321+
322+
state.PauseTiming();
323+
auto end = std::chrono::high_resolution_clock::now();
324+
latencies.push_back(std::chrono::duration<double, std::nano>(end - start).count());
325+
state.ResumeTiming();
326+
}
327+
328+
if (!latencies.empty()) {
329+
std::sort(latencies.begin(), latencies.end());
330+
size_t N = latencies.size();
331+
double sum = 0;
332+
for (double l : latencies) sum += l;
333+
state.counters["avg_ns"] = sum / N;
334+
state.counters["p50_ns"] = latencies[N * 50 / 100];
335+
state.counters["p95_ns"] = latencies[N * 95 / 100];
336+
state.counters["p99_ns"] = latencies[N * 99 / 100];
337+
state.counters["min_ns"] = latencies[0];
338+
state.counters["max_ns"] = latencies[N - 1];
339+
}
340+
}
341+
BENCHMARK(BM_Percentile_Set_Simple);
342+
343+
static void BM_Percentile_Select_Simple(benchmark::State& state) {
344+
Parser<Dialect::MySQL> parser;
345+
const char* sql = "SELECT col FROM t WHERE id = 1";
346+
size_t len = strlen(sql);
347+
348+
std::vector<double> latencies;
349+
latencies.reserve(1 << 20);
350+
for (auto _ : state) {
351+
state.PauseTiming();
352+
auto start = std::chrono::high_resolution_clock::now();
353+
state.ResumeTiming();
354+
355+
auto r = parser.parse(sql, len);
356+
benchmark::DoNotOptimize(r.ast);
357+
358+
state.PauseTiming();
359+
auto end = std::chrono::high_resolution_clock::now();
360+
latencies.push_back(std::chrono::duration<double, std::nano>(end - start).count());
361+
state.ResumeTiming();
362+
}
363+
364+
if (!latencies.empty()) {
365+
std::sort(latencies.begin(), latencies.end());
366+
size_t N = latencies.size();
367+
double sum = 0;
368+
for (double l : latencies) sum += l;
369+
state.counters["avg_ns"] = sum / N;
370+
state.counters["p50_ns"] = latencies[N * 50 / 100];
371+
state.counters["p95_ns"] = latencies[N * 95 / 100];
372+
state.counters["p99_ns"] = latencies[N * 99 / 100];
373+
state.counters["min_ns"] = latencies[0];
374+
state.counters["max_ns"] = latencies[N - 1];
375+
}
376+
}
377+
BENCHMARK(BM_Percentile_Select_Simple);
378+
379+
static void BM_Percentile_Select_Complex(benchmark::State& state) {
380+
Parser<Dialect::MySQL> parser;
381+
const char* sql =
382+
"SELECT u.id, u.name, COUNT(o.id) AS order_count "
383+
"FROM users u "
384+
"LEFT JOIN orders o ON u.id = o.user_id "
385+
"WHERE u.status = 'active' "
386+
"GROUP BY u.id, u.name "
387+
"HAVING COUNT(o.id) > 5 "
388+
"ORDER BY order_count DESC "
389+
"LIMIT 50";
390+
size_t len = strlen(sql);
391+
392+
std::vector<double> latencies;
393+
latencies.reserve(1 << 20);
394+
for (auto _ : state) {
395+
state.PauseTiming();
396+
auto start = std::chrono::high_resolution_clock::now();
397+
state.ResumeTiming();
398+
399+
auto r = parser.parse(sql, len);
400+
benchmark::DoNotOptimize(r.ast);
401+
402+
state.PauseTiming();
403+
auto end = std::chrono::high_resolution_clock::now();
404+
latencies.push_back(std::chrono::duration<double, std::nano>(end - start).count());
405+
state.ResumeTiming();
406+
}
407+
408+
if (!latencies.empty()) {
409+
std::sort(latencies.begin(), latencies.end());
410+
size_t N = latencies.size();
411+
double sum = 0;
412+
for (double l : latencies) sum += l;
413+
state.counters["avg_ns"] = sum / N;
414+
state.counters["p50_ns"] = latencies[N * 50 / 100];
415+
state.counters["p95_ns"] = latencies[N * 95 / 100];
416+
state.counters["p99_ns"] = latencies[N * 99 / 100];
417+
state.counters["min_ns"] = latencies[0];
418+
state.counters["max_ns"] = latencies[N - 1];
419+
}
420+
}
421+
BENCHMARK(BM_Percentile_Select_Complex);
422+
423+
static void BM_Percentile_Classify_Begin(benchmark::State& state) {
424+
Parser<Dialect::MySQL> parser;
425+
const char* sql = "BEGIN";
426+
size_t len = strlen(sql);
427+
428+
std::vector<double> latencies;
429+
latencies.reserve(1 << 20);
430+
for (auto _ : state) {
431+
state.PauseTiming();
432+
auto start = std::chrono::high_resolution_clock::now();
433+
state.ResumeTiming();
434+
435+
auto r = parser.parse(sql, len);
436+
benchmark::DoNotOptimize(r.stmt_type);
437+
438+
state.PauseTiming();
439+
auto end = std::chrono::high_resolution_clock::now();
440+
latencies.push_back(std::chrono::duration<double, std::nano>(end - start).count());
441+
state.ResumeTiming();
442+
}
443+
444+
if (!latencies.empty()) {
445+
std::sort(latencies.begin(), latencies.end());
446+
size_t N = latencies.size();
447+
double sum = 0;
448+
for (double l : latencies) sum += l;
449+
state.counters["avg_ns"] = sum / N;
450+
state.counters["p50_ns"] = latencies[N * 50 / 100];
451+
state.counters["p95_ns"] = latencies[N * 95 / 100];
452+
state.counters["p99_ns"] = latencies[N * 99 / 100];
453+
state.counters["min_ns"] = latencies[0];
454+
state.counters["max_ns"] = latencies[N - 1];
455+
}
456+
}
457+
BENCHMARK(BM_Percentile_Classify_Begin);

0 commit comments

Comments
 (0)