151c0b2f7Stbbdev /*
2b15aabb3Stbbdev     Copyright (c) 2017-2021 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #include "common/test.h"
1851c0b2f7Stbbdev #include "common/utils.h"
1951c0b2f7Stbbdev #include "common/utils_assert.h"
2051c0b2f7Stbbdev #include "common/utils_concurrency_limit.h"
2151c0b2f7Stbbdev 
2251c0b2f7Stbbdev //! \file conformance_blocked_rangeNd.cpp
2351c0b2f7Stbbdev //! \brief Test for [preview] functionality
2451c0b2f7Stbbdev 
2551c0b2f7Stbbdev #define TBB_PREVIEW_BLOCKED_RANGE_ND 1
2649e08aacStbbdev #include "oneapi/tbb/blocked_rangeNd.h"
2749e08aacStbbdev #include "oneapi/tbb/parallel_for.h"
2849e08aacStbbdev #include "oneapi/tbb/global_control.h"
2951c0b2f7Stbbdev 
3051c0b2f7Stbbdev #include <algorithm> // std::for_each
3151c0b2f7Stbbdev #include <array>
3251c0b2f7Stbbdev 
3351c0b2f7Stbbdev // AbstractValueType class represents Value concept's requirements in the most abstract way
3451c0b2f7Stbbdev class AbstractValueType {
3551c0b2f7Stbbdev     int value;
AbstractValueType()3651c0b2f7Stbbdev     AbstractValueType() {}
3751c0b2f7Stbbdev public:
3851c0b2f7Stbbdev     friend AbstractValueType MakeAbstractValue(int i);
3951c0b2f7Stbbdev     friend int GetValueOf(const AbstractValueType& v);
4051c0b2f7Stbbdev };
4151c0b2f7Stbbdev 
GetValueOf(const AbstractValueType & v)4251c0b2f7Stbbdev int GetValueOf(const AbstractValueType& v) { return v.value; }
4351c0b2f7Stbbdev 
MakeAbstractValue(int i)4451c0b2f7Stbbdev AbstractValueType MakeAbstractValue(int i) {
4551c0b2f7Stbbdev     AbstractValueType x;
4651c0b2f7Stbbdev     x.value = i;
4751c0b2f7Stbbdev     return x;
4851c0b2f7Stbbdev }
4951c0b2f7Stbbdev 
5051c0b2f7Stbbdev // operator- returns amount of elements of AbstractValueType between u and v
operator -(const AbstractValueType & u,const AbstractValueType & v)5151c0b2f7Stbbdev std::size_t operator-(const AbstractValueType& u, const AbstractValueType& v) {
5251c0b2f7Stbbdev     return GetValueOf(u) - GetValueOf(v);
5351c0b2f7Stbbdev }
5451c0b2f7Stbbdev 
operator <(const AbstractValueType & u,const AbstractValueType & v)5551c0b2f7Stbbdev bool operator<(const AbstractValueType& u, const AbstractValueType& v) {
5651c0b2f7Stbbdev     return GetValueOf(u) < GetValueOf(v);
5751c0b2f7Stbbdev }
5851c0b2f7Stbbdev 
operator +(const AbstractValueType & u,std::size_t offset)5951c0b2f7Stbbdev AbstractValueType operator+(const AbstractValueType& u, std::size_t offset) {
6051c0b2f7Stbbdev     return MakeAbstractValue(GetValueOf(u) + int(offset));
6151c0b2f7Stbbdev }
6251c0b2f7Stbbdev 
6351c0b2f7Stbbdev template<typename range_t, unsigned int N>
6451c0b2f7Stbbdev struct range_utils {
6551c0b2f7Stbbdev     using val_t = typename range_t::value_type;
6651c0b2f7Stbbdev 
6751c0b2f7Stbbdev     template<typename EntityType, std::size_t DimSize>
6851c0b2f7Stbbdev     using data_type = std::array<typename range_utils<range_t, N - 1>::template data_type<EntityType, DimSize>, DimSize>;
6951c0b2f7Stbbdev 
7051c0b2f7Stbbdev     template<typename EntityType, std::size_t DimSize>
init_datarange_utils7151c0b2f7Stbbdev     static void init_data(data_type<EntityType, DimSize>& data) {
7251c0b2f7Stbbdev         std::for_each(data.begin(), data.end(), range_utils<range_t, N - 1>::template init_data<EntityType, DimSize>);
7351c0b2f7Stbbdev     }
7451c0b2f7Stbbdev 
7551c0b2f7Stbbdev     template<typename EntityType, std::size_t DimSize>
increment_datarange_utils7651c0b2f7Stbbdev     static void increment_data(const range_t& range, data_type<EntityType, DimSize>& data) {
7751c0b2f7Stbbdev         auto begin = data.begin() + range.dim(N - 1).begin();
7851c0b2f7Stbbdev         // same as "auto end = out.begin() + range.dim(N - 1).end();"
7951c0b2f7Stbbdev         auto end = begin + range.dim(N - 1).size();
8051c0b2f7Stbbdev         for (auto i = begin; i != end; ++i) {
8151c0b2f7Stbbdev             range_utils<range_t, N - 1>::template increment_data<EntityType, DimSize>(range, *i);
8251c0b2f7Stbbdev         }
8351c0b2f7Stbbdev     }
8451c0b2f7Stbbdev 
8551c0b2f7Stbbdev     template<typename EntityType, std::size_t DimSize>
check_datarange_utils8651c0b2f7Stbbdev     static void check_data(const range_t& range, data_type<EntityType, DimSize>& data) {
8751c0b2f7Stbbdev         auto begin = data.begin() + range.dim(N - 1).begin();
8851c0b2f7Stbbdev         // same as "auto end = out.begin() + range.dim(N - 1).end();"
8951c0b2f7Stbbdev         auto end = begin + range.dim(N - 1).size();
9051c0b2f7Stbbdev         for (auto i = begin; i != end; ++i) {
9151c0b2f7Stbbdev             range_utils<range_t, N - 1>::template check_data<EntityType, DimSize>(range, *i);
9251c0b2f7Stbbdev         }
9351c0b2f7Stbbdev     }
9451c0b2f7Stbbdev 
9551c0b2f7Stbbdev // BullseyeCoverage Compile C++ with GCC 5.4 warning suppression
9651c0b2f7Stbbdev // Sequence points error in braced initializer list
9751c0b2f7Stbbdev #if __GNUC__ && !defined(__clang__) && !defined(__INTEL_COMPILER)
9851c0b2f7Stbbdev #pragma GCC diagnostic push
9951c0b2f7Stbbdev #pragma GCC diagnostic ignored "-Wsequence-point"
10051c0b2f7Stbbdev #endif
10151c0b2f7Stbbdev     template<typename input_t, std::size_t... Is>
make_rangerange_utils10249e08aacStbbdev     static range_t make_range(std::size_t shift, bool negative, val_t(*gen)(input_t), oneapi::tbb::detail::index_sequence<Is...>) {
10351c0b2f7Stbbdev         return range_t( { { gen(negative ? -input_t(Is + shift) : 0), gen(input_t(Is + shift)), Is + 1} ... } );
10451c0b2f7Stbbdev     }
10551c0b2f7Stbbdev #if __GNUC__ && !defined(__clang__) && !defined(__INTEL_COMPILER)
10651c0b2f7Stbbdev #pragma GCC diagnostic pop
10751c0b2f7Stbbdev #endif
10851c0b2f7Stbbdev 
is_emptyrange_utils10951c0b2f7Stbbdev     static bool is_empty(const range_t& range) {
11051c0b2f7Stbbdev         if (range.dim(N - 1).empty()) { return true; }
11151c0b2f7Stbbdev         return range_utils<range_t, N - 1>::is_empty(range);
11251c0b2f7Stbbdev     }
11351c0b2f7Stbbdev 
is_divisiblerange_utils11451c0b2f7Stbbdev     static bool is_divisible(const range_t& range) {
11551c0b2f7Stbbdev         if (range.dim(N - 1).is_divisible()) { return true; }
11651c0b2f7Stbbdev         return range_utils<range_t, N - 1>::is_divisible(range);
11751c0b2f7Stbbdev     }
11851c0b2f7Stbbdev 
check_splittingrange_utils11951c0b2f7Stbbdev     static void check_splitting(const range_t& range_split, const range_t& range_new, int(*get)(const val_t&), bool split_checker = false) {
12051c0b2f7Stbbdev         if (get(range_split.dim(N - 1).begin()) == get(range_new.dim(N - 1).begin())) {
12151c0b2f7Stbbdev             REQUIRE(get(range_split.dim(N - 1).end()) == get(range_new.dim(N - 1).end()));
12251c0b2f7Stbbdev         }
12351c0b2f7Stbbdev         else {
12451c0b2f7Stbbdev             REQUIRE((get(range_split.dim(N - 1).end()) == get(range_new.dim(N - 1).begin()) && !split_checker));
12551c0b2f7Stbbdev             split_checker = true;
12651c0b2f7Stbbdev         }
12751c0b2f7Stbbdev         range_utils<range_t, N - 1>::check_splitting(range_split, range_new, get, split_checker);
12851c0b2f7Stbbdev     }
12951c0b2f7Stbbdev 
13051c0b2f7Stbbdev };
13151c0b2f7Stbbdev 
13251c0b2f7Stbbdev template<typename range_t>
13351c0b2f7Stbbdev struct range_utils<range_t, 0> {
13451c0b2f7Stbbdev     using val_t = typename range_t::value_type;
13551c0b2f7Stbbdev 
13651c0b2f7Stbbdev     template<typename EntityType, std::size_t DimSize>
13751c0b2f7Stbbdev     using data_type = EntityType;
13851c0b2f7Stbbdev 
13951c0b2f7Stbbdev     template<typename EntityType, std::size_t DimSize>
init_datarange_utils14051c0b2f7Stbbdev     static void init_data(data_type<EntityType, DimSize>& data) { data = 0; }
14151c0b2f7Stbbdev 
14251c0b2f7Stbbdev     template<typename EntityType, std::size_t DimSize>
increment_datarange_utils14351c0b2f7Stbbdev     static void increment_data(const range_t&, data_type<EntityType, DimSize>& data) { ++data; }
14451c0b2f7Stbbdev 
14551c0b2f7Stbbdev     template<typename EntityType, std::size_t DimSize>
check_datarange_utils14651c0b2f7Stbbdev     static void check_data(const range_t&, data_type<EntityType, DimSize>& data) {
14751c0b2f7Stbbdev         REQUIRE(data == 1);
14851c0b2f7Stbbdev     }
14951c0b2f7Stbbdev 
is_emptyrange_utils15051c0b2f7Stbbdev     static bool is_empty(const range_t&) { return false; }
15151c0b2f7Stbbdev 
is_divisiblerange_utils15251c0b2f7Stbbdev     static bool is_divisible(const range_t&) { return false; }
15351c0b2f7Stbbdev 
check_splittingrange_utils15451c0b2f7Stbbdev     static void check_splitting(const range_t&, const range_t&, int(*)(const val_t&), bool) {}
15551c0b2f7Stbbdev };
15651c0b2f7Stbbdev 
15751c0b2f7Stbbdev // We need MakeInt function to pass it into make_range as factory function
15851c0b2f7Stbbdev // because of matching make_range with AbstractValueType and other types too
MakeInt(int i)15951c0b2f7Stbbdev int MakeInt(int i) { return i; }
16051c0b2f7Stbbdev 
16151c0b2f7Stbbdev template<unsigned int DimAmount>
SerialTest()16251c0b2f7Stbbdev void SerialTest() {
16349e08aacStbbdev     static_assert((oneapi::tbb::blocked_rangeNd<int, DimAmount>::ndims() == oneapi::tbb::blocked_rangeNd<AbstractValueType, DimAmount>::ndims()),
16451c0b2f7Stbbdev                          "different amount of dimensions");
16551c0b2f7Stbbdev 
16649e08aacStbbdev     using range_t = oneapi::tbb::blocked_rangeNd<AbstractValueType, DimAmount>;
16751c0b2f7Stbbdev     using utils_t = range_utils<range_t, DimAmount>;
16851c0b2f7Stbbdev 
16951c0b2f7Stbbdev     // Generate empty range
17049e08aacStbbdev     range_t r = utils_t::make_range(0, true, &MakeAbstractValue, oneapi::tbb::detail::make_index_sequence<DimAmount>());
17151c0b2f7Stbbdev 
17251c0b2f7Stbbdev     utils::AssertSameType(r.is_divisible(), bool());
17351c0b2f7Stbbdev     utils::AssertSameType(r.empty(), bool());
17451c0b2f7Stbbdev     utils::AssertSameType(range_t::ndims(), 0U);
17551c0b2f7Stbbdev 
17651c0b2f7Stbbdev     REQUIRE((r.empty() == utils_t::is_empty(r) && r.empty()));
17751c0b2f7Stbbdev     REQUIRE(r.is_divisible() == utils_t::is_divisible(r));
17851c0b2f7Stbbdev 
17951c0b2f7Stbbdev     // Generate not-empty range divisible range
18049e08aacStbbdev     r = utils_t::make_range(1, true, &MakeAbstractValue, oneapi::tbb::detail::make_index_sequence<DimAmount>());
18151c0b2f7Stbbdev     REQUIRE((r.empty() == utils_t::is_empty(r) && !r.empty()));
18251c0b2f7Stbbdev     REQUIRE((r.is_divisible() == utils_t::is_divisible(r) && r.is_divisible()));
18351c0b2f7Stbbdev 
18449e08aacStbbdev     range_t r_new(r, oneapi::tbb::split());
18551c0b2f7Stbbdev     utils_t::check_splitting(r, r_new, &GetValueOf);
18651c0b2f7Stbbdev 
18751c0b2f7Stbbdev     SerialTest<DimAmount - 1>();
18851c0b2f7Stbbdev }
SerialTest()18951c0b2f7Stbbdev template<> void SerialTest<0>() {}
19051c0b2f7Stbbdev 
19151c0b2f7Stbbdev template<unsigned int DimAmount>
ParallelTest()19251c0b2f7Stbbdev void ParallelTest() {
19349e08aacStbbdev     using range_t = oneapi::tbb::blocked_rangeNd<int, DimAmount>;
19451c0b2f7Stbbdev     using utils_t = range_utils<range_t, DimAmount>;
19551c0b2f7Stbbdev 
19651c0b2f7Stbbdev     // Max size is                                 1 << 20 - 1 bytes
19751c0b2f7Stbbdev     // Thus size of one dimension's elements is    1 << (20 / DimAmount - 1) bytes
19851c0b2f7Stbbdev     typename utils_t::template data_type<unsigned char, 1 << (20 / DimAmount - 1)> data;
19951c0b2f7Stbbdev     utils_t::init_data(data);
20051c0b2f7Stbbdev 
20149e08aacStbbdev     range_t r = utils_t::make_range((1 << (20 / DimAmount - 1)) - DimAmount, false, &MakeInt, oneapi::tbb::detail::make_index_sequence<DimAmount>());
20251c0b2f7Stbbdev 
20349e08aacStbbdev     oneapi::tbb::parallel_for(r, [&data](const range_t& range) {
20451c0b2f7Stbbdev         utils_t::increment_data(range, data);
20551c0b2f7Stbbdev     });
20651c0b2f7Stbbdev 
20751c0b2f7Stbbdev     utils_t::check_data(r, data);
20851c0b2f7Stbbdev 
20951c0b2f7Stbbdev     ParallelTest<DimAmount - 1>();
21051c0b2f7Stbbdev }
ParallelTest()21151c0b2f7Stbbdev template<> void ParallelTest<0>() {}
21251c0b2f7Stbbdev 
21351c0b2f7Stbbdev //! Testing blocked_rangeNd construction
21451c0b2f7Stbbdev //! \brief \ref interface
21551c0b2f7Stbbdev TEST_CASE("Construction") {
21649e08aacStbbdev     oneapi::tbb::blocked_rangeNd<int, 1>{ { 0,13,3 } };
21751c0b2f7Stbbdev 
21849e08aacStbbdev     oneapi::tbb::blocked_rangeNd<int, 1>{ oneapi::tbb::blocked_range<int>{ 0,13,3 } };
21951c0b2f7Stbbdev 
22049e08aacStbbdev     oneapi::tbb::blocked_rangeNd<int, 2>(oneapi::tbb::blocked_range<int>(-8923, 8884, 13), oneapi::tbb::blocked_range<int>(-8923, 5, 13));
22151c0b2f7Stbbdev 
22249e08aacStbbdev     oneapi::tbb::blocked_rangeNd<int, 2>({ -8923, 8884, 13 }, { -8923, 8884, 13 });
22351c0b2f7Stbbdev 
22449e08aacStbbdev     oneapi::tbb::blocked_range<int> r1(0, 13);
22551c0b2f7Stbbdev 
22649e08aacStbbdev     oneapi::tbb::blocked_range<int> r2(-12, 23);
22751c0b2f7Stbbdev 
22849e08aacStbbdev     oneapi::tbb::blocked_rangeNd<int, 2>({ { -8923, 8884, 13 }, r1});
22951c0b2f7Stbbdev 
23049e08aacStbbdev     oneapi::tbb::blocked_rangeNd<int, 2>({ r2, r1 });
23151c0b2f7Stbbdev 
23249e08aacStbbdev     oneapi::tbb::blocked_rangeNd<int, 2>(r1, r2);
23351c0b2f7Stbbdev 
23449e08aacStbbdev     oneapi::tbb::blocked_rangeNd<AbstractValueType, 4>({ MakeAbstractValue(-3), MakeAbstractValue(13), 8 },
23551c0b2f7Stbbdev                                                { MakeAbstractValue(-53), MakeAbstractValue(23), 2 },
23651c0b2f7Stbbdev                                                { MakeAbstractValue(-23), MakeAbstractValue(33), 1 },
23751c0b2f7Stbbdev                                                { MakeAbstractValue(-13), MakeAbstractValue(43), 7 });
23851c0b2f7Stbbdev }
23951c0b2f7Stbbdev 
24051c0b2f7Stbbdev static const std::size_t N = 4;
24151c0b2f7Stbbdev 
24251c0b2f7Stbbdev //! Testing blocked_rangeNd interface
24351c0b2f7Stbbdev //! \brief \ref interface \ref requirement
24451c0b2f7Stbbdev TEST_CASE("Serial test") {
24551c0b2f7Stbbdev     SerialTest<N>();
24651c0b2f7Stbbdev }
24751c0b2f7Stbbdev 
24851c0b2f7Stbbdev //! Testing blocked_rangeNd interface with parallel_for
24951c0b2f7Stbbdev //! \brief \ref requirement
25051c0b2f7Stbbdev TEST_CASE("Parallel test") {
25151c0b2f7Stbbdev     for ( auto concurrency_level : utils::concurrency_range() ) {
25249e08aacStbbdev         oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level);
25351c0b2f7Stbbdev         ParallelTest<N>();
25451c0b2f7Stbbdev     }
25551c0b2f7Stbbdev }
25651c0b2f7Stbbdev 
25751c0b2f7Stbbdev //! Testing blocked_rangeNd with proportional splitting
25851c0b2f7Stbbdev //! \brief \ref interface \ref requirement
25951c0b2f7Stbbdev TEST_CASE("blocked_rangeNd proportional splitting") {
26049e08aacStbbdev     oneapi::tbb::blocked_rangeNd<int, 2> original{{0, 100}, {0, 100}};
26149e08aacStbbdev     oneapi::tbb::blocked_rangeNd<int, 2> first(original);
26249e08aacStbbdev     oneapi::tbb::proportional_split ps(3, 1);
26349e08aacStbbdev     oneapi::tbb::blocked_rangeNd<int, 2> second(first, ps);
26451c0b2f7Stbbdev 
265*55f9b178SIvan Kochin     int expected_first_end = static_cast<int>(
266*55f9b178SIvan Kochin         original.dim(0).begin() + ps.left() * (original.dim(0).end() - original.dim(0).begin()) / (ps.left() + ps.right())
267*55f9b178SIvan Kochin     );
26851c0b2f7Stbbdev     if (first.dim(0).size() == second.dim(0).size()) {
26951c0b2f7Stbbdev         // Splitting was made by cols
27051c0b2f7Stbbdev         utils::check_range_bounds_after_splitting(original.dim(1), first.dim(1), second.dim(1), expected_first_end);
27151c0b2f7Stbbdev     } else {
27251c0b2f7Stbbdev         // Splitting was made by rows
27351c0b2f7Stbbdev         utils::check_range_bounds_after_splitting(original.dim(0), first.dim(0), second.dim(0), expected_first_end);
27451c0b2f7Stbbdev     }
27551c0b2f7Stbbdev }
276