LCOV - code coverage report
Current view: top level - include/geom - stored_range.h (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 50 50 100.0 %
Date: 2025-08-19 19:27:09 Functions: 36 36 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : 
      20             : #ifndef LIBMESH_STORED_RANGE_H
      21             : #define LIBMESH_STORED_RANGE_H
      22             : 
      23             : // Local includes
      24             : #include "libmesh/threads.h"
      25             : 
      26             : // C++ includes
      27             : #include <vector>
      28             : #include <memory> // std::unique_ptr
      29             : #include <functional> // std::function
      30             : 
      31             : namespace libMesh
      32             : {
      33             : 
      34             : /**
      35             :  * The \p StoredRange class defines a contiguous, divisible set of objects.
      36             :  * This class is used primarily as the argument to function objects.  The
      37             :  * range can then be subdivided into a number of "tasks" which can be executed
      38             :  * in parallel.  This concept is central to the Threading Building Blocks
      39             :  * template library which can optionally be used by \p libMesh to implement
      40             :  * shared-memory parallelism.
      41             :  *
      42             :  * The implementation takes a user-provided object range and packs it into
      43             :  * a contiguous vector which can then be subdivided efficiently.  A first-cut
      44             :  * implementation using raw element iterators incurred simply too much overhead
      45             :  * by using the predicated iterators, specifically operations such as advancing
      46             :  * such iterators has a cost proportional to the amount the iterator is advanced.
      47             :  * Hence in this implementation the user-provided range is packed into a vector.
      48             :  *
      49             :  * \author Benjamin S. Kirk
      50             :  * \date 2008
      51             :  * \brief Utility class for defining generic ranges for threading.
      52             :  */
      53             : template <typename iterator_type, typename object_type>
      54             : class StoredRange
      55             : {
      56             : public:
      57             :   /**
      58             :    * Allows an \p StoredRange to behave like an STL container.
      59             :    */
      60             :   typedef typename std::vector<object_type> vec_type;
      61             :   typedef typename vec_type::const_iterator const_iterator;
      62             :   typedef typename std::unique_ptr<vec_type, std::function<void (vec_type *)>> ptr_type;
      63             : 
      64             :   /**
      65             :    * Constructor. Optionally takes the \p grainsize parameter, which is the
      66             :    * smallest chunk the range may be broken into for parallel
      67             :    * execution.
      68             :    */
      69       16389 :   StoredRange (const unsigned int new_grainsize = 1000) :
      70             :     _end(),
      71             :     _begin(),
      72       15465 :     _last(),
      73       15465 :     _first(),
      74       15465 :     _grainsize(new_grainsize),
      75       49167 :     _objs(ptr_type(new vec_type(), [](vec_type * p){delete p;}))
      76       16389 :   {}
      77             : 
      78             :   /**
      79             :    * Constructor.  Takes the beginning and end of the range.
      80             :    * Optionally takes the \p grainsize parameter, which is the
      81             :    * smallest chunk the range may be broken into for parallel
      82             :    * execution.
      83             :    */
      84     2903540 :   StoredRange (const iterator_type & first,
      85             :                const iterator_type & last,
      86             :                const unsigned int new_grainsize = 1000) :
      87             :     _end(),
      88             :     _begin(),
      89     2803850 :     _last(),
      90     2803850 :     _first(),
      91     2803850 :     _grainsize(new_grainsize),
      92     8710620 :     _objs(ptr_type(new vec_type(), [](vec_type * p){delete p;}))
      93             :   {
      94     2903540 :     this->reset(first, last);
      95     2903540 :   }
      96             : 
      97             :   /**
      98             :    * Constructor.  Takes a std::vector of objects.
      99             :    * Optionally takes the \p grainsize parameter, which is the
     100             :    * smallest chunk the range may be broken into for parallel
     101             :    * execution.
     102             :    *
     103             :    * \note The std::vector passed in here MUST live for the
     104             :    * lifetime of this StoredRange! We are not responsible for
     105             :    * deleting this pointer.
     106             :    */
     107      944048 :   StoredRange (vec_type * objs,
     108             :                const unsigned int new_grainsize = 1000) :
     109             :     _end(objs->end()),
     110             :     _begin(objs->begin()),
     111      915168 :     _last(objs->size()),
     112      915168 :     _first(0),
     113      915168 :     _grainsize(new_grainsize),
     114     1030688 :     _objs(ptr_type(objs, [](vec_type *){/*don't delete*/}))
     115             :   {
     116      944048 :   }
     117             : 
     118             :   /**
     119             :    * Copy constructor.  The \p StoredRange can be copied into
     120             :    * subranges for parallel execution.  In this way the
     121             :    * initial \p StoredRange can be thought of as the root of
     122             :    * a binary tree.  The root element is the only element
     123             :    * which interacts with the user.  It takes a specified
     124             :    * range of objects and packs it into a contiguous vector
     125             :    * which can be split efficiently. However, there is no need
     126             :    * for the child ranges to contain this vector, so long as
     127             :    * the parent outlives the children.  So we implement
     128             :    * the copy constructor to specifically omit the \p _objs
     129             :    * vector.
     130             :    */
     131             :   StoredRange (const StoredRange<iterator_type,object_type> & er):
     132             :     _end(er._end),
     133             :     _begin(er._begin),
     134             :     _last(er._last),
     135             :     _first(er._first),
     136             :     _grainsize(er._grainsize),
     137             :     _objs(nullptr)
     138             :   {
     139             :     // specifically, do *not* copy the vector
     140             :   }
     141             : 
     142             :   /**
     143             :    * NOTE: When using pthreads this constructor is MANDATORY!!!
     144             :    *
     145             :    * Copy constructor.  The \p StoredRange can be copied into
     146             :    * subranges for parallel execution.  In this way the
     147             :    * initial \p StoredRange can be thought of as the root of
     148             :    * a binary tree.  The root element is the only element
     149             :    * which interacts with the user.  It takes a specified
     150             :    * range of objects and packs it into a contiguous vector
     151             :    * which can be split efficiently. However, there is no need
     152             :    * for the child ranges to contain this vector, so long as
     153             :    * the parent outlives the children.  So we implement
     154             :    * the copy constructor to specifically omit the \p _objs
     155             :    * vector. This version allows you to set the beginning and
     156             :    * ending of this new range to be different from that of the
     157             :    * one we're copying.
     158             :    */
     159      251926 :   StoredRange (const StoredRange<iterator_type,object_type> & er,
     160             :                const const_iterator & begin_range,
     161             :                const const_iterator & end_range):
     162      118143 :     _end(end_range),
     163      118143 :     _begin(begin_range),
     164             :     _last(0), // Initialize these in a moment
     165             :     _first(0),
     166      251926 :     _grainsize(er._grainsize),
     167      133783 :     _objs(nullptr)
     168             :   {
     169             :     // specifically, do *not* copy the vector
     170             : 
     171      251926 :     _first = std::distance(er._begin, _begin);
     172      251926 :     _last = _first + std::distance(_begin, _end);
     173      251926 :   }
     174             : 
     175             :   /**
     176             :    * Splits the range \p r.  The first half
     177             :    * of the range is left in place, the second
     178             :    * half of the range is placed in *this.
     179             :    */
     180             :   StoredRange (StoredRange<iterator_type,object_type> & r, Threads::split ) :
     181             :     _end(r._end),
     182             :     _begin(r._begin),
     183             :     _last(r._last),
     184             :     _first(r._first),
     185             :     _grainsize(r._grainsize),
     186             :     _objs(nullptr)
     187             :   {
     188             :     const_iterator
     189             :       beginning = r._begin,
     190             :       ending    = r._end,
     191             :       middle    = beginning + std::distance(beginning, ending)/2u;
     192             : 
     193             :     r._end = _begin = middle;
     194             : 
     195             :     std::size_t
     196             :       first = r._first,
     197             :       last  = r._last,
     198             :       half  = first + (last-first)/2u;
     199             : 
     200             :     r._last = _first = half;
     201             :   }
     202             : 
     203             :   /**
     204             :    * Destructor.  Releases the object array if we created it.
     205             :    */
     206     4144783 :   ~StoredRange () = default;
     207             : 
     208             :   /**
     209             :    * Resets the \p StoredRange to contain [first,last).
     210             :    *
     211             :    * \returns A reference to itself for convenience, so functions
     212             :    * expecting a \p StoredRange<> can be passed e.g. foo.reset(begin,end).
     213             :    */
     214             :   StoredRange<iterator_type, object_type> &
     215     3164109 :   reset (const iterator_type & first,
     216             :          const iterator_type & last)
     217             :   {
     218             :     // _objs must be initialized in order to call reset()
     219       57429 :     libmesh_assert(_objs);
     220             : 
     221       57429 :     _objs->clear();
     222             : 
     223   171067459 :     for (iterator_type it=first; it!=last; ++it)
     224    91350508 :       _objs->push_back(*it);
     225             : 
     226     3164109 :     _begin = _objs->begin();
     227     3164109 :     _end   = _objs->end();
     228             : 
     229     3164109 :     _first = 0;
     230     3164109 :     _last  = _objs->size();
     231             : 
     232     3164109 :     return *this;
     233             :   }
     234             : 
     235             :   /**
     236             :    * Resets the range to the last specified range.  This method only exists
     237             :    * for efficiency -- it is more efficient to set the range to its previous
     238             :    * value without rebuilding the underlying vector.
     239             :    *
     240             :    * \returns A reference to itself for convenience, so functions
     241             :    * expecting a StoredRange<> can be passed e.g. foo.reset().
     242             :    */
     243        7584 :   StoredRange<iterator_type, object_type> & reset ()
     244             :   {
     245             :     // _objs must be initialized in order to call reset()
     246        7584 :     libmesh_assert(_objs);
     247             : 
     248      263303 :     _begin = _objs->begin();
     249      263303 :     _end   = _objs->end();
     250             : 
     251      263303 :     _first = 0;
     252       24813 :     _last  = _objs->size();
     253             : 
     254      253658 :     return *this;
     255             :   }
     256             : 
     257             :   /**
     258             :    * Beginning of the range.
     259             :    */
     260     4323585 :   const_iterator begin () const { return _begin; }
     261             : 
     262             :   /**
     263             :    * End of the range.
     264             :    */
     265     4229579 :   const_iterator end () const { return _end; }
     266             : 
     267             :   /**
     268             :    * Index in the stored vector of the first object.
     269             :    */
     270       40060 :   std::size_t first_idx () const { return _first; }
     271             : 
     272             :   /**
     273             :    * Index in the stored vector of the last object.
     274             :    */
     275             :   std::size_t last_idx () const { return _last; }
     276             : 
     277             :   /**
     278             :    * The grain size for the range.  The range will be subdivided into
     279             :    * subranges not to exceed the grain size.
     280             :    */
     281             :   std::size_t grainsize () const {return _grainsize;}
     282             : 
     283             :   /**
     284             :    * Set the grain size.
     285             :    */
     286             :   void grainsize (const unsigned int & gs) {_grainsize = gs;}
     287             : 
     288             :   /**
     289             :    * \returns The size of the range.
     290             :    */
     291      853192 :   std::size_t size () const { return std::distance(_begin, _end); }
     292             : 
     293             :   //------------------------------------------------------------------------
     294             :   // Methods that implement Range concept
     295             :   //------------------------------------------------------------------------
     296             : 
     297             :   /**
     298             :    * \returns \p true if the range is empty.
     299             :    */
     300         598 :   bool empty() const { return (_begin == _end); }
     301             : 
     302             :   /**
     303             :    * \returns \p true if the range can be subdivided.
     304             :    */
     305             :   bool is_divisible() const { return this->grainsize() < static_cast<unsigned int>(std::distance(_begin, _end)); }
     306             : 
     307             : private:
     308             : 
     309             :   const_iterator _end;
     310             :   const_iterator _begin;
     311             :   std::size_t _last;
     312             :   std::size_t _first;
     313             :   std::size_t _grainsize;
     314             : 
     315             :   ptr_type _objs;
     316             : };
     317             : 
     318             : } // namespace libMesh
     319             : 
     320             : #endif // LIBMESH_STORED_RANGE_H

Generated by: LCOV version 1.14