Itoyori  v0.0.1
unique_file_ptr.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include "ityr/common/util.hpp"
4 #include "ityr/ito/ito.hpp"
5 #include "ityr/ori/ori.hpp"
7 
8 namespace ityr {
9 
10 /*
11  * @brief Unique pointer for an mmaped file.
12  *
13  * This class manages a virtual address space mapped to a file on the disk.
14  * The specified file is opened and mapped to the same virtual address at construction,
15  * and closed at destruction.
16  * The pointer to the mmapped file (which can be obtained via `get()` method) can be uniformly used
17  * across different processes.
18  *
19  * The constructor and destructor must be called collectively by all processes
20  * (i.e., in the SPMD region or in the root thread).
21  * At construction, the same virtual address space is allocated among all processes and
22  * the file content is directly mapped to the virtual address (via `mmap()`).
23  *
24  * Currently, only the read-only mode is supported.
25  *
26  * @see `ityr::make_unique_ptr()`
27  */
28 template <typename T>
30 public:
31  using element_type = T;
33  using reference = std::add_lvalue_reference_t<element_type>;
34 
35  constexpr unique_file_ptr() noexcept {}
36 
37  explicit unique_file_ptr(const std::string& fpath, bool mlock = false)
38  : ptr_(reinterpret_cast<pointer>(alloc_coll(fpath, mlock))) {
39  ITYR_CHECK(ori::file_mem_get(ptr_).size() % sizeof(T) == 0);
40  }
41 
42  unique_file_ptr(const unique_file_ptr&) = delete;
44 
45  unique_file_ptr(std::nullptr_t) noexcept {}
46  unique_file_ptr& operator=(std::nullptr_t) noexcept {
47  destroy();
48  ptr_ = nullptr;
49  return *this;
50  }
51 
53  : ptr_(ufp.ptr_) { ufp.ptr_ = nullptr; }
55  destroy();
56  ptr_ = ufp.ptr_;
57  ufp.ptr_ = nullptr;
58  return *this;
59  }
60 
61  template <typename U>
63  : ptr_(ufp.ptr_) { ufp.ptr_ = nullptr; }
64  template <typename U>
66  destroy();
67  ptr_ = ufp.ptr_;
68  ufp.ptr_ = nullptr;
69  return *this;
70  }
71 
72  ~unique_file_ptr() { destroy(); }
73 
74  explicit operator bool() const noexcept {
75  return ptr_;
76  }
77 
78  pointer get() const noexcept {
79  return ptr_;
80  }
81 
82  std::size_t size() const noexcept {
83  auto&& fm = ori::file_mem_get(ptr_);
84  return fm.size() / sizeof(T);
85  }
86 
87  reference operator*() const {
88  ITYR_CHECK(get() != nullptr);
89  return *get();
90  }
91 
92  reference operator[](std::size_t i) const {
93  ITYR_CHECK(get() != nullptr);
94  ITYR_CHECK(i <= size());
95  return *(get() + i);
96  }
97 
98  pointer operator->() const noexcept {
99  return get();
100  }
101 
102  pointer release() noexcept {
103  pointer p = ptr_;
104  ptr_ = nullptr;
105  return p;
106  }
107 
109  std::swap(ptr_, ufp.ptr_);
110  }
111 
112 private:
113  void destroy() {
114  if (ptr_) {
115  free_coll(ptr_);
116  }
117  }
118 
119  static void* alloc_coll(const std::string& fpath, bool mlock) {
120  if (ito::is_spmd()) {
121  return ori::file_mem_alloc_coll(fpath, mlock);
122  } else if (ito::is_root()) {
123  // FIXME: ugly hack to pass heap-allocated string to other processes
124  constexpr std::size_t max_chars = 256;
125  if (fpath.size() >= max_chars) {
126  common::die("File path length for unique_file_ptr must be less than %ld.", max_chars);
127  }
128  std::array<char, max_chars> buf;
129  std::strncpy(buf.data(), fpath.c_str(), max_chars - 1);
130  buf.back() = '\0';
131  return ito::coll_exec([=] {
132  return ori::file_mem_alloc_coll(buf.data(), mlock);
133  });
134  } else {
135  common::die("Collective operations for ityr::global_vector must be executed on the root thread or SPMD region.");
136  }
137  }
138 
139  static void free_coll(void* addr) {
140  if (ito::is_spmd()) {
142  } else if (ito::is_root()) {
143  ito::coll_exec([=] { ori::file_mem_free_coll(addr); });
144  } else {
145  common::die("Collective operations for ityr::global_vector must be executed on the root thread or SPMD region.");
146  }
147  }
148 
149  pointer ptr_ = nullptr;
150 };
151 
152 template <typename T1, typename T2>
153 inline bool operator==(const unique_file_ptr<T1>& ufp1, const unique_file_ptr<T2>& ufp2) {
154  return ufp1.get() == ufp2.get();
155 }
156 
157 template <typename T>
158 inline bool operator==(const unique_file_ptr<T>& ufp, std::nullptr_t) {
159  return ufp.get() == nullptr;
160 }
161 
162 template <typename T>
163 inline bool operator==(std::nullptr_t, const unique_file_ptr<T>& ufp) {
164  return nullptr == ufp.get();
165 }
166 
167 template <typename T1, typename T2>
168 inline bool operator!=(const unique_file_ptr<T1>& ufp1, const unique_file_ptr<T2>& ufp2) {
169  return ufp1.get() != ufp2.get();
170 }
171 
172 template <typename T>
173 inline bool operator!=(const unique_file_ptr<T>& ufp, std::nullptr_t) {
174  return ufp.get() != nullptr;
175 }
176 
177 template <typename T>
178 inline bool operator!=(std::nullptr_t, const unique_file_ptr<T>& ufp) {
179  return nullptr != ufp.get();
180 }
181 
182 template <typename T1, typename T2>
183 inline bool operator>(const unique_file_ptr<T1>& ufp1, const unique_file_ptr<T2>& ufp2) {
184  return ufp1.get() > ufp2.get();
185 }
186 
187 template <typename T>
188 inline bool operator>(const unique_file_ptr<T>& ufp, std::nullptr_t) {
189  return ufp.get() > nullptr;
190 }
191 
192 template <typename T>
193 inline bool operator>(std::nullptr_t, const unique_file_ptr<T>& ufp) {
194  return nullptr > ufp.get();
195 }
196 
197 template <typename T1, typename T2>
198 inline bool operator>=(const unique_file_ptr<T1>& ufp1, const unique_file_ptr<T2>& ufp2) {
199  return ufp1.get() >= ufp2.get();
200 }
201 
202 template <typename T>
203 inline bool operator>=(const unique_file_ptr<T>& ufp, std::nullptr_t) {
204  return ufp.get() >= nullptr;
205 }
206 
207 template <typename T>
208 inline bool operator>=(std::nullptr_t, const unique_file_ptr<T>& ufp) {
209  return nullptr >= ufp.get();
210 }
211 
212 template <typename T1, typename T2>
213 inline bool operator<(const unique_file_ptr<T1>& ufp1, const unique_file_ptr<T2>& ufp2) {
214  return ufp1.get() < ufp2.get();
215 }
216 
217 template <typename T>
218 inline bool operator<(const unique_file_ptr<T>& ufp, std::nullptr_t) {
219  return ufp.get() < nullptr;
220 }
221 
222 template <typename T>
223 inline bool operator<(std::nullptr_t, const unique_file_ptr<T>& ufp) {
224  return nullptr < ufp.get();
225 }
226 
227 template <typename T1, typename T2>
228 inline bool operator<=(const unique_file_ptr<T1>& ufp1, const unique_file_ptr<T2>& ufp2) {
229  return ufp1.get() <= ufp2.get();
230 }
231 
232 template <typename T>
233 inline bool operator<=(const unique_file_ptr<T>& ufp, std::nullptr_t) {
234  return ufp.get() <= nullptr;
235 }
236 
237 template <typename T>
238 inline bool operator<=(std::nullptr_t, const unique_file_ptr<T>& ufp) {
239  return nullptr <= ufp.get();
240 }
241 
242 template <typename T>
243 inline void swap(unique_file_ptr<T> ufp1, unique_file_ptr<T> ufp2) {
244  ufp1.swap(ufp2);
245 }
246 
247 template <typename CharT, typename Traits, typename T>
248 inline std::basic_ostream<CharT, Traits>&
249 operator<<(std::basic_ostream<CharT, Traits>& ostream, const unique_file_ptr<T>& ufp) {
250  ostream << ufp.get();
251  return ostream;
252 }
253 
254 /*
255  * @brief Create a unique pointer for an mmaped file.
256  *
257  * @see `ityr::unique_file_ptr`
258  */
259 template <typename T>
260 inline unique_file_ptr<T> make_unique_file(const std::string& fpath, bool mlock = false) {
261  return unique_file_ptr<T>(fpath, mlock);
262 }
263 
264 ITYR_TEST_CASE("[ityr::unique_file_ptr] unique_file_ptr") {
265  ito::init();
266  ori::init();
267 
269 
270  long n = 100000;
271  std::string filename = "test.bin";
272 
273  if (my_rank == 0) {
274  std::vector<long> buf(n);
275  for (long i = 0; i < n; i++) {
276  buf[i] = i;
277  }
278  std::ofstream ostream(filename, std::ios::binary);
279  ostream.write(reinterpret_cast<char*>(buf.data()), buf.size() * sizeof(long));
280  }
281 
283 
284  ITYR_SUBCASE("spmd") {
285  unique_file_ptr<long> fp = make_unique_file<long>(filename);
286  ITYR_CHECK(fp.size() == n);
287 
288  for (long i = 0; i < n; i++) {
289  ITYR_CHECK(fp[i] == i);
290  }
291 
293 
294  fp = {}; // destroy;
295  }
296 
297  ITYR_SUBCASE("root exec") {
298  ito::root_exec([&] {
299  unique_file_ptr<long> fp = make_unique_file<long>(filename);
300  ITYR_CHECK(fp.size() == n);
301 
302  for_each(
303  execution::parallel_policy(100),
304  count_iterator<long>(0),
305  count_iterator<long>(n),
306  fp.get(),
307  [](long i, long v) { ITYR_CHECK(v == i); });
308  });
309  }
310 
312 
313  if (my_rank == 0) {
314  remove(filename.c_str());
315  }
316 
317  ori::fini();
318  ito::fini();
319 }
320 
321 }
Definition: unique_file_ptr.hpp:29
void swap(unique_file_ptr< T > ufp)
Definition: unique_file_ptr.hpp:108
unique_file_ptr(const unique_file_ptr &)=delete
T element_type
Definition: unique_file_ptr.hpp:31
pointer operator->() const noexcept
Definition: unique_file_ptr.hpp:98
std::size_t size() const noexcept
Definition: unique_file_ptr.hpp:82
element_type * pointer
Definition: unique_file_ptr.hpp:32
unique_file_ptr & operator=(unique_file_ptr &&ufp) noexcept
Definition: unique_file_ptr.hpp:54
reference operator*() const
Definition: unique_file_ptr.hpp:87
unique_file_ptr(unique_file_ptr &&ufp) noexcept
Definition: unique_file_ptr.hpp:52
unique_file_ptr(const std::string &fpath, bool mlock=false)
Definition: unique_file_ptr.hpp:37
unique_file_ptr(unique_file_ptr< U > &&ufp) noexcept
Definition: unique_file_ptr.hpp:62
std::add_lvalue_reference_t< element_type > reference
Definition: unique_file_ptr.hpp:33
pointer release() noexcept
Definition: unique_file_ptr.hpp:102
constexpr unique_file_ptr() noexcept
Definition: unique_file_ptr.hpp:35
unique_file_ptr & operator=(std::nullptr_t) noexcept
Definition: unique_file_ptr.hpp:46
pointer get() const noexcept
Definition: unique_file_ptr.hpp:78
~unique_file_ptr()
Definition: unique_file_ptr.hpp:72
unique_file_ptr & operator=(unique_file_ptr< U > &&ufp) noexcept
Definition: unique_file_ptr.hpp:65
unique_file_ptr & operator=(const unique_file_ptr &)=delete
reference operator[](std::size_t i) const
Definition: unique_file_ptr.hpp:92
unique_file_ptr(std::nullptr_t) noexcept
Definition: unique_file_ptr.hpp:45
#define ITYR_SUBCASE(name)
Definition: util.hpp:41
#define ITYR_CHECK(cond)
Definition: util.hpp:48
MPI_Comm mpicomm()
Definition: topology.hpp:206
rank_t my_rank()
Definition: topology.hpp:207
void mpi_barrier(MPI_Comm comm)
Definition: mpi_util.hpp:42
void fini()
Definition: ito.hpp:45
auto root_exec(Fn &&fn, Args &&... args)
Definition: ito.hpp:50
auto coll_exec(const Fn &fn, const Args &... args)
Definition: ito.hpp:72
void init(MPI_Comm comm=MPI_COMM_WORLD)
Definition: ito.hpp:41
bool is_root()
Definition: ito.hpp:66
bool is_spmd()
Definition: ito.hpp:61
void fini()
Definition: ori.hpp:49
void init(MPI_Comm comm=MPI_COMM_WORLD)
Definition: ori.hpp:45
file_mem & file_mem_get(void *addr)
Definition: ori.hpp:252
void file_mem_free_coll(void *addr)
Definition: ori.hpp:248
void * file_mem_alloc_coll(const std::string &fpath, bool mlock)
Definition: ori.hpp:244
Definition: allocator.hpp:16
rank_t my_rank()
Return the rank of the process running the current thread.
Definition: ityr.hpp:99
bool operator==(const global_vector< T > &x, const global_vector< T > &y)
Definition: global_vector.hpp:669
bool operator<=(const unique_file_ptr< T1 > &ufp1, const unique_file_ptr< T2 > &ufp2)
Definition: unique_file_ptr.hpp:228
bool operator!=(const global_vector< T > &x, const global_vector< T > &y)
Definition: global_vector.hpp:676
unique_file_ptr< T > make_unique_file(const std::string &fpath, bool mlock=false)
Definition: unique_file_ptr.hpp:260
void for_each(const ExecutionPolicy &policy, ForwardIterator first, ForwardIterator last, Op op)
Apply an operator to each element in a range.
Definition: parallel_loop.hpp:136
std::basic_ostream< CharT, Traits > & operator<<(std::basic_ostream< CharT, Traits > &ostream, const unique_file_ptr< T > &ufp)
Definition: unique_file_ptr.hpp:249
void swap(global_vector< T > &v1, global_vector< T > &v2) noexcept
Definition: global_vector.hpp:664
bool operator>=(const unique_file_ptr< T1 > &ufp1, const unique_file_ptr< T2 > &ufp2)
Definition: unique_file_ptr.hpp:198
bool operator<(const unique_file_ptr< T1 > &ufp1, const unique_file_ptr< T2 > &ufp2)
Definition: unique_file_ptr.hpp:213
bool operator>(const unique_file_ptr< T1 > &ufp1, const unique_file_ptr< T2 > &ufp2)
Definition: unique_file_ptr.hpp:183
void swap(unique_file_ptr< T > ufp1, unique_file_ptr< T > ufp2)
Definition: unique_file_ptr.hpp:243