c++申请一段大虚拟内存,然后在使用时再映射物理内存,以获得高性能数组
#include <cstdint>
#include <iostream>
#include <numeric>
#ifdef _WIN32
#include <windows.h>
#else
#include <errno.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
void* reserveVirtualMemory(void* address, size_t size)
{
#ifdef _WIN32
return VirtualAlloc(static_cast<LPVOID>(address), size, MEM_RESERVE, PAGE_READWRITE);
#else
auto addr = mmap(address, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr == MAP_FAILED)
{
return nullptr;
}
return addr;
#endif
}
void* mapPhysicalMemory(void* address, size_t size)
{
#ifdef _WIN32
return VirtualAlloc(static_cast<LPVOID>(address), size, MEM_COMMIT, PAGE_READWRITE);
#else
auto retCode = mprotect(address, size, PROT_READ | PROT_WRITE);
if (retCode == -1)
{
return nullptr;
}
return address;
#endif
}
bool freeVirtualMemory(void* address, size_t size)
{
#ifdef _WIN32
return VirtualFree(address, 0, MEM_RELEASE) != 0;
#else
return munmap(address, size) != -1;
#endif
}
int getSystemPageSize()
{
#ifdef _WIN32
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
return static_cast<int>(systemInfo.dwPageSize);
#else
return sysconf(_SC_PAGE_SIZE);
#endif
}
int64_t getPhysicalMemorySize()
{
int64_t total = -1;
#ifdef _WIN32
MEMORYSTATUSEX memoryStatus;
memoryStatus.dwLength = sizeof(memoryStatus);
if (GlobalMemoryStatusEx(&memoryStatus))
{
total = memoryStatus.ullTotalPhys;
}
#else
long physPages = sysconf(_SC_PHYS_PAGES);
long pageSize = sysconf(_SC_PAGE_SIZE);
total = physPages * pageSize;
#endif
return total;
}
template <typename T>
class MemoryPage
{
public:
MemoryPage() = default;
bool init(size_t itemCount)
{
int64_t memSize = getPhysicalMemorySize();
size_t capacity = memSize / sizeof(T);
if (itemCount > capacity)
{
m_itemCount = capacity;
}
else
{
m_itemCount = itemCount;
}
const int commitPages = 2048; // commit 8M one time
int sysPageSize = getSystemPageSize();
m_commitSize = sysPageSize * commitPages;
m_commitObjects = m_commitSize / sizeof(T);
size_t total = m_itemCount * sizeof(T);
if (total % m_commitSize == 0)
{
m_size = total;
}
else
{
m_size = (total / m_commitSize + 1) * m_commitSize;
}
m_base = static_cast<T*>(reserveVirtualMemory(nullptr, m_size));
[[maybe_unused]] auto addr = static_cast<T*>(mapPhysicalMemory(static_cast<void*>(m_base), m_commitSize));
assert(addr == m_base);
if (!m_base)
{
PSGM_LOG_FATAL("Allocating virtual memory failed, size: " << m_size);
return false;
}
return true;
}
~MemoryPage()
{
m_maxIndex = -1;
if (!m_base)
{
return;
}
if (!freeVirtualMemory(m_base, m_size))
{
// Whether to provide an option to decide whether or not to abort?
// PSGM_LOG_FATAL("Release memory failed.");
abort();
}
m_base = nullptr;
}
/**
* @brief Check if memory page is valid.
* @return True if memory page is valid, false otherwise.
*/
bool good() const
{
return m_base != nullptr;
}
/**
* @brief Get item in memory page.
* @param[in] index Index of the item.
* @return Return pointer to the item on success, otherwise nullptr.
*/
T get(int32_t index) const
{
return m_base[index];
}
/**
* @brief Set item in memory page.
* @param[in] index Index of the item.
* @param[in] value Value of the item.
* @return If successful, return true, otherwise return false.
*/
bool set(int32_t index, T value)
{
m_maxIndex = index > m_maxIndex ? index : m_maxIndex;
if (index / m_commitObjects > m_commitCount)
{
void* pageAddress = reinterpret_cast<char*>(m_base) + index * sizeof(T) / m_commitSize * m_commitSize;
void* result = mapPhysicalMemory(pageAddress, m_commitSize);
if (!result)
{
return false;
}
++m_commitCount;
}
m_base[index] = value;
return true;
}
/**
* @brief Gets the number of elements that can be stored in memory page.
* @return The number of elements that can be stored in memory page.
*/
size_t capacity() const
{
return m_itemCount;
}
/**
* @brief Get size of the table.
* @return Size of the table.
*/
int32_t size() const
{
return m_maxIndex + 1;
}
private:
T* m_base{nullptr};
size_t m_itemCount{0};
size_t m_size{0}; // memory size
int32_t m_maxIndex{-1}; // max index
size_t m_commitSize{0};
size_t m_commitObjects{0};
int m_commitCount{0};
};