From 6bdcca6a854e32cb563415a666f3c3f6020fb520 Mon Sep 17 00:00:00 2001 From: Daniel Agar Date: Thu, 9 Jul 2020 14:12:25 -0400 Subject: [PATCH] systemcmds/tests: add simple atomic microbenchmark --- Tools/HIL/run_tests.py | 3 + platforms/posix/cmake/sitl_tests.cmake | 1 + src/systemcmds/tests/CMakeLists.txt | 1 + .../tests/test_microbench_atomic.cpp | 291 ++++++++++++++++++ src/systemcmds/tests/tests_main.c | 1 + src/systemcmds/tests/tests_main.h | 1 + 6 files changed, 298 insertions(+) create mode 100644 src/systemcmds/tests/test_microbench_atomic.cpp diff --git a/Tools/HIL/run_tests.py b/Tools/HIL/run_tests.py index 6d1bfba860..3bd05133d1 100755 --- a/Tools/HIL/run_tests.py +++ b/Tools/HIL/run_tests.py @@ -111,6 +111,9 @@ class TestHadrwareMethods(unittest.TestCase): def test_matrix(self): self.assertTrue(do_test(self.TEST_DEVICE, self.TEST_BAUDRATE, "matrix")) + def test_microbench_atomic(self): + self.assertTrue(do_test(self.TEST_DEVICE, self.TEST_BAUDRATE, "microbench_atomic")) + def test_microbench_hrt(self): self.assertTrue(do_test(self.TEST_DEVICE, self.TEST_BAUDRATE, "microbench_hrt")) diff --git a/platforms/posix/cmake/sitl_tests.cmake b/platforms/posix/cmake/sitl_tests.cmake index 0a94367965..130556162a 100644 --- a/platforms/posix/cmake/sitl_tests.cmake +++ b/platforms/posix/cmake/sitl_tests.cmake @@ -20,6 +20,7 @@ set(tests List mathlib matrix + microbench_atomic microbench_hrt microbench_math microbench_matrix diff --git a/src/systemcmds/tests/CMakeLists.txt b/src/systemcmds/tests/CMakeLists.txt index 5804b24f90..8abf00dc5d 100644 --- a/src/systemcmds/tests/CMakeLists.txt +++ b/src/systemcmds/tests/CMakeLists.txt @@ -52,6 +52,7 @@ set(srcs test_List.cpp test_mathlib.cpp test_matrix.cpp + test_microbench_atomic.cpp test_microbench_hrt.cpp test_microbench_math.cpp test_microbench_matrix.cpp diff --git a/src/systemcmds/tests/test_microbench_atomic.cpp b/src/systemcmds/tests/test_microbench_atomic.cpp new file mode 100644 index 0000000000..66c0b5d4a0 --- /dev/null +++ b/src/systemcmds/tests/test_microbench_atomic.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** + * + * Copyright (C) 2020 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_atomic.cpp + * Microbenchmark atomic operations. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef __PX4_NUTTX +#include +#endif + +namespace MicroBenchAtomic +{ + +#define PERF(name, op, count) do { \ + px4_usleep(100); \ + reset(); \ + perf_counter_t p = perf_alloc(PC_ELAPSED, name); \ + for (int i = 0; i < count; i++) { \ + px4_usleep(1); \ + lock(); \ + perf_begin(p); \ + op; \ + perf_end(p); \ + unlock(); \ + reset(); \ + } \ + perf_print_counter(p); \ + perf_free(p); \ + } while (0) + +class MicroBenchAtomic : public UnitTest +{ +public: + bool run_tests() override; + +private: + + bool time_atomic_bool(); + bool time_atomic_int8(); + bool time_atomic_int16(); + bool time_atomic_int32(); + bool time_atomic_uint32(); + bool time_atomic_float(); + + void reset(); + + void lock() + { +#ifdef __PX4_NUTTX + _flags = px4_enter_critical_section(); +#endif + } + + void unlock() + { +#ifdef __PX4_NUTTX + px4_leave_critical_section(_flags); +#endif + } + +#ifdef __PX4_NUTTX + irqstate_t _flags {}; +#endif + + px4::atomic _atomic_bool{false}; + px4::atomic _atomic_bool_storage{false}; + bool _test_load_bool{}; + + px4::atomic _atomic_int8{0}; + px4::atomic _atomic_int8_storage{0}; + int8_t _test_load_int8{}; + + px4::atomic _atomic_int16{0}; + px4::atomic _atomic_int16_storage{0}; + int16_t _test_load_int16{}; + + px4::atomic _atomic_int32{0}; + px4::atomic _atomic_int32_storage{0}; + int32_t _test_load_int32{}; + + px4::atomic _atomic_uint32{0}; + px4::atomic _atomic_uint32_storage{0}; + uint32_t _test_load_uint32{}; + + px4::atomic _atomic_float{0}; + px4::atomic _atomic_float_storage{0}; + float _test_load_float{}; +}; + +bool MicroBenchAtomic::run_tests() +{ + ut_run_test(time_atomic_bool); + ut_run_test(time_atomic_int8); + ut_run_test(time_atomic_int16); + ut_run_test(time_atomic_int32); + ut_run_test(time_atomic_uint32); + ut_run_test(time_atomic_float); + + return (_tests_failed == 0); +} + +void MicroBenchAtomic::reset() +{ + srand(time(nullptr)); + + _atomic_bool.store(rand()); + _atomic_bool_storage.store(rand()); + _test_load_bool = rand(); + + _atomic_int8.store(rand()); + _atomic_int8_storage.store(rand()); + _test_load_int8 = rand(); + + _atomic_int16.store(rand()); + _atomic_int16_storage.store(rand()); + _test_load_int16 = rand(); + + _atomic_int32.store(rand()); + _atomic_int32_storage.store(rand()); + _test_load_int32 = rand(); + + _atomic_uint32.store(rand()); + _atomic_uint32_storage.store(rand()); + _test_load_uint32 = rand(); + + _atomic_float.store(rand()); + _atomic_float_storage.store(rand()); + _test_load_float = rand(); +} + +ut_declare_test_c(test_microbench_atomic, MicroBenchAtomic) + +bool MicroBenchAtomic::time_atomic_bool() +{ + PERF("atomic bool load", _test_load_bool = _atomic_bool.load(), 100); + PERF("atomic bool store", _atomic_bool.store(_test_load_bool), 100); + PERF("atomic bool load and store", _atomic_bool_storage.store(_atomic_bool.load()), 100); + + bool expected = true; + PERF("atomic bool compare exchange (same)", + volatile bool compare_exchange = _atomic_bool.compare_exchange(&expected, true), 100); + PERF("atomic bool compare exchange (different)", + volatile bool compare_exchange = _atomic_bool.compare_exchange(&expected, false), 100); + + return true; +} + +bool MicroBenchAtomic::time_atomic_int8() +{ + PERF("atomic int8 load", _test_load_int8 = _atomic_int8.load(), 100); + PERF("atomic int8 store", _atomic_int8.store(_test_load_int8), 100); + PERF("atomic int8 load and store", _atomic_int8_storage.store(_atomic_int8.load()), 100); + PERF("atomic int8 fetch add", _test_load_int8 = _atomic_int8.fetch_add(_test_load_int8), 100); + PERF("atomic int8 fetch sub", _test_load_int8 = _atomic_int8.fetch_sub(_test_load_int8), 100); + PERF("atomic int8 fetch and", _test_load_int8 = _atomic_int8.fetch_and(_test_load_int8), 100); + PERF("atomic int8 fetch xor", _test_load_int8 = _atomic_int8.fetch_xor(_test_load_int8), 100); + PERF("atomic int8 fetch or", _test_load_int8 = _atomic_int8.fetch_or(_test_load_int8), 100); + PERF("atomic int8 fetch nand", _test_load_int8 = _atomic_int8.fetch_nand(_test_load_int8), 100); + + int8_t expected = 42; + PERF("atomic int8 compare exchange (same)", + volatile bool compare_exchange = _atomic_int8.compare_exchange(&expected, 42), 100); + PERF("atomic int8 compare exchange (different)", + volatile bool compare_exchange = _atomic_int8.compare_exchange(&expected, 0), 100); + + return true; +} + +bool MicroBenchAtomic::time_atomic_int16() +{ + PERF("atomic int16 load", _test_load_int16 = _atomic_int16.load(), 100); + PERF("atomic int16 store", _atomic_int16.store(_test_load_int16), 100); + PERF("atomic int16 load and store", _atomic_int16_storage.store(_atomic_int16.load()), 100); + PERF("atomic int16 fetch add", _test_load_int16 = _atomic_int16.fetch_add(_test_load_int16), 100); + PERF("atomic int16 fetch sub", _test_load_int16 = _atomic_int16.fetch_sub(_test_load_int16), 100); + PERF("atomic int16 fetch and", _test_load_int16 = _atomic_int16.fetch_and(_test_load_int16), 100); + PERF("atomic int16 fetch xor", _test_load_int16 = _atomic_int16.fetch_xor(_test_load_int16), 100); + PERF("atomic int16 fetch or", _test_load_int16 = _atomic_int16.fetch_or(_test_load_int16), 100); + PERF("atomic int16 fetch nand", _test_load_int16 = _atomic_int16.fetch_nand(_test_load_int16), 100); + + int16_t expected = 42; + PERF("atomic int16 compare exchange (same)", + volatile bool compare_exchange = _atomic_int16.compare_exchange(&expected, 42), 100); + PERF("atomic int16 compare exchange (different)", + volatile bool compare_exchange = _atomic_int16.compare_exchange(&expected, 0), 100); + + return true; +} + +bool MicroBenchAtomic::time_atomic_int32() +{ + PERF("atomic int32 load", _test_load_int32 = _atomic_int32.load(), 100); + PERF("atomic int32 store", _atomic_int32.store(_test_load_int32), 100); + PERF("atomic int32 load and store", _atomic_int32_storage.store(_atomic_int32.load()), 100); + PERF("atomic int32 fetch add", _test_load_int32 = _atomic_int32.fetch_add(_test_load_int32), 100); + PERF("atomic int32 fetch sub", _test_load_int32 = _atomic_int32.fetch_sub(_test_load_int32), 100); + PERF("atomic int32 fetch and", _test_load_int32 = _atomic_int32.fetch_and(_test_load_int32), 100); + PERF("atomic int32 fetch xor", _test_load_int32 = _atomic_int32.fetch_xor(_test_load_int32), 100); + PERF("atomic int32 fetch or", _test_load_int32 = _atomic_int32.fetch_or(_test_load_int32), 100); + PERF("atomic int32 fetch nand", _test_load_int32 = _atomic_int32.fetch_nand(_test_load_int32), 100); + + int32_t expected = 42; + PERF("atomic int32 compare exchange (same)", + volatile bool compare_exchange = _atomic_int32.compare_exchange(&expected, 42), 100); + PERF("atomic int32 compare exchange (different)", + volatile bool compare_exchange = _atomic_int32.compare_exchange(&expected, 0), 100); + + return true; +} + +bool MicroBenchAtomic::time_atomic_uint32() +{ + PERF("atomic uint32 load", _test_load_uint32 = _atomic_uint32.load(), 100); + PERF("atomic uint32 store", _atomic_uint32_storage.store(_test_load_uint32), 100); + PERF("atomic uint32 load and store", _atomic_uint32_storage.store(_atomic_uint32.load()), 100); + PERF("atomic uint32 fetch add", _test_load_uint32 = _atomic_uint32.fetch_add(_test_load_uint32), 100); + PERF("atomic uint32 fetch sub", _test_load_uint32 = _atomic_uint32.fetch_sub(_test_load_uint32), 100); + PERF("atomic uint32 fetch and", _test_load_uint32 = _atomic_uint32.fetch_and(_test_load_uint32), 100); + PERF("atomic uint32 fetch xor", _test_load_uint32 = _atomic_uint32.fetch_xor(_test_load_uint32), 100); + PERF("atomic uint32 fetch or", _test_load_uint32 = _atomic_uint32.fetch_or(_test_load_uint32), 100); + PERF("atomic uint32 fetch nand", _test_load_uint32 = _atomic_uint32.fetch_nand(_test_load_uint32), 100); + + uint32_t expected = 42; + PERF("atomic uint32 compare exchange (same)", + volatile bool compare_exchange = _atomic_uint32.compare_exchange(&expected, 42), 100); + PERF("atomic uint32 compare exchange (different)", + volatile bool compare_exchange = _atomic_uint32.compare_exchange(&expected, 0), 100); + + return true; +} + +bool MicroBenchAtomic::time_atomic_float() +{ + //PERF("atomic float load", volatile float test_load = _atomic_float.load(), 100); + PERF("atomic float store", _atomic_float.store(_test_load_float), 100); + //PERF("atomic float load and store", _atomic_float_storage.store(_atomic_float.load()), 100); + + float expected = 42; + PERF("atomic float compare exchange (same)", + volatile bool compare_exchange = _atomic_float.compare_exchange(&expected, 42), 100); + PERF("atomic float compare exchange (different)", + volatile bool compare_exchange = _atomic_float.compare_exchange(&expected, 0), 100); + + return true; +} + +} // namespace MicroBenchAtomic diff --git a/src/systemcmds/tests/tests_main.c b/src/systemcmds/tests/tests_main.c index 07bb7aba6d..ffd2ad2372 100644 --- a/src/systemcmds/tests/tests_main.c +++ b/src/systemcmds/tests/tests_main.c @@ -96,6 +96,7 @@ const struct { {"List", test_List, 0}, {"mathlib", test_mathlib, 0}, {"matrix", test_matrix, 0}, + {"microbench_atomic", test_microbench_atomic, 0}, {"microbench_hrt", test_microbench_hrt, 0}, {"microbench_math", test_microbench_math, 0}, {"microbench_matrix", test_microbench_matrix, 0}, diff --git a/src/systemcmds/tests/tests_main.h b/src/systemcmds/tests/tests_main.h index be357f960c..a4efd50bc8 100644 --- a/src/systemcmds/tests/tests_main.h +++ b/src/systemcmds/tests/tests_main.h @@ -63,6 +63,7 @@ extern int test_led(int argc, char *argv[]); extern int test_List(int argc, char *argv[]); extern int test_mathlib(int argc, char *argv[]); extern int test_matrix(int argc, char *argv[]); +extern int test_microbench_atomic(int argc, char *argv[]); 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[]);