Skip to content

Commit 87ead7d

Browse files
committed
Add prototype for shared allocator
1 parent 879dc69 commit 87ead7d

File tree

4 files changed

+118
-8
lines changed

4 files changed

+118
-8
lines changed

c++/mpi/allocator.hpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) 2023 Simons Foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0.txt
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// Authors: Philipp Dumitrescu, Olivier Parcollet, Nils Wentzell
16+
17+
#pragma once
18+
#include "mpi.hpp"
19+
#include <algorithm>
20+
#include <memory>
21+
22+
namespace mpi {
23+
24+
template <class T>
25+
class shared_allocator {
26+
shared_communicator shm = communicator{}.split_shared();
27+
std::shared_ptr<std::vector<shared_window<T>>> blocks = std::make_shared<std::vector<shared_window<T>>>();
28+
public:
29+
using value_type = T;
30+
31+
shared_allocator() = default;
32+
explicit shared_allocator(shared_communicator const &shm) noexcept : shm{shm} {}
33+
34+
[[nodiscard]] shared_communicator get_communicator() { return shm; }
35+
36+
[[nodiscard]] auto get_window(T *p) {
37+
return std::find_if(blocks->begin(), blocks->end(), [p](shared_window<T> const &win) {
38+
return win.base(0) == p;
39+
});
40+
}
41+
42+
[[nodiscard]] T* allocate(std::size_t n) {
43+
shared_window<T> &win = blocks->emplace_back(shm, shm.rank() == 0 ? n : 0);
44+
return win.base(0);
45+
}
46+
47+
void deallocate(T *p, std::size_t) {
48+
blocks->erase(std::remove_if(blocks->begin(), blocks->end(), [p](shared_window<T> const &win) {
49+
return win.base(0) == p;
50+
}), blocks->end());
51+
}
52+
};
53+
54+
} // namespace mpi

c++/mpi/mpi.hpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <complex>
2424
#include <type_traits>
2525
#include <algorithm>
26+
#include <utility>
2627
#include <unistd.h>
2728

2829
/// Library namespace
@@ -364,9 +365,15 @@ namespace mpi {
364365
public:
365366
window() = default;
366367
window(window const&) = delete;
367-
window(window &&) = delete;
368+
window(window &&other) noexcept : win{std::exchange(other.win, MPI_WIN_NULL)} {}
368369
window& operator=(window const&) = delete;
369-
window& operator=(window &&) = delete;
370+
window& operator=(window &&rhs) noexcept {
371+
if (this != std::addressof(rhs)) {
372+
this->free();
373+
this->win = std::exchange(rhs.win, MPI_WIN_NULL);
374+
}
375+
return *this;
376+
}
370377

371378
/// Create a window over an existing local memory buffer
372379
explicit window(communicator &c, BaseType *base, MPI_Aint size = 0) noexcept {
@@ -379,15 +386,17 @@ namespace mpi {
379386
MPI_Win_allocate(size * sizeof(BaseType), alignof(BaseType), MPI_INFO_NULL, c.get(), &baseptr, &win);
380387
}
381388

382-
~window() {
389+
~window() { free(); }
390+
391+
explicit operator MPI_Win() const noexcept { return win; };
392+
explicit operator MPI_Win*() noexcept { return &win; };
393+
394+
void free() noexcept {
383395
if (win != MPI_WIN_NULL) {
384396
MPI_Win_free(&win);
385397
}
386398
}
387399

388-
explicit operator MPI_Win() const noexcept { return win; };
389-
explicit operator MPI_Win*() noexcept { return &win; };
390-
391400
/// Synchronization routine in active target RMA. It opens and closes an access epoch.
392401
void fence(int assert = 0) const noexcept {
393402
MPI_Win_fence(assert, win);
@@ -474,8 +483,10 @@ namespace mpi {
474483
template <class BaseType>
475484
class shared_window : public window<BaseType> {
476485
public:
486+
shared_window() = default;
487+
477488
/// Create a window and allocate memory for a shared memory buffer
478-
shared_window(shared_communicator& c, MPI_Aint size) noexcept {
489+
explicit shared_window(shared_communicator& c, MPI_Aint size) noexcept {
479490
void* baseptr = nullptr;
480491
MPI_Win_allocate_shared(size * sizeof(BaseType), alignof(BaseType), MPI_INFO_NULL, c.get(), &baseptr, &(this->win));
481492
}

test/c++/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ file(GLOB_RECURSE all_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
1010
# List of all no mpi tests
1111
file(GLOB_RECURSE nompi_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
1212
# remove custom mpi test, as this one explicitly uses MPI
13-
list(REMOVE_ITEM nompi_tests mpi_custom.cpp mpi_monitor.cpp mpi_window.cpp)
13+
list(REMOVE_ITEM nompi_tests mpi_custom.cpp mpi_monitor.cpp mpi_window.cpp mpi_allocator.cpp)
1414

1515
# ========= OpenMP Dependency ==========
1616

test/c++/mpi_allocator.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) 2023 Simons Foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0.txt
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// Authors: Philipp Dumitrescu, Olivier Parcollet, Nils Wentzell
16+
17+
#include <mpi/mpi.hpp>
18+
#include <mpi/allocator.hpp>
19+
#include <mpi/vector.hpp>
20+
#include <gtest/gtest.h>
21+
#include <numeric>
22+
23+
TEST(MPI_Allocator, SharedAllocator) {
24+
mpi::shared_allocator<int> alloc;
25+
int *p = alloc.allocate(1);
26+
alloc.deallocate(p, 1);
27+
}
28+
29+
TEST(MPI_Allocator, SharedAllocatorVector) {
30+
std::vector<int, mpi::shared_allocator<int>> v(128);
31+
auto shm = v.get_allocator().get_communicator();
32+
33+
// Fill the vector in parallel
34+
v.get_allocator().get_window(v.data())->fence();
35+
auto slice = itertools::chunk_range(0, v.size(), shm.size(), shm.rank());
36+
for (auto i = slice.first; i < slice.second; ++i) {
37+
v.at(i) = i;
38+
}
39+
v.get_allocator().get_window(v.data())->fence();
40+
41+
int const sum = std::accumulate(v.begin(), v.end(), int{0});
42+
EXPECT_EQ(sum, v.size() * (v.size() - 1) / 2);
43+
}
44+
45+
MPI_TEST_MAIN;

0 commit comments

Comments
 (0)