Quinoa unit test code coverage report
Current view: top level - LoadBalance - UnsMeshMap.cpp (source / functions) Hit Total Coverage
Commit: Quinoa_v0.3-957-gb4f0efae0 Lines: 41 41 100.0 %
Date: 2021-11-09 12:13:43 Functions: 4 4 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 39 54 72.2 %

           Branch data     Line data    Source code
       1                 :            : // *****************************************************************************
       2                 :            : /*!
       3                 :            :   \file      src/LoadBalance/UnsMeshMap.cpp
       4                 :            :   \copyright 2012-2015 J. Bakosi,
       5                 :            :              2016-2018 Los Alamos National Security, LLC.,
       6                 :            :              2019-2021 Triad National Security, LLC.
       7                 :            :              All rights reserved. See the LICENSE file for details.
       8                 :            :   \brief     Advanced Charm++ array creation with a map using an unstructured
       9                 :            :              grid
      10                 :            :   \details   Advanced Charm++ array creation refers to various ways arrays can
      11                 :            :      be created with the Charm++ runtime system. See
      12                 :            :      http://charm.cs.illinois.edu/manuals/html/charm++/manual.html, Sec.
      13                 :            :      Advanced Array Creation. This class does a distribution that is based on
      14                 :            :      which portion of a distributed sparse matrix resulting from discretization
      15                 :            :      on an unstructured grid residing on a PE should hold a given chare array
      16                 :            :      element. (The one that owns most on-PE rows to minimize off-PE
      17                 :            :      communication.)
      18                 :            : 
      19                 :            :      Note that to help with performance, it is not advised to do heavy
      20                 :            :      computations in the overridden member functions, procNum() and
      21                 :            :      populateInitial(), since they can be potentially called many times. See
      22                 :            :      also this note on the inner workings of the Charm++ runtime system
      23                 :            :      regarding map objects and array element creation from long-time Charm++
      24                 :            :      developer, Eric Bohm, also at:
      25                 :            :      http://lists.cs.uiuc.edu/pipermail/charm/2015-May/002047.html
      26                 :            : 
      27                 :            :      _Procnum will be called to construct your chare, the first time a message
      28                 :            :      is sent to it from a node, and each time subsequent sends do not find a
      29                 :            :      precached location record for it. The latter event can occur when many
      30                 :            :      sends have pushed it out of cache, or after migration._
      31                 :            : 
      32                 :            :      _The potential global memory footprint of location management caching is
      33                 :            :      proportional to the total number of objects multiplied by the number of
      34                 :            :      nodes. Therefore, the runtime system keeps a finite number on each node.
      35                 :            :      At the limit, procnum could be called for nearly every message send,
      36                 :            :      therefore procnum should be designed to be inexpensive._
      37                 :            : 
      38                 :            :      The heavy portion of array element placement should therefore be done in
      39                 :            :      the constructor.
      40                 :            : */
      41                 :            : // *****************************************************************************
      42                 :            : 
      43                 :            : #include <algorithm>
      44                 :            : 
      45                 :            : #include "NoWarning/charm.hpp"
      46                 :            : #include "Exception.hpp"
      47                 :            : #include "UnsMeshMap.hpp"
      48                 :            : 
      49                 :            : using tk::UnsMeshMap;
      50                 :            : 
      51                 :        180 : UnsMeshMap::UnsMeshMap( std::size_t npoin,
      52                 :        180 :                         const std::vector< std::vector< std::size_t > >& point )
      53         [ +  - ]:        180 :  : m_pe()
      54                 :            : // *****************************************************************************
      55                 :            : // Constructor
      56                 :            : //! \param[in] npoin Total number of points in mesh
      57                 :            : //! \param[in] point Global mesh point ids owned by each array element
      58                 :            : // *****************************************************************************
      59                 :            : {
      60                 :            :   Assert( npoin > 0, "Need at least a single mesh point" );
      61                 :            :   Assert( point.size() >= static_cast< std::size_t >( CkNumPes() ),
      62                 :            :           "UnsMeshMap only works with nchare >= numPEs" );
      63                 :            : 
      64                 :            :   // Vector of maps to associate the number of mesh points an array element
      65                 :            :   // contributes to a pe to the array element
      66 [ +  - ][ +  - ]:        360 :   std::vector< std::map< std::size_t, std::size_t > > owner( point.size() );
                 [ -  - ]
      67                 :            : 
      68                 :            :   // Compute number of mesh points per PE (chunksize)
      69                 :        180 :   const auto numpes = static_cast< std::size_t >( CkNumPes() );
      70         [ +  - ]:        180 :   const auto chunksize = npoin>numpes ? npoin/numpes : 1;
      71                 :            : 
      72                 :            :   // Count up number of mesh points contributing to a PE for each array element
      73         [ +  + ]:       9252 :   for (std::size_t e=0; e<point.size(); ++e)    // for all array elements
      74         [ +  + ]:     522288 :     for (auto p : point[e]) {           // for all points array element e owns
      75         [ -  + ]:     513216 :       auto pe = p / chunksize;          // PE array element e contributes to
      76         [ -  + ]:     513216 :       if (pe == static_cast<std::size_t>(CkNumPes())) --pe;
      77         [ +  - ]:     513216 :       ++owner[e][pe];  // count up number of points element e contributing to pe
      78                 :            :     }
      79                 :            : 
      80                 :            :   // Find and store the PE with the largest number of points owned by an array
      81                 :            :   // element. This is the key (PE) of the largest mapped_type of the element's
      82                 :            :   // map, owner[e].
      83                 :            :   using pr = decltype(owner)::value_type::value_type;
      84         [ +  - ]:        180 :   m_pe.resize( owner.size() );
      85         [ +  + ]:       9252 :   for (std::size_t e=0; e<owner.size(); ++e) {
      86                 :            :     const auto most = std::max_element( begin(owner[e]), end(owner[e]),
      87                 :            :                                         []( const pr& p1, const pr& p2 )
      88                 :       9072 :                                           { return p1.second < p2.second; } );
      89                 :       9072 :     m_pe[e] = most->first;
      90                 :            :   }
      91                 :            : 
      92                 :            :   // Check if all PEs have at least one array element to create, fix if not
      93         [ +  - ]:        180 :   fixPEs();
      94                 :        180 : }
      95                 :            : 
      96                 :            : void
      97                 :        180 : UnsMeshMap::fixPEs()
      98                 :            : // *****************************************************************************
      99                 :            : //  Check that all PEs create at least a single array element, fix if not
     100                 :            : //! \details This is required because if the array elements, placed using this
     101                 :            : //!   map object, send messages to some Charm++ chare group branches and the
     102                 :            : //!   group happens to use Charm++'s structured dagger, such as Solver,
     103                 :            : //!   memory problems will occur.
     104                 :            : // *****************************************************************************
     105                 :            : {
     106                 :            :   // Build unique set of PEs the array elements are assigned to
     107                 :            :   std::set< std::size_t > nkind;
     108 [ +  + ][ +  - ]:       9252 :   for (auto p : m_pe) nkind.insert( p );
     109                 :            : 
     110                 :            :   // If not all PEs have at least one array element to create, go through all
     111                 :            :   // PEs that have no array elements assigned, and construct a map that
     112                 :            :   // associates the number of PEs to array elements. Then as a replacement PE,
     113                 :            :   // pick the PE that has the most array elements assigned. Then assign the
     114                 :            :   // first of the elements assigned to the PE with the most work to the
     115                 :            :   // replacement PE.
     116         [ +  + ]:        180 :   if (nkind.size() != static_cast<std::size_t>(CkNumPes()))
     117         [ +  + ]:       2664 :     for (std::size_t p=0; p<static_cast<std::size_t>(CkNumPes()); ++p)
     118                 :            :       if (!nkind.count(p)) {    // if PE p has no array elements to create
     119                 :            :         // Count up number elements for each PE
     120                 :            :         std::map< std::size_t, std::size_t > npe;
     121                 :            :         using pr = decltype(npe)::value_type;
     122 [ +  + ][ +  - ]:     138600 :         for (auto q : m_pe) ++npe[q];
     123                 :            :         // Find the PE with the most array elements assigned
     124                 :            :         const auto most = std::max_element( begin(npe), end(npe),
     125                 :            :                                             []( const pr& p1, const pr& p2 )
     126                 :       2520 :                                             { return p1.second < p2.second; } );
     127         [ +  + ]:      47880 :         for (std::size_t q=0; q<m_pe.size(); ++q)
     128         [ +  + ]:      45360 :           if (m_pe[q] == most->first) { // first element of the most loaded PE
     129                 :            :             //std::cout << CkMyPe() << ": replace " << m_pe[q] << " by " << p
     130                 :            :             //          << " for e " << q << std::endl;
     131                 :       2520 :             m_pe[q] = p;        // replace PE of m_pe[q] of element q by p
     132                 :            :             q = m_pe.size();    // quit (enough to replace one)
     133                 :            :           }
     134                 :            :       }
     135                 :            : 
     136                 :            :   nkind.clear();
     137 [ +  + ][ +  - ]:       9252 :   for (auto p : m_pe) nkind.insert( p );
     138                 :            :   Assert( nkind.size() == static_cast<std::size_t>(CkNumPes()),
     139                 :            :           "There are still PE(s) with no array elements assigned" );
     140                 :        180 : }
     141                 :            : 
     142                 :            : int
     143                 :        216 : UnsMeshMap::procNum( int, const CkArrayIndex& idx )
     144                 :            : // *****************************************************************************
     145                 :            : //  Return the home processor number for the array element based on
     146                 :            : //  unstructured-mesh-aware distribution computed in the constructor
     147                 :            : //! \param[in] idx Charm++ array index object containing the array element index
     148                 :            : //!   to assign a PE to
     149                 :            : //! \return PE assigned
     150                 :            : // *****************************************************************************
     151                 :            : {
     152                 :            :   // array element we assign PE for
     153                 :        216 :   auto elem = static_cast<std::size_t>(*idx.data());
     154                 :            : 
     155                 :            :   Assert( elem < m_pe.size(),
     156                 :            :           "Array index larger than the number of chares for which the "
     157                 :            :           "UnsMeshMap object holds indices for" );
     158                 :            : 
     159                 :        216 :   auto pe = static_cast< int >( m_pe[ elem ] );
     160                 :            : 
     161                 :            :   Assert( pe < CkNumPes(), "Assigned PE (" + std::to_string(pe) +
     162                 :            :           ") larger than NumPEs (" + std::to_string(CkNumPes()) + ")" );
     163                 :            : 
     164                 :        216 :   return pe;
     165                 :            : }
     166                 :            : 
     167                 :            : void
     168                 :        144 : UnsMeshMap::populateInitial( int, CkArrayOptions& opt,
     169                 :            :                              void *msg,
     170                 :            :                              CkArrMgr *mgr )
     171                 :            : // *****************************************************************************
     172                 :            : //  Create initial set of array elements based on the unstructured-mesh-aware
     173                 :            : //  distribution computed in the constructor
     174                 :            : //! \param[in] opt Charm++ array options object containing the number of initial
     175                 :            : //!   array elements to be created
     176                 :            : //! \param[in] msg Charm++ messsage to use for array element creation
     177                 :            : //! \param[in] mgr Array manager to use
     178                 :            : // *****************************************************************************
     179                 :            : {
     180                 :        144 :   int nelem = *opt.getNumInitial().data(); // number of array elements requested
     181         [ +  - ]:        144 :   if (nelem == 0) return;                  // no initial elements requested
     182                 :            : 
     183                 :            :   Assert( static_cast<std::size_t>(nelem) <= m_pe.size(),
     184                 :            :           "Number of initial array elements larger than "
     185                 :            :           "the UnsMeshMap object holds PEs for" );
     186                 :            : 
     187         [ +  + ]:       7920 :   for (int e=0; e<nelem; ++e)
     188         [ +  + ]:       7776 :     if (m_pe[ static_cast<std::size_t>(e) ] ==
     189         [ +  + ]:       7776 :         static_cast<std::size_t>(CkMyPe()))
     190                 :        216 :       mgr->insertInitial( CkArrayIndex(e), CkCopyMsg(&msg) );
     191                 :            : 
     192                 :        144 :   mgr->doneInserting();
     193                 :        144 :   CkFreeMsg( msg );
     194                 :            : }
     195                 :            : 
     196                 :            : #include "NoWarning/unsmeshmap.def.h"

Generated by: LCOV version 1.14