@@ -127,11 +127,65 @@ class Summa
127127 const ordinal_type
128128 right_stride_local_; // /< stride for local right row iterators
129129
130+ // 3-d (h-grouped) grid information. The world's first
131+ // proc_h_ * proc_h_stride ranks are partitioned into proc_h_ contiguous
132+ // groups; slab h belongs to group h % proc_h_, and each group runs its
133+ // own 2-d SUMMA grid (proc_grid_ is this rank's GROUP-LOCAL grid,
134+ // constructed over the group's rank interval). proc_h_ == 1 is the
135+ // ordinary shared-grid batched contraction.
136+ const ordinal_type proc_h_; // /< Number of slab (h) groups
137+ const ordinal_type
138+ proc_h_stride_; // /< World ranks per slab group (the
139+ // /< group of slab h spans world ranks
140+ // /< [(h % proc_h_) * proc_h_stride_, ...))
141+ const ordinal_type first_slab_; // /< This rank's group's first slab (== its
142+ // /< group index), or nh_ if this rank is
143+ // /< in no group (idle for this eval)
144+ const ordinal_type my_slabs_; // /< Number of slabs of this rank's group
145+
146+ // / \return the world rank that owns result tile \p i: the within-group
147+ // / owner (from the group-local process grid) shifted by the world-rank
148+ // / offset of the group that owns \p i's slab. For proc_h_ == 1 the offset
149+ // / is 0 and this is the ordinary cyclic owner.
150+ ProcessID result_tile_owner (const ordinal_type i) const {
151+ const ordinal_type source_index = DistEvalImpl_::perm_index_to_source (i);
152+ // owner is independent of slab index *within a group*
153+ const ordinal_type slab_index = source_index % result_slab_size_;
154+ const ordinal_type tile_row = slab_index / proc_grid_.cols ();
155+ const ordinal_type tile_col = slab_index % proc_grid_.cols ();
156+ const ordinal_type proc_row = tile_row % proc_grid_.proc_rows ();
157+ const ordinal_type proc_col = tile_col % proc_grid_.proc_cols ();
158+ const ProcessID within_group = proc_row * proc_grid_.proc_cols () + proc_col;
159+ // shift by the offset of the group that owns this tile's slab
160+ const ordinal_type slab = source_index / result_slab_size_;
161+ const ordinal_type group = (proc_h_ > 1ul ) ? (slab % proc_h_) : 0ul ;
162+ return ProcessID (group * proc_h_stride_) + within_group;
163+ }
164+
130165 // / \return the slab index of SUMMA step \p s
131166 ordinal_type step_h (const ordinal_type s) const { return s / k_; }
132167 // / \return the within-slab inner-dimension index of SUMMA step \p s
133168 ordinal_type step_k (const ordinal_type s) const { return s % k_; }
134169
170+ // / \return the smallest SUMMA step >= \p s that belongs to one of this
171+ // / rank's group's slabs, or nsteps_ if there is none
172+ ordinal_type next_step (ordinal_type s) const {
173+ if (proc_h_ == 1ul ) return std::min (s, nsteps_);
174+ if (first_slab_ >= nh_) return nsteps_; // not in any group
175+ while (s < nsteps_ && (step_h (s) % proc_h_) != first_slab_)
176+ s = (step_h (s) + 1ul ) * k_; // jump to the start of the next slab
177+ return std::min (s, nsteps_);
178+ }
179+
180+ // / \return this rank's group-local ordinal of slab \p h (which must
181+ // / belong to this rank's group)
182+ ordinal_type slab_ord (const ordinal_type h) const {
183+ return (h - first_slab_) / proc_h_;
184+ }
185+
186+ // / \return the number of SUMMA steps of this rank's group's slabs
187+ ordinal_type my_steps () const { return my_slabs_ * k_; }
188+
135189 typedef Future<typename right_type::eval_type>
136190 right_future; // /< Future to a right-hand argument tile
137191 typedef Future<typename left_type::eval_type>
@@ -225,7 +279,7 @@ class Summa
225279 // / \param end The end of the row or column range
226280 // / \param stride The row or column index stride
227281 // / \param k The broadcast group index
228- // / \param max_group_size The maximum number of processes in the result
282+ // / \param max_proc_h_stride The maximum number of processes in the result
229283 // / group, which is equal to the number of process in this process row or
230284 // / column as defined by \c proc_grid_.
231285 // / \param key_offset The key that will be used to identify the process group
@@ -238,21 +292,21 @@ class Summa
238292 const std::vector<bool >& process_mask,
239293 ordinal_type index, const ordinal_type end,
240294 const ordinal_type stride,
241- const ordinal_type max_group_size ,
295+ const ordinal_type max_proc_h_stride ,
242296 const ordinal_type k, const ordinal_type key_offset,
243297 const ProcMap& proc_map) const {
244298 // Generate the list of processes in rank_row
245- std::vector<ProcessID> proc_list (max_group_size , -1 );
299+ std::vector<ProcessID> proc_list (max_proc_h_stride , -1 );
246300
247301 // Flag the root processes of the broadcast, which may not be included
248302 // by shape.
249- ordinal_type p = k % max_group_size ;
303+ ordinal_type p = k % max_proc_h_stride ;
250304 proc_list[p] = proc_map (p);
251305 ordinal_type count = 1ul ;
252306
253307 // Flag all processes that have non-zero tiles
254- for (p = 0ul ; (index < end) && (count < max_group_size );
255- index += stride, p = (p + 1u ) % max_group_size ) {
308+ for (p = 0ul ; (index < end) && (count < max_proc_h_stride );
309+ index += stride, p = (p + 1u ) % max_proc_h_stride ) {
256310 if ((proc_list[p] != -1 ) || (shape.is_zero (index)) || !process_mask.at (p))
257311 continue ;
258312
@@ -734,7 +788,7 @@ class Summa
734788 // broadcast root (i.e. within-slab k congruent to rank_col mod Pcols)
735789 const ordinal_type Pcols = proc_grid_.proc_cols ();
736790
737- for (; s < end; ++s ) {
791+ for (s = next_step (s) ; s < end; s = next_step (s + 1ul ) ) {
738792 const ordinal_type k = step_k (s);
739793 if (k % Pcols != static_cast <ordinal_type>(proc_grid_.rank_col ()))
740794 continue ;
@@ -787,7 +841,7 @@ class Summa
787841 // broadcast root (i.e. within-slab k congruent to rank_row mod Prows)
788842 const ordinal_type Prows = proc_grid_.proc_rows ();
789843
790- for (; s < end; ++s ) {
844+ for (s = next_step (s) ; s < end; s = next_step (s + 1ul ) ) {
791845 const ordinal_type k = step_k (s);
792846 if (k % Prows != static_cast <ordinal_type>(proc_grid_.rank_row ()))
793847 continue ;
@@ -847,9 +901,9 @@ class Summa
847901 // / \return The first step, greater than or equal to \c s with non-zero
848902 // / tiles, or \c nsteps_ if none is found.
849903 ordinal_type iterate_row (ordinal_type s) const {
850- // Iterate over steps until a non-zero tile is found or the end of the
851- // matrix is reached.
852- for (; s < nsteps_; ++s ) {
904+ // Iterate over this rank's group's steps until a non-zero tile is found
905+ // or the end of the matrix is reached.
906+ for (s = next_step (s) ; s < nsteps_; s = next_step (s + 1ul ) ) {
853907 // Search for non-zero tiles in row k of slab h of right
854908 ordinal_type i =
855909 step_h (s) * right_slab_size_ + step_k (s) * proc_grid_.cols ();
@@ -871,9 +925,9 @@ class Summa
871925 // / \return The first step, greater than or equal to \c s, that contains
872926 // / a non-zero tile. If no non-zero tile is not found, return \c nsteps_.
873927 ordinal_type iterate_col (ordinal_type s) const {
874- // Iterate over steps until a non-zero tile is found or the end of the
875- // matrix is reached.
876- for (; s < nsteps_; ++s ) {
928+ // Iterate over this rank's group's steps until a non-zero tile is found
929+ // or the end of the matrix is reached.
930+ for (s = next_step (s) ; s < nsteps_; s = next_step (s + 1ul ) ) {
877931 // Search column k of slab h for non-zero tiles
878932 const ordinal_type base = step_h (s) * left_slab_size_;
879933 for (ordinal_type i = base + left_start_local_ + step_k (s);
@@ -963,11 +1017,17 @@ class Summa
9631017 return tile_count;
9641018 } else {
9651019 // Construct static broadcast groups for dense arguments
966- // (key space [0, 2*nsteps_) is reserved for the sparse per-step groups)
967- const madness::DistributedID col_did (DistEvalImpl_::id (), 2ul * nsteps_);
1020+ // (key space [0, 2*nsteps_) is reserved for the sparse per-step groups,
1021+ // whose keys h*k_ and h*k_+nsteps_ are disjoint across h-groups; the
1022+ // two static keys are offset PAST that range and made group-unique so
1023+ // that two different groups' single-grid static groups never claim the
1024+ // same DistributedID with inconsistent membership)
1025+ const std::size_t static_key_base = 2ul * nsteps_ + 2ul * first_slab_;
1026+ const madness::DistributedID col_did (DistEvalImpl_::id (),
1027+ static_key_base);
9681028 col_group_ = proc_grid_.make_col_group (col_did);
9691029 const madness::DistributedID row_did (DistEvalImpl_::id (),
970- 2ul * nsteps_ + 1ul );
1030+ static_key_base + 1ul );
9711031 row_group_ = proc_grid_.make_row_group (row_did);
9721032
9731033#ifdef TILEDARRAY_ENABLE_SUMMA_TRACE_INITIALIZE
@@ -985,12 +1045,12 @@ class Summa
9851045#endif // TILEDARRAY_ENABLE_SUMMA_TRACE_INITIALIZE
9861046
9871047 // Allocate memory for the reduce pair tasks (one per local result tile
988- // per slab).
1048+ // per slab of this rank's group ).
9891049 std::allocator<ReducePairTask<op_type>> alloc;
990- reduce_tasks_ = alloc.allocate (nh_ * proc_grid_.local_size ());
1050+ reduce_tasks_ = alloc.allocate (my_slabs_ * proc_grid_.local_size ());
9911051
9921052 // Iterate over all local tiles
993- const ordinal_type n = nh_ * proc_grid_.local_size ();
1053+ const ordinal_type n = my_slabs_ * proc_grid_.local_size ();
9941054 for (ordinal_type t = 0ul ; t < n; ++t) {
9951055 // Initialize the reduction task
9961056 ReducePairTask<op_type>* MADNESS_RESTRICT const reduce_task =
@@ -1019,9 +1079,9 @@ class Summa
10191079 if (k_ == 0 ) return 0 ;
10201080
10211081 // Allocate memory for the reduce pair tasks (one per local result tile
1022- // per slab).
1082+ // per slab of this rank's group ).
10231083 std::allocator<ReducePairTask<op_type>> alloc;
1024- reduce_tasks_ = alloc.allocate (nh_ * proc_grid_.local_size ());
1084+ reduce_tasks_ = alloc.allocate (my_slabs_ * proc_grid_.local_size ());
10251085
10261086 // Initialize iteration variables
10271087 const ordinal_type col_stride = // The stride to iterate down a column
@@ -1030,10 +1090,11 @@ class Summa
10301090 proc_grid_.proc_cols ();
10311091
10321092 // Iterate over all local tiles, slab by slab (the block-cyclic phase
1033- // restarts at every slab: the owner of tile (h,i,j) does not depend on h)
1093+ // restarts at every slab: within a group, the owner of tile (h,i,j)
1094+ // does not depend on h)
10341095 ordinal_type tile_count = 0ul ;
10351096 ReducePairTask<op_type>* MADNESS_RESTRICT reduce_task = reduce_tasks_;
1036- for (ordinal_type h = 0ul ; h < nh_; ++h ) {
1097+ for (ordinal_type h = first_slab_ ; h < nh_; h += proc_h_ ) {
10371098 const ordinal_type slab_base = h * result_slab_size_;
10381099 ordinal_type row_start =
10391100 slab_base + proc_grid_.rank_row () * proc_grid_.cols ();
@@ -1103,7 +1164,7 @@ class Summa
11031164
11041165 // Iterate over all local tiles, slab by slab
11051166 ReducePairTask<op_type>* reduce_task = reduce_tasks_;
1106- for (ordinal_type h = 0ul ; h < nh_; ++h ) {
1167+ for (ordinal_type h = first_slab_ ; h < nh_; h += proc_h_ ) {
11071168 const ordinal_type slab_base = h * result_slab_size_;
11081169 ordinal_type row_start =
11091170 slab_base + proc_grid_.rank_row () * proc_grid_.cols ();
@@ -1126,7 +1187,7 @@ class Summa
11261187
11271188 // Deallocate the memory for the reduce pair tasks.
11281189 std::allocator<ReducePairTask<op_type>>().deallocate (
1129- reduce_tasks_, nh_ * proc_grid_.local_size ());
1190+ reduce_tasks_, my_slabs_ * proc_grid_.local_size ());
11301191 }
11311192
11321193 // / Set the result tiles and destroy reduce tasks
@@ -1145,7 +1206,7 @@ class Summa
11451206
11461207 // Iterate over all local tiles, slab by slab
11471208 ReducePairTask<op_type>* reduce_task = reduce_tasks_;
1148- for (ordinal_type h = 0ul ; h < nh_; ++h ) {
1209+ for (ordinal_type h = first_slab_ ; h < nh_; h += proc_h_ ) {
11491210 const ordinal_type slab_base = h * result_slab_size_;
11501211 ordinal_type row_start =
11511212 slab_base + proc_grid_.rank_row () * proc_grid_.cols ();
@@ -1177,7 +1238,7 @@ class Summa
11771238 }
11781239 // Deallocate the memory for the reduce pair tasks.
11791240 std::allocator<ReducePairTask<op_type>>().deallocate (
1180- reduce_tasks_, nh_ * proc_grid_.local_size ());
1241+ reduce_tasks_, my_slabs_ * proc_grid_.local_size ());
11811242
11821243#ifdef TILEDARRAY_ENABLE_SUMMA_TRACE_FINALIZE
11831244 ss << " }\n " ;
@@ -1229,9 +1290,10 @@ class Summa
12291290 const std::vector<col_datum>& col,
12301291 const std::vector<row_datum>& row,
12311292 madness::TaskInterface* const task) {
1232- // The reduce tasks of slab h occupy
1233- // [h * local_size, (h+1) * local_size)
1234- const ordinal_type slab_offset = step_h (s) * proc_grid_.local_size ();
1293+ // The reduce tasks of this group's slab h occupy
1294+ // [slab_ord(h) * local_size, (slab_ord(h)+1) * local_size)
1295+ const ordinal_type slab_offset =
1296+ slab_ord (step_h (s)) * proc_grid_.local_size ();
12351297
12361298 // Iterate over the row
12371299 for (ordinal_type i = 0ul ; i < col.size (); ++i) {
@@ -1266,9 +1328,10 @@ class Summa
12661328 const std::vector<col_datum>& col,
12671329 const std::vector<row_datum>& row,
12681330 madness::TaskInterface* const task) {
1269- // The reduce tasks of slab h occupy
1270- // [h * local_size, (h+1) * local_size)
1271- const ordinal_type slab_offset = step_h (s) * proc_grid_.local_size ();
1331+ // The reduce tasks of this group's slab h occupy
1332+ // [slab_ord(h) * local_size, (slab_ord(h)+1) * local_size)
1333+ const ordinal_type slab_offset =
1334+ slab_ord (step_h (s)) * proc_grid_.local_size ();
12721335
12731336 // Iterate over the row
12741337 for (ordinal_type i = 0ul ; i < col.size (); ++i) {
@@ -1540,13 +1603,14 @@ class Summa
15401603 public:
15411604 DenseStepTask (const std::shared_ptr<Summa_>& owner,
15421605 const ordinal_type depth)
1543- : StepTask(owner, owner->nsteps_ + 1ul ), k_(0 ) {
1606+ : StepTask(owner, owner->my_steps () + 1ul), k_(owner-> next_step ( 0ul ) ) {
15441607 StepTask::make_next_step_tasks (this , depth);
1545- StepTask::spawn_get_row_col_tasks (k_);
1608+ if (k_ < owner_-> nsteps_ ) StepTask::spawn_get_row_col_tasks (k_);
15461609 }
15471610
15481611 DenseStepTask (DenseStepTask* const parent, const int ndep)
1549- : StepTask(parent, ndep), k_(parent->k_ + 1ul ) {
1612+ : StepTask(parent, ndep),
1613+ k_(parent->owner_->next_step (parent->k_ + 1ul )) {
15501614 // Spawn tasks to get k-th row and column tiles
15511615 if (k_ < owner_->nsteps_ ) StepTask::spawn_get_row_col_tasks (k_);
15521616 }
@@ -1671,7 +1735,8 @@ class Summa
16711735 const trange_type trange, const shape_type& shape,
16721736 const std::shared_ptr<const pmap_interface>& pmap, const Perm& perm,
16731737 const op_type& op, const ordinal_type k, const ProcGrid& proc_grid,
1674- const ordinal_type nh = 1ul )
1738+ const ordinal_type nh = 1ul , const ordinal_type proc_h = 1ul ,
1739+ const ordinal_type proc_h_stride = 0ul )
16751740 : DistEvalImpl_(world, trange, shape, pmap, outer(perm)),
16761741 left_(left),
16771742 right_(right),
@@ -1685,6 +1750,11 @@ class Summa
16851750 left_slab_size_(left.size() / nh),
16861751 right_slab_size_(right.size() / nh),
16871752 result_slab_size_(proc_grid.rows() * proc_grid.cols()),
1753+ proc_h_(proc_h),
1754+ proc_h_stride_(proc_h_stride),
1755+ first_slab_(compute_first_slab(world, nh, proc_h, proc_h_stride)),
1756+ my_slabs_(first_slab_ < nh ? (nh - first_slab_ + proc_h - 1ul ) / proc_h
1757+ : 0ul),
16881758 reduce_tasks_(NULL ),
16891759 left_start_local_(proc_grid_.rank_row() * k),
16901760 left_end_(left.size() / nh),
@@ -1703,6 +1773,19 @@ class Summa
17031773 TA_ASSERT (nh_ > 0 );
17041774 TA_ASSERT (left.size () % nh_ == 0 );
17051775 TA_ASSERT (right.size () % nh_ == 0 );
1776+ TA_ASSERT (proc_h_ > 0 );
1777+ TA_ASSERT (proc_h_ == 1ul || proc_h_stride > 0ul );
1778+ TA_ASSERT (proc_h_ <= nh_);
1779+ }
1780+
1781+ // / \return this rank's group's first slab (== its group index), or
1782+ // / \p nh if this rank is outside the grouped rank interval
1783+ static ordinal_type compute_first_slab (World& world, const ordinal_type nh,
1784+ const ordinal_type proc_h,
1785+ const ordinal_type proc_h_stride) {
1786+ if (proc_h == 1ul ) return 0ul ;
1787+ const auto rank = ordinal_type (world.rank ());
1788+ return (rank < proc_h * proc_h_stride) ? (rank / proc_h_stride) : nh;
17061789 }
17071790
17081791 virtual ~Summa () {}
@@ -1717,18 +1800,11 @@ class Summa
17171800 TA_ASSERT (TensorImpl_::is_local (i));
17181801 TA_ASSERT (!TensorImpl_::is_zero (i));
17191802
1720- const ordinal_type source_index = DistEvalImpl_::perm_index_to_source (i);
1721-
1722- // Compute tile coordinate in tile grid (the owner of a tile is
1723- // independent of its slab index)
1724- const ordinal_type slab_index = source_index % result_slab_size_;
1725- const ordinal_type tile_row = slab_index / proc_grid_.cols ();
1726- const ordinal_type tile_col = slab_index % proc_grid_.cols ();
1727- // Compute process coordinate of tile in the process grid
1728- const ordinal_type proc_row = tile_row % proc_grid_.proc_rows ();
1729- const ordinal_type proc_col = tile_col % proc_grid_.proc_cols ();
1730- // Compute the process that owns tile
1731- const ProcessID source = proc_row * proc_grid_.proc_cols () + proc_col;
1803+ // The process that owns tile i: the within-group cyclic owner shifted by
1804+ // the world-rank offset of the tile's slab group (see
1805+ // result_tile_owner). For proc_h_ == 1 this is the ordinary cyclic
1806+ // owner over the whole world.
1807+ const ProcessID source = result_tile_owner (i);
17321808
17331809 const madness::DistributedID key (DistEvalImpl_::id (), i);
17341810 return TensorImpl_::world ().gop .template recv <value_type>(source, key);
0 commit comments