You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无拷贝从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:55:30