Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 63 additions & 3 deletions ddsrouter_utils/include/ddsrouter_utils/math/math.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
75 changes: 73 additions & 2 deletions ddsrouter_utils/src/cpp/math/math.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,28 @@ namespace eprosima {
namespace ddsrouter {
namespace utils {

#include <assert.h>

#include <ddsrouter_utils/math/math.hpp>

bool is_even(
uint32_t number) noexcept
{
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
{
assert(divisor != 0);

if (dividend < divisor)
{
// Optimize to 1 operation [if]
Expand All @@ -42,13 +58,68 @@ uint32_t fast_module(
// Optimize to 4 operations [if, if, if, and]
return dividend & 1;
}
else if (is_power_of_2(divisor))
{
// Optimize to ~6 operations [if, if, if, if(and), and]
return dividend & (divisor - 1);
}
else
{
// 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 == 1)
{
// Optimize to 3 operations [if, if, if]
return dividend;
}
else if (divisor == 2)
{
// 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
{
// 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 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 */
3 changes: 3 additions & 0 deletions ddsrouter_utils/test/unittest/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ set(TEST_SOURCES
)

set(TEST_LIST
is_even
fast_module
fast_division
arithmetic_progression_sum
)

set(TEST_EXTRA_LIBRARIES
Expand Down
140 changes: 94 additions & 46 deletions ddsrouter_utils/test/unittest/math/mathTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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;
}
}
}
}

Expand Down