Skip to content

Commit

Permalink
exhaustive tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jtlap authored and jfalcou committed Jun 29, 2016
1 parent a92db49 commit d4d9cc9
Show file tree
Hide file tree
Showing 26 changed files with 1,266 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ NS_prevent_in_source_build()
set(STF_STANDALONE_DESTINATION ${PROJECT_SOURCE_DIR}/test)
set(BRIGAND_STANDALONE_DESTINATION ${PROJECT_SOURCE_DIR}/include/boost/simd/detail)
set(NS_BENCH_STANDALONE_DESTINATION ${PROJECT_SOURCE_DIR}/bench)
set(NS_EXHAUSTIVE_STANDALONE_DESTINATION ${PROJECT_SOURCE_DIR}/exhaustive)

set(NS_CMAKE_PROJECT_OPTIONS
CMAKE_ARGS "-DBOOST_ROOT=${BOOST_HEADER_ONLY_DESTINATION}/include"
Expand Down Expand Up @@ -88,6 +89,7 @@ include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/test
${PROJECT_SOURCE_DIR}/bench
${PROJECT_SOURCE_DIR}/exhaustive
${Boost_INCLUDE_DIRS}
)

Expand All @@ -103,6 +105,9 @@ add_subdirectory(test)
add_custom_target(bench)
add_subdirectory(bench)

add_custom_target(exhaustive)
add_subdirectory(exhaustive)

if (NOT DEFINED IS_TRAVIS_CI)
NS_include(coverage)
enable_coverage(boost.simd)
Expand Down
39 changes: 39 additions & 0 deletions cmake/ns/make_exhaustive.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## -------------------------------------------------------------------------------------------------
## Copyright 2016 - NumScale SAS
##
## Distributed under the Boost Software License, Version 1.0.
## See accompanying file LICENSE.txt or copy at
## http://www.boost.org/LICENSE_1_0.txt
## -------------------------------------------------------------------------------------------------

include(${NS_CMAKE_ROOT}/ns.cmake)
NS_guard(NS_CMAKE_MAKE_EXHAUSTIVE)
NS_include(compilers)
NS_include(add_target_parent)

## Process a list of source files to generate corresponding exhaustive target
## -------------------------------------------------------------------------------------------------
function(make_exhaustive root)
foreach(file ${ARGN})
string(REPLACE ".cpp" ".exhaustive" base ${file})
set(exhaustive "${root}.${base}")

add_executable(${exhaustive} ${file})
set_property( TARGET ${exhaustive}
PROPERTY RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/exhaustive"
)
set_target_properties ( ${exhaustive} PROPERTIES
EXCLUDE_FROM_DEFAULT_BUILD TRUE
EXCLUDE_FROM_ALL TRUE
)

add_target_parent(${exhaustive})
add_dependencies(exhaustive ${exhaustive})
endforeach()
endfunction()

set(CMAKE_CXX_FLAGS_EXHAUSTIVE
" -O3 -fopenmp"
CACHE STRING "Flags used by the C++ compiler during exhaustive builds."
FORCE)

13 changes: 13 additions & 0 deletions exhaustive/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## -------------------------------------------------------------------------------------------------
## Copyright 2016 - NumScale SAS
##
## Distributed under the Boost Software License, Version 1.0.
## See accompanying file LICENSE.txt or copy at
## http://www.boost.org/LICENSE_1_0.txt
## -------------------------------------------------------------------------------------------------

NS_include(make_exhaustive)

set(CMAKE_BUILD_TYPE "Release")

add_subdirectory(function)
310 changes: 310 additions & 0 deletions exhaustive/exhaustive.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
// -------------------------------------------------------------------------------------------------
/*!
@file Main header for exhaustive components
@copyright 2016 - NumScale SAS
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
**/
// -------------------------------------------------------------------------------------------------

#ifndef NS_BENCH_HPP_INCLUDED
#define NS_BENCH_HPP_INCLUDED

#include <boost/simd/function/simd/load.hpp>
#include <boost/simd/function/simd/extract.hpp>
#include <boost/simd/function/simd/successor.hpp>
#include <boost/simd/function/simd/splat.hpp>
#include <boost/simd/function/scalar/is_invalid.hpp>
#include <boost/simd/function/scalar/ulpdist.hpp>
#include <boost/simd/function/scalar/iround.hpp>
#include <boost/simd/function/scalar/ilog2.hpp>
#include <boost/simd/function/scalar/min.hpp>
#include <boost/simd/function/scalar/abs.hpp>
#include <boost/simd/function/scalar/bitwise_cast.hpp>
#include <boost/simd/constant/valmin.hpp>
#include <boost/simd/constant/valmax.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/meta/cardinal_of.hpp>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstddef>
#include "omp.hpp"
#include <boost/simd/detail/dispatch/function/overload.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>
#include <boost/simd/detail/dispatch/meta/scalar_of.hpp>
#include <boost/config.hpp>

namespace bd = boost::dispatch;
namespace bs = boost::simd;
namespace boost { namespace simd {
/*!
@defgroup group-exhaustive exhaustive tests components
Exhaustive components
**/

#include <boost/core/demangle.hpp>
#include <type_traits>

template<typename T> inline std::string type_id()
{
typedef std::is_const<typename std::remove_reference<T>::type> const_t;
typedef std::is_lvalue_reference<T> lref_t;
typedef std::is_rvalue_reference<T> rref_t;
std::string s = boost::core::demangle(typeid(T).name());
s += const_t::value ? " const" : "";
s += lref_t::value ? "&" : "";
s += rref_t::value ? "&&" : "";
return s;
}
template<typename T> inline std::string type_id( const T& )
{
return type_id<T>();
}
}
}


namespace boost
{
namespace simd
{
/*!
@brief Exhaustive precision test for single precision
Perform a ULP test on every representable single precision value
in a given interval. Results are reported using a bucket histogram that
gives hint on how many values fall in a given range of ULPs,
the smallest and greatest inputs in the chosen range
leading to this precision and the result from this minimum input
against the awaited result.
@par Note:
Currently this function is designed to take care of single precision only as
running such a test on double precision values take an absurd amount of time.
@tparam Type Data type used for computation
@param mini Lower bound of the test interval
@param maxi Upper bound of the test interval
@param test_f Function to test
@param reference_f Reference function to compare to
@par Example:
Here is an example to test SIMD single-precision nt2::log against scalar double-precision std::log.
@code
#include <boost/simd/sdk/simd/native.hpp>
#include <boost/simd/function/log.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/valmax.hpp>
#include <nt2/sdk/exhaustive/exhaustive.hpp>
// specific to nt2 tests: specify assert handling
#define NT2_ASSERTS_AS_TRAP
#include <nt2/sdk/error/assert_as_trap.hpp>
#include <cmath>
#include <cstdlib>
// function object used for the reference
struct std_log
{
float operator()(float x) const
{
return float(std::log(double(x)));
}
};
int main(int argc, char* argv[])
{
// define boundaries from command-line arguments,
// fallback to default values if not provided
float mini = argc >= 2 ? std::atof(argv[1]) : nt2::Zero<float>();
float mini = argc >= 3 ? std::atof(argv[2]) : nt2::Valmin<float>();
// type to call the function object to test with
typedef boost::simd::native<float, BOOST_SIMD_DEFAULT_EXTENSION> n_t;
// run the test
nt2::exhaustive_test<n_t>( mini
, maxi
, nt2::functor<nt2::tag::log_>() // function object to test
, std_log()
);
}
@endcode
**/
template<typename Type, typename TestF, typename RefF> inline
void exhaustive_test(float mini, float maxi, TestF test_f, RefF reference_f)
{
typedef Type n_t;
typedef bd::as_integer_t<Type> in_t;

static const std::uint32_t M = 128;
static const std::uint32_t N = bs::cardinal_of<n_t>::value;
in_t vN = bs::splat<in_t>(N);

std::uint32_t histo[M+2];
float maxin[M+2], minin[M+2], minref[M+2], minval[M+2];

for(auto& e : histo ) e = 0;
for(auto& e : minin ) e = bs::Valmax<float>();
for(auto& e : maxin ) e = bs::Valmin<float>();
for(auto& e : minval) e = bs::Nan<float>();
for(auto& e : minref) e = bs::Valmax<float>();

auto t_min = bs::bitwise_cast<std::uint32_t>(bs::abs(mini));
auto t_max = bs::bitwise_cast<std::uint32_t>(bs::abs(maxi));

auto diff = (mini*maxi < 0) ? std::max(t_max,t_min) + std::min(t_max,t_min)
: std::max(t_max,t_min) - std::min(t_max,t_min);

std::cout << "Exhaustive test for: " << bs::type_id(test_f) << "\n"
<< " versus: " << bs::type_id(reference_f) << "\n"
<< " With T: " << bs::type_id<Type>() << "\n"
<< " in range: [" << mini << ", " << maxi << "]" << "\n"
<< " # of tests: " << diff << "\n";
std::cout << std::endl;

std::uint32_t k = 0;

#ifdef _OPENMP
#pragma omp parallel firstprivate(mini,maxi,diff)
#endif
{
bool hit_loc[M+2];
std::uint32_t histo_loc[M+2];
float maxin_loc[M+2] , minin_loc[M+2], minref_loc[M+2], minval_loc[M+2];

for(auto& e : hit_loc ) e = false;
for(auto& e : histo_loc ) e = 0;
for(auto& e : minin_loc ) e = bs::Valmax<float>();
for(auto& e : maxin_loc ) e = bs::Valmin<float>();
for(auto& e : minval_loc) e = bs::Nan<float>();
for(auto& e : minref_loc) e = bs::Valmax<float>();

std::size_t numThreads = get_num_threads();
std::size_t my_id = get_thread_num();
std::size_t num_inc = diff / numThreads;

float my_mini = bs::successor(mini,my_id*num_inc);
float my_maxi = bs::successor(my_mini,num_inc);
if (my_id == numThreads -1) my_maxi = maxi;

// Fill up the reference SIMD register data
float a[N];
a[0] = my_mini;
for(std::size_t i = 1; i < N; i++) a[i] = bs::successor(a[i-1], 1);
n_t a0 = bs::load<n_t>(&a[0],0);

std::uint32_t k_loc = 0;

while( bs::extract(a0,1) < my_maxi )
{
n_t z = test_f(a0);

for(std::size_t i = 0; i < N; i++)
{
if (bs::extract(a0,i)> my_maxi) break;
float v = reference_f(bs::extract(a0,i));
float sz = bs::extract(z,i);

double u = bs::ulpdist(v, sz)*2.0+1.0;

if(bs::is_invalid(u))
{
++histo_loc[M+1];
if (!hit_loc[M+1])
{
maxin_loc [M+1] = minin_loc [M+1] = bs::extract(a0,i);
minref_loc[M+1] = v;
minval_loc[M+1] = bs::extract(z,i);
hit_loc[M+1] = true;
}
else
{
maxin_loc [M+1] = bs::extract(a0,i);
}
}
else
{
int p = bs::min(int(M), int(bs::ilog2(bs::iround(u))));

if (!hit_loc[p])
{
maxin_loc [p] = minin_loc [p] = bs::extract(a0,i);
minref_loc[p] = v;
minval_loc[p] = bs::extract(z,i);
hit_loc[p] = true;
}
else
{
maxin_loc [p] = bs::extract(a0,i);
}
++histo_loc[p];
}

++k_loc;
}
a0 = bs::successor(a0, vN);
}

#ifdef _OPENMP
#pragma omp critical
#endif
{
for (std::size_t kk=0;kk<M+2;kk++)
{
maxin[kk] = std::max(maxin_loc[kk],maxin[kk]);

if (minin_loc[kk]<minin[kk])
{
minin[kk] = std::min(minin_loc[kk],minin[kk]);
minval[kk] = minval_loc[kk];
}

minref[kk] = std::min(minref_loc[kk],minref[kk]);
histo[kk] += histo_loc[kk];
}
k += k_loc;
}
} //end omp parallel

std::cout << k << "/" << diff << " values computed.\n";

double d = 1;
for(std::size_t i = 0; i < M+1; i++, d*= 2.0)
{
if(histo[i])
{
printf("%10u values (%.2f%%)\twithin %1.1f ULPs\t"
, histo[i], (histo[i]*100.0/k), (d < 2 ? 0 : d/4)
);
if(i)
std::cout << std::scientific << std::setprecision(9)
<< "in range [" << minin[i] << ", "<< maxin[i] << "]" << "."
<< " Example: "<< minin[i] << " returns " << minval[i]
<< " instead of " << minref[i];
std::cout << std::endl;
}
}

if( histo[M+1])
{
printf("%10u values (%.2f%%)\twith invalid result.\t"
, histo[M+1], (histo[M+1]*100.0/k)
);
std::cout << std::scientific << std::setprecision(9)
<< "in range [" << minin[M+1] << ", "<< maxin[M+1] << "]" << "."
<< " Example: "<< minin[M+1] << " returns " << minval[M+1]
<< " instead of " << minref[M+1]
<< std::endl;
}
}
}
}


#endif
Loading

0 comments on commit d4d9cc9

Please sign in to comment.