Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring of buffer handling system #324

Merged
merged 21 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e93a0b8
added new buffer class and unit tests
JoMee Sep 17, 2024
c7704c4
removed old buffer handler and integrated my buffer handler into ippl…
JoMee Sep 22, 2024
7a37e85
changed debug-name for get buffer method
JoMee Sep 22, 2024
797f900
changed interface to allow for logging wrapper class with unit tests
JoMee Sep 29, 2024
18b8fca
made methods for size public
JoMee Oct 3, 2024
3aa99f9
made bufferhandler more readable
JoMee Oct 3, 2024
af35761
run tests for every memory space
JoMee Oct 3, 2024
ee7a8f6
added loggingbufferhandler and unit tests for it
JoMee Oct 4, 2024
0963571
implemented logging of bufferhandler
JoMee Oct 14, 2024
6f59a03
removed unnecessary comments
JoMee Oct 14, 2024
c5221d2
attempted fix for memory space issue
JoMee Oct 28, 2024
edeb450
attempted fix for memory space issue for logging as well
JoMee Oct 28, 2024
bb2171d
made communication for logging run over the communicator class and fi…
JoMee Oct 28, 2024
79bfe25
added freeing of memory after communication in ffropenpoissonsolver a…
JoMee Oct 28, 2024
daf23b9
removed parameters that were used for tag computation and aren't used…
JoMee Oct 28, 2024
a6ff99c
made file name variable and made print function work with the Inform …
JoMee Nov 4, 2024
8bfbe92
added hpp file for implementation of bufferhandler and added document…
JoMee Nov 4, 2024
6f8ec5a
updated documentation for interface of buffer handler.
JoMee Nov 4, 2024
6689800
separated loggingbufferhandler into h and hpp file and added document…
JoMee Nov 4, 2024
10ecd87
incorporated suggested changes into PR
JoMee Nov 6, 2024
50ab147
Renamed BufferHandler and added comments for tests
JoMee Nov 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions src/Communicate/BufferHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#ifndef IPPL_BUFFER_HANDLER_H
#define IPPL_BUFFER_HANDLER_H

#include <memory>
#include <set>

#include "Communicate/Archive.h"

namespace ippl {

/**
* @brief Interface for memory buffer handling.
*
* Defines methods for acquiring, freeing, and managing memory buffers.
* Implementations are responsible for managing buffers efficiently,
* ensuring that allocated buffers are reused where possible.
*
* @tparam MemorySpace The memory space type used for buffer allocation.
*/
template <typename MemorySpace>
class BufferHandler {
public:
using archive_type = ippl::detail::Archive<MemorySpace>;
using buffer_type = std::shared_ptr<archive_type>;
using size_type = ippl::detail::size_type;

virtual ~BufferHandler() {}

/**
* @brief Requests a memory buffer of a specified size.
*
* Provides a buffer of at least the specified size, with the option
* to allocate additional space based on an overallocation multiplier.
* This function attempts to reuse available buffers if possible.
*
* @param size The required size of the buffer, in bytes.
* @param overallocation A multiplier to allocate extra space, which may help
* avoid frequent reallocation in some use cases.
* @return A shared pointer to the allocated buffer.
*/
virtual buffer_type getBuffer(size_type size, double overallocation) = 0;

/**
* @brief Frees a specified buffer.
*
* Moves the specified buffer to a free state, making it available
* for reuse in future buffer requests.
*
* @param buffer The buffer to be freed.
*/
virtual void freeBuffer(buffer_type buffer) = 0;

/**
* @brief Frees all currently used buffers.
*
* Transfers all used buffers to the free state, making them available
* for reuse. This does not deallocate memory but resets buffer usage.
*/
virtual void freeAllBuffers() = 0;

/**
* @brief Deletes all buffers.
*
* Releases all allocated memory buffers, both used and free.
* After this call, no buffers are available until new allocations.
*/
virtual void deleteAllBuffers() = 0;

/**
* @brief Gets the size of all buffers in use.
*
* @return Total size of buffers that are in use in bytes.
*/
virtual size_type getUsedSize() const = 0;

/**
* @brief Gets the size of all free buffers.
*
* @return Total size of free buffers in bytes.
*/
virtual size_type getFreeSize() const = 0;
};

/**
* @class DefaultBufferHandler
* @brief Concrete implementation of BufferHandler for managing memory buffers.
*
* This class implements the BufferHandler interface, providing concrete behavior for
* buffer allocation, freeing, and memory management. It maintains two sorted sets of free and
* in-use buffers to allow for efficient queries.
*
* @tparam MemorySpace The memory space type for the buffer (e.g., `Kokkos::HostSpace`).
*/
template <typename MemorySpace>
class DefaultBufferHandler : public BufferHandler<MemorySpace> {
public:
using typename BufferHandler<MemorySpace>::archive_type;
using typename BufferHandler<MemorySpace>::buffer_type;
using typename BufferHandler<MemorySpace>::size_type;

~DefaultBufferHandler() override;

/**
* @brief Acquires a buffer of at least the specified size.
*
* Requests a memory buffer of the specified size, with the option
* to request a buffer larger than the base size by an overallocation
* multiplier. If a sufficiently large buffer is available, it is returned. If not, the
* largest free buffer is reallocated. If there are no free buffers available, only then a
* new buffer is allocated.
*
* @param size The required buffer size.
* @param overallocation A multiplier to allocate additional buffer space.
* @return A shared pointer to the allocated buffer.
*/
buffer_type getBuffer(size_type size, double overallocation) override;

/**
* @copydoc BufferHandler::freeBuffer
*/
void freeBuffer(buffer_type buffer) override;

/**
* @copydoc BufferHandler::freeBuffer
*/
void freeAllBuffers() override;

/**
* @copydoc BufferHandler::freeBuffer
*/
void deleteAllBuffers() override;

/**
* @copydoc BufferHandler::freeBuffer
*/
size_type getUsedSize() const override;

/**
* @copydoc BufferHandler::freeBuffer
*/
size_type getFreeSize() const override;

private:
using buffer_comparator_type = bool (*)(const buffer_type&, const buffer_type&);
using buffer_set_type = std::set<buffer_type, buffer_comparator_type>;

static bool bufferSizeComparator(const buffer_type& lhs, const buffer_type& rhs);

bool isBufferUsed(buffer_type buffer) const;
void releaseUsedBuffer(buffer_type buffer);
buffer_type findFreeBuffer(size_type requiredSize);
buffer_set_type::iterator findSmallestSufficientBuffer(size_type requiredSize);
buffer_type getFreeBuffer(buffer_type buffer);
buffer_type reallocateLargestFreeBuffer(size_type requiredSize);
buffer_type allocateNewBuffer(size_type requiredSize);

size_type usedSize_m; ///< Total size of all allocated buffers
size_type freeSize_m; ///< Total size of all free buffers

protected:
buffer_set_type used_buffers{
&DefaultBufferHandler::bufferSizeComparator}; ///< Set of used buffers
buffer_set_type free_buffers{
&DefaultBufferHandler::bufferSizeComparator}; ///< Set of free buffers
};
} // namespace ippl

#include "Communicate/BufferHandler.hpp"

#endif
147 changes: 147 additions & 0 deletions src/Communicate/BufferHandler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#ifndef IPPL_BUFFER_HANDLER_HPP
#define IPPL_BUFFER_HANDLER_HPP

namespace ippl {

template <typename MemorySpace>
DefaultBufferHandler<MemorySpace>::~DefaultBufferHandler() {}

template <typename MemorySpace>
typename DefaultBufferHandler<MemorySpace>::buffer_type DefaultBufferHandler<MemorySpace>::getBuffer(
size_type size, double overallocation) {
size_type requiredSize = static_cast<size_type>(size * overallocation);

auto freeBuffer = findFreeBuffer(requiredSize);
if (freeBuffer != nullptr) {
return getFreeBuffer(freeBuffer);
}

if (!free_buffers.empty()) {
return reallocateLargestFreeBuffer(requiredSize);
}

return allocateNewBuffer(requiredSize);
}

template <typename MemorySpace>
void DefaultBufferHandler<MemorySpace>::freeBuffer(buffer_type buffer) {
if (isBufferUsed(buffer)) {
releaseUsedBuffer(buffer);
}
}

template <typename MemorySpace>
void DefaultBufferHandler<MemorySpace>::freeAllBuffers() {
free_buffers.insert(used_buffers.begin(), used_buffers.end());
used_buffers.clear();

freeSize_m += usedSize_m;
usedSize_m = 0;
}

template <typename MemorySpace>
void DefaultBufferHandler<MemorySpace>::deleteAllBuffers() {
freeSize_m = 0;
usedSize_m = 0;

used_buffers.clear();
free_buffers.clear();
}

template <typename MemorySpace>
typename DefaultBufferHandler<MemorySpace>::size_type DefaultBufferHandler<MemorySpace>::getUsedSize()
const {
return usedSize_m;
}

template <typename MemorySpace>
typename DefaultBufferHandler<MemorySpace>::size_type DefaultBufferHandler<MemorySpace>::getFreeSize() const {
return freeSize_m;
}

template <typename MemorySpace>
bool DefaultBufferHandler<MemorySpace>::bufferSizeComparator(const buffer_type& lhs,
const buffer_type& rhs) {
if (lhs->getBufferSize() != rhs->getBufferSize()) {
return lhs->getBufferSize() < rhs->getBufferSize();
}

// Use memory address as a tie-breaker to enforce total ordering of buffers.
return lhs < rhs;
}

template <typename MemorySpace>
bool DefaultBufferHandler<MemorySpace>::isBufferUsed(buffer_type buffer) const {
return used_buffers.find(buffer) != used_buffers.end();
}

template <typename MemorySpace>
void DefaultBufferHandler<MemorySpace>::releaseUsedBuffer(buffer_type buffer) {
auto it = used_buffers.find(buffer);

usedSize_m -= buffer->getBufferSize();
freeSize_m += buffer->getBufferSize();

used_buffers.erase(it);
free_buffers.insert(buffer);
}

template <typename MemorySpace>
typename DefaultBufferHandler<MemorySpace>::buffer_type DefaultBufferHandler<MemorySpace>::findFreeBuffer(
size_type requiredSize) {
auto it = findSmallestSufficientBuffer(requiredSize);
if (it != free_buffers.end()) {
return *it;
}
return nullptr;
}

template <typename MemorySpace>
typename DefaultBufferHandler<MemorySpace>::buffer_set_type::iterator
DefaultBufferHandler<MemorySpace>::findSmallestSufficientBuffer(size_type requiredSize) {
return std::find_if(free_buffers.begin(), free_buffers.end(),
[requiredSize](const buffer_type& buffer) {
return buffer->getBufferSize() >= requiredSize;
});
}

template <typename MemorySpace>
typename DefaultBufferHandler<MemorySpace>::buffer_type
DefaultBufferHandler<MemorySpace>::getFreeBuffer(buffer_type buffer) {
freeSize_m -= buffer->getBufferSize();
usedSize_m += buffer->getBufferSize();

free_buffers.erase(buffer);
used_buffers.insert(buffer);
return buffer;
}

template <typename MemorySpace>
typename DefaultBufferHandler<MemorySpace>::buffer_type
DefaultBufferHandler<MemorySpace>::reallocateLargestFreeBuffer(size_type requiredSize) {
auto largest_it = std::prev(free_buffers.end());
buffer_type buffer = *largest_it;

freeSize_m -= buffer->getBufferSize();
usedSize_m += requiredSize;

free_buffers.erase(buffer);
buffer->reallocBuffer(requiredSize);

used_buffers.insert(buffer);
return buffer;
}

template <typename MemorySpace>
typename DefaultBufferHandler<MemorySpace>::buffer_type DefaultBufferHandler<MemorySpace>::allocateNewBuffer(
size_type requiredSize) {
buffer_type newBuffer = std::make_shared<archive_type>(requiredSize);

usedSize_m += newBuffer->getBufferSize();
used_buffers.insert(newBuffer);
return newBuffer;
}

} // namespace ippl

#endif
11 changes: 9 additions & 2 deletions src/Communicate/Buffers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,16 @@ namespace ippl {
}

void Communicator::deleteAllBuffers() {
buffers_m.forAll([]<typename Map>(Map&& m) {
m.clear();
buffer_handlers_m.forAll([]<typename BufferHandler>(BufferHandler&& bh) {
bh.deleteAllBuffers();
});
}

void Communicator::freeAllBuffers() {
buffer_handlers_m.forAll([]<typename BufferHandler>(BufferHandler&& bh) {
bh.freeAllBuffers();
});
}

} // namespace mpi
} // namespace ippl
27 changes: 14 additions & 13 deletions src/Communicate/Buffers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,21 @@ namespace ippl {
namespace mpi {

template <typename MemorySpace, typename T>
Communicator::buffer_type<MemorySpace> Communicator::getBuffer(int id, size_type size,
double overallocation) {
auto& buffers = buffers_m.get<MemorySpace>();
size *= sizeof(T);
if (buffers.contains(id)) {
if (buffers[id]->getBufferSize() < size) {
buffers[id]->reallocBuffer(size);
}
return buffers[id];
}
buffers[id] = std::make_shared<archive_type<MemorySpace>>(
(size_type)(size * std::max(overallocation, defaultOveralloc_m)));
return buffers[id];
Communicator::buffer_type<MemorySpace> Communicator::getBuffer(size_type size,
double overallocation) {
auto& buffer_handler = buffer_handlers_m.get<MemorySpace>();

return buffer_handler.getBuffer(size * sizeof(T), std::max(overallocation, defaultOveralloc_m));
}


template <typename MemorySpace>
void Communicator::freeBuffer(Communicator::buffer_type<MemorySpace> buffer) {
auto& buffer_handler = buffer_handlers_m.get<MemorySpace>();

buffer_handler.freeBuffer(buffer);
}

} // namespace mpi

} // namespace ippl
6 changes: 6 additions & 0 deletions src/Communicate/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
set (_SRCS
Communicator.cpp
CommunicatorLogging.cpp
Environment.cpp
Buffers.cpp
Request.cpp
LogEntry.cpp
)

set (_HDRS
LogEntry.h
BufferHandler.h
LoggingBufferHandler.h
Archive.h
Archive.hpp
Buffers.hpp
Expand All @@ -23,6 +28,7 @@ set (_HDRS
Window.h
Window.hpp
PointToPoint.hpp
CommunicatorLogging.hpp
)

include_directories (
Expand Down
Loading