【问题标题】:Is there a BOOST pool fixed-sized allocator?是否有 BOOST 池固定大小的分配器?
【发布时间】:2015-03-08 09:52:36
【问题描述】:

我想创建unordered_map(因为我特别想要一个哈希映射)。我想在开始时分配它的最大大小(根据我的限制)。
所以,如果我想分配 256 个条目,每个条目的大小是 1B(只是一个例子。假设 1Byte 包括键和值)。那么我的 unordered_map 键 + 条目的总大小是 256B。我想在分配器中预分配 256B。
然后,当unordered_map 将调用allocate()/deallocate() 时,allocator 将从已分配的内存中为其分配 1B。

typedef boost::unordered::unordered_map<int, MyClass, boost::hash<int>, std::equal_to<MyClass>, ??? > > myMap

在 BOOST 中是否存在?还是其他地方?

----编辑----

正如我所见(感谢此处的答案) - 我的问题有两种解决方案:

  1. 实现一个allocator,其中包含一个boost::pool<>。这个pool 是在allocator 构造函数中构建的。当allocate()unordered_map调用时,它实际上调用了pool.malloc(),而当deallocate()unordered_map调用时,它实际上调用了pool.free()

  2. 使用已经实现的allocator,例如pool_allocator,如下所示:

typedef pool_allocator<std::pair<MyKey, MyClass>, boost::default_user_allocator_new_delete, boost::mutex, 1024 >) MyAllocator;
typedef unordered_map<MyKey, MyClass, hash, eq, MyAllocator> MyUnorderedMap;

我仍然不清楚秒选项,因为:
一种。我可以只声明一个 MyUnorderedMap 吗?
湾。如何在运行时声明一个新的 MyUnorderedMap,其 next_block 大小与 1024 不同?

【问题讨论】:

  • 为什么不能在最后一个 Alloc 模板参数中提供自己的分配器(即来自任何库)?是否有一些限制阻止您这样做?
  • 我可以编写自己的分配器。我认为它已经存在于 BOOST 中,因为它看起来像是一个通用请求。

标签: c++ memory-management boost unordered-map allocator


【解决方案1】:

您所描述的实际上只能通过诸如 Boost Intrusive “地图”(实际上是 sets 然后)之类的东西来实现。

但是,要获得真正的 1B 分配元素,您需要定义 custom stateful value traits,这样您就可以将节点索引元数据与元素有效负载分开存储。

但是,从您声称元素类型为 1B 的事实来看(对于具体的键和值类型,这显然永远不会是真的),我不会假设您实际上出于“某种原因”想要这种人为的解决方案。

相反,让我建议另外三种普通的方法:

  • 使用flat_map
  • 使用 Boost Intrusive 无序集
  • 将无序集与 Boost Pool 固定大小分配器结合使用¹

提升flat_map

如果哈希查找不是强制性的,您可以通过预先保留连续元素存储并改为存储有序映射来简化很多:

Live On Coliru

#include <boost/container/flat_map.hpp>
#include <iostream>

using Elements = boost::container::flat_map<std::string, std::string>;

int main() {
    Elements map;
    map.reserve(256); // pre-allocate 256 "nodes"!

    map.insert({
            { "one",   "Eins"  },
            { "two",   "Zwei"  },
            { "three", "Drei"  },
            { "four",  "Vier"  },
            { "five",  "Fuenf" },
        });

    for (auto& e : map) {
        std::cout << "Entry: " << e.first << " -> " << e.second << "\n";
    }

    std::cout << "map[\"three\"] -> " << map["three"] << "\n";
}

打印

Entry: five -> Fuenf
Entry: four -> Vier
Entry: one -> Eins
Entry: three -> Drei
Entry: two -> Zwei
map["three"] -> Drei

增强侵入性

CAVEAT 侵入式容器有自己的权衡取舍。管理元素的底层存储可能容易出错。钩子的自动链接行为抑制了 size() 和类似的常量时间实现(empty() 在一些无序集配置上)所以这可能不是你的事。

Live On Coliru

#include <boost/intrusive/unordered_set.hpp>
#include <boost/intrusive/unordered_set_hook.hpp>
#include <iostream>

namespace bi = boost::intrusive;

struct Element;

namespace boost {
    template <> struct hash<Element> {
        size_t operator()(Element const& e) const;
    };
}

struct Element : bi::unordered_set_base_hook<> {
    std::string key;
    mutable std::string value;

    Element(std::string k = "", std::string v = "") 
        : key(std::move(k)), value(std::move(v)) { }

    bool operator==(Element const& other) const { return key == other.key; }
};

size_t boost::hash<Element>::operator()(Element const& e) const {
    return hash_value(e.key); 
}

using Elements = bi::unordered_set<Element>;

int main() {
    std::array<Element, 256> storage;               // reserved 256 entries
    std::array<Elements::bucket_type, 100> buckets; // buckets for the hashtable

    Elements hashtable(Elements::bucket_traits(buckets.data(), buckets.size()));

    storage[0] = { "one",   "Eins"  };
    storage[1] = { "two",   "Zwei"  };
    storage[2] = { "three", "Drei"  };
    storage[3] = { "four",  "Vier"  };
    storage[4] = { "five",  "Fuenf" };

    hashtable.insert(storage.data(), storage.data() + 5);

    for (auto& e : hashtable) {
        std::cout << "Hash entry: " << e.key << " -> " << e.value << "\n";
    }

    std::cout << "hashtable[\"three\"] -> " << hashtable.find({"three"})->value << "\n";
}

打印

Hash entry: two -> Zwei
Hash entry: four -> Vier
Hash entry: five -> Fuenf
Hash entry: three -> Drei
Hash entry: one -> Eins
hashtable["three"] -> Drei

池固定大小分配器¹

如果您绝对需要基于节点的存储,请考虑使用自定义分配器。

¹ 你会注意到(至少在 Boost 的 unordered_map 实现中)分配器用于两种类型(桶指针和值节点),因此有两种 可以进行固定大小的分配。

(参见示例底部的清理调用)

Live On Coliru

#include <boost/pool/pool_alloc.hpp>
#include <boost/unordered/unordered_map.hpp>
#include <iostream>

using RawMap = boost::unordered_map<std::string, std::string>;
using Elements = boost::unordered_map<
        std::string, std::string,
        RawMap::hasher, RawMap::key_equal,
        boost::fast_pool_allocator<RawMap::value_type>
    >;

int main() {
    {
        Elements hashtable;

        hashtable.insert({
                { "one",   "Eins"  },
                { "two",   "Zwei"  },
                { "three", "Drei"  },
                { "four",  "Vier"  },
                { "five",  "Fuenf" },
                });

        for (auto& e : hashtable) {
            std::cout << "Hash entry: " << e.first << " -> " << e.second << "\n";
        }

        std::cout << "hashtable[\"three\"] -> " << hashtable.find("three")->second << "\n";
    }

    // OPTIONALLY: free up system allocations in fixed size pools
    // Two sizes, are implementation specific. My 64 system has the following:
    boost::singleton_pool<boost::fast_pool_allocator_tag, 8>::release_memory();  // the bucket pointer allocation
    boost::singleton_pool<boost::fast_pool_allocator_tag, 32>::release_memory(); // the ptr_node<std::pair<std::string const, std::string> >
}

【讨论】:

  • 为什么不使用 boost::pool ?
  • 池就是池,分配器就是分配器。我可能在这里遗漏了一些东西,但是您不能让 STL 容器使用池,AFAIK
  • 我可以编写一个使用池的分配器,并将其提供给 stl ... \ )
  • 这已经是这个分配器了。我不明白什么是不清楚的。代码对话。也许您对poolsingleton_poolpool_allocator 的基础)感到困惑。如果是这样,是的,似乎缺少相应的有状态分配器(用于非单例池)。这可能是因为有状态分配器是 C++11 的特性。 (如果您需要 C++03 支持,也许您可​​以将它与 Boost Container 库拼凑在一起)。背景:boost.org/doc/libs/1_57_0/doc/html/container/…
  • 我需要了解几件事: 1. flat_map::reserve(),它只分配桶指针吗?还是价值节点? 2.看起来我想创建池本身,所以我不想在堆栈中分配内存 - 或者类似于侵入性示例的东西(我想在某个地方的池中分配它)。 3.singleton_pool,如何预分配内存? (pool_allocator&lt;std::pair&lt;MyKey, MyClass&gt;, boost::default_user_allocator_new_delete, boost::mutex, 1024 &gt;)。我可以只有一个unordered_map,因为它使用singleton_pool吗?
猜你喜欢
  • 2013-10-28
  • 2013-01-05
  • 2018-04-01
  • 1970-01-01
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 2011-02-28
  • 1970-01-01
相关资源
最近更新 更多