Skip to content

Commit

Permalink
Add a special atomic type for the reverse pass.
Browse files Browse the repository at this point in the history
Perform only += operations atomically.

Merge pull request #51 from feature/reverseAtomic.
Reviewed-by: Max Sagebaum <[email protected]>
  • Loading branch information
jblueh committed Feb 28, 2024
2 parents 924c1d3 + 33257c4 commit bb7689f
Show file tree
Hide file tree
Showing 6 changed files with 297 additions and 11 deletions.
12 changes: 9 additions & 3 deletions include/codi/tools/parallel/atomicInterface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,21 @@
namespace codi {

/**
* @brief Provides a data type on which operations are performed atomically.
* @brief Provides a data type on which all operations are performed atomically.
*
* Provides also increment and decrement operators for the use case of an underlying integer type. The increment and
* decrement operators don't have to be implemented for underlying non-integer types.
*
* If used with an underlying floating point type or an active CoDiPack type, this data type is suitable as an adjoint
* variable type. Provides also increment and decrement operators for the use case of an underlying integer type. The
* increment and decrement operators don't have to be implemented for underlying non-integer types.
* variable type. Note, however, that it is not optimal because it performs all operations atomically. Data types
* derived from ReverseAtomicInterface are preferred as adjoint variable types.
*
* Implementations likely require template specializations with respect to the underlying type, especially if it is an
* active CoDiPack type.
*
* An implementation should preserve the memory footprint of the underlying type, e.g., by inheriting from the under-
* lying type or by having a variable of the underlying type as the only member variable.
*
* @tparam T_Type The underlying data type.
* @tparam T_Impl Implementing class.
*/
Expand Down
4 changes: 2 additions & 2 deletions include/codi/tools/parallel/openmp/codiOpDiLibTool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
#include "../../../tapes/interfaces/editingTapeInterface.hpp"
#include "../../../tapes/misc/vectorAccessInterface.hpp"
#include "../../../traits/atomicTraits.hpp"
#include "openMPAtomic.hpp"
#include "openMPReverseAtomic.hpp"

#ifndef DOXYGEN_DISABLE

Expand Down Expand Up @@ -167,7 +167,7 @@ struct CoDiOpDiLibTool : public opdi::ToolInterface {

typename Tape::Gradient* adjoints = &tape->gradient(0);
using NonAtomicGradient = codi::AtomicTraits::RemoveAtomic<typename Tape::Gradient>;
using AtomicGradient = codi::OpenMPAtomic<NonAtomicGradient>;
using AtomicGradient = codi::OpenMPReverseAtomic<NonAtomicGradient>;

if (useAtomics) {
AtomicGradient* safeAdjoints = (AtomicGradient*)adjoints;
Expand Down
7 changes: 4 additions & 3 deletions include/codi/tools/parallel/openmp/codiOpenMP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "../../data/direction.hpp"
#include "openMPAtomic.hpp"
#include "openMPMutex.hpp"
#include "openMPReverseAtomic.hpp"
#include "openMPStaticThreadLocalPointer.hpp"
#include "openMPSynchronization.hpp"
#include "openMPThreadInformation.hpp"
Expand All @@ -49,7 +50,7 @@
namespace codi {

/// Parallel toolbox for OpenMP.
using OpenMPToolbox = ParallelToolbox<OpenMPThreadInformation, OpenMPAtomic, OpenMPMutex,
using OpenMPToolbox = ParallelToolbox<OpenMPThreadInformation, OpenMPAtomic, OpenMPReverseAtomic, OpenMPMutex,
OpenMPStaticThreadLocalPointer, OpenMPSynchronization>;

/// Thread-safe external function helper for external functions jointly worked on by multiple OpenMP threads.
Expand All @@ -62,7 +63,7 @@ namespace codi {

/// \copydoc codi::RealReverseIndexGen <br><br>
/// This a thread-safe implementation for use with OpenMP. See \ref Example_23_OpenMP_Parallel_Codes for an example.
template<typename Real, typename Gradient = OpenMPAtomic<Real>,
template<typename Real, typename Gradient = OpenMPReverseAtomic<Real>,
typename IndexManager = ParallelReuseIndexManager<int, OpenMPToolbox>>
using RealReverseIndexOpenMPGen = ParallelActiveType<
JacobianReuseTape<JacobianTapeTypes<Real, Gradient, IndexManager, DefaultChunkedData, OpenMPGlobalAdjoints>>,
Expand All @@ -73,5 +74,5 @@ namespace codi {

/// \copydoc codi::RealReverseIndexOpenMPGen
template<size_t dim>
using RealReverseIndexVecOpenMP = RealReverseIndexOpenMPGen<double, Direction<OpenMPAtomic<double>, dim>>;
using RealReverseIndexVecOpenMP = RealReverseIndexOpenMPGen<double, Direction<OpenMPReverseAtomic<double>, dim>>;
}
188 changes: 188 additions & 0 deletions include/codi/tools/parallel/openmp/openMPReverseAtomic.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* CoDiPack, a Code Differentiation Package
*
* Copyright (C) 2015-2024 Chair for Scientific Computing (SciComp), University of Kaiserslautern-Landau
* Homepage: http://www.scicomp.uni-kl.de
* Contact: Prof. Nicolas R. Gauger ([email protected])
*
* Lead developers: Max Sagebaum, Johannes Blühdorn (SciComp, University of Kaiserslautern-Landau)
*
* This file is part of CoDiPack (http://www.scicomp.uni-kl.de/software/codi).
*
* CoDiPack is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* CoDiPack is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
* You should have received a copy of the GNU
* General Public License along with CoDiPack.
* If not, see <http://www.gnu.org/licenses/>.
*
* For other licensing options please contact us.
*
* Authors:
* - SciComp, University of Kaiserslautern-Landau:
* - Max Sagebaum
* - Johannes Blühdorn
* - Former members:
* - Tim Albring
*/
#pragma once

#include <type_traits>

#include "../../../expressions/activeType.hpp"
#include "../../../traits/atomicTraits.hpp"
#include "../../../traits/realTraits.hpp"
#include "../../../traits/tapeTraits.hpp"
#include "../reverseAtomicInterface.hpp"
#include "macros.hpp"

/** \copydoc codi::Namespace */
namespace codi {

/**
* @brief Reverse atomic implementation for OpenMP.
*
* OpenMP reverse atomics are disabled for all types by default. Reverse atomics for arithmetic types and forward
* CoDiPack types are enabled by specializations.
*
* See also ReverseAtomicInterface.
*
* @tparam T_Type The underlying data type.
* @tparam T_Sfinae Additional SFNIAE parameter for enable-if constructs.
*/
template<typename T_Type, typename T_Sfinae = void>
struct OpenMPReverseAtomicImpl : public ReverseAtomicInterface<T_Type, OpenMPReverseAtomicImpl<T_Type, T_Sfinae>> {
public:
using Type = T_Type; ///< See OpenMPReverseAtomicImpl.

OpenMPReverseAtomicImpl() = delete; ///< Constructor is deleted, will throw errors for unspecialized
///< instantiations.
};

#ifndef DOXYGEN_DISABLE

// Specialization for arithmetic types.
template<typename T_Type>
struct OpenMPReverseAtomicImpl<T_Type, typename std::enable_if<std::is_arithmetic<T_Type>::value>::type>
: public ReverseAtomicInterface<
T_Type, OpenMPReverseAtomicImpl<T_Type, typename std::enable_if<std::is_arithmetic<T_Type>::value>::type>> {
public:
using Type = T_Type;
using Base = ReverseAtomicInterface<
T_Type, OpenMPReverseAtomicImpl<T_Type, typename std::enable_if<std::is_arithmetic<T_Type>::value>::type>>;

private:
Type value;

public:
CODI_INLINE OpenMPReverseAtomicImpl() : Base(), value() {}

CODI_INLINE OpenMPReverseAtomicImpl(OpenMPReverseAtomicImpl const& other) : Base(), value(other.value) {}

CODI_INLINE OpenMPReverseAtomicImpl(Type const& other) : Base(), value(other) {}

CODI_INLINE OpenMPReverseAtomicImpl& operator=(OpenMPReverseAtomicImpl const& other) {
return operator=(other.value);
}

CODI_INLINE OpenMPReverseAtomicImpl& operator=(Type const& other) {
this->value = other;
return *this;
}

CODI_INLINE void operator+=(OpenMPReverseAtomicImpl const& other) {
operator+=(other.value);
}

CODI_INLINE void operator+=(Type const& other) {
CODI_OMP_ATOMIC(update)
this->value += other;
}

CODI_INLINE operator Type() const {
return value;
}
};

// Specialization for forward CoDiPack types. Acts on value and gradient with individual atomic operations.
template<typename T_Type>
struct OpenMPReverseAtomicImpl<T_Type, TapeTraits::EnableIfForwardTape<typename T_Type::Tape>>
: public ReverseAtomicInterface<
T_Type, OpenMPReverseAtomicImpl<T_Type, TapeTraits::EnableIfForwardTape<typename T_Type::Tape>>>,
public T_Type {
public:
using Type = CODI_DD(T_Type, CODI_DEFAULT_LHS_EXPRESSION);
using Base = ReverseAtomicInterface<
T_Type, OpenMPReverseAtomicImpl<T_Type, TapeTraits::EnableIfForwardTape<typename T_Type::Tape>>>;
using Tape = typename Type::Tape;
using Real = typename Type::Real;
using Gradient = typename Type::Gradient;

CODI_INLINE OpenMPReverseAtomicImpl() : Base(), Type() {}

CODI_INLINE OpenMPReverseAtomicImpl(OpenMPReverseAtomicImpl const& other) : Base(), Type(other) {}

CODI_INLINE OpenMPReverseAtomicImpl(Type const& other) : Base(), Type(other) {}

CODI_INLINE OpenMPReverseAtomicImpl& operator=(OpenMPReverseAtomicImpl const& other) {
return operator=(static_cast<Type const&>(other));
}

CODI_INLINE OpenMPReverseAtomicImpl& operator=(Type const& other) {
Type::operator=(other);
return *this;
}

CODI_INLINE void operator+=(OpenMPReverseAtomicImpl const& other) {
operator+=(static_cast<Type const&>(other));
}

CODI_INLINE void operator+=(Type const& other) {
OpenMPReverseAtomicImpl<Real>* atomicValue = reinterpret_cast<OpenMPReverseAtomicImpl<Real>*>(&this->value());
OpenMPReverseAtomicImpl<Gradient>* atomicGradient =
reinterpret_cast<OpenMPReverseAtomicImpl<Gradient>*>(&this->gradient());

*atomicValue += other.value();
*atomicGradient += other.gradient();
}

CODI_INLINE operator Type() const {
return static_cast<Type>(*this);
}
};

#endif

/// Wrapper for reverse atomics for OpenMP.
/// @tparam Type An arithmetic type or CoDiPack forward type.
template<typename Type>
using OpenMPReverseAtomic = OpenMPReverseAtomicImpl<Type>;

/// Declare OpenMPAtomic to be atomic in terms of AtomicTraits.
template<typename T_Type>
struct AtomicTraits::IsAtomic<OpenMPReverseAtomic<T_Type>> : std::true_type {};

#ifndef DOXYGEN_DISABLE
// Specialize IsTotalZero for OpenMPReverseAtomic on arithmetic types.
template<typename T_Type>
struct RealTraits::IsTotalZero<
OpenMPReverseAtomicImpl<T_Type, typename std::enable_if<std::is_arithmetic<T_Type>::value>::type>> {
public:

using Type = CODI_DD(
CODI_T(OpenMPReverseAtomicImpl<T_Type, typename std::enable_if<std::is_arithmetic<T_Type>::value>::type>),
OpenMPReverseAtomic<double>);

static CODI_INLINE bool isTotalZero(Type const& v) {
return typename Type::Type() == v;
}
};
#endif
}
12 changes: 9 additions & 3 deletions include/codi/tools/parallel/parallelToolbox.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "atomicInterface.hpp"
#include "mutexInterface.hpp"
#include "readWriteMutex.hpp"
#include "reverseAtomicInterface.hpp"
#include "staticThreadLocalPointerInterface.hpp"
#include "synchronizationInterface.hpp"
#include "threadInformationInterface.hpp"
Expand All @@ -57,21 +58,26 @@ namespace codi {
*
* @tparam T_ThreadInformation Thread information used by the toolbox. See codi::ThreadInformationInterface.
* @tparam T_Atomic Atomic implementation used by the toolbox. See codi::AtomicInterface.
* @tparam T_ReverseAtomic Reverse atomic implementation used by the toolbox. See
* codi::ReverseAtomicInterface.
* @tparam T_Mutex Mutex implementation used by the toolbox. See codi::MutexInterface.
* @tparam T_StaticThreadLocalPointer Static thread-local pointer implementation used by the toolbox.
* See codi::StaticThreadLocalPointerInterface.
* @tparam T_Synchronization Synchronization facalities that comply with the toolbox.
* See codi::SynchronizationInterface.
*/
template<typename T_ThreadInformation, template<typename> class T_Atomic, typename T_Mutex,
template<typename, typename> class T_StaticThreadLocalPointer, typename T_Synchronization>
template<typename T_ThreadInformation, template<typename> class T_Atomic, template<typename> class T_ReverseAtomic,
typename T_Mutex, template<typename, typename> class T_StaticThreadLocalPointer, typename T_Synchronization>
struct ParallelToolbox {
public:
/// See codi::ParallelToolbox.
using ThreadInformation = CODI_DD(T_ThreadInformation, ThreadInformationInterface);
template<typename Type>
using Atomic = CODI_DD(T_Atomic<Type>, CODI_DEFAULT_ATOMIC<Type>); ///< See codi::ParallelToolbox.
using Mutex = CODI_DD(T_Mutex, MutexInterface); ///< See codi::ParallelToolbox.
/// See codi::ParallelToolbox.
template<typename Type>
using ReverseAtomic = CODI_DD(T_ReverseAtomic<Type>, CODI_DEFAULT_REVERSE_ATOMIC<Type>);
using Mutex = CODI_DD(T_Mutex, MutexInterface); ///< See codi::ParallelToolbox.

/// See codi::StaticThreadLocalPointerInterface.
template<typename Type, typename Owner>
Expand Down
85 changes: 85 additions & 0 deletions include/codi/tools/parallel/reverseAtomicInterface.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* CoDiPack, a Code Differentiation Package
*
* Copyright (C) 2015-2024 Chair for Scientific Computing (SciComp), University of Kaiserslautern-Landau
* Homepage: http://www.scicomp.uni-kl.de
* Contact: Prof. Nicolas R. Gauger ([email protected])
*
* Lead developers: Max Sagebaum, Johannes Blühdorn (SciComp, University of Kaiserslautern-Landau)
*
* This file is part of CoDiPack (http://www.scicomp.uni-kl.de/software/codi).
*
* CoDiPack is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* CoDiPack is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
* You should have received a copy of the GNU
* General Public License along with CoDiPack.
* If not, see <http://www.gnu.org/licenses/>.
*
* For other licensing options please contact us.
*
* Authors:
* - SciComp, University of Kaiserslautern-Landau:
* - Max Sagebaum
* - Johannes Blühdorn
* - Former members:
* - Tim Albring
*/
#pragma once

#include "../../misc/macros.hpp"

/** \copydoc codi::Namespace */
namespace codi {

/**
* @brief Provides a data type on which += update operations are performed atomically.
*
* In a multithreaded environment, data races on adjoint variables are fixed by performing updates atomically, whereas
* other read and write operations do not need to be atomic. This template takes an ordinary adjoint variable type,
* like a floating point type or a CoDiPack forward type, and ensures that the corresponding += update operation is
* performed atomically.
*
* Implementations likely require template specializations with respect to the underlying type, especially if it is an
* active CoDiPack type.
*
* An implementation should preserve the memory footprint of the underlying type, e.g., by inheriting from the under-
* lying type or by having a variable of the underlying type as the only member variable.
*
* @tparam T_Type The underlying data type.
* @tparam T_Impl Implementing class.
*/
template<typename T_Type, typename T_Impl>
struct ReverseAtomicInterface {
public:
using Type = T_Type; ///< See ReverseAtomicInterface.
using Impl = T_Impl; ///< See ReverseAtomicInterface.

CODI_INLINE ReverseAtomicInterface() {} ///< Constructor
CODI_INLINE ReverseAtomicInterface(ReverseAtomicInterface const&) {} ///< Constructor
CODI_INLINE ReverseAtomicInterface(Type const&) {} ///< Constructor
~ReverseAtomicInterface() {} ///< Destructor

/// Assignment operator with implementing type as rhs. Not atomic.
CODI_INLINE Impl& operator=(Impl const& other);
CODI_INLINE Impl& operator=(Type const& other); ///< Assignment operator with underlying type as rhs. Not atomic.

CODI_INLINE void operator+=(Impl const& other); ///< Atomic incremental update with implementing type as rhs.
CODI_INLINE void operator+=(Type const& other); ///< Atomic incremental update with underlying type as rhs.

CODI_INLINE operator Type() const; ///< Implicit cast to underlying type for rhs access. Not atomic.
};

#if CODI_IDE
/// Helper for IDE code completion.
template<typename Type>
using CODI_DEFAULT_REVERSE_ATOMIC = ReverseAtomicInterface<Type, CODI_IMPLEMENTATION>;
#endif
}

0 comments on commit bb7689f

Please sign in to comment.