mpd/src/util/HugeAllocator.hxx

237 lines
4.8 KiB
C++
Raw Normal View History

// SPDX-License-Identifier: BSD-2-Clause
// author: Max Kellermann <max.kellermann@gmail.com>
2013-01-04 14:54:49 +01:00
#pragma once
2013-01-04 14:54:49 +01:00
#include "SpanCast.hxx"
2013-01-04 14:54:49 +01:00
#include <cstddef>
#include <span>
#include <utility>
2013-01-04 14:54:49 +01:00
#ifdef __linux__
/**
* Allocate a huge amount of memory. This will be done in a way that
* allows giving the memory back to the kernel as soon as we don't
* need it anymore. On the downside, this call is expensive.
*
* Throws std::bad_alloc on error
*
* @returns the allocated buffer with a size which may be rounded up
* (to the next page size), so callers can take advantage of this
* allocation overhead
2013-01-04 14:54:49 +01:00
*/
std::span<std::byte>
HugeAllocate(size_t size);
2013-01-04 14:54:49 +01:00
/**
* @param p an allocation returned by HugeAllocate()
* @param size the allocation's size as passed to HugeAllocate()
*/
void
2016-06-17 17:51:27 +02:00
HugeFree(void *p, size_t size) noexcept;
2013-01-04 14:54:49 +01:00
2022-04-26 20:18:07 +02:00
/**
* Set a name for the specified virtual memory area.
*
* This feature requires Linux 5.17.
*/
void
HugeSetName(void *p, size_t size, const char *name) noexcept;
/**
* Control whether this allocation is copied to newly forked child
* processes. Disabling that makes forking a little bit cheaper.
*/
void
HugeForkCow(void *p, size_t size, bool enable) noexcept;
2013-01-04 14:54:49 +01:00
/**
* Discard any data stored in the allocation and give the memory back
* to the kernel. After returning, the allocation still exists and
* can be reused at any time, but its contents are undefined.
*
* @param p an allocation returned by HugeAllocate()
* @param size the allocation's size as passed to HugeAllocate()
*/
void
2016-06-17 17:51:27 +02:00
HugeDiscard(void *p, size_t size) noexcept;
2013-01-04 14:54:49 +01:00
#elif defined(_WIN32)
#include <memoryapi.h>
std::span<std::byte>
HugeAllocate(size_t size);
static inline void
2019-08-15 17:57:20 +02:00
HugeFree(void *p, size_t) noexcept
{
VirtualFree(p, 0, MEM_RELEASE);
}
2022-04-26 20:18:07 +02:00
static inline void
HugeSetName(void *, size_t, const char *) noexcept
{
}
static inline void
HugeForkCow(void *, size_t, bool) noexcept
{
}
static inline void
2016-06-17 17:51:27 +02:00
HugeDiscard(void *p, size_t size) noexcept
{
VirtualAlloc(p, size, MEM_RESET, PAGE_NOACCESS);
}
2013-01-04 14:54:49 +01:00
#else
/* not Linux: fall back to standard C calls */
#include <cstdint>
2013-01-04 14:54:49 +01:00
static inline std::span<std::byte>
HugeAllocate(size_t size)
2013-01-04 14:54:49 +01:00
{
return {new std::byte[size], size};
2013-01-04 14:54:49 +01:00
}
static inline void
2016-06-17 17:51:27 +02:00
HugeFree(void *_p, size_t) noexcept
2013-01-04 14:54:49 +01:00
{
auto *p = (std::byte *)_p;
delete[] p;
2013-01-04 14:54:49 +01:00
}
2022-04-26 20:18:07 +02:00
static inline void
HugeSetName(void *, size_t, const char *) noexcept
{
}
static inline void
HugeForkCow(void *, size_t, bool) noexcept
{
}
2013-01-04 14:54:49 +01:00
static inline void
2016-06-17 17:51:27 +02:00
HugeDiscard(void *, size_t) noexcept
2013-01-04 14:54:49 +01:00
{
}
#endif
/**
* Automatic memory management for a dynamic array in "huge" memory.
*/
template<typename T>
class HugeArray {
using Buffer = std::span<T>;
Buffer buffer{nullptr};
public:
typedef typename Buffer::size_type size_type;
typedef typename Buffer::value_type value_type;
typedef typename Buffer::reference reference;
typedef typename Buffer::const_reference const_reference;
typedef typename Buffer::iterator iterator;
constexpr HugeArray() = default;
explicit HugeArray(size_type _size)
:buffer(FromBytesFloor<value_type>(HugeAllocate(sizeof(value_type) * _size))) {}
2019-05-08 22:32:50 +02:00
constexpr HugeArray(HugeArray &&other) noexcept
:buffer(std::exchange(other.buffer, nullptr)) {}
2019-05-08 22:32:50 +02:00
~HugeArray() noexcept {
if (!buffer.empty()) {
auto v = std::as_writable_bytes(buffer);
HugeFree(v.data(), v.size());
}
}
2019-05-08 22:32:50 +02:00
HugeArray &operator=(HugeArray &&other) noexcept {
2019-05-08 22:33:41 +02:00
using std::swap;
swap(buffer, other.buffer);
return *this;
}
2022-04-26 20:18:07 +02:00
void SetName(const char *name) noexcept {
const auto v = std::as_writable_bytes(buffer);
HugeSetName(v.data(), v.size(), name);
2022-04-26 20:18:07 +02:00
}
void ForkCow(bool enable) noexcept {
const auto v = std::as_writable_bytes(buffer);
HugeForkCow(v.data(), v.size(), enable);
}
void Discard() noexcept {
const auto v = std::as_writable_bytes(buffer);
HugeDiscard(v.data(), v.size());
}
2019-05-08 22:32:50 +02:00
constexpr bool operator==(std::nullptr_t) const noexcept {
return buffer == nullptr;
}
2019-05-08 22:32:50 +02:00
constexpr bool operator!=(std::nullptr_t) const noexcept {
return buffer != nullptr;
}
/**
* Returns the number of allocated elements.
*/
2019-05-08 22:32:50 +02:00
constexpr size_type size() const noexcept {
return buffer.size();
}
2019-05-08 22:32:50 +02:00
reference front() noexcept {
return buffer.front();
}
2019-05-08 22:32:50 +02:00
const_reference front() const noexcept {
return buffer.front();
}
2019-05-08 22:32:50 +02:00
reference back() noexcept {
return buffer.back();
}
2019-05-08 22:32:50 +02:00
const_reference back() const noexcept {
return buffer.back();
}
/**
* Returns one element. No bounds checking.
*/
2019-05-08 22:32:50 +02:00
reference operator[](size_type i) noexcept {
return buffer[i];
}
/**
* Returns one constant element. No bounds checking.
*/
2019-05-08 22:32:50 +02:00
const_reference operator[](size_type i) const noexcept {
return buffer[i];
}
2019-05-08 22:32:50 +02:00
iterator begin() noexcept {
return buffer.begin();
}
constexpr auto begin() const noexcept {
return buffer.begin();
}
2019-05-08 22:32:50 +02:00
iterator end() noexcept {
return buffer.end();
}
constexpr auto end() const noexcept {
return buffer.end();
}
};