是否有BOOST池固定大小的分配器?

时间:2015-03-08 09:52:37

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

我想创建unordered_map(因为我特别需要哈希映射)。我想在开头分配它的最大尺寸(根据我的约束) 所以,如果我想分配256个条目,每个条目的大小是1B(只是一个例子。假设1Byte包括Key和Value)。然后我的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,如下所示:

  3. 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吗? 湾如何在运行时声明大小不同next_block 1024的新MyUnorderedMap?

1 个答案:

答案 0 :(得分:4)

您所描述的内容实际上只能通过Boost Intrusive“Maps”(实际上,sets)来实现。

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

然而,从你声称元素类型为1B(对于具体的键和值类型显然永远不会是真的)这一事实,我不会假设你真的想要这个设计的解决方案“某种原因”。

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

  • 使用flat_map
  • 使用Boost Intrusive无序集
  • 使用带有Boost Pool固定大小allocator¹的无序集合

提升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

Boost Intrusive

  

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> >
}