From 48bca6a7f9927072dfae8bb44a7d81d0bce9194d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Widera?= Date: Wed, 3 Aug 2022 13:33:37 +0200 Subject: [PATCH] tests: rewrite atomic tests fix #1769 - test all hierarchies which can be used for atomics - extent test cases to test for equal, smaller, larger and zero as left side operand - test calling the atomic interface `atomic*()` and `atomicOp<*>()` --- test/unit/atomic/src/AtomicFunctors.hpp | 143 +++++++++++ test/unit/atomic/src/AtomicTest.cpp | 304 +++++++----------------- 2 files changed, 232 insertions(+), 215 deletions(-) create mode 100644 test/unit/atomic/src/AtomicFunctors.hpp diff --git a/test/unit/atomic/src/AtomicFunctors.hpp b/test/unit/atomic/src/AtomicFunctors.hpp new file mode 100644 index 000000000000..30aeaba81988 --- /dev/null +++ b/test/unit/atomic/src/AtomicFunctors.hpp @@ -0,0 +1,143 @@ +/* Copyright 2022 Axel Huebl, Benjamin Worpitz, Matthias Werner, Jan Stephan, Bernhard Manfred Gruber, + * Antonio Di Pilato + * + * 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/. + */ + +//! @file This file provides functors where a atomic function and the corresponding operation availble within one +//! object. This allwos testing of the atomic function and atomicOp() without the usage of function pointers whcih +//! is not suppoted by all backends. + +#include +#include + +#include + +namespace alpaka::test::unit::atomic +{ + struct Add + { + using Op = alpaka::AtomicAdd; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicAdd(std::forward(args)...); + } + }; + + struct Sub + { + using Op = alpaka::AtomicSub; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicSub(std::forward(args)...); + } + }; + + struct Min + { + using Op = alpaka::AtomicMin; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicMin(std::forward(args)...); + } + }; + + struct Max + { + using Op = alpaka::AtomicMax; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicMax(std::forward(args)...); + } + }; + + struct Exch + { + using Op = alpaka::AtomicExch; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicExch(std::forward(args)...); + } + }; + + struct Dec + { + using Op = alpaka::AtomicDec; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicDec(std::forward(args)...); + } + }; + + struct Inc + { + using Op = alpaka::AtomicInc; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicInc(std::forward(args)...); + } + }; + + struct Cas + { + using Op = alpaka::AtomicCas; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicCas(std::forward(args)...); + } + }; + + struct Or + { + using Op = alpaka::AtomicOr; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicOr(std::forward(args)...); + } + }; + + struct Xor + { + using Op = alpaka::AtomicXor; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicXor(std::forward(args)...); + } + }; + + struct And + { + using Op = alpaka::AtomicAnd; + + template + static ALPAKA_FN_ACC auto atomic(TArgs&&... args) + { + return alpaka::atomicAnd(std::forward(args)...); + } + }; + +} // namespace alpaka::test::unit::atomic diff --git a/test/unit/atomic/src/AtomicTest.cpp b/test/unit/atomic/src/AtomicTest.cpp index f0f0cf4e78c0..4002cc13d1cc 100644 --- a/test/unit/atomic/src/AtomicTest.cpp +++ b/test/unit/atomic/src/AtomicTest.cpp @@ -8,6 +8,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "AtomicFunctors.hpp" + #include #include #include @@ -19,6 +21,9 @@ #include #include + +using namespace alpaka::test::unit::atomic; + template ALPAKA_FN_INLINE ALPAKA_FN_HOST_ACC auto equals(T1 a, T2 b) -> bool { @@ -36,219 +41,78 @@ ALPAKA_FN_INLINE ALPAKA_FN_HOST_ACC auto equals(double a, double b) -> bool } ALPAKA_NO_HOST_ACC_WARNING -template -ALPAKA_FN_ACC auto testAtomicAdd(TAcc const& acc, bool* success, T operandOrig) -> void +template +ALPAKA_FN_ACC auto testAtomicCall(TAcc const& acc, bool* success, T operandOrig, T value) -> void { - T const value = static_cast(4); - T const reference = static_cast(operandOrig + value); - auto& operand = alpaka::declareSharedVar(acc); - { - operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } - { - operand = operandOrig; - T const ret = alpaka::atomicAdd(acc, &operand, value, alpaka::hierarchy::Threads{}); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } -} + auto op = typename TOp::Op{}; -ALPAKA_NO_HOST_ACC_WARNING -template -ALPAKA_FN_ACC auto testAtomicSub(TAcc const& acc, bool* success, T operandOrig) -> void -{ - T const value = static_cast(4); - T const reference = static_cast(operandOrig - value); - auto& operand = alpaka::declareSharedVar(acc); - { - operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } + // check if the function `alpaka::atomicOp<*>` is callable { + auto& operand = alpaka::declareSharedVar(acc); + // left operand is half of the right operand = operandOrig; - T const ret = alpaka::atomicSub(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } -} + T reference = operand; + op(&reference, value); -ALPAKA_NO_HOST_ACC_WARNING -template -ALPAKA_FN_ACC auto testAtomicMin(TAcc const& acc, bool* success, T operandOrig) -> void -{ - T const value = static_cast(4); - T const reference = (operandOrig < value) ? operandOrig : value; - auto& operand = alpaka::declareSharedVar(acc); - { - operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } - { - operand = operandOrig; - T const ret = alpaka::atomicMin(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } -} - -ALPAKA_NO_HOST_ACC_WARNING -template -ALPAKA_FN_ACC auto testAtomicMax(TAcc const& acc, bool* success, T operandOrig) -> void -{ - T const value = static_cast(4); - T const reference = (operandOrig > value) ? operandOrig : value; - auto& operand = alpaka::declareSharedVar(acc); - { - operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } - { - operand = operandOrig; - T const ret = alpaka::atomicMax(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } -} - -ALPAKA_NO_HOST_ACC_WARNING -template -ALPAKA_FN_ACC auto testAtomicExch(TAcc const& acc, bool* success, T operandOrig) -> void -{ - T const value = static_cast(4); - T const reference = value; - auto& operand = alpaka::declareSharedVar(acc); - { - operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } - { - operand = operandOrig; - T const ret = alpaka::atomicExch(acc, &operand, value); + T const ret = alpaka::atomicOp(acc, &operand, value, THierarchy{}); + // check that always the old value is returned ALPAKA_CHECK(*success, equals(operandOrig, ret)); + // check that result in memory is correct ALPAKA_CHECK(*success, equals(operand, reference)); } -} -ALPAKA_NO_HOST_ACC_WARNING -template -ALPAKA_FN_ACC auto testAtomicInc(TAcc const& acc, bool* success, T operandOrig) -> void -{ - // \TODO: Check reset to 0 at 'value'. - T const value = static_cast(42); - T const reference = static_cast(operandOrig + 1); - auto& operand = alpaka::declareSharedVar(acc); - { - operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } + // check if the function `alpaka::atomic*()` is callable { + auto& operand = alpaka::declareSharedVar(acc); + // left operand is half of the right operand = operandOrig; - T const ret = alpaka::atomicInc(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } -} + T reference = operand; + op(&reference, value); -ALPAKA_NO_HOST_ACC_WARNING -template -ALPAKA_FN_ACC auto testAtomicDec(TAcc const& acc, bool* success, T operandOrig) -> void -{ - // \TODO: Check reset to 'value' at 0. - T const value = static_cast(42); - T const reference = static_cast(operandOrig - 1); - auto& operand = alpaka::declareSharedVar(acc); - { - operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } - { - operand = operandOrig; - T const ret = alpaka::atomicDec(acc, &operand, value); + T const ret = TOp::atomic(acc, &operand, value, THierarchy{}); + // check that always the old value is returned ALPAKA_CHECK(*success, equals(operandOrig, ret)); + // check that result in memory is correct ALPAKA_CHECK(*success, equals(operand, reference)); } } ALPAKA_NO_HOST_ACC_WARNING -template -ALPAKA_FN_ACC auto testAtomicAnd(TAcc const& acc, bool* success, T operandOrig) -> void +template +ALPAKA_FN_ACC auto testAtomicCombinations(TAcc const& acc, bool* success, T operandOrig) -> void { - T const value = static_cast(4); - T const reference = operandOrig & value; - auto& operand = alpaka::declareSharedVar(acc); + // helper variables to avoid compiler conversion warnings/errors + T constexpr one = static_cast(1); + T constexpr two = static_cast(2); { - operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); + // left operand is half of the right + T const value = static_cast(operandOrig / two); + testAtomicCall(acc, success, operandOrig, value); } { - operand = operandOrig; - T const ret = alpaka::atomicAnd(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); + // left operand is twice as large as the right + T const value = static_cast(operandOrig * two); + testAtomicCall(acc, success, operandOrig, value); } -} - -ALPAKA_NO_HOST_ACC_WARNING -template -ALPAKA_FN_ACC auto testAtomicOr(TAcc const& acc, bool* success, T operandOrig) -> void -{ - T const value = static_cast(4); - T const reference = operandOrig | value; - auto& operand = alpaka::declareSharedVar(acc); { - operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); + // left operand is larger by one + T const value = static_cast(operandOrig + one); + testAtomicCall(acc, success, operandOrig, value); } { - operand = operandOrig; - T const ret = alpaka::atomicOr(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } -} - -ALPAKA_NO_HOST_ACC_WARNING -template -ALPAKA_FN_ACC auto testAtomicXor(TAcc const& acc, bool* success, T operandOrig) -> void -{ - T const value = static_cast(operandOrig + static_cast(4)); - T const reference = operandOrig ^ value; - auto& operand = alpaka::declareSharedVar(acc); - { - operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); + // left operand is smaller by one + T const value = static_cast(operandOrig - one); + testAtomicCall(acc, success, operandOrig, value); } { - operand = operandOrig; - T const ret = alpaka::atomicXor(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); + // both operands are equal + T const value = operandOrig; + testAtomicCall(acc, success, operandOrig, value); } } ALPAKA_NO_HOST_ACC_WARNING -template +template ALPAKA_FN_ACC auto testAtomicCas(TAcc const& acc, bool* success, T operandOrig) -> void { T const value = static_cast(4); @@ -260,13 +124,13 @@ ALPAKA_FN_ACC auto testAtomicCas(TAcc const& acc, bool* success, T operandOrig) T const reference = value; { operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, compare, value); + T const ret = alpaka::atomicOp(acc, &operand, compare, value, THierarchy{}); ALPAKA_CHECK(*success, equals(operandOrig, ret)); ALPAKA_CHECK(*success, equals(operand, reference)); } { operand = operandOrig; - T const ret = alpaka::atomicCas(acc, &operand, compare, value); + T const ret = alpaka::atomicCas(acc, &operand, compare, value, THierarchy{}); ALPAKA_CHECK(*success, equals(operandOrig, ret)); ALPAKA_CHECK(*success, equals(operand, reference)); } @@ -278,19 +142,39 @@ ALPAKA_FN_ACC auto testAtomicCas(TAcc const& acc, bool* success, T operandOrig) T const reference = operandOrig; { operand = operandOrig; - T const ret = alpaka::atomicOp(acc, &operand, compare, value); + T const ret = alpaka::atomicOp(acc, &operand, compare, value, THierarchy{}); ALPAKA_CHECK(*success, equals(operandOrig, ret)); ALPAKA_CHECK(*success, equals(operand, reference)); } { operand = operandOrig; - T const ret = alpaka::atomicCas(acc, &operand, compare, value); + T const ret = alpaka::atomicCas(acc, &operand, compare, value, THierarchy{}); ALPAKA_CHECK(*success, equals(operandOrig, ret)); ALPAKA_CHECK(*success, equals(operand, reference)); } } } +//! check threads hierarchy +ALPAKA_NO_HOST_ACC_WARNING +template +ALPAKA_FN_ACC auto testAtomicHierarchies(TAcc const& acc, bool* success, T operandOrig) -> void +{ + testAtomicCombinations(acc, success, operandOrig); + testAtomicCombinations(acc, success, operandOrig); + testAtomicCombinations(acc, success, operandOrig); +} + +//! check all alpaka hierarchies +ALPAKA_NO_HOST_ACC_WARNING +template +ALPAKA_FN_ACC auto testAtomicCasHierarchies(TAcc const& acc, bool* success, T operandOrig) -> void +{ + testAtomicCas(acc, success, operandOrig); + testAtomicCas(acc, success, operandOrig); + testAtomicCas(acc, success, operandOrig); +} + template class AtomicTestKernel { @@ -298,22 +182,19 @@ class AtomicTestKernel ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto operator()(TAcc const& acc, bool* success, T operandOrig) const -> void { - testAtomicAdd(acc, success, operandOrig); - testAtomicSub(acc, success, operandOrig); - - testAtomicMin(acc, success, operandOrig); - testAtomicMax(acc, success, operandOrig); - - testAtomicExch(acc, success, operandOrig); - - testAtomicInc(acc, success, operandOrig); - testAtomicDec(acc, success, operandOrig); - - testAtomicAnd(acc, success, operandOrig); - testAtomicOr(acc, success, operandOrig); - testAtomicXor(acc, success, operandOrig); - - testAtomicCas(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + + testAtomicCasHierarchies(acc, success, operandOrig); } }; @@ -324,22 +205,15 @@ class AtomicTestKernel>> ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto operator()(TAcc const& acc, bool* success, T operandOrig) const -> void { - testAtomicAdd(acc, success, operandOrig); - testAtomicSub(acc, success, operandOrig); - - testAtomicMin(acc, success, operandOrig); - testAtomicMax(acc, success, operandOrig); - - testAtomicExch(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); + testAtomicHierarchies(acc, success, operandOrig); - // These are not supported on float/double types - // testAtomicInc(acc, success, operandOrig); - // testAtomicDec(acc, success, operandOrig); - // testAtomicAnd(acc, success, operandOrig); - // testAtomicOr(acc, success, operandOrig); - // testAtomicXor(acc, success, operandOrig); + // Inc, Dec, Or, And, Xor are not supported on float/double types - testAtomicCas(acc, success, operandOrig); + testAtomicCasHierarchies(acc, success, operandOrig); } };