From bc6b3607c83a2ed6f5d6507d161da210cec5267f Mon Sep 17 00:00:00 2001 From: jparisu Date: Fri, 12 Aug 2022 09:18:30 +0200 Subject: [PATCH 1/2] Increment math methods Signed-off-by: jparisu --- .../include/ddsrouter_utils/math/math.hpp | 66 ++++++++- ddsrouter_utils/src/cpp/math/math.cpp | 55 ++++++- .../test/unittest/math/CMakeLists.txt | 3 + .../test/unittest/math/mathTest.cpp | 140 ++++++++++++------ 4 files changed, 213 insertions(+), 51 deletions(-) diff --git a/ddsrouter_utils/include/ddsrouter_utils/math/math.hpp b/ddsrouter_utils/include/ddsrouter_utils/math/math.hpp index c17b5be94..8f9c106ee 100644 --- a/ddsrouter_utils/include/ddsrouter_utils/math/math.hpp +++ b/ddsrouter_utils/include/ddsrouter_utils/math/math.hpp @@ -29,25 +29,85 @@ namespace eprosima { namespace ddsrouter { namespace utils { +/** + * @brief Optimize % 2 operation + * + * @param number + * + * @return whether \c number is odd + */ +DDSROUTER_UTILS_DllAPI bool is_even( + uint32_t number) noexcept; + /** * @brief Module (%) operation with performance optimization * * This function optimizes the % operation, that executes a division, by optimizing these cases: - * - If the dividend is smaller or equal than the divisor, the result is the dividend + * - If the dividend is smaller than the divisor, the result is the dividend + * - If the dividend is equal than the divisor, the result is 0 * - If the divisor is 2, the result is the dividend % 2 calculated by a logic AND operation * - If the divisor is a power of 2, the result is calculated by a logic AND operation + * - Otherwise uses % operation * * @param dividend Dividend * @param divisor Divisor (must be greater than 0 so the operation make sense) * - * @attention if divisor is 0, the result is \c dividend + * @pre \c divisor must not be 0 + * + * @return The result of the operation % * - * @return The result of the operation + * @attention Do only use this function with non literal values. Literal values are optimized by compiler. */ DDSROUTER_UTILS_DllAPI uint32_t fast_module( uint32_t dividend, uint32_t divisor) noexcept; +/** + * @brief Integer Division (/) operation with performance optimization + * + * This function optimizes the / operation by optimizing these cases: + * - If \c dividend is smaller or equal than the \c, the result is \c dividend + * - If the \c is 2, the result is \c dividend % 2 calculated by a logic AND operation + * - If the \c is a power of 2, the result is calculated by a logic AND operation + * - Otherwise uses / operation + * + * @param dividend Dividend + * @param divisor Divisor + * + * @pre \c divisor must not be 0 + * + * @return The result of the operation / + * + * @attention Do only use this function with non literal values. Literal values are optimized by compiler. + */ +DDSROUTER_UTILS_DllAPI uint32_t fast_division( + uint32_t dividend, + uint32_t divisor) noexcept; + +/** + * @brief Calculate the sum of an arithmetic progression from an initial to a final number. + * + * This function uses the fast operation to calculate an arithmetic sum: + * S = ((a1 + an) / 2) * (n) + * + * @pre \c interval must be greater than 0 + * @pre \c steps must be greater than 0 + * + * @param lowest lowest element of the arithmetic progression + * @param interval interval between two elements of the arithmetic progression + * @param steps number of steps of the arithmetic progression + * + * @return The result of the sum + * + * EXAMPLE OF USE + * 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = arithmetic_progression_sum(1, 1, 10) + * 0 + 2 + 4 + 6 + 8 = arithmetic_progression_sum(0, 2, 5) + */ +DDSROUTER_UTILS_DllAPI uint32_t arithmetic_progression_sum( + uint32_t lowest, + uint32_t interval, + uint32_t steps) noexcept; + } /* namespace utils */ } /* namespace ddsrouter */ } /* namespace eprosima */ diff --git a/ddsrouter_utils/src/cpp/math/math.cpp b/ddsrouter_utils/src/cpp/math/math.cpp index 1a325cb34..6fc8e8c9d 100644 --- a/ddsrouter_utils/src/cpp/math/math.cpp +++ b/ddsrouter_utils/src/cpp/math/math.cpp @@ -21,12 +21,22 @@ namespace eprosima { namespace ddsrouter { namespace utils { +#include + #include +bool is_even( + uint32_t number) noexcept +{ + return (number & 0x1) == 0; +} + uint32_t fast_module( uint32_t dividend, uint32_t divisor) noexcept { + assert(divisor != 0); + if (dividend < divisor) { // Optimize to 1 operation [if] @@ -42,13 +52,54 @@ uint32_t fast_module( // Optimize to 4 operations [if, if, if, and] return dividend & 1; } + else if (is_even(divisor)) + { + // Optimize to 6 operations [if, if, if, if(and), and] + return dividend & (divisor - 1); + } else { - // Optimize to 7 operations [if, if, if, -, and, -, and] in case E(n){divisor = 2^n} - return divisor & (divisor - 1) ? dividend % divisor : dividend& (divisor - 1); + // Not optimum + return dividend % divisor; } } +uint32_t fast_division( + uint32_t dividend, + uint32_t divisor) noexcept +{ + assert(divisor != 0); + + if (dividend < divisor) + { + // Optimize to 1 operation [if] + return 0; + } + else if (dividend == divisor) + { + // Optimize to 2 operation [if, if] + return 1; + } + else if (divisor == 2) + { + // Optimize to 4 operations [if, if, if, swift] + return (dividend >> 1); + } + else + { + // Not optimum + return dividend / divisor; + } +} + +uint32_t arithmetic_progression_sum( + uint32_t lowest, + uint32_t interval, + uint32_t steps) noexcept +{ + return (((2 * lowest + ((steps - 1) * interval)) * steps) / 2); +} + } /* namespace utils */ } /* namespace ddsrouter */ } /* namespace eprosima */ diff --git a/ddsrouter_utils/test/unittest/math/CMakeLists.txt b/ddsrouter_utils/test/unittest/math/CMakeLists.txt index e8aebb0ef..597498345 100644 --- a/ddsrouter_utils/test/unittest/math/CMakeLists.txt +++ b/ddsrouter_utils/test/unittest/math/CMakeLists.txt @@ -20,7 +20,10 @@ set(TEST_SOURCES ) set(TEST_LIST + is_even fast_module + fast_division + arithmetic_progression_sum ) set(TEST_EXTRA_LIBRARIES diff --git a/ddsrouter_utils/test/unittest/math/mathTest.cpp b/ddsrouter_utils/test/unittest/math/mathTest.cpp index cafc884a0..e09655d2d 100644 --- a/ddsrouter_utils/test/unittest/math/mathTest.cpp +++ b/ddsrouter_utils/test/unittest/math/mathTest.cpp @@ -26,11 +26,55 @@ namespace ddsrouter { namespace utils { namespace test { -void compare_fast_module( +uint32_t NUMBERS_TO_TEST = 1000; +uint32_t NUMBERS_TO_TEST_SHORT = 100; + +bool compare_is_even( + uint32_t number) +{ + return ( + is_even(number) + == + number % 2 == 0); +} + +bool compare_fast_module( + uint32_t dividend, + uint32_t divisor) +{ + return ( + fast_module(dividend, divisor) + == + dividend % divisor); +} + +bool compare_fast_division( uint32_t dividend, uint32_t divisor) { - ASSERT_EQ(fast_module(dividend, divisor), dividend % divisor) << dividend << " % " << divisor; + return ( + fast_division(dividend, divisor) + == + dividend / divisor); +} + +bool compare_arithmetic_progression_sum( + uint32_t lowest, + uint32_t interval, + uint32_t steps) +{ + uint32_t current_number = lowest; + uint32_t real_result = 0; + for (int i = 0; i < steps; ++i) + { + real_result += current_number; + current_number += interval; + } + + return ( + arithmetic_progression_sum(lowest, interval, steps) + == + real_result); } } /* namespace test */ @@ -39,62 +83,66 @@ void compare_fast_module( } /* namespace eprosima */ /** - * Test \c is_file_accesible method - * - * CASES: - * - dividend lower than divisor - * - dividend equal to divisor - * - divisor = 2 - * - divisor = 2^N - * - divisor even no 2^N - * - divisor odd + * Test \c is_even method */ -TEST(mathTest, fast_module) +TEST(mathTest, is_even) { - // dividend lower than divisor + // calculate module in many cases + for (uint32_t number = 0; number < test::NUMBERS_TO_TEST; ++number) { - test::compare_fast_module(3, 4); - test::compare_fast_module(0, 4); - test::compare_fast_module(101223, 20921341); - } - - // dividend equal to divisor - { - test::compare_fast_module(3, 3); - test::compare_fast_module(4, 4); - test::compare_fast_module(66666, 66666); - } - - // divisor = 2 - { - test::compare_fast_module(3, 2); - test::compare_fast_module(4, 2); - test::compare_fast_module(66666, 2); - test::compare_fast_module(431253426, 2); + ASSERT_TRUE(test::compare_is_even(number)) + << number; } +} - // divisor = 2^N +/** + * Test \c fast_module method + */ +TEST(mathTest, fast_module) +{ + // calculate module in many cases + for (uint32_t dividend = 0; dividend < test::NUMBERS_TO_TEST; ++dividend) { - test::compare_fast_module(3, 4); - test::compare_fast_module(32, 8); - test::compare_fast_module(66666, 128); - test::compare_fast_module(431253426, 2048); + for (uint32_t divisor = 1; divisor < test::NUMBERS_TO_TEST; ++divisor) + { + ASSERT_TRUE(test::compare_fast_module(dividend, divisor)) + << dividend << " % " << divisor; + } } +} - // divisor even no 2^N +/** + * Test \c fast_division method + */ +TEST(mathTest, fast_division) +{ + // calculate module in many cases + for (uint32_t dividend = 0; dividend < test::NUMBERS_TO_TEST; ++dividend) { - test::compare_fast_module(3, 8); - test::compare_fast_module(12, 10); - test::compare_fast_module(66666, 120); - test::compare_fast_module(431253426, 2040); + for (uint32_t divisor = 1; divisor < test::NUMBERS_TO_TEST; ++divisor) + { + ASSERT_TRUE(test::compare_fast_division(dividend, divisor)) + << dividend << " % " << divisor; + } } +} - // divisor odd +/** + * Test \c arithmetic_progression_sum method + */ +TEST(mathTest, arithmetic_progression_sum) +{ + // calculate module in many cases + for (uint32_t lowest = 0; lowest < test::NUMBERS_TO_TEST_SHORT; ++lowest) { - test::compare_fast_module(3, 5); - test::compare_fast_module(12, 11); - test::compare_fast_module(66666, 127); - test::compare_fast_module(431253426, 2041); + for (uint32_t interval = 1; interval < test::NUMBERS_TO_TEST_SHORT; ++interval) + { + for (uint32_t steps = 1; steps < test::NUMBERS_TO_TEST_SHORT; ++steps) + { + ASSERT_TRUE(test::compare_arithmetic_progression_sum(lowest, interval, steps)) + << lowest << " , " << interval << " , " << steps; + } + } } } From 9310b98dc66e99d1cf1b397d42d2ac15981cdac9 Mon Sep 17 00:00:00 2001 From: jparisu Date: Fri, 12 Aug 2022 10:00:00 +0200 Subject: [PATCH 2/2] fix module Signed-off-by: jparisu --- ddsrouter_utils/src/cpp/math/math.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/ddsrouter_utils/src/cpp/math/math.cpp b/ddsrouter_utils/src/cpp/math/math.cpp index 6fc8e8c9d..63b3b4203 100644 --- a/ddsrouter_utils/src/cpp/math/math.cpp +++ b/ddsrouter_utils/src/cpp/math/math.cpp @@ -31,6 +31,12 @@ bool is_even( return (number & 0x1) == 0; } +bool is_power_of_2( + uint32_t number) noexcept +{ + return number && (!(number & (number-1))); +} + uint32_t fast_module( uint32_t dividend, uint32_t divisor) noexcept @@ -52,9 +58,9 @@ uint32_t fast_module( // Optimize to 4 operations [if, if, if, and] return dividend & 1; } - else if (is_even(divisor)) + else if (is_power_of_2(divisor)) { - // Optimize to 6 operations [if, if, if, if(and), and] + // Optimize to ~6 operations [if, if, if, if(and), and] return dividend & (divisor - 1); } else @@ -80,11 +86,25 @@ uint32_t fast_division( // Optimize to 2 operation [if, if] return 1; } + else if (divisor == 1) + { + // Optimize to 3 operations [if, if, if] + return dividend; + } else if (divisor == 2) { - // Optimize to 4 operations [if, if, if, swift] + // Optimize to 5 operations [if, if, if, if, swift] return (dividend >> 1); } + else if (is_power_of_2(divisor)) + { + while (divisor != 1) + { + dividend >>= 1; + divisor >>= 1; + } + return dividend; + } else { // Not optimum