Commit da1d02b1 authored by Jakub Klinkovský's avatar Jakub Klinkovský
Browse files

Implemented DistributedArray

parent 40a6fbec
Loading
Loading
Loading
Loading
+142 −0
Original line number Diff line number Diff line
/***************************************************************************
                          DistributedArray.h  -  description
                             -------------------
    begin                : Sep 6, 2018
    copyright            : (C) 2018 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

// Implemented by: Jakub Klinkovský

#pragma once

#include <type_traits>  // std::add_const

#include <TNL/Containers/Array.h>
#include <TNL/Containers/ArrayView.h>
#include <TNL/Communicators/MpiCommunicator.h>
#include <TNL/DistributedContainers/IndexMap.h>

namespace TNL {
namespace DistributedContainers {

template< typename Value,
          typename Device = Devices::Host,
          typename Communicator = Communicators::MpiCommunicator,
          typename Index = int,
          typename IndexMap = Subrange< Index > >
class DistributedArray
: public Object
{
   using CommunicationGroup = typename Communicator::CommunicationGroup;
public:
   using ValueType = Value;
   using DeviceType = Device;
   using CommunicatorType = Communicator;
   using IndexType = Index;
   using IndexMapType = IndexMap;
   using LocalArrayType = Containers::Array< Value, Device, Index >;
   using LocalArrayViewType = Containers::ArrayView< Value, Device, Index >;
   using ConstLocalArrayViewType = Containers::ArrayView< typename std::add_const< Value >::type, Device, Index >;
   using HostType = DistributedArray< Value, Devices::Host, Communicator, Index, IndexMap >;
   using CudaType = DistributedArray< Value, Devices::Cuda, Communicator, Index, IndexMap >;

   DistributedArray() = default;

   DistributedArray( DistributedArray& ) = default;

   DistributedArray( IndexMap indexMap, CommunicationGroup group = Communicator::AllGroup );

   void setDistribution( IndexMap indexMap, CommunicationGroup group = Communicator::AllGroup );

   const IndexMap& getIndexMap() const;

   CommunicationGroup getCommunicationGroup() const;

   // we return only the view so that the user cannot resize it
   LocalArrayViewType getLocalArrayView();

   ConstLocalArrayViewType getLocalArrayView() const;

   void copyFromGlobal( ConstLocalArrayViewType globalArray );


   static String getType();

   virtual String getTypeVirtual() const;

   // TODO: no getSerializationType method until there is support for serialization


   /*
    * Usual Array methods follow below.
    */
   template< typename Array >
   void setLike( const Array& array );

   void reset();

   // TODO: swap

   // Returns the *global* size
   IndexType getSize() const;

   // Sets all elements of the array to the given value
   void setValue( ValueType value );

   // Safe device-independent element setter
   void setElement( IndexType i, ValueType value );

   // Safe device-independent element getter
   ValueType getElement( IndexType i ) const;

   // Unsafe element accessor usable only from the Device
   __cuda_callable__
   ValueType& operator[]( IndexType i );

   // Unsafe element accessor usable only from the Device
   __cuda_callable__
   const ValueType& operator[]( IndexType i ) const;

   // Copy-assignment operator
   DistributedArray& operator=( const DistributedArray& array );

   template< typename Array >
   DistributedArray& operator=( const Array& array );

   // Comparison operators
   template< typename Array >
   bool operator==( const Array& array ) const;

   template< typename Array >
   bool operator!=( const Array& array ) const;

   // Checks if there is an element with given value in this array
   bool containsValue( ValueType value ) const;

   // Checks if all elements in this array have the same given value
   bool containsOnlyValue( ValueType value ) const;

   // Returns true iff non-zero size is set
   operator bool() const;

   // TODO: serialization (save, load, boundLoad)

protected:
   IndexMap indexMap;
   CommunicationGroup group = Communicator::NullGroup;
   LocalArrayType localData;

private:
   // TODO: disabled until they are implemented
   using Object::save;
   using Object::load;
   using Object::boundLoad;
};

} // namespace DistributedContainers
} // namespace TNL

#include "DistributedArray_impl.h"
+373 −0
Original line number Diff line number Diff line
/***************************************************************************
                          DistributedArray_impl.h  -  description
                             -------------------
    begin                : Sep 6, 2018
    copyright            : (C) 2018 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

// Implemented by: Jakub Klinkovský

#pragma once

#include "DistributedArray.h"

#include <TNL/ParallelFor.h>
#include <TNL/Communicators/MpiDefs.h>  // important only when MPI is disabled

namespace TNL {
namespace DistributedContainers {

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
DistributedArray( IndexMap indexMap, CommunicationGroup group )
{
   setDistribution( indexMap, group );
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
void
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
setDistribution( IndexMap indexMap, CommunicationGroup group )
{
   this->indexMap = indexMap;
   this->group = group;
   if( group != Communicator::NullGroup )
      localData.setSize( indexMap.getLocalSize() );
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
const IndexMap&
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
getIndexMap() const
{
   return indexMap;
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
typename Communicator::CommunicationGroup
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
getCommunicationGroup() const
{
   return group;
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
typename DistributedArray< Value, Device, Communicator, Index, IndexMap >::LocalArrayViewType
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
getLocalArrayView()
{
   return localData;
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
typename DistributedArray< Value, Device, Communicator, Index, IndexMap >::ConstLocalArrayViewType
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
getLocalArrayView() const
{
   return localData;
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
void
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
copyFromGlobal( ConstLocalArrayViewType globalArray )
{
   TNL_ASSERT_EQ( indexMap.getGlobalSize(), globalArray.getSize(),
                  "given global array has different size than the distributed array" );

   LocalArrayViewType localView( localData );
   const IndexMap indexMap = getIndexMap();

   auto kernel = [=] __cuda_callable__ ( IndexType i ) mutable
   {
      if( indexMap.isLocal( i ) )
         localView[ indexMap.getLocalIndex( i ) ] = globalArray[ i ];
   };

   ParallelFor< DeviceType >::exec( (IndexType) 0, indexMap.getGlobalSize(), kernel );
}


/*
 * Usual Array methods follow below.
 */

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
String
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
getType()
{
   return String( "DistributedContainers::DistributedArray< " ) +
          TNL::getType< Value >() + ", " +
          Device::getDeviceType() + ", " +
          // TODO: communicators don't have a getType method
          "<Communicator>, " +
          TNL::getType< Index >() + ", " +
          IndexMap::getType() + " >";
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
String
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
getTypeVirtual() const
{
   return getType();
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
   template< typename Array >
void
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
setLike( const Array& array )
{
   indexMap = array.getIndexMap();
   group = array.getCommunicationGroup();
   localData.setLike( array.getLocalArrayView() );
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
void
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
reset()
{
   indexMap.reset();
   group = Communicator::NullGroup;
   localData.reset();
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
Index
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
getSize() const
{
   return indexMap.getGlobalSize();
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
void
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
setValue( ValueType value )
{
   localData.setValue( value );
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
void
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
setElement( IndexType i, ValueType value )
{
   const IndexType li = indexMap.getLocalIndex( i );
   localData.setElement( li, value );
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
Value
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
getElement( IndexType i ) const
{
   const IndexType li = indexMap.getLocalIndex( i );
   return localData.getElement( li );
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
__cuda_callable__
Value&
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
operator[]( IndexType i )
{
   const IndexType li = indexMap.getLocalIndex( i );
   return localData[ li ];
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
__cuda_callable__
const Value&
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
operator[]( IndexType i ) const
{
   const IndexType li = indexMap.getLocalIndex( i );
   return localData[ li ];
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
DistributedArray< Value, Device, Communicator, Index, IndexMap >&
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
operator=( const DistributedArray& array )
{
   setLike( array );
   localData = array.getLocalArrayView();
   return *this;
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
   template< typename Array >
DistributedArray< Value, Device, Communicator, Index, IndexMap >&
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
operator=( const Array& array )
{
   setLike( array );
   localData = array.getLocalArrayView();
   return *this;
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
   template< typename Array >
bool
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
operator==( const Array& array ) const
{
   // we can't run allreduce if the communication groups are different
   if( group != array.getCommunicationGroup() )
      return false;
   const bool localResult =
         indexMap == array.getIndexMap() &&
         localData == array.getLocalArrayView();
   bool result = true;
   if( group != CommunicatorType::NullGroup )
      CommunicatorType::Allreduce( &localResult, &result, 1, MPI_LAND, group );
   return result;
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
   template< typename Array >
bool
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
operator!=( const Array& array ) const
{
   return ! (*this == array);
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
bool
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
containsValue( ValueType value ) const
{
   bool result = false;
   if( group != CommunicatorType::NullGroup ) {
      const bool localResult = localData.containsValue( value );
      CommunicatorType::Allreduce( &localResult, &result, 1, MPI_LOR, group );
   }
   return result;
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
bool
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
containsOnlyValue( ValueType value ) const
{
   bool result = true;
   if( group != CommunicatorType::NullGroup ) {
      const bool localResult = localData.containsOnlyValue( value );
      CommunicatorType::Allreduce( &localResult, &result, 1, MPI_LAND, group );
   }
   return result;
}

template< typename Value,
          typename Device,
          typename Communicator,
          typename Index,
          typename IndexMap >
DistributedArray< Value, Device, Communicator, Index, IndexMap >::
operator bool() const
{
   return getSize() != 0;
}

} // namespace DistributedContainers
} // namespace TNL
+130 −0
Original line number Diff line number Diff line
/***************************************************************************
                          IndexMap.h  -  description
                             -------------------
    begin                : Sep 6, 2018
    copyright            : (C) 2018 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

// Implemented by: Jakub Klinkovský

#pragma once

#include <TNL/Assert.h>
#include <TNL/String.h>
#include <TNL/param-types.h>

namespace TNL {
namespace DistributedContainers {

// Specifies a subrange [begin, end) of a range [0, gloablSize).
template< typename Index >
class Subrange
{
public:
   using IndexType = Index;

   __cuda_callable__
   Subrange() = default;

   __cuda_callable__
   Subrange( Index begin, Index end, Index globalSize )
   {
      setSubrange( begin, end, globalSize );
   }

   // Sets the local subrange and global range size.
   __cuda_callable__
   void setSubrange( Index begin, Index end, Index globalSize )
   {
      TNL_ASSERT_LE( begin, end, "begin must be before end" );
      TNL_ASSERT_GE( begin, 0, "begin must be non-negative" );
      TNL_ASSERT_LE( end - begin, globalSize, "end of the subrange is outside of gloabl range" );
      offset = begin;
      localSize = end - begin;
      this->globalSize = globalSize;
   }

   __cuda_callable__
   void reset()
   {
      offset = 0;
      localSize = 0;
      globalSize = 0;
   }

   static String getType()
   {
      return "Subrange< " + TNL::getType< Index >() + " >";
   }

   // Checks if a global index is in the set of local indices.
   __cuda_callable__
   bool isLocal( Index i ) const
   {
      return offset <= i && i < offset + localSize;
   }

   // Gets the offset of the subrange.
   __cuda_callable__
   Index getOffset() const
   {
      return offset;
   }

   // Gets number of local indices.
   __cuda_callable__
   Index getLocalSize() const
   {
      return localSize;
   }

   // Gets number of global indices.
   __cuda_callable__
   Index getGlobalSize() const
   {
      return globalSize;
   }

   // Gets local index for given global index.
   __cuda_callable__
   Index getLocalIndex( Index i ) const
   {
      TNL_ASSERT_TRUE( isLocal( i ), "Given global index was not found in the local index set." );
      return i - offset;
   }

   // Gets global index for given local index.
   __cuda_callable__
   Index getGlobalIndex( Index i ) const
   {
      TNL_ASSERT_GE( i, 0, "Given local index was not found in the local index set." );
      TNL_ASSERT_LT( i, localSize, "Given local index was not found in the local index set." );
      return i + offset;
   }

   bool operator==( const Subrange& other ) const
   {
      return offset == other.offset &&
             localSize == other.localSize &&
             globalSize == other.globalSize;
   }

   bool operator!=( const Subrange& other ) const
   {
      return ! (*this == other);
   }

protected:
   Index offset = 0;
   Index localSize = 0;
   Index globalSize = 0;
};

// TODO: implement a general IndexMap class, e.g. based on collection of subranges as in deal.II:
// https://www.dealii.org/8.4.0/doxygen/deal.II/classIndexSet.html

} // namespace DistributedContainers
} // namespace TNL
+48 −0
Original line number Diff line number Diff line
/***************************************************************************
                          DistributedArray.h  -  description
                             -------------------
    begin                : Sep 6, 2018
    copyright            : (C) 2018 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

// Implemented by: Jakub Klinkovský

#pragma once

#include "IndexMap.h"

#include <TNL/Math.h>

namespace TNL {
namespace DistributedContainers {

template< typename IndexMap, typename Communicator >
class Partitioner
{};

template< typename Index, typename Communicator >
class Partitioner< Subrange< Index >, Communicator >
{
   using CommunicationGroup = typename Communicator::CommunicationGroup;
public:
   using IndexMap = Subrange< Index >;

   static IndexMap splitRange( Index globalSize, CommunicationGroup group )
   {
      if( group != Communicator::NullGroup ) {
         const int rank = Communicator::GetRank( group );
         const int partitions = Communicator::GetSize( group );
         const Index begin = min( globalSize, rank * globalSize / partitions );
         const Index end = min( globalSize, (rank + 1) * globalSize / partitions );
         return IndexMap( begin, end, globalSize );
      }
      else
         return IndexMap( 0, 0, globalSize );
   }
};

} // namespace DistributedContainers
} // namespace TNL
+1 −0
Original line number Diff line number Diff line
ADD_SUBDIRECTORY( Containers )
ADD_SUBDIRECTORY( DistributedContainers )
ADD_SUBDIRECTORY( Functions )
ADD_SUBDIRECTORY( Matrices )
ADD_SUBDIRECTORY( Meshes )
Loading