Skip to content

Commit 0055607

Browse files
committed
proc_grid/pmap: rank-subset ProcGrid + 3-d (proc_h) SlabbedPmap
Infrastructure for a 3-d (proc_h x proc_r x proc_c) batched-Summa grid that distributes the fused/batch (h, slab) dimension of a general product across process planes: - ProcGrid gains a rank-subset constructor (tagged rank_subset to avoid colliding with the same-arity test-only ctor) that builds a 2-d grid over a contiguous interval [rank_offset, rank_offset + nprocs) of the world's ranks; map_row/map_col and the row/col group factories emit world-correct ranks via the offset. The legacy full-world ctor is unchanged (offset 0). - SlabbedPmap gains a 3-d variant (proc_h, proc_h_stride): slab h belongs to plane h % proc_h of proc_h_stride contiguous ranks, and the per-slab base map's plane-local owners are offset by the slab's plane. The original 3-argument form (proc_h == 1, slab-replicated) is unchanged.
1 parent 2ae292f commit 0055607

2 files changed

Lines changed: 142 additions & 17 deletions

File tree

src/TiledArray/pmap/slabbed_pmap.h

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ namespace detail {
4444
/// (Hadamard/batch) modes as their leading dimensions, every slab is
4545
/// distributed identically over the same 2-d process grid, and the slab
4646
/// index never participates in inter-process communication patterns.
47+
/// The 3-d-grid variant adds a process dimension \c proc_h along the slab
48+
/// (h) axis: the world's first `proc_h * proc_h_stride` ranks are partitioned
49+
/// into `proc_h` contiguous h-planes of `proc_h_stride` ranks each; slab
50+
/// \f$ h \f$ belongs to plane \f$ h \% proc\_h \f$, and within its plane is
51+
/// distributed by the base map, whose owners must then be PLANE-LOCAL ranks
52+
/// in \f$ [0, proc\_h\_stride) \f$: the owner of index \f$ o \f$ is
53+
/// \f$ (h \% proc\_h) \cdot proc\_h\_stride + base(o \% S) \f$.
54+
/// `proc_h == 1` reduces to the slab-replicated map above (and is constructed
55+
/// via the 2-argument-base constructor, which delegates locality to the base
56+
/// map).
4757
class SlabbedPmap : public Pmap {
4858
protected:
4959
// Import Pmap protected variables
@@ -56,6 +66,14 @@ class SlabbedPmap : public Pmap {
5666
const std::shared_ptr<const Pmap> base_; ///< The per-slab base map
5767
const size_type slab_size_; ///< The number of indices per slab
5868
const size_type nslabs_; ///< The number of slabs
69+
const size_type proc_h_ = 1; ///< Process-grid extent along the slab
70+
///< (h) axis (the number of h-planes)
71+
const size_type proc_h_stride_ = 0; ///< Ranks per h-plane (0 = the whole
72+
///< world; base owners are world ranks
73+
///< and locality delegates to the base)
74+
75+
/// \return whether this map distributes the slab axis over a process plane
76+
bool hgrouped() const { return proc_h_stride_ != 0ul; }
5977

6078
public:
6179
typedef Pmap::size_type size_type; ///< Size type
@@ -76,6 +94,49 @@ class SlabbedPmap : public Pmap {
7694
this->local_size_ = base_->local_size() * nslabs_;
7795
}
7896

97+
/// Construct an h-grouped (3-d grid) slabbed process map
98+
99+
/// \param world The world where the tiles will be mapped
100+
/// \param base The base process map, defined over one slab; its owners
101+
/// must be GROUP-LOCAL ranks in [0, proc_h_stride)
102+
/// \param nslabs The number of slabs
103+
/// \param proc_h The number of slab groups (slab h -> group h % proc_h)
104+
/// \param proc_h_stride The number of (contiguous) world ranks per group
105+
SlabbedPmap(World& world, std::shared_ptr<const Pmap> base,
106+
const size_type nslabs, const size_type proc_h,
107+
const size_type proc_h_stride)
108+
: Pmap(world, base->size() * nslabs),
109+
base_(std::move(base)),
110+
slab_size_(base_->size()),
111+
nslabs_(nslabs),
112+
proc_h_(proc_h),
113+
proc_h_stride_(proc_h_stride) {
114+
TA_ASSERT(base_);
115+
TA_ASSERT(nslabs_ > 0ul);
116+
TA_ASSERT(proc_h_ > 0ul);
117+
TA_ASSERT(proc_h_stride_ > 0ul);
118+
TA_ASSERT(proc_h_ * proc_h_stride_ <= size_type(world.size()));
119+
120+
// this rank's group and group-local rank; ranks beyond the grouped
121+
// prefix of the world own nothing
122+
const size_type rank = world.rank();
123+
if (rank < proc_h_ * proc_h_stride_) {
124+
const size_type my_group = rank / proc_h_stride_;
125+
const size_type my_group_rank = rank % proc_h_stride_;
126+
// count of my group's slabs: h in [0, nslabs) with h % proc_h ==
127+
// my_group
128+
const size_type my_slabs =
129+
(nslabs_ / proc_h_) + (my_group < (nslabs_ % proc_h_) ? 1u : 0u);
130+
// count of slab indices the base assigns to my group-local rank
131+
size_type base_local = 0ul;
132+
for (size_type j = 0ul; j < slab_size_; ++j)
133+
if (base_->owner(j) == my_group_rank) ++base_local;
134+
this->local_size_ = my_slabs * base_local;
135+
} else {
136+
this->local_size_ = 0ul;
137+
}
138+
}
139+
79140
virtual ~SlabbedPmap() {}
80141

81142
/// \return the per-slab base map
@@ -84,25 +145,32 @@ class SlabbedPmap : public Pmap {
84145
size_type slab_size() const { return slab_size_; }
85146
/// \return the number of slabs
86147
size_type nslabs() const { return nslabs_; }
148+
/// \return the number of slab (h) groups
149+
size_type proc_h() const { return proc_h_; }
87150

88151
/// Maps \c tile to the process that owns it
89152

90153
/// \param tile The tile to be queried
91154
/// \return Process that logically owns \c tile
92155
virtual size_type owner(const size_type tile) const {
93156
TA_ASSERT(tile < size_);
94-
return base_->owner(tile % slab_size_);
157+
if (!hgrouped()) return base_->owner(tile % slab_size_);
158+
const size_type group = (tile / slab_size_) % proc_h_;
159+
return group * proc_h_stride_ + base_->owner(tile % slab_size_);
95160
}
96161

97162
/// Check that the tile is owned by this process
98163

99164
/// \param tile The tile to be checked
100165
/// \return \c true if \c tile is owned by this process, otherwise \c false
101166
virtual bool is_local(const size_type tile) const {
102-
return base_->is_local(tile % slab_size_);
167+
if (!hgrouped()) return base_->is_local(tile % slab_size_);
168+
return owner(tile) == size_type(rank_);
103169
}
104170

105-
virtual bool known_local_size() const { return base_->known_local_size(); }
171+
virtual bool known_local_size() const {
172+
return hgrouped() ? true : base_->known_local_size();
173+
}
106174

107175
virtual const_iterator begin() const {
108176
return Iterator(*this, 0ul, size_, 0ul, /*checking=*/true);

src/TiledArray/proc_grid.h

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ namespace detail {
5555
/// \f]
5656
/// where the positive, real root of \f$P_{\rm{row}}\f$ give the optimal
5757
/// optimal communication time.
58+
59+
/// Tag to disambiguate the rank-subset ProcGrid constructor from the
60+
/// (same-arity) test-only constructor.
61+
struct rank_subset_t {
62+
explicit rank_subset_t() = default;
63+
};
64+
inline constexpr rank_subset_t rank_subset{};
65+
5866
class ProcGrid {
5967
public:
6068
typedef uint_fast32_t size_type;
@@ -71,9 +79,12 @@ class ProcGrid {
7179
///< may be less than the number of processes in world.
7280
ProcessID rank_row_; ///< This process's row in the process grid
7381
ProcessID rank_col_; ///< This process's column in the process grid
74-
size_type local_rows_; ///< The number of local element rows
75-
size_type local_cols_; ///< The number of local element columns
76-
size_type local_size_; ///< Number of local elements
82+
ProcessID rank_offset_ = 0; ///< World rank of the grid's first process
83+
///< (nonzero for a grid over a contiguous
84+
///< subset of the world's ranks)
85+
size_type local_rows_; ///< The number of local element rows
86+
size_type local_cols_; ///< The number of local element columns
87+
size_type local_size_; ///< Number of local elements
7788

7889
/// Compute the number of process rows that minimizes communication
7990

@@ -180,14 +191,16 @@ class ProcGrid {
180191
proc_cols_ = 1u;
181192
proc_size_ = 1u;
182193

183-
// Set this process rank
184-
rank_row_ = 0;
185-
rank_col_ = 0;
194+
if (rank < proc_size_) {
195+
// Set this process rank
196+
rank_row_ = 0;
197+
rank_col_ = 0;
186198

187-
// Set local counts
188-
local_rows_ = rows_;
189-
local_cols_ = cols_;
190-
local_size_ = size_;
199+
// Set local counts
200+
local_rows_ = rows_;
201+
local_cols_ = cols_;
202+
local_size_ = size_;
203+
}
191204

192205
} else if (size_ <= nprocs) { // Max one tile per process
193206

@@ -291,6 +304,48 @@ class ProcGrid {
291304
init(world_->rank(), world_->size(), row_size, col_size);
292305
}
293306

307+
/// Construct a process grid over a contiguous subset of the world's ranks
308+
309+
/// The grid spans world ranks [rank_offset, rank_offset + nprocs); ranks
310+
/// outside that interval construct a valid "not in the grid" instance
311+
/// (zero local sizes, empty groups). Used by the h-grouped (3-d) batched
312+
/// Summa, where each fused-index slab group runs its own 2-d grid.
313+
/// \param world The world where the process grid will live
314+
/// \param rank_offset The world rank of the grid's first process
315+
/// \param nprocs The number of processes spanned by the grid
316+
/// \param rows The number of tile rows
317+
/// \param cols The number of tile columns
318+
/// \param row_size The number of element rows
319+
/// \param col_size The number of element columns
320+
ProcGrid(World& world, rank_subset_t, const ProcessID rank_offset,
321+
const size_type nprocs, const size_type rows, const size_type cols,
322+
const std::size_t row_size, const std::size_t col_size)
323+
: world_(&world),
324+
rows_(rows),
325+
cols_(cols),
326+
size_(rows_ * cols_),
327+
proc_rows_(0ul),
328+
proc_cols_(0ul),
329+
proc_size_(0ul),
330+
rank_row_(-1),
331+
rank_col_(-1),
332+
rank_offset_(rank_offset),
333+
local_rows_(0ul),
334+
local_cols_(0ul),
335+
local_size_(0ul) {
336+
TA_ASSERT(rank_offset >= 0);
337+
TA_ASSERT(nprocs >= 1u);
338+
TA_ASSERT(rank_offset + nprocs <= size_type(world.size()));
339+
const auto world_rank = world.rank();
340+
// out-of-grid ranks pass rank == nprocs, which every init() branch
341+
// treats as "not in the grid"
342+
const size_type rank = (world_rank >= rank_offset &&
343+
world_rank < rank_offset + ProcessID(nprocs))
344+
? world_rank - rank_offset
345+
: nprocs;
346+
init(rank, nprocs, row_size, col_size);
347+
}
348+
294349
#ifdef TILEDARRAY_ENABLE_TEST_PROC_GRID
295350
// Note: The following function is here for testing purposes only. It
296351
// has the same functionality as the default constructor above, except the
@@ -350,6 +405,7 @@ class ProcGrid {
350405
proc_size_(other.proc_size_),
351406
rank_row_(other.rank_row_),
352407
rank_col_(other.rank_col_),
408+
rank_offset_(other.rank_offset_),
353409
local_rows_(other.local_rows_),
354410
local_cols_(other.local_cols_),
355411
local_size_(other.local_size_) {}
@@ -367,6 +423,7 @@ class ProcGrid {
367423
proc_size_ = other.proc_size_;
368424
rank_row_ = other.rank_row_;
369425
rank_col_ = other.rank_col_;
426+
rank_offset_ = other.rank_offset_;
370427
local_rows_ = other.local_rows_;
371428
local_cols_ = other.local_cols_;
372429
local_size_ = other.local_size_;
@@ -447,7 +504,7 @@ class ProcGrid {
447504
// Populate the row process list
448505
size_type p = rank_row_ * proc_cols_;
449506
const size_type row_end = p + proc_cols_;
450-
for (; p < row_end; ++p) proc_list.push_back(p);
507+
for (; p < row_end; ++p) proc_list.push_back(p + rank_offset_);
451508

452509
// Construct the group
453510
group = madness::Group(*world_, proc_list, did);
@@ -472,7 +529,7 @@ class ProcGrid {
472529

473530
// Populate the column process list
474531
for (size_type p = rank_col_; p < proc_size_; p += proc_cols_)
475-
proc_list.push_back(p);
532+
proc_list.push_back(p + rank_offset_);
476533

477534
// Construct the group
478535
if (proc_list.size() != 0)
@@ -489,7 +546,7 @@ class ProcGrid {
489546
/// (row,rank_col)
490547
ProcessID map_row(const size_type row) const {
491548
TA_ASSERT(row < proc_rows_);
492-
return rank_col_ + row * proc_cols_;
549+
return rank_col_ + row * proc_cols_ + rank_offset_;
493550
}
494551

495552
/// Map a column to the process in this process's row
@@ -499,7 +556,7 @@ class ProcGrid {
499556
/// (rank_row,col)
500557
ProcessID map_col(const size_type col) const {
501558
TA_ASSERT(col < proc_cols_);
502-
return rank_row_ * proc_cols_ + col;
559+
return rank_row_ * proc_cols_ + col + rank_offset_;
503560
}
504561

505562
/// Construct a cyclic process

0 commit comments

Comments
 (0)