diff --git a/src/systemcmds/microbench/CMakeLists.txt b/src/systemcmds/microbench/CMakeLists.txt index 912a6663b10..4ec6f992131 100644 --- a/src/systemcmds/microbench/CMakeLists.txt +++ b/src/systemcmds/microbench/CMakeLists.txt @@ -49,6 +49,7 @@ px4_add_module( test_microbench_math.cpp test_microbench_matrix.cpp test_microbench_uorb.cpp + test_microbench_perf.cpp DEPENDS lat_lon_alt diff --git a/src/systemcmds/microbench/microbench_main.cpp b/src/systemcmds/microbench/microbench_main.cpp index 5489407532f..746260ac70e 100644 --- a/src/systemcmds/microbench/microbench_main.cpp +++ b/src/systemcmds/microbench/microbench_main.cpp @@ -47,6 +47,7 @@ extern int test_microbench_hrt(int argc, char *argv[]); extern int test_microbench_math(int argc, char *argv[]); extern int test_microbench_matrix(int argc, char *argv[]); extern int test_microbench_uorb(int argc, char *argv[]); +extern int test_microbench_perf(int argc, char *argv[]); __END_DECLS @@ -70,6 +71,7 @@ const struct { {"microbench_math", test_microbench_math, 0}, {"microbench_matrix", test_microbench_matrix, 0}, {"microbench_uorb", test_microbench_uorb, 0}, + {"microbench_perf", test_microbench_perf, 0}, {"null", nullptr, 0} }; diff --git a/src/systemcmds/microbench/test_microbench_perf.cpp b/src/systemcmds/microbench/test_microbench_perf.cpp new file mode 100644 index 00000000000..f7b67849259 --- /dev/null +++ b/src/systemcmds/microbench/test_microbench_perf.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file test_microbench_perf.cpp + * Microbenchmark perf_counter hot-path operations (perf_count / perf_begin / + * perf_end). Used to quantify the overhead of making the counters atomic. + * + * Each operation is run in a tight loop and timed once with hrt_absolute_time(), + * then reported as ns per call. A plain (non-atomic) volatile counter loop is + * included as a reference baseline so the atomic overhead is visible within a + * single binary, without needing a separate non-atomic build. + */ + +#include + +#include + +#include +#include +#include +#include + +namespace MicroBenchPerf +{ + +// Number of iterations per measurement. Large enough that the loop runs for +// several hundred microseconds even on fast hosts, so the hrt (1 us) timer +// resolution does not dominate the per-op result. +static constexpr unsigned ITERATIONS = 1000000; + +// Report total elapsed and ns/op for a measured loop. +#define REPORT(name, elapsed_us) \ + PX4_INFO("%-28s %6.1f ns/op (%llu us / %u iters)", name, \ + (double)(elapsed_us) * 1000.0 / (double)ITERATIONS, \ + (unsigned long long)(elapsed_us), ITERATIONS) + +class MicroBenchPerf : public UnitTest +{ +public: + bool run_tests() override; + +private: + bool time_reference_nonatomic(); + bool time_perf_count(); + bool time_perf_begin_end(); +}; + +// Plain non-atomic increment of a volatile counter: the baseline the atomic +// perf_count() is compared against. volatile prevents the loop being elided. +bool MicroBenchPerf::time_reference_nonatomic() +{ + static volatile uint64_t counter = 0; + + const hrt_abstime t0 = hrt_absolute_time(); + + for (unsigned i = 0; i < ITERATIONS; i++) { + counter = counter + 1; + } + + const hrt_abstime elapsed = hrt_absolute_time() - t0; + REPORT("reference non-atomic ++", elapsed); + return true; +} + +bool MicroBenchPerf::time_perf_count() +{ + perf_counter_t c = perf_alloc(PC_COUNT, "microbench_perf_count"); + + const hrt_abstime t0 = hrt_absolute_time(); + + for (unsigned i = 0; i < ITERATIONS; i++) { + perf_count(c); + } + + const hrt_abstime elapsed = hrt_absolute_time() - t0; + REPORT("perf_count (PC_COUNT)", elapsed); + perf_free(c); + return true; +} + +bool MicroBenchPerf::time_perf_begin_end() +{ + perf_counter_t c = perf_alloc(PC_ELAPSED, "microbench_perf_elapsed"); + + const hrt_abstime t0 = hrt_absolute_time(); + + for (unsigned i = 0; i < ITERATIONS; i++) { + perf_begin(c); + perf_end(c); + } + + const hrt_abstime elapsed = hrt_absolute_time() - t0; + REPORT("perf_begin + perf_end", elapsed); + perf_free(c); + return true; +} + +bool MicroBenchPerf::run_tests() +{ + ut_run_test(time_reference_nonatomic); + ut_run_test(time_perf_count); + ut_run_test(time_perf_begin_end); + + return (_tests_failed == 0); +} + +ut_declare_test_c(test_microbench_perf, MicroBenchPerf) + +} // namespace MicroBenchPerf