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