From 2061fa23935ddb832642f66aa3623b5de1945fd1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 16 Mar 2026 23:53:19 -1000 Subject: [PATCH] Add inner loops to _Fresh benchmarks for CodSpeed consistency Both _Fresh variants now use kInnerIterations with fresh buffer creation inside the inner loop. This amortizes CodSpeed's per-iteration instrumentation overhead while still measuring alloc+calc+encode per item. --- .../components/api/bench_proto_encode.cpp | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/tests/benchmarks/components/api/bench_proto_encode.cpp b/tests/benchmarks/components/api/bench_proto_encode.cpp index 11e4ccc4e3..656c1e17db 100644 --- a/tests/benchmarks/components/api/bench_proto_encode.cpp +++ b/tests/benchmarks/components/api/bench_proto_encode.cpp @@ -70,8 +70,10 @@ static void CalcAndEncode_SensorStateResponse(benchmark::State &state) { } BENCHMARK(CalcAndEncode_SensorStateResponse); -// Cold path: fresh buffer each iteration (measures heap allocation). -// No inner loop — the point is to measure one alloc+encode per iteration. +// Cold path: fresh buffer each iteration (measures heap allocation cost). +// Inner loop still needed to amortize CodSpeed instrumentation overhead. +// Each inner iteration creates a fresh buffer, so this measures +// alloc+calc+encode per item. static void CalcAndEncode_SensorStateResponse_Fresh(benchmark::State &state) { SensorStateResponse msg; msg.key = 0x12345678; @@ -79,13 +81,16 @@ static void CalcAndEncode_SensorStateResponse_Fresh(benchmark::State &state) { msg.missing_state = false; for (auto _ : state) { - APIBuffer buffer; - uint32_t size = msg.calculate_size(); - buffer.resize(size); - ProtoWriteBuffer writer(&buffer, 0); - msg.encode(writer); - benchmark::DoNotOptimize(buffer.data()); + for (int i = 0; i < kInnerIterations; i++) { + APIBuffer buffer; + uint32_t size = msg.calculate_size(); + buffer.resize(size); + ProtoWriteBuffer writer(&buffer, 0); + msg.encode(writer); + benchmark::DoNotOptimize(buffer.data()); + } } + state.SetItemsProcessed(state.iterations() * kInnerIterations); } BENCHMARK(CalcAndEncode_SensorStateResponse_Fresh); @@ -269,19 +274,24 @@ static void CalcAndEncode_DeviceInfoResponse(benchmark::State &state) { } BENCHMARK(CalcAndEncode_DeviceInfoResponse); -// Cold path: fresh buffer each iteration (measures heap allocation). -// No inner loop — the point is to measure one alloc+encode per iteration. +// Cold path: fresh buffer each iteration (measures heap allocation cost). +// Inner loop still needed to amortize CodSpeed instrumentation overhead. +// Each inner iteration creates a fresh buffer, so this measures +// alloc+calc+encode per item. static void CalcAndEncode_DeviceInfoResponse_Fresh(benchmark::State &state) { auto msg = make_device_info_response(); for (auto _ : state) { - APIBuffer buffer; - uint32_t size = msg.calculate_size(); - buffer.resize(size); - ProtoWriteBuffer writer(&buffer, 0); - msg.encode(writer); - benchmark::DoNotOptimize(buffer.data()); + for (int i = 0; i < kInnerIterations; i++) { + APIBuffer buffer; + uint32_t size = msg.calculate_size(); + buffer.resize(size); + ProtoWriteBuffer writer(&buffer, 0); + msg.encode(writer); + benchmark::DoNotOptimize(buffer.data()); + } } + state.SetItemsProcessed(state.iterations() * kInnerIterations); } BENCHMARK(CalcAndEncode_DeviceInfoResponse_Fresh);