diff --git a/platforms/posix/cmake/sitl_tests.cmake b/platforms/posix/cmake/sitl_tests.cmake index 47ba74564f..cb6e52283c 100644 --- a/platforms/posix/cmake/sitl_tests.cmake +++ b/platforms/posix/cmake/sitl_tests.cmake @@ -21,6 +21,7 @@ set(tests matrix mavlink mc_pos_control + microbench mixer param parameters diff --git a/src/include/unit_test.h b/src/include/unit_test.h index c0deddf7e0..84425d6e2f 100644 --- a/src/include/unit_test.h +++ b/src/include/unit_test.h @@ -105,6 +105,7 @@ protected: _tests_passed++; \ } \ _cleanup(); \ + printf("\n"); \ } while (0) /// @brief Used to assert a value within a unit test. diff --git a/src/lib/perf/perf_counter.c b/src/lib/perf/perf_counter.c index a29cabb9c2..b804cfcabf 100644 --- a/src/lib/perf/perf_counter.c +++ b/src/lib/perf/perf_counter.c @@ -458,11 +458,11 @@ perf_print_counter_fd(int fd, perf_counter_t handle) case PC_ELAPSED: { struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle; float rms = sqrtf(pce->M2 / (pce->event_count - 1)); - dprintf(fd, "%s: %llu events, %lluus elapsed, %lluus avg, min %lluus max %lluus %5.3fus rms\n", + dprintf(fd, "%s: %llu events, %lluus elapsed, %.2fus avg, min %lluus max %lluus %5.3fus rms\n", handle->name, (unsigned long long)pce->event_count, (unsigned long long)pce->time_total, - (pce->event_count == 0) ? 0 : (unsigned long long)pce->time_total / pce->event_count, + (pce->event_count == 0) ? 0 : (double)pce->time_total / (double)pce->event_count, (unsigned long long)pce->time_least, (unsigned long long)pce->time_most, (double)(1e6f * rms)); @@ -473,10 +473,10 @@ perf_print_counter_fd(int fd, perf_counter_t handle) struct perf_ctr_interval *pci = (struct perf_ctr_interval *)handle; float rms = sqrtf(pci->M2 / (pci->event_count - 1)); - dprintf(fd, "%s: %llu events, %lluus avg, min %lluus max %lluus %5.3fus rms\n", + dprintf(fd, "%s: %llu events, %.2fus avg, min %lluus max %lluus %5.3fus rms\n", handle->name, (unsigned long long)pci->event_count, - (pci->event_count == 0) ? 0 : (unsigned long long)(pci->time_last - pci->time_first) / pci->event_count, + (pci->event_count == 0) ? 0 : (double)(pci->time_last - pci->time_first) / (double)pci->event_count, (unsigned long long)pci->time_least, (unsigned long long)pci->time_most, (double)(1e6f * rms)); @@ -508,11 +508,11 @@ perf_print_counter_buffer(char *buffer, int length, perf_counter_t handle) case PC_ELAPSED: { struct perf_ctr_elapsed *pce = (struct perf_ctr_elapsed *)handle; float rms = sqrtf(pce->M2 / (pce->event_count - 1)); - num_written = snprintf(buffer, length, "%s: %llu events, %lluus elapsed, %lluus avg, min %lluus max %lluus %5.3fus rms", + num_written = snprintf(buffer, length, "%s: %llu events, %lluus elapsed, %.2fus avg, min %lluus max %lluus %5.3fus rms", handle->name, (unsigned long long)pce->event_count, (unsigned long long)pce->time_total, - (pce->event_count == 0) ? 0 : (unsigned long long)pce->time_total / pce->event_count, + (pce->event_count == 0) ? 0 : (double)pce->time_total / (double)pce->event_count, (unsigned long long)pce->time_least, (unsigned long long)pce->time_most, (double)(1e6f * rms)); @@ -523,10 +523,10 @@ perf_print_counter_buffer(char *buffer, int length, perf_counter_t handle) struct perf_ctr_interval *pci = (struct perf_ctr_interval *)handle; float rms = sqrtf(pci->M2 / (pci->event_count - 1)); - num_written = snprintf(buffer, length, "%s: %llu events, %lluus avg, min %lluus max %lluus %5.3fus rms", + num_written = snprintf(buffer, length, "%s: %llu events, %.2f avg, min %lluus max %lluus %5.3fus rms", handle->name, (unsigned long long)pci->event_count, - (pci->event_count == 0) ? 0 : (unsigned long long)(pci->time_last - pci->time_first) / pci->event_count, + (pci->event_count == 0) ? 0 : (double)(pci->time_last - pci->time_first) / (double)pci->event_count, (unsigned long long)pci->time_least, (unsigned long long)pci->time_most, (double)(1e6f * rms)); diff --git a/src/modules/sensors/CMakeLists.txt b/src/modules/sensors/CMakeLists.txt index 3bf96aba97..45f18f6df1 100644 --- a/src/modules/sensors/CMakeLists.txt +++ b/src/modules/sensors/CMakeLists.txt @@ -52,4 +52,5 @@ px4_add_module( drivers__device git_ecl ecl_validation + mathlib ) diff --git a/src/systemcmds/tests/CMakeLists.txt b/src/systemcmds/tests/CMakeLists.txt index b88b2f796d..e328ae71b5 100644 --- a/src/systemcmds/tests/CMakeLists.txt +++ b/src/systemcmds/tests/CMakeLists.txt @@ -49,6 +49,7 @@ set(srcs test_led.c test_mathlib.cpp test_matrix.cpp + test_microbench.cpp test_mixer.cpp test_mount.c test_param.c diff --git a/src/systemcmds/tests/test_microbench.cpp b/src/systemcmds/tests/test_microbench.cpp new file mode 100644 index 0000000000..2dc0b486ee --- /dev/null +++ b/src/systemcmds/tests/test_microbench.cpp @@ -0,0 +1,239 @@ + +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef __PX4_NUTTX +#include +static irqstate_t flags; +#endif + +void lock() +{ +#ifdef __PX4_NUTTX + flags = px4_enter_critical_section(); +#endif +} + +void unlock() +{ +#ifdef __PX4_NUTTX + px4_leave_critical_section(flags); +#endif +} + +#define PERF(name, op, count) do { \ + usleep(1000); \ + reset(); \ + perf_counter_t p = perf_alloc(PC_ELAPSED, name); \ + for (int i = 0; i < count; i++) { \ + lock(); \ + perf_begin(p); \ + op; \ + perf_end(p); \ + unlock(); \ + reset(); \ + } \ + perf_print_counter(p); \ + perf_free(p); \ + } while (0) + +class MicroBench : public UnitTest +{ +public: + virtual bool run_tests(); + +private: + bool time_single_prevision_float(); + bool time_single_prevision_float_trig(); + + bool time_double_prevision_float(); + bool time_double_prevision_float_trig(); + + bool time_8bit_integers(); + bool time_16bit_integers(); + bool time_32bit_integers(); + bool time_64bit_integers(); + + bool time_px4_hrt(); + + void reset(); + + + volatile float f32; + volatile float f32_out; + + volatile double f64; + volatile double f64_out; + + volatile uint8_t i_8; + volatile uint8_t i_8_out; + + volatile uint16_t i_16; + volatile uint16_t i_16_out; + + volatile uint32_t i_32; + volatile uint32_t i_32_out; + + volatile int64_t i_64; + volatile int64_t i_64_out; + + volatile uint64_t u_64; + volatile uint64_t u_64_out; +}; + +bool MicroBench::run_tests() +{ + ut_run_test(time_single_prevision_float); + ut_run_test(time_single_prevision_float_trig); + ut_run_test(time_double_prevision_float); + ut_run_test(time_double_prevision_float_trig); + ut_run_test(time_8bit_integers); + ut_run_test(time_16bit_integers); + ut_run_test(time_32bit_integers); + ut_run_test(time_64bit_integers); + ut_run_test(time_px4_hrt); + + return (_tests_failed == 0); +} + +template +T random(T min, T max) +{ + const T scale = rand() / (T) RAND_MAX; /* [0, 1.0] */ + return min + scale * (max - min); /* [min, max] */ +} + +void MicroBench::reset() +{ + srand(time(nullptr)); + + // initialize with random data + f32 = random(-2.0f * M_PI, 2.0f * M_PI); // somewhat representative range for angles in radians + f32_out = random(-2.0f * M_PI, 2.0f * M_PI); + + f64 = random(-2.0 * M_PI, 2.0 * M_PI); + f64_out = random(-2.0 * M_PI, 2.0 * M_PI); + + i_8 = rand(); + i_8_out = rand(); + + i_16 = rand(); + i_16_out = rand(); + + i_32 = rand(); + i_32_out = rand(); + + i_64 = rand(); + i_64_out = rand(); + + u_64 = rand(); + u_64_out = rand(); +} + +ut_declare_test_c(test_microbench, MicroBench) + +bool MicroBench::time_single_prevision_float() +{ + PERF("float add", f32_out += f32, 1000); + PERF("float sub", f32_out -= f32, 1000); + PERF("float mul", f32_out *= f32, 1000); + PERF("float div", f32_out /= f32, 1000); + PERF("float sqrt", f32_out = sqrtf(f32), 1000); + + return true; +} + +bool MicroBench::time_single_prevision_float_trig() +{ + PERF("sinf()", f32_out = sinf(f32), 1000); + PERF("cosf()", f32_out = cosf(f32), 1000); + PERF("tanf()", f32_out = tanf(f32), 1000); + + PERF("acosf()", f32_out = acosf(f32), 1000); + PERF("asinf()", f32_out = asinf(f32), 1000); + PERF("atan2f()", f32_out = atan2f(f32, 2.0f * f32), 1000); + + return true; +} + +bool MicroBench::time_double_prevision_float() +{ + PERF("double add", f64_out += f64, 1000); + PERF("double sub", f64_out -= f64, 1000); + PERF("double mul", f64_out *= f64, 1000); + PERF("double div", f64_out /= f64, 1000); + PERF("double sqrt", f64_out = sqrt(f64), 1000); + + return true; +} + +bool MicroBench::time_double_prevision_float_trig() +{ + PERF("sin()", f64_out = sin(f64), 1000); + PERF("cos()", f64_out = cos(f64), 1000); + PERF("tan()", f64_out = tan(f64), 1000); + + PERF("acos()", f64_out = acos(f64 * 0.5), 1000); + PERF("asin()", f64_out = asin(f64 * 0.6), 1000); + PERF("atan2()", f64_out = atan2(f64 * 0.7, f64 * 0.8), 1000); + + PERF("sqrt()", f64_out = sqrt(f64), 1000); + + return true; +} + + +bool MicroBench::time_8bit_integers() +{ + PERF("int8 add", i_8_out += i_8, 1000); + PERF("int8 sub", i_8_out -= i_8, 1000); + PERF("int8 mul", i_8_out *= i_8, 1000); + PERF("int8 div", i_8_out /= i_8, 1000); + + return true; +} + +bool MicroBench::time_16bit_integers() +{ + PERF("int16 add", i_16_out += i_16, 1000); + PERF("int16 sub", i_16_out -= i_16, 1000); + PERF("int16 mul", i_16_out *= i_16, 1000); + PERF("int16 div", i_16_out /= i_16, 1000); + + return true; +} + +bool MicroBench::time_32bit_integers() +{ + PERF("int32 add", i_32_out += i_32, 1000); + PERF("int32 sub", i_32_out -= i_32, 1000); + PERF("int32 mul", i_32_out *= i_32, 1000); + PERF("int32 div", i_32_out /= i_32, 1000); + + return true; +} + +bool MicroBench::time_64bit_integers() +{ + PERF("int64 add", i_64_out += i_64, 1000); + PERF("int64 sub", i_64_out -= i_64, 1000); + PERF("int64 mul", i_64_out *= i_64, 1000); + PERF("int64 div", i_64_out /= i_64, 1000); + + return true; +} + +bool MicroBench::time_px4_hrt() +{ + PERF("hrt_absolute_time()", u_64_out = hrt_absolute_time(), 1000); + PERF("hrt_elapsed_time()", u_64_out = hrt_elapsed_time(&u_64), 1000); + + return true; +} diff --git a/src/systemcmds/tests/tests_main.c b/src/systemcmds/tests/tests_main.c index dcc7248fbf..8a725d3512 100644 --- a/src/systemcmds/tests/tests_main.c +++ b/src/systemcmds/tests/tests_main.c @@ -115,6 +115,7 @@ const struct { {"jig_voltages", test_jig_voltages, OPT_NOALLTEST}, {"mathlib", test_mathlib, 0}, {"matrix", test_matrix, 0}, + {"microbench", test_microbench, 0}, {"mount", test_mount, OPT_NOJIGTEST | OPT_NOALLTEST}, {"param", test_param, 0}, {"parameters", test_parameters, 0}, diff --git a/src/systemcmds/tests/tests_main.h b/src/systemcmds/tests/tests_main.h index 7e7d9166d7..a5ad9b93ba 100644 --- a/src/systemcmds/tests/tests_main.h +++ b/src/systemcmds/tests/tests_main.h @@ -69,6 +69,7 @@ extern int test_jig_voltages(int argc, char *argv[]); extern int test_led(int argc, char *argv[]); extern int test_mathlib(int argc, char *argv[]); extern int test_matrix(int argc, char *argv[]); +extern int test_microbench(int argc, char *argv[]); extern int test_mixer(int argc, char *argv[]); extern int test_mount(int argc, char *argv[]); extern int test_param(int argc, char *argv[]);