From 86102bdf4caf92422ae02a354adfa72b6a88bb01 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 `atomic*()` and `atomicOp<*>()` --- test/unit/atomic/src/AtomicTest.cpp | 391 +++++++++++++--------------- 1 file changed, 174 insertions(+), 217 deletions(-) diff --git a/test/unit/atomic/src/AtomicTest.cpp b/test/unit/atomic/src/AtomicTest.cpp index f0f0cf4e78c0..67a55eb8ffd9 100644 --- a/test/unit/atomic/src/AtomicTest.cpp +++ b/test/unit/atomic/src/AtomicTest.cpp @@ -36,219 +36,87 @@ 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, + T (*atomicFunctionPtr)(TAcc const&, T* const, T const&, THierarchy const&), + 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 = TOp{}; -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); + 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 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)); - } + // 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::atomicMax(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 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 = atomicFunctionPtr(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 +template +ALPAKA_FN_ACC auto testAtomicCombinations( + TAcc const& acc, + T (*atomicFunctionPtr)(TAcc const&, T* const, T const&, THierarchy const&), + 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); + // 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, atomicFunctionPtr, success, operandOrig, value); } { - operand = operandOrig; - T const ret = alpaka::atomicInc(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 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); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - 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 -{ - 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 twice as large as the right + T const value = static_cast(operandOrig * two); + testAtomicCall(acc, atomicFunctionPtr, 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 larger by one + T const value = static_cast(operandOrig + one); + testAtomicCall(acc, atomicFunctionPtr, 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 smaller by one + T const value = static_cast(operandOrig - one); + testAtomicCall(acc, atomicFunctionPtr, 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)); + // both operands are equal + T const value = operandOrig; + testAtomicCall(acc, atomicFunctionPtr, success, operandOrig, value); } } 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)); - } - { - operand = operandOrig; - T const ret = alpaka::atomicXor(acc, &operand, value); - ALPAKA_CHECK(*success, equals(operandOrig, ret)); - ALPAKA_CHECK(*success, equals(operand, reference)); - } -} - -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 +128,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 +146,63 @@ 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 testAtomicThreads( + TAcc const& acc, + T (*atomicFunction)(TAcc const&, T* const, T const&, alpaka::hierarchy::Threads const&), + bool* success, + T operandOrig) -> void +{ + testAtomicCombinations(acc, atomicFunction, success, operandOrig); +} +//! check block hierarchy +ALPAKA_NO_HOST_ACC_WARNING +template +ALPAKA_FN_ACC auto testAtomicBlocks( + TAcc const& acc, + T (*atomicFunction)(TAcc const&, T* const, T const&, alpaka::hierarchy::Blocks const&), + bool* success, + T operandOrig) -> void +{ + testAtomicCombinations(acc, atomicFunction, success, operandOrig); +} +//! check grid hierarchy +ALPAKA_NO_HOST_ACC_WARNING +template +ALPAKA_FN_ACC auto testAtomicGrids( + TAcc const& acc, + T (*atomicFunction)(TAcc const&, T* const, T const&, alpaka::hierarchy::Grids const&), + bool* success, + T operandOrig) -> void +{ + testAtomicCombinations(acc, atomicFunction, 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 +210,56 @@ 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); + // Add + testAtomicThreads(acc, alpaka::atomicAdd, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicAdd, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicAdd, success, operandOrig); + // Sub + testAtomicThreads(acc, alpaka::atomicSub, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicSub, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicSub, success, operandOrig); + + // Min + testAtomicThreads(acc, alpaka::atomicMin, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicMin, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicMin, success, operandOrig); + + // Max + testAtomicThreads(acc, alpaka::atomicMax, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicMax, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicMax, success, operandOrig); + + // Exch + testAtomicThreads(acc, alpaka::atomicExch, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicExch, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicExch, success, operandOrig); + + // Inc + testAtomicThreads(acc, alpaka::atomicInc, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicInc, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicInc, success, operandOrig); + + // Dec + testAtomicThreads(acc, alpaka::atomicDec, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicDec, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicDec, success, operandOrig); + + // And + testAtomicThreads(acc, alpaka::atomicAnd, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicAnd, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicAnd, success, operandOrig); + + // Or + testAtomicThreads(acc, alpaka::atomicOr, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicOr, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicOr, success, operandOrig); + + // Xor + testAtomicThreads(acc, alpaka::atomicXor, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicXor, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicXor, success, operandOrig); + + testAtomicCasHierarchies(acc, success, operandOrig); } }; @@ -324,22 +270,33 @@ 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); - - // 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); - - testAtomicCas(acc, success, operandOrig); + // Add + testAtomicThreads(acc, alpaka::atomicAdd, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicAdd, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicAdd, success, operandOrig); + // Sub + testAtomicThreads(acc, alpaka::atomicSub, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicSub, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicSub, success, operandOrig); + + // Min + testAtomicThreads(acc, alpaka::atomicMin, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicMin, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicMin, success, operandOrig); + + // Max + testAtomicThreads(acc, alpaka::atomicMax, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicMax, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicMax, success, operandOrig); + + // Exch + testAtomicThreads(acc, alpaka::atomicExch, success, operandOrig); + testAtomicBlocks(acc, alpaka::atomicExch, success, operandOrig); + testAtomicGrids(acc, alpaka::atomicExch, success, operandOrig); + + // Inc, Dec, Or, And, Xor are not supported on float/double types + + testAtomicCasHierarchies(acc, success, operandOrig); } };