diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 69eabc345538..6beec90151fa 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -26,6 +26,7 @@ add_subdirectory("core/") add_subdirectory("event/") add_subdirectory("idx/") add_subdirectory("kernel/") +add_subdirectory("math/mathOps/") add_subdirectory("math/sincos/") add_subdirectory("mem/buf/") add_subdirectory("mem/view/") diff --git a/test/unit/math/mathOps/CMakeLists.txt b/test/unit/math/mathOps/CMakeLists.txt new file mode 100644 index 000000000000..ce1825a39d84 --- /dev/null +++ b/test/unit/math/mathOps/CMakeLists.txt @@ -0,0 +1,47 @@ +# +# Copyright 2017-2019 Benjamin Worpitz, Jakob Krude +# +# This file is part of Alpaka. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +set(_TARGET_NAME "mathOps") + + +append_recursive_files_add_to_src_group("src/" "src/" "cpp" _FILES_SOURCE) + + +message(STATUS "Disabling fast math options for math operation tests only.") +if(ALPAKA_ACC_GPU_CUDA_ENABLE) + list(REMOVE_ITEM + CUDA_NVCC_FLAGS "--ftz=true" "--prec-div=false" "--prec-sqrt=false" "--fmad=true" "--use_fast_math" "-use_fast_math") + +endif() +if(ALPAKA_ACC_GPU_HIP_ENABLE) + list(REMOVE_ITEM + HIP_NVCC_FLAGS "--ftz=true" "--prec-div=false" "--prec-sqrt=false" "--fmad=true" "--use_fast_math" "-use_fast_math") +endif() + + +alpaka_add_executable( + ${_TARGET_NAME} + ${_FILES_SOURCE}) +target_include_directories( + ${_TARGET_NAME} + PRIVATE ${Boost_INCLUDE_DIRS}) +target_link_libraries( + ${_TARGET_NAME} + PRIVATE common) +set_target_properties( + ${_TARGET_NAME} + PROPERTIES + COMPILE_OPTIONS + $<$:"-ffp-contract=off"> # ffp-contract: https://llvm.org/docs/CompileCudaWithLLVM.html#id5 + ) + +set_target_properties(${_TARGET_NAME} PROPERTIES FOLDER "test/unit") + +add_test(NAME ${_TARGET_NAME} COMMAND ${_TARGET_NAME} ${_ALPAKA_TEST_OPTIONS}) diff --git a/test/unit/math/mathOps/include/Buffer.hpp b/test/unit/math/mathOps/include/Buffer.hpp new file mode 100644 index 000000000000..710c0cc1f803 --- /dev/null +++ b/test/unit/math/mathOps/include/Buffer.hpp @@ -0,0 +1,151 @@ +/** Copyright 2019 Jakob Krude, Benjamin Worpitz + * + * This file is part of Alpaka. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Defines.hpp" +#include +#include + +namespace alpaka { +namespace test { +namespace unit { +namespace math { + +//! Provides Alpaka-style buffer with arguments' data. +//! TData can be a plain value or a complex data-structure. +//! The operator() is overloaded and returns the value from the correct Buffer, +//! either from the host (index) or device buffer (index, acc). +//! Index out of range errors are not checked. +//! @brief Encapsulates buffer initialisation and communication with Device. +//! @tparam TAcc Used accelerator, not interchangeable +//! @tparam TData The Data-type, only restricted by the alpaka-interface. +//! @tparam Tcapacity The size of the buffer. +template< + typename TAcc, + typename TData, + size_t Tcapacity +> +struct Buffer +{ + using value_type = TData; + static constexpr size_t capacity = Tcapacity; + using Dim = typename alpaka::dim::traits::DimType::type; + using Idx = typename alpaka::idx::traits::IdxType::type; + + // Defines using's for alpaka-buffer. + using DevAcc = alpaka::dev::Dev< TAcc >; + using DevHost = alpaka::dev::DevCpu; + using PltfHost = alpaka::pltf::Pltf< DevHost >; + + using BufHost = alpaka::mem::buf::Buf< + DevHost, + TData, + Dim, + Idx + >; + using BufAcc = alpaka::mem::buf::Buf< + DevAcc, + TData, + Dim, + Idx + >; + + DevHost devHost; + + BufHost hostBuffer; + BufAcc devBuffer; + + // Native pointer to access buffer. + TData * const pHostBuffer; + TData * const pDevBuffer; + + + // This constructor cant be used, + // because BufHost and BufAcc need to be initialised. + Buffer( ) = delete; + + // Constructor needs to initialize all Buffer. + Buffer(const DevAcc & devAcc) + : + devHost{ alpaka::pltf::getDevByIdx< PltfHost >( 0u ) }, + hostBuffer + { + alpaka::mem::buf::alloc(devHost, Tcapacity) + }, + devBuffer + { + alpaka::mem::buf::alloc(devAcc, Tcapacity) + }, + pHostBuffer{ alpaka::mem::view::getPtrNative( hostBuffer ) }, + pDevBuffer{ alpaka::mem::view::getPtrNative( devBuffer ) } + {} + + // Copy Host -> Acc. + template< typename Queue > + auto copyToDevice( Queue queue ) -> void + { + alpaka::mem::view::copy( + queue, + devBuffer, + hostBuffer, + Tcapacity + ); + } + + // Copy Acc -> Host. + template< typename Queue > + auto copyFromDevice( Queue queue ) -> void + { + alpaka::mem::view::copy( + queue, + hostBuffer, + devBuffer, + Tcapacity + ); + } + + ALPAKA_FN_ACC + auto operator()( + size_t idx, + TAcc const & acc ) const -> TData& + { + alpaka::ignore_unused(acc); + return pDevBuffer[idx]; + } + + ALPAKA_FN_HOST + auto operator()( + size_t idx ) const -> TData& + { + return pHostBuffer[idx]; + } + + ALPAKA_FN_HOST + friend std::ostream & operator<<( + std::ostream & os, + const Buffer & buffer + ) + { + os << "capacity: " << capacity + << "\n"; + for( size_t i = 0; i < capacity; ++i ) + { + os << i + << ": " << buffer.pHostBuffer[i] + << "\n"; + } + return os; + } +}; + +} // math +} // unit +} // test +} // alpaka diff --git a/test/unit/math/mathOps/include/DataGen.hpp b/test/unit/math/mathOps/include/DataGen.hpp new file mode 100644 index 000000000000..5ac9607b1e7f --- /dev/null +++ b/test/unit/math/mathOps/include/DataGen.hpp @@ -0,0 +1,139 @@ +/** Copyright 2019 Jakob Krude, Benjamin Worpitz + * + * This file is part of Alpaka. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Defines.hpp" +#include +#include +#include + +namespace alpaka { +namespace test { +namespace unit { +namespace math { + + + /** + * Fills buffer with random numbers (host-only). + * + * @tparam TData The used data-type (float || double). + * @tparam TArgs The args-buffer to be filled. + * @tparam TFunctor The used Functor-type. + * @param args The buffer that should be filled. + * @param functor The Functor, needed for ranges. + * @param seed The used seed. + */ + template< + typename TData, + typename TArgs, + typename TFunctor> + auto fillWithRndArgs( + TArgs & args, + TFunctor functor, + unsigned long const & seed + ) -> void + { + /* + * Each "sub-buffer" is filled with zero and/or max and/or lowest, + * depending on the specified range (at [0] - [2]). + * + * Every switch case needs to return! + * If no switch case was matched an assert(false) will be triggered. + * + * This function is easily extendable. It is only necessary to add extra + * definitions in the switch case, for more Range-types. + */ + static_assert( TArgs::value_type::arity == TFunctor::arity, + "Buffer properties must match TFunctor::arity" ); + static_assert( TArgs::capacity > 2, + "Set of args must provide > 2 entries." ); + constexpr auto max = std::numeric_limits< TData >::max(); + constexpr auto low = std::numeric_limits< TData >::lowest(); + std::default_random_engine eng{ + static_cast< std::default_random_engine::result_type >( seed ) }; + + // These pseudo-random numbers are implementation/platform specific! + std::uniform_real_distribution< TData > dist( 0, 1000 ); + std::uniform_real_distribution< TData > distOne( -1, 1 ); + for( size_t k = 0; k < TFunctor::arity_nr; ++k ) + { + bool matchedSwitch = false; + switch( functor.ranges[k] ) + { + case Range::OneNeighbourhood: + matchedSwitch = true; + for( size_t i = 0; i < TArgs::capacity; ++i ) + { + args( i ).arg[k] = distOne( eng ); + } + break; + + case Range::PositiveOnly: + matchedSwitch = true; + args( 0 ).arg[k] = max; + for( size_t i = 1; i < TArgs::capacity; ++i ) + { + args( i ).arg[k] = dist( eng ) + static_cast(1); + } + break; + + case Range::PositiveAndZero: + matchedSwitch = true; + args( 0 ).arg[k] = 0.0; + args( 1 ).arg[k] = max; + for( size_t i = 2; i < TArgs::capacity; ++i ) + { + args( i ).arg[k] = dist( eng ); + } + break; + + case Range::NotZero: + matchedSwitch = true; + args( 0 ).arg[k] = max; + args( 1 ).arg[k] = low; + for( size_t i = 2; i < TArgs::capacity; ++i ) + { + TData arg; + do + { + arg = dist( eng ); + } + while( std::equal_to()(arg,1) ); + if( i % 2 == 0 ) + args( i ).arg[k] = arg; + else + args( i ).arg[k] = -arg; + } + break; + + case Range::Unrestricted: + matchedSwitch = true; + args( 0 ).arg[k] = 0.0; + args( 1 ).arg[k] = max; + args( 2 ).arg[k] = low; + for( size_t i = 3; i < TArgs::capacity; ++i ) + { + if( i % 2 == 0 ) + args( i ).arg[k] = dist( eng ); + else + args( i ).arg[k] = -dist( eng ); + } + break; + } + // disable gcc-warning "unused variable" + alpaka::ignore_unused(matchedSwitch); + assert(matchedSwitch); + } + } + +} // math +} // unit +} // test +} // alpaka diff --git a/test/unit/math/mathOps/include/Defines.hpp b/test/unit/math/mathOps/include/Defines.hpp new file mode 100644 index 000000000000..a4bd0397ae40 --- /dev/null +++ b/test/unit/math/mathOps/include/Defines.hpp @@ -0,0 +1,72 @@ +/** Copyright 2019 Jakob Krude, Benjamin Worpitz + * + * This file is part of Alpaka. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include +#include +#include + + +namespace alpaka { +namespace test { +namespace unit { +namespace math { + + // New types need to be added to the switch-case in DataGen.hpp + enum class Range + { + OneNeighbourhood, + PositiveOnly, + PositiveAndZero, + NotZero, + Unrestricted + }; + + // New types need to be added to the operator() function in Functor.hpp + enum class Arity + { + Unary = 1, + Binary = 2 + }; + + template + struct ArgsItem{ + static constexpr Arity arity = Tarity; + static constexpr size_t arity_nr = static_cast(Tarity); + + T arg[arity_nr]; // represents arg0, arg1, ... + + friend std::ostream & operator<<( + std::ostream & os, + const ArgsItem & argsItem + ) + { + os.precision(17); + os << "[ "; + for( size_t i = 0; i < argsItem.arity_nr; ++i ) + os << std::setprecision( + std::numeric_limits::digits10 + 1) << + argsItem.arg[i] << ", "; + os << "]"; + return os; + } + }; + + template< typename T > + auto rsqrt( T const & arg ) -> decltype( std::sqrt( arg ) ) + { + return static_cast(1) / std::sqrt( arg ); + } + +} // math +} // unit +} // test +} // alpaka diff --git a/test/unit/math/mathOps/include/Functor.hpp b/test/unit/math/mathOps/include/Functor.hpp new file mode 100644 index 000000000000..c14716b9752e --- /dev/null +++ b/test/unit/math/mathOps/include/Functor.hpp @@ -0,0 +1,284 @@ +/** Copyright 2019 Jakob Krude, Benjamin Worpitz + * + * This file is part of Alpaka. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Defines.hpp" +#include +#include + +namespace alpaka { +namespace test { +namespace unit { +namespace math { + + +// Can be used with operator() that will use either the std. function or the +// equivalent alpaka function (if an accelerator is passed additionally). +//! @param NAME The Name used for the Functor, e.g. OpAbs +//! @param ARITY Enum-type can be one ... n +//! @param STD_OP Function used for the host side, e.g. std::abs +//! @param ALPAKA_OP Function used for the device side, e.g. alpaka::math::abs. +//! @param ... List of Ranges. Needs to match the arity. +#define ALPAKA_TEST_MATH_OP_FUNCTOR( NAME, ARITY, STD_OP, ALPAKA_OP, ... ) \ + struct NAME \ + { \ + /* ranges is not a constexpr, so that it's accessible via for loop*/ \ + static constexpr Arity arity = ARITY; \ + static constexpr size_t arity_nr = static_cast(ARITY); \ + const Range ranges[ arity_nr ] = {__VA_ARGS__}; \ + \ + ALPAKA_NO_HOST_ACC_WARNING \ + template::value, \ + int>::type = 0> \ + ALPAKA_FN_ACC \ + auto execute( \ + TAcc const & acc, \ + TArgs const & ... args ) const \ + { \ + return ALPAKA_OP(acc, args... ); \ + } \ + \ + ALPAKA_NO_HOST_ACC_WARNING \ + template< \ + typename TAcc = std::nullptr_t, \ + typename... TArgs, \ + /* SFINAE: Enables if called from host. */ \ + typename std::enable_if< \ + std::is_same< TAcc, std::nullptr_t>::value, \ + int>::type = 0> \ + ALPAKA_FN_HOST \ + auto execute( \ + TAcc const & acc, \ + TArgs const &... args ) const \ + { \ + alpaka::ignore_unused( acc ); \ + return STD_OP( args... ); \ + } \ + \ + /* assigns args by arity */ \ + ALPAKA_NO_HOST_ACC_WARNING \ + template< \ + typename T, \ + typename TAcc = std::nullptr_t> \ + ALPAKA_FN_HOST_ACC \ + auto operator()( \ + ArgsItem const & args, \ + TAcc const & acc = nullptr) const \ + { \ + return execute(acc, args.arg[0]); \ + } \ + \ + /* assigns args by arity */ \ + ALPAKA_NO_HOST_ACC_WARNING \ + template< \ + typename T, \ + typename TAcc = std::nullptr_t> \ + ALPAKA_FN_HOST_ACC \ + auto operator()( \ + ArgsItem const & args, \ + TAcc const & acc = nullptr) const \ + { \ + return execute(acc, args.arg[0], args.arg[1]); \ + } \ + \ + friend std::ostream & operator << ( \ + std::ostream &out, \ + const NAME &op) \ + { \ + out << #NAME; \ + alpaka::ignore_unused( op ); \ + return out; \ + } \ + }; + + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpAbs, + Arity::Unary, + std::abs, + alpaka::math::abs, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpAcos, + Arity::Unary, + std::acos, + alpaka::math::acos, + Range::OneNeighbourhood ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpAsin, + Arity::Unary, + std::asin, + alpaka::math::asin, + Range::OneNeighbourhood ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpAtan, + Arity::Unary, + std::atan, + alpaka::math::atan, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpCbrt, + Arity::Unary, + std::cbrt, + alpaka::math::cbrt, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpCeil, + Arity::Unary, + std::ceil, + alpaka::math::ceil, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpCos, + Arity::Unary, + std::cos, + alpaka::math::cos, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpErf, + Arity::Unary, + std::erf, + alpaka::math::erf, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpExp, + Arity::Unary, + std::exp, + alpaka::math::exp, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpFloor, + Arity::Unary, + std::floor, + alpaka::math::floor, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpLog, + Arity::Unary, + std::log, + alpaka::math::log, + Range::PositiveOnly ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpRound, + Arity::Unary, + std::round, + alpaka::math::round, + Range::Unrestricted ) + +// There is no std implementation look in Defines.hpp. +ALPAKA_TEST_MATH_OP_FUNCTOR( OpRsqrt, + Arity::Unary, + alpaka::test::unit::math::rsqrt, + alpaka::math::rsqrt, + Range::PositiveOnly ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpSin, + Arity::Unary, + std::sin, + alpaka::math::sin, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpSqrt, + Arity::Unary, + std::sqrt, + alpaka::math::sqrt, + Range::PositiveAndZero ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpTan, + Arity::Unary, + std::tan, + alpaka::math::tan, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpTrunc, + Arity::Unary, + std::trunc, + alpaka::math::trunc, + Range::Unrestricted ) + +// All binary operators. +ALPAKA_TEST_MATH_OP_FUNCTOR( OpAtan2, + Arity::Binary, + std::atan2, + alpaka::math::atan2, + Range::NotZero, + Range::NotZero ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpFmod, + Arity::Binary, + std::fmod, + alpaka::math::fmod, + Range::Unrestricted, + Range::NotZero ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpMax, + Arity::Binary, + std::max, + alpaka::math::max, + Range::Unrestricted, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpMin, + Arity::Binary, + std::min, + alpaka::math::min, + Range::Unrestricted, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpPow, + Arity::Binary, + std::pow, + alpaka::math::pow, + Range::PositiveAndZero, + Range::Unrestricted ) + +ALPAKA_TEST_MATH_OP_FUNCTOR( OpRemainder, + Arity::Binary, + std::remainder, + alpaka::math::remainder, + Range::Unrestricted, + Range::NotZero ) + +using BinaryFunctors = std::tuple< + OpAtan2, + OpFmod, + OpMax, + OpMin, + OpPow, + OpRemainder + >; + +using UnaryFunctors = std::tuple< + OpAbs, + OpAcos, + OpAsin, + OpAtan, + OpCbrt, + OpCeil, + OpCos, + OpErf, + OpExp, + OpFloor, + OpLog, + OpRound, + OpRsqrt, + OpSin, + OpSqrt, + OpTan, + OpTrunc + >; + +} // math +} // unit +} // test +} // alpaka diff --git a/test/unit/math/mathOps/src/mathOps.cpp b/test/unit/math/mathOps/src/mathOps.cpp new file mode 100644 index 000000000000..5efebfa2c674 --- /dev/null +++ b/test/unit/math/mathOps/src/mathOps.cpp @@ -0,0 +1,254 @@ +/** Copyright 2019 Jakob Krude, Benjamin Worpitz + * + * This file is part of Alpaka. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "../include/Defines.hpp" +#include "../include/Buffer.hpp" +#include "../include/Functor.hpp" +#include "../include/DataGen.hpp" + +#include +#include +#include +#include + +struct TestKernel +{ + //! @tparam TAcc Accelerator. + //! @tparam TFunctor Functor defined in Functor.hpp. + //! @param acc Accelerator given from alpaka. + //! @param functor Accessible with operator(). + ALPAKA_NO_HOST_ACC_WARNING + template + + ALPAKA_FN_ACC auto operator()( + TAcc const & acc, + TResults const & results, + TFunctor const & functor, + TArgs const & args) const noexcept + -> void + { + for( size_t i = 0; i < TArgs::capacity; ++i ) + { + results(i, acc) = functor(args(i, acc), acc); + } + } +}; + + +//############################################################################# +// The TestTemplate runs the main code and the tests (Buffer,Functor,...). +//! @tparam TAcc One of the possible accelerator types, that need to be tested. +//! @tparam TData By now either double or float. +template < + typename TAcc, + typename TData> +struct TestTemplate +{ + template < typename TFunctor > + auto operator() ( unsigned long seed ) -> void + { + // SETUP (defines and initialising) + // DevAcc and DevHost are defined in Buffer.hpp too. + using DevAcc = alpaka::dev::Dev< TAcc >; + using DevHost = alpaka::dev::DevCpu; + using PltfAcc = alpaka::pltf::Pltf< DevAcc >; + using PltfHost = alpaka::pltf::Pltf< DevHost >; + + using Dim = alpaka::dim::DimInt< 1u >; + using Idx = std::size_t; + using WorkDiv = alpaka::workdiv::WorkDivMembers; + using QueueAcc = alpaka::test::queue::DefaultQueue< DevAcc >; + using TArgsItem = alpaka::test::unit::math::ArgsItem; + + static constexpr auto capacity = 1000; + + using Args = alpaka::test::unit::math::Buffer< + TAcc, + TArgsItem, + capacity + >; + using Results = alpaka::test::unit::math::Buffer< + TAcc, + TData, + capacity + >; + + // Every functor is executed individual on one kernel. + static constexpr size_t elementsPerThread = 1u; + static constexpr size_t sizeExtent = 1u; + + DevAcc const devAcc{ alpaka::pltf::getDevByIdx< PltfAcc >( 0u ) }; + DevHost const devHost{ alpaka::pltf::getDevByIdx< PltfHost >( 0u ) }; + + QueueAcc queue{ devAcc }; + + TestKernel kernel; + TFunctor functor; + Args args{ devAcc }; + Results results{ devAcc }; + + WorkDiv const workDiv{ + alpaka::workdiv::getValidWorkDiv< TAcc >( + devAcc, + sizeExtent, + elementsPerThread, + false, + alpaka::workdiv::GridBlockExtentSubDivRestrictions::Unrestricted + )}; + // SETUP COMPLETED. + + // Fill the buffer with random test-numbers. + alpaka::test::unit::math::fillWithRndArgs( args, functor, seed ); + for( size_t i = 0; i < Results::capacity; ++i ) + results(i) = static_cast(std::nan( "" )); + + // Copy both buffer to the device + args.copyToDevice(queue); + results.copyToDevice(queue); + + auto const taskKernel( + alpaka::kernel::createTaskKernel< TAcc >( + workDiv, + kernel, + results, + functor, + args + ) + ); + // Enqueue the kernel execution task. + alpaka::queue::enqueue( queue, taskKernel ); + // Copy back the results (encapsulated in the buffer class). + results.copyFromDevice( queue ); + alpaka::wait::wait( queue ); + std::cout.precision( std::numeric_limits::digits10 + 1 ); + + INFO("Operator: " << functor) + INFO("Type: " << typeid( TData ).name() ) // Compiler specific. +#if ALPAKA_DEBUG_FULL + INFO("The args buffer: \n" << std::setprecision( + std::numeric_limits::digits10 + 1) + << args << "\n") +#endif + for( size_t i = 0; i < Args::capacity; ++i ) + { + INFO("Idx i: " << i) + TData std_result = functor(args(i)); + REQUIRE( results(i) == Approx(std_result) ); + } + } +}; + +template< typename TData > +struct ForEachFunctor +{ + template< typename TAcc > + auto operator()( unsigned long seed ) -> void + { + alpaka::meta::forEachType < alpaka::test::unit::math::UnaryFunctors >( + TestTemplate< + TAcc, + TData + >( ), + seed + ); + + alpaka::meta::forEachType< alpaka::test::unit::math::BinaryFunctors >( + TestTemplate< + TAcc, + TData + >( ), + seed + ); + } +}; + +TEST_CASE("mathOps", "[math] [operator]") +{ + /* + * All alpaka::math:: functions are tested here except sincos. + * The function will be called with a buffer from the custom Buffer class. + * This argument Buffer contains ArgsItems from Defines.hpp and can be + * accessed with the overloaded operator(). + * The args Buffer looks similar like [[0, 1], [2, 3], [4, 5]], + * where every sub-list makes one functor-call so the result Buffer would be: + * [f(0, 1), f(2, 3), f(4, 5)]. + * The results are saved in a different Buffer witch contains plain data. + * The results are than compared to the result of a std:: implementation. + * The default result is nan and should fail a test. + * + * BE AWARE that: + * - ALPAKA_CUDA_FAST_MATH should be disabled + * - not all casts between float and double can be detected. + * - no explicit edge cases are tested, rather than 0, maximum and minimum + * - but it is easy to add a new Range:: enum-type with custom edge cases + * - some tests may fail if ALPAKA_CUDA_FAST_MATH is turned on + * - nan typically fails every test, but could be normal defined behaviour + * - inf/-inf typically dont fail a test + * - for easy debugging the << operator is overloaded for Buffer objects + * - arguments are generated between 0 and 1000 + * and the default argument-buffer-extent is 1000 + * The arguments are generated in DataGen.hpp and can easily be modified. + * The arguments depend on the Range:: enum-type specified for each functor. + * ---------------------------------------------------------------------- + * TEST_CASE | sets seed | specifies datatype & acc-list + * ForEachFunctor | - | specifies functor + * (from Functor.hpp) + * TestTemplate | functor, device, host, queue, kernel, usw. + * - main execution: + * - each functor has an arity and a array of ranges + * - there is one args Buffer and one results Buffer + * - each buffer encapsulated the host/device communication + * - as well as the data access and the initialisation + * - all operators are tested independent, one per kernel + * - tests the results against the std implementation ( catch REQUIRES) + * + * TestKernel + * - uses the alpaka::math:: option from the functor + * - uses the device-buffer option from the args + * + * EXTENSIBILITY: + * - Add new operators in Functor.hpp and add them to the ...Functors tuple. + * - Add a new Range:: enum-type in Defines.hpp + * - specify a fill-method in DataGen.hpp + * - Add a new Arity:: enum-type in Defines.hpp + * - add a matching operator() function in Functor.hpp, + * - add a new ...Functors tuple + * - call alpaka::meta::forEachType with the tuple in ForEachFunctor + */ + + using TestAccs = alpaka::test::acc::EnabledAccs< + alpaka::dim::DimInt< 1u >, + std::size_t + >; + const unsigned long seed = 1337; + std::cout << "using seed: " << seed << "\n\n"; + std::cout << "testing:\n " + << std::tuple_size::value + << " - accelerators !\n" + << std::tuple_size::value + << " - unary math operators\n" + << std::tuple_size::value + << " - binary math operators\n" + << "testing with two data types\n" + << "total 2 * accelerators * (unary + binary) * capacity\n\n"; + + + alpaka::meta::forEachType< TestAccs >( + ForEachFunctor< double >( ), + seed + ); + + alpaka::meta::forEachType< TestAccs >( + ForEachFunctor< float >( ), + seed + ); +}