Itoyori  v0.0.1
mem_mapper.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include "ityr/common/util.hpp"
4 #include "ityr/ori/util.hpp"
5 
7 
8 struct segment {
9  int owner;
10  std::size_t offset_b;
11  std::size_t offset_e;
12  std::size_t pm_offset;
13 
14  bool operator==(const segment& b) const {
15  return owner == b.owner && offset_b == b.offset_b && offset_e == b.offset_e && pm_offset == b.pm_offset;
16  }
17  bool operator!=(const segment& b) const {
18  return !(*this == b);
19  }
20 };
21 
22 struct numa_segment {
23  int owner; // -1: interleave
24  std::size_t pm_offset_b;
25  std::size_t pm_offset_e;
26 
27  bool operator==(const numa_segment& b) const {
28  return owner == b.owner && pm_offset_b == b.pm_offset_b && pm_offset_e == b.pm_offset_e;
29  }
30  bool operator!=(const numa_segment& b) const {
31  return !(*this == b);
32  }
33 };
34 
35 class base {
36 public:
37  base(std::size_t size, int n_inter_ranks, int n_intra_ranks)
38  : size_(size), n_inter_ranks_(n_inter_ranks), n_intra_ranks_(n_intra_ranks) {}
39 
40  virtual ~base() = default;
41 
42  virtual std::size_t block_size() const = 0;
43 
44  virtual std::size_t local_size(int inter_rank) const = 0;
45 
46  virtual std::size_t effective_size() const = 0;
47 
48  // Returns the segment info that specifies the owner and the range [offset_b, offset_e)
49  // of the block that contains the given offset.
50  // pm_offset is the offset from the beginning of the owner's local physical memory for the block.
51  virtual segment get_segment(std::size_t offset) const = 0;
52 
53  virtual numa_segment get_numa_segment(int inter_rank, std::size_t pm_offset) const = 0;
54 
55  virtual bool should_map_all_home() const = 0;
56 
57 protected:
58  std::size_t size_;
61 };
62 
63 template <block_size_t BlockSize>
64 class block : public base {
65 public:
66  block(std::size_t size, int n_inter_ranks, int n_intra_ranks)
67  : base(size, n_inter_ranks, n_intra_ranks),
68  n_blk_((size + BlockSize - 1) / BlockSize) {}
69 
70  std::size_t block_size() const override { return BlockSize; }
71 
72  std::size_t local_size(int inter_rank) const override {
73  int seg_id = inter_rank;
74  auto [blk_id_b, blk_id_e] = get_seg_range(seg_id);
75  return std::max(std::size_t(1), blk_id_e - blk_id_b) * BlockSize;
76  }
77 
78  std::size_t effective_size() const override {
79  return n_blk_ * BlockSize;
80  }
81 
82  segment get_segment(std::size_t offset) const override {
83  ITYR_CHECK(offset < effective_size());
84 
85  std::size_t blk_id = offset / BlockSize;
86  int seg_id = blk_id * n_inter_ranks_ / n_blk_;
87 
88  auto [blk_id_b, blk_id_e] = get_seg_range(seg_id);
89  ITYR_CHECK(blk_id_b <= blk_id);
90  ITYR_CHECK(blk_id < blk_id_e);
91 
92  return segment{seg_id,
93  blk_id_b * BlockSize,
94  blk_id_e * BlockSize,
95  0};
96  }
97 
98  numa_segment get_numa_segment(int inter_rank, std::size_t pm_offset) const override {
99  ITYR_CHECK(pm_offset < local_size(inter_rank));
100 
101  auto n_numa_blk = (local_size(inter_rank) + BlockSize - 1) / BlockSize;
102 
103  std::size_t blk_id = pm_offset / BlockSize;
104  int seg_id = blk_id * n_intra_ranks_ / n_numa_blk;
105 
106  std::size_t blk_id_b = (seg_id * n_numa_blk + n_intra_ranks_ - 1) / n_intra_ranks_;
107  std::size_t blk_id_e = ((seg_id + 1) * n_numa_blk + n_intra_ranks_ - 1) / n_intra_ranks_;
108 
109  ITYR_CHECK(blk_id_b <= blk_id);
110  ITYR_CHECK(blk_id < blk_id_e);
111 
112  return numa_segment{seg_id,
113  blk_id_b * BlockSize,
114  blk_id_e * BlockSize};
115  }
116 
117  bool should_map_all_home() const override {
118  return true;
119  }
120 
121 private:
122  std::tuple<std::size_t, std::size_t> get_seg_range(int seg_id) const {
123  std::size_t blk_id_b = (seg_id * n_blk_ + n_inter_ranks_ - 1) / n_inter_ranks_;
124  std::size_t blk_id_e = ((seg_id + 1) * n_blk_ + n_inter_ranks_ - 1) / n_inter_ranks_;
125  return {blk_id_b, blk_id_e};
126  }
127 
128  std::size_t n_blk_;
129 };
130 
131 ITYR_TEST_CASE("[ityr::ori::mem_mapper::block] calculate local block size") {
132  constexpr block_size_t bs = 65536;
133  auto local_block_size = [](std::size_t size, int n_inter_ranks, int inter_rank) -> std::size_t {
134  return block<bs>(size, n_inter_ranks, 1).local_size(inter_rank);
135  };
136  ITYR_CHECK(local_block_size(bs * 4 , 4, 0) == bs );
137  ITYR_CHECK(local_block_size(bs * 12 , 4, 0) == bs * 3);
138  ITYR_CHECK(local_block_size(bs * 14 , 4, 0) == bs * 4);
139  ITYR_CHECK(local_block_size(bs * 14 , 4, 1) == bs * 3);
140  ITYR_CHECK(local_block_size(bs * 14 , 4, 2) == bs * 4);
141  ITYR_CHECK(local_block_size(bs * 14 , 4, 3) == bs * 3);
142  ITYR_CHECK(local_block_size(1 , 4, 0) == bs );
143  ITYR_CHECK(local_block_size(1 , 4, 1) == bs ); // cannot be zero
144  ITYR_CHECK(local_block_size(1 , 1, 0) == bs );
145  ITYR_CHECK(local_block_size(bs * 3 , 1, 0) == bs * 3);
146 }
147 
148 ITYR_TEST_CASE("[ityr::ori::mem_mapper::block] get block information at specified offset") {
149  constexpr block_size_t bs = 65536;
150  auto get_segment = [](std::size_t size, int n_inter_ranks, std::size_t offset) -> segment {
151  return block<bs>(size, n_inter_ranks, 1).get_segment(offset);
152  };
153  ITYR_CHECK(get_segment(bs * 4 , 4, 0 ) == (segment{0, 0 , bs , 0}));
154  ITYR_CHECK(get_segment(bs * 4 , 4, bs ) == (segment{1, bs , bs * 2 , 0}));
155  ITYR_CHECK(get_segment(bs * 4 , 4, bs * 2 ) == (segment{2, bs * 2 , bs * 3 , 0}));
156  ITYR_CHECK(get_segment(bs * 4 , 4, bs * 3 ) == (segment{3, bs * 3 , bs * 4 , 0}));
157  ITYR_CHECK(get_segment(bs * 4 , 4, bs * 4 - 1 ) == (segment{3, bs * 3 , bs * 4 , 0}));
158  ITYR_CHECK(get_segment(bs * 14 , 4, 0 ) == (segment{0, 0 , bs * 4 , 0}));
159  ITYR_CHECK(get_segment(bs * 14 , 4, bs ) == (segment{0, 0 , bs * 4 , 0}));
160  ITYR_CHECK(get_segment(bs * 14 , 4, bs * 5 ) == (segment{1, bs * 4 , bs * 7 , 0}));
161  ITYR_CHECK(get_segment(bs * 14 - 1, 4, bs * 14 - 1) == (segment{3, bs * 11, bs * 14, 0}));
162 }
163 
164 template <block_size_t BlockSize>
165 class cyclic : public base {
166 public:
167  cyclic(std::size_t size, int n_inter_ranks, int n_intra_ranks, std::size_t seg_size = BlockSize)
168  : base(size, n_inter_ranks, n_intra_ranks),
169  seg_size_(seg_size) {
170  ITYR_CHECK(seg_size >= BlockSize);
171  ITYR_CHECK(seg_size % BlockSize == 0);
172  }
173 
174  std::size_t block_size() const override { return BlockSize; }
175 
176  std::size_t local_size(int) const override {
177  return local_size_impl();
178  }
179 
180  std::size_t effective_size() const override {
181  return local_size_impl() * n_inter_ranks_;
182  }
183 
184  segment get_segment(std::size_t offset) const override {
185  ITYR_CHECK(offset < effective_size());
186  std::size_t blk_id_g = offset / seg_size_;
187  std::size_t blk_id_l = blk_id_g / n_inter_ranks_;
188  return segment{static_cast<int>(blk_id_g % n_inter_ranks_),
189  blk_id_g * seg_size_,
190  (blk_id_g + 1) * seg_size_,
191  blk_id_l * seg_size_};
192  }
193 
194  numa_segment get_numa_segment(int inter_rank, std::size_t) const override {
195  // interleave all
196  return numa_segment{-1, 0, local_size(inter_rank)};
197  }
198 
199  bool should_map_all_home() const override {
200  return false;
201  }
202 
203 private:
204  // non-virtual common part
205  std::size_t local_size_impl() const {
206  std::size_t n_blk_g = (size_ + seg_size_ - 1) / seg_size_;
207  std::size_t n_blk_l = (n_blk_g + n_inter_ranks_ - 1) / n_inter_ranks_;
208  return n_blk_l * seg_size_;
209  }
210 
211  std::size_t seg_size_;
212 };
213 
214 ITYR_TEST_CASE("[ityr::ori::mem_mapper::cyclic] calculate local block size") {
215  constexpr block_size_t bs = 65536;
216  std::size_t ss = bs * 2;
217  auto local_block_size = [=](std::size_t size, int n_inter_ranks, int inter_rank) -> std::size_t {
218  return cyclic<bs>(size, n_inter_ranks, 1, ss).local_size(inter_rank);
219  };
220  ITYR_CHECK(local_block_size(ss * 4 , 4, 0) == ss );
221  ITYR_CHECK(local_block_size(ss * 12 , 4, 0) == ss * 3);
222  ITYR_CHECK(local_block_size(ss * 13 , 4, 0) == ss * 4);
223  ITYR_CHECK(local_block_size(ss * 12 + 1, 4, 0) == ss * 4);
224  ITYR_CHECK(local_block_size(ss * 12 - 1, 4, 0) == ss * 3);
225  ITYR_CHECK(local_block_size(1 , 4, 0) == ss );
226  ITYR_CHECK(local_block_size(1 , 1, 0) == ss );
227  ITYR_CHECK(local_block_size(ss * 3 , 1, 0) == ss * 3);
228 }
229 
230 ITYR_TEST_CASE("[ityr::ori::mem_mapper::cyclic] get block information at specified offset") {
231  constexpr block_size_t bs = 65536;
232  std::size_t ss = bs * 2;
233  auto get_segment = [=](std::size_t size, int n_inter_ranks, std::size_t offset) -> segment {
234  return cyclic<bs>(size, n_inter_ranks, 1, ss).get_segment(offset);
235  };
236  ITYR_CHECK(get_segment(ss * 4 , 4, 0 ) == (segment{0, 0 , ss , 0 }));
237  ITYR_CHECK(get_segment(ss * 4 , 4, ss ) == (segment{1, ss , ss * 2 , 0 }));
238  ITYR_CHECK(get_segment(ss * 4 , 4, ss * 2 ) == (segment{2, ss * 2 , ss * 3 , 0 }));
239  ITYR_CHECK(get_segment(ss * 4 , 4, ss * 3 ) == (segment{3, ss * 3 , ss * 4 , 0 }));
240  ITYR_CHECK(get_segment(ss * 4 , 4, ss * 4 - 1) == (segment{3, ss * 3 , ss * 4 , 0 }));
241  ITYR_CHECK(get_segment(ss * 12 , 4, 0 ) == (segment{0, 0 , ss , 0 }));
242  ITYR_CHECK(get_segment(ss * 12 , 4, ss ) == (segment{1, ss , ss * 2 , 0 }));
243  ITYR_CHECK(get_segment(ss * 12 , 4, ss * 3 ) == (segment{3, ss * 3 , ss * 4 , 0 }));
244  ITYR_CHECK(get_segment(ss * 12 , 4, ss * 5 + 2) == (segment{1, ss * 5 , ss * 6 , ss }));
245  ITYR_CHECK(get_segment(ss * 12 - 1, 4, ss * 11 ) == (segment{3, ss * 11, ss * 12, ss * 2}));
246 }
247 
248 template <block_size_t BlockSize>
249 class block_adws : public base {
250 public:
251  block_adws(std::size_t size, int n_inter_ranks, int n_intra_ranks)
252  : base(size, n_inter_ranks, n_intra_ranks),
253  n_blk_((size + BlockSize - 1) / BlockSize) {}
254 
255  std::size_t block_size() const override { return BlockSize; }
256 
257  std::size_t local_size(int inter_rank) const override {
258  int seg_id = n_inter_ranks_ - inter_rank - 1;
259  auto [blk_id_b, blk_id_e] = get_seg_range(seg_id);
260  return std::max(std::size_t(1), blk_id_e - blk_id_b) * BlockSize;
261  }
262 
263  std::size_t effective_size() const override {
264  return n_blk_ * BlockSize;
265  }
266 
267  segment get_segment(std::size_t offset) const override {
268  ITYR_CHECK(offset < effective_size());
269 
270  std::size_t blk_id = offset / BlockSize;
271  int seg_id = ((blk_id + 1) * n_inter_ranks_ + n_blk_ - 1) / n_blk_ - 1;
272 
273  auto [blk_id_b, blk_id_e] = get_seg_range(seg_id);
274  ITYR_CHECK(blk_id_b <= blk_id);
275  ITYR_CHECK(blk_id < blk_id_e);
276 
277  return segment{n_inter_ranks_ - seg_id - 1,
278  blk_id_b * BlockSize,
279  blk_id_e * BlockSize,
280  0};
281  }
282 
283  numa_segment get_numa_segment(int inter_rank, std::size_t pm_offset) const override {
284  ITYR_CHECK(pm_offset < local_size(inter_rank));
285 
286  auto n_numa_blk = (local_size(inter_rank) + BlockSize - 1) / BlockSize;
287 
288  std::size_t blk_id = pm_offset / BlockSize;
289  int seg_id = ((blk_id + 1) * n_intra_ranks_ + n_numa_blk - 1) / n_numa_blk - 1;
290 
291  std::size_t blk_id_b = (seg_id * n_numa_blk) / n_intra_ranks_;
292  std::size_t blk_id_e = ((seg_id + 1) * n_numa_blk) / n_intra_ranks_;
293 
294  ITYR_CHECK(blk_id_b <= blk_id);
295  ITYR_CHECK(blk_id < blk_id_e);
296 
297  return numa_segment{n_intra_ranks_ - seg_id - 1,
298  blk_id_b * BlockSize,
299  blk_id_e * BlockSize};
300  }
301 
302  bool should_map_all_home() const override {
303  return true;
304  }
305 
306 private:
307  std::tuple<std::size_t, std::size_t> get_seg_range(int seg_id) const {
308  std::size_t blk_id_b = (seg_id * n_blk_) / n_inter_ranks_;
309  std::size_t blk_id_e = ((seg_id + 1) * n_blk_) / n_inter_ranks_;
310  return {blk_id_b, blk_id_e};
311  }
312 
313  std::size_t n_blk_;
314 };
315 
316 }
Definition: mem_mapper.hpp:35
int n_intra_ranks_
Definition: mem_mapper.hpp:60
virtual numa_segment get_numa_segment(int inter_rank, std::size_t pm_offset) const =0
virtual bool should_map_all_home() const =0
base(std::size_t size, int n_inter_ranks, int n_intra_ranks)
Definition: mem_mapper.hpp:37
virtual ~base()=default
virtual std::size_t local_size(int inter_rank) const =0
virtual std::size_t effective_size() const =0
std::size_t size_
Definition: mem_mapper.hpp:58
int n_inter_ranks_
Definition: mem_mapper.hpp:59
virtual segment get_segment(std::size_t offset) const =0
virtual std::size_t block_size() const =0
Definition: mem_mapper.hpp:249
std::size_t local_size(int inter_rank) const override
Definition: mem_mapper.hpp:257
std::size_t block_size() const override
Definition: mem_mapper.hpp:255
segment get_segment(std::size_t offset) const override
Definition: mem_mapper.hpp:267
std::size_t effective_size() const override
Definition: mem_mapper.hpp:263
block_adws(std::size_t size, int n_inter_ranks, int n_intra_ranks)
Definition: mem_mapper.hpp:251
bool should_map_all_home() const override
Definition: mem_mapper.hpp:302
numa_segment get_numa_segment(int inter_rank, std::size_t pm_offset) const override
Definition: mem_mapper.hpp:283
Definition: mem_mapper.hpp:64
std::size_t block_size() const override
Definition: mem_mapper.hpp:70
std::size_t effective_size() const override
Definition: mem_mapper.hpp:78
segment get_segment(std::size_t offset) const override
Definition: mem_mapper.hpp:82
block(std::size_t size, int n_inter_ranks, int n_intra_ranks)
Definition: mem_mapper.hpp:66
numa_segment get_numa_segment(int inter_rank, std::size_t pm_offset) const override
Definition: mem_mapper.hpp:98
bool should_map_all_home() const override
Definition: mem_mapper.hpp:117
std::size_t local_size(int inter_rank) const override
Definition: mem_mapper.hpp:72
Definition: mem_mapper.hpp:165
bool should_map_all_home() const override
Definition: mem_mapper.hpp:199
std::size_t local_size(int) const override
Definition: mem_mapper.hpp:176
segment get_segment(std::size_t offset) const override
Definition: mem_mapper.hpp:184
numa_segment get_numa_segment(int inter_rank, std::size_t) const override
Definition: mem_mapper.hpp:194
cyclic(std::size_t size, int n_inter_ranks, int n_intra_ranks, std::size_t seg_size=BlockSize)
Definition: mem_mapper.hpp:167
std::size_t effective_size() const override
Definition: mem_mapper.hpp:180
std::size_t block_size() const override
Definition: mem_mapper.hpp:174
#define ITYR_CHECK(cond)
Definition: util.hpp:48
rank_t inter_rank(rank_t global_rank)
Definition: topology.hpp:219
Definition: mem_mapper.hpp:6
uint32_t block_size_t
Definition: util.hpp:30
monoid< T, max_functor<>, lowest< T > > max
Definition: reducer.hpp:104
constexpr auto size(const checkout_span< T, Mode > &cs) noexcept
Definition: checkout_span.hpp:178
Definition: mem_mapper.hpp:22
bool operator!=(const numa_segment &b) const
Definition: mem_mapper.hpp:30
std::size_t pm_offset_e
Definition: mem_mapper.hpp:25
std::size_t pm_offset_b
Definition: mem_mapper.hpp:24
int owner
Definition: mem_mapper.hpp:23
bool operator==(const numa_segment &b) const
Definition: mem_mapper.hpp:27
Definition: mem_mapper.hpp:8
int owner
Definition: mem_mapper.hpp:9
bool operator==(const segment &b) const
Definition: mem_mapper.hpp:14
bool operator!=(const segment &b) const
Definition: mem_mapper.hpp:17
std::size_t pm_offset
Definition: mem_mapper.hpp:12
std::size_t offset_e
Definition: mem_mapper.hpp:11
std::size_t offset_b
Definition: mem_mapper.hpp:10