lumiera_/tests/library/util-floordiv-test.cpp
Ichthyostega 9c21164ae6 Doxygen Fixes (#1062)
This changeset fixes a huge pile of problems, as indicated in the
error log of the Doxygen run after merging all the recent Doxygen improvements

unfortunately, auto-linking does still not work at various places.
There is no clear indication what might be the problem.
Possibly the rather unstable Sqlite support in this Doxygen version
is the cause. Anyway, needs to be investigated further.
2017-04-02 04:22:51 +02:00

290 lines
8.9 KiB
C++

/*
UtilFloordiv(Test) - verify integer rounding function
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
/** @file util-floordiv-test.cpp
** unit test \ref UtilFloordiv_test
*/
#include "lib/test/run.hpp"
#include "lib/util-quant.hpp"
#include "lib/util.hpp"
#include "lib/format-cout.hpp"
#include "lib/format-string.hpp"
#include <cmath>
#include <time.h>
#include <vector>
using ::Test;
using std::rand;
using util::isnil;
using util::_Fmt;
namespace util {
namespace test {
namespace{ // Test data and operations
const uint NUM_ELMS_PERFORMANCE_TEST = 50000000;
const uint NUMBER_LIMIT = 1 << 30;
typedef std::vector<int> VecI;
VecI
buildTestNumberz (uint cnt)
{
VecI data;
for (uint i=0; i<cnt; ++i)
{
int someNumber (rand() % (2*NUMBER_LIMIT) -NUMBER_LIMIT);
if (!someNumber) someNumber -=(1 +rand() % NUMBER_LIMIT);
data.push_back (someNumber);
}
return data;
}
/** the built-in integer division operator,
* packaged as inline function for timing comparison
*/
inline long
integerDiv (long num, long den)
{
return num / den;
}
/** an alternate formulation,
* which turned out to perform slightly worse
*/
inline long
floordiv_alternate (long num, long den)
{
ldiv_t res = ldiv(num,den);
return (0 >= res.quot && res.rem)? res.quot-1
: res.quot;
}
} // (End) test data and operations
/******************************************************************//**
* @test Evaluate a custom built integer floor function.
* Also known as Knuth's floor division.
* This function is crucial for Lumiera's rule of quantisation
* of time values into frame intervals. This rule requires time
* points to be rounded towards the next lower frame border always,
* irrespective of the relation to the actual time origin.
* Contrast this to the built-in integer division operator, which
* truncates towards zero.
*
* @note if invoked with an non empty parameter, this test performs
* some interesting timing comparisons, which initially were
* used to tweak the implementation a bit.
* @see lib/util.hpp
* @see QuantiserBasics_test
*/
class UtilFloordiv_test : public Test
{
virtual void
run (Arg arg)
{
verifyBehaviour ();
verifyIntegerTypes<int>();
verifyIntegerTypes<long>();
verifyIntegerTypes<short>();
verifyIntegerTypes<int64_t>();
verifyIntegerTypes<long long int>();
if (!isnil (arg))
runPerformanceTest();
}
void
verifyBehaviour ()
{
CHECK ( 3 == floordiv ( 12,4));
CHECK ( 2 == floordiv ( 11,4));
CHECK ( 2 == floordiv ( 10,4));
CHECK ( 2 == floordiv ( 9,4));
CHECK ( 2 == floordiv ( 8,4));
CHECK ( 1 == floordiv ( 7,4));
CHECK ( 1 == floordiv ( 6,4));
CHECK ( 1 == floordiv ( 5,4));
CHECK ( 1 == floordiv ( 4,4));
CHECK ( 0 == floordiv ( 3,4));
CHECK ( 0 == floordiv ( 2,4));
CHECK ( 0 == floordiv ( 1,4));
CHECK ( 0 == floordiv ( 0,4));
CHECK (-1 == floordiv (- 1,4));
CHECK (-1 == floordiv (- 2,4));
CHECK (-1 == floordiv (- 3,4));
CHECK (-1 == floordiv (- 4,4));
CHECK (-2 == floordiv (- 5,4));
CHECK (-2 == floordiv (- 6,4));
CHECK (-2 == floordiv (- 7,4));
CHECK (-2 == floordiv (- 8,4));
CHECK (-3 == floordiv (- 9,4));
CHECK (-3 == floordiv (-10,4));
CHECK (-3 == floordiv (-11,4));
CHECK (-3 == floordiv (-12,4));
}
template<typename I>
void
verifyIntegerTypes ()
{
I n,d,expectedRes;
for (int i=-12; i <= 12; ++i)
{
n = i;
d = 4;
expectedRes = floordiv (i,4);
CHECK (floordiv(n,d) == expectedRes);
}
}
/** @test timing measurements to compare implementation details.
* This test uses a sequence of random integers, where the values
* used as denominator are ensured not to be zero.
*
* \par measurement results
* My experiments (AMD Athlon-64 4200 X2) gave me
* the following timing measurements in nanoseconds:
*
* Verification.......... 127.7
* Integer_div........... 111.7
* double_floor.......... 74.8
* floordiv_int.......... 112.7
* floordiv_long......... 119.8
* floordiv_int64_t...... 121.4
* floordiv_long_alt..... 122.7
*
* These figures are the average of 6 runs with 50 million
* iterations each (as produced by this function)
*
* \par conclusions
* The most significant result is the striking performance of the
* fpu based calculation. Consequently, integer arithmetics should
* only be used when necessary due to resolution requirements, as
* is the case for int64_t based Lumiera Time values, which require
* a precision beyond the 16 digits provided by double.
* Besides that, we can conclude that the additional tests and
* adjustment of the custom floordiv only creates a slight overhead
* compared to the built-in integer div function. An oddity to note
* is the slightly better performance of long over int64_t. Also,
* the alternative formulation of the function, which uses the
* \c fdiv() function also to divide the positive results,
* performs only slightly worse. So this implementation
* was chosen mainly because it seems to state its
* intent more clearly in code.
*/
void
runPerformanceTest ()
{
VecI testdata = buildTestNumberz (2*NUM_ELMS_PERFORMANCE_TEST);
typedef VecI::const_iterator I;
clock_t start(0), stop(0);
_Fmt resultDisplay{"timings(%s)%|30T.|%5.3fsec\n"};
#define START_TIMINGS start=clock();
#define DISPLAY_TIMINGS(ID) \
stop = clock(); \
cout << resultDisplay % STRINGIFY (ID) % (double(stop-start)/CLOCKS_PER_SEC) ;
START_TIMINGS
for (I ii =testdata.begin(); ii!=testdata.end(); )
{
int num = *ii;
++ii;
int den = *ii;
++ii;
CHECK (floor(double(num)/den) == floordiv(num,den));
}
DISPLAY_TIMINGS (Verification)
START_TIMINGS
for (I ii =testdata.begin(); ii!=testdata.end(); )
{
integerDiv (*ii++, *ii++);
}
DISPLAY_TIMINGS (Integer_div)
START_TIMINGS
for (I ii =testdata.begin(); ii!=testdata.end(); )
{
floor (double(*ii++) / *ii++);
}
DISPLAY_TIMINGS (double_floor)
START_TIMINGS
for (I ii =testdata.begin(); ii!=testdata.end(); )
{
floordiv (*ii++, *ii++);
}
DISPLAY_TIMINGS (floordiv_int)
START_TIMINGS
for (I ii =testdata.begin(); ii!=testdata.end(); )
{
floordiv (long(*ii++), long(*ii++));
}
DISPLAY_TIMINGS (floordiv_long)
START_TIMINGS
for (I ii =testdata.begin(); ii!=testdata.end(); )
{
floordiv (int64_t(*ii++), int64_t(*ii++));
}
DISPLAY_TIMINGS (floordiv_int64_t)
START_TIMINGS
for (I ii =testdata.begin(); ii!=testdata.end(); )
{
floordiv_alternate (*ii++, *ii++);
}
DISPLAY_TIMINGS (floordiv_long_alt)
}
};
LAUNCHER (UtilFloordiv_test, "unit common");
}} // namespace util::test