如何无拷贝从mmap内存块构造std::vector<boost::dynamic_bitset<X>>及反向操作
无拷贝构造
std::vector<boost::dynamic_bitset<X>>及获取uint8*缓冲区方案 一、无拷贝从mmap内存构造std::vector<boost::dynamic_bitset<X>>
要实现无拷贝,核心是让每个boost::dynamic_bitset<X>直接复用mmap映射的内存块,而非分配新内存后拷贝数据。这需要借助自定义分配器,让分配器直接返回mmap内存中的对应地址,避免内存分配与拷贝。
步骤1:实现自定义分配器
这个分配器不会实际分配内存,而是从mmap的内存块中返回预分配的区域,且不负责内存释放(mmap内存由外部管理):
#include <boost/dynamic_bitset.hpp> #include <vector> #include <cstring> #include <cassert> template <typename Block> struct MmapAllocator { using value_type = Block; Block* m_base; size_t& m_used_blocks; // 跟踪已使用的Block数量 // 构造函数:传入mmap内存的起始地址和计数器引用 MmapAllocator(Block* base, size_t& used_blocks) : m_base(base), m_used_blocks(used_blocks) {} // 分配器拷贝构造(用于容器元素构造) template <typename U> MmapAllocator(const MmapAllocator<U>& other) noexcept : m_base(other.m_base), m_used_blocks(other.m_used_blocks) {} // 分配内存:直接返回mmap中未使用的区域 Block* allocate(size_t n) { Block* ptr = m_base + m_used_blocks; m_used_blocks += n; return ptr; } // 释放内存:空实现,因为内存由mmap管理 void deallocate(Block* ptr, size_t n) noexcept {} }; // 分配器相等性判断(容器要求) template <typename Block1, typename Block2> bool operator==(const MmapAllocator<Block1>& a, const MmapAllocator<Block2>& b) noexcept { return a.m_base == b.m_base && &a.m_used_blocks == &b.m_used_blocks; } template <typename Block1, typename Block2> bool operator!=(const MmapAllocator<Block1>& a, const MmapAllocator<Block2>& b) noexcept { return !(a == b); }
步骤2:用自定义分配器构造vector
假设你已经通过mmap获取了内存指针uint8_t* mmap_ptr和内存大小size_t mmap_size,每个dynamic_bitset的位数为BITS_PER_BITSET:
using Block = X; // X是你的模板参数,比如uint64_t const size_t BITS_PER_BLOCK = sizeof(Block) * 8; const size_t BLOCKS_PER_BITSET = (BITS_PER_BITSET + BITS_PER_BLOCK - 1) / BITS_PER_BLOCK; const size_t TOTAL_BLOCKS = mmap_size / sizeof(Block); const size_t NUM_BITSETS = TOTAL_BLOCKS / BLOCKS_PER_BITSET; // 确保mmap内存对齐且大小匹配,否则会有越界风险 assert(mmap_size % sizeof(Block) == 0); assert(TOTAL_BLOCKS % BLOCKS_PER_BITSET == 0); // 转换为Block类型指针(mmap内存需按Block对齐) Block* block_base = reinterpret_cast<Block*>(mmap_ptr); size_t used_blocks = 0; // 构造vector,每个元素直接复用mmap内存 std::vector<boost::dynamic_bitset<Block, MmapAllocator<Block>>> bitsets; bitsets.reserve(NUM_BITSETS); for (size_t i = 0; i < NUM_BITSETS; ++i) { // 构造bitset,指定位数和自定义分配器 bitsets.emplace_back(BITS_PER_BITSET, MmapAllocator<Block>(block_base, used_blocks)); }
注意事项:
- mmap内存必须按
Block类型对齐,否则reinterpret_cast会导致未定义行为。 - mmap的总大小必须恰好等于
NUM_BITSETS * BLOCKS_PER_BITSET * sizeof(Block),避免内存越界。
二、从std::vector<boost::dynamic_bitset<X>>获取uint8*缓冲区
场景1:vector由自定义分配器构造(复用mmap内存)
此时所有dynamic_bitset的底层存储是连续的mmap内存块,直接返回原mmap指针即可:
uint8_t* buffer = mmap_ptr; // 直接使用最初的mmap指针
如果需要从vector本身获取,可以通过第一个元素的分配器拿到基地址(需确保所有元素使用同一分配器):
const auto& first_alloc = bitsets[0].get_allocator(); uint8_t* buffer = reinterpret_cast<uint8_t*>(first_alloc.m_base);
场景2:vector为普通构造(每个bitset独立分配内存)
此时每个dynamic_bitset的底层存储分散,必须将所有bitset的数据拷贝到连续内存块中:
using Block = X; size_t total_bytes = 0; // 计算总所需字节数 for (const auto& bs : bitsets) { total_bytes += (bs.size() + 7) / 8; } // 分配连续缓冲区 uint8_t* buffer = new uint8_t[total_bytes]; uint8_t* current_ptr = buffer; for (const auto& bs : bitsets) { const size_t num_blocks = bs.num_blocks(); std::vector<Block> temp_blocks; temp_blocks.reserve(num_blocks); // 导出bitset的底层块数据 bs.to_block_range(std::back_inserter(temp_blocks)); // 拷贝到连续缓冲区 const size_t copy_size = num_blocks * sizeof(Block); std::memcpy(current_ptr, temp_blocks.data(), copy_size); current_ptr += copy_size; } // 使用buffer后记得释放 // delete[] buffer;
注意:to_block_range会导出bitset的所有底层块,包括最后一个块中未使用的高位(这些位的值与原bitset存储一致)。
内容的提问来源于stack exchange,提问作者Venkata Subbarao
相关产品推荐
相关产品推荐

