std::unordered_map 如果输入大小已知,如何优化批量插入
std::unordered_map how to optimize bulk insert if input size is known
如果我知道我要向 std::unordered_map
中插入大量数据(大约一百万个条目),我可以提前做些什么来提高性能吗? (就像std::vector::reserve
可以预留足够的内存space避免在批量插入之前我大致知道数据大小时重新分配)
更具体的说,hashmap中的key是一个二维平面坐标,自定义了hash函数,如下图
using CellIndex = std::pair<int32_t, int32_t>;
struct IdxHash {
std::size_t operator()(const std::pair<int32_t, int32_t> &idx) const { return ((size_t)idx.second << 31) ^ idx.first; }
};
std::unordered_map<CellIndex, double, IdxHash> my_map;
// bluk insert into my_map
...
reserve(x)
为 x
个元素准备无序容器。相比之下,rehash(x)
为 x/max_load_factor()
个元素准备了无序容器。
关于您的散列函数,如果您的目标是 return 一对坐标的唯一值,那么它应该 return ((size_t)idx.second << 32) ^ idx.first
。 ((size_t)idx.second << 31) ^ idx.first
将 return 与 (1, -1)
和 (0, 2^31-1)
相同的值。
std::unordered_map
通常实现为带有链表 的 链式哈希 table。因此,插入 std::unordered_map
平均需要常数时间,在最坏的情况下,时间与容器大小成线性关系。这种插入的最坏情况对应于散列 table 元素必须 重新散列 的情况,因为 table 中的当前桶数不足以满足load factor,因此,需要重新分配桶数组。
记住这一点,如果您事先知道要插入 std::unordered_map
的元素数量,您应该考虑 std::unordered_map::reserve()
以防止在插入时发生重新散列。这样,您将避免发生桶数组重新分配和重新散列。
std::unordered_map::insert()
with hint
与 std::map
一样,insert()
成员函数的一些重载采用所谓的 hint:
iterator insert(const_iterator hint, const value_type& value);
此提示迭代器可用于提供一些可用于加快插入速度的附加信息。不过,在std::unordered_map
taking a hint中存在这些成员函数只是出于接口兼容性的考虑,使其接口更适合table泛型编程。因此,它们不会缩短插入时间。
关于哈希函数
就插入时间而言,您的散列函数有多完美并不重要——重要的是它计算密钥散列的速度有多快。但是,当通过键查找散列 table 中的元素时,它变得相关。
如果我知道我要向 std::unordered_map
中插入大量数据(大约一百万个条目),我可以提前做些什么来提高性能吗? (就像std::vector::reserve
可以预留足够的内存space避免在批量插入之前我大致知道数据大小时重新分配)
更具体的说,hashmap中的key是一个二维平面坐标,自定义了hash函数,如下图
using CellIndex = std::pair<int32_t, int32_t>;
struct IdxHash {
std::size_t operator()(const std::pair<int32_t, int32_t> &idx) const { return ((size_t)idx.second << 31) ^ idx.first; }
};
std::unordered_map<CellIndex, double, IdxHash> my_map;
// bluk insert into my_map
...
reserve(x)
为 x
个元素准备无序容器。相比之下,rehash(x)
为 x/max_load_factor()
个元素准备了无序容器。
关于您的散列函数,如果您的目标是 return 一对坐标的唯一值,那么它应该 return ((size_t)idx.second << 32) ^ idx.first
。 ((size_t)idx.second << 31) ^ idx.first
将 return 与 (1, -1)
和 (0, 2^31-1)
相同的值。
std::unordered_map
通常实现为带有链表 的 链式哈希 table。因此,插入 std::unordered_map
平均需要常数时间,在最坏的情况下,时间与容器大小成线性关系。这种插入的最坏情况对应于散列 table 元素必须 重新散列 的情况,因为 table 中的当前桶数不足以满足load factor,因此,需要重新分配桶数组。
记住这一点,如果您事先知道要插入 std::unordered_map
的元素数量,您应该考虑 std::unordered_map::reserve()
以防止在插入时发生重新散列。这样,您将避免发生桶数组重新分配和重新散列。
std::unordered_map::insert()
with hint
与 std::map
一样,insert()
成员函数的一些重载采用所谓的 hint:
iterator insert(const_iterator hint, const value_type& value);
此提示迭代器可用于提供一些可用于加快插入速度的附加信息。不过,在std::unordered_map
taking a hint中存在这些成员函数只是出于接口兼容性的考虑,使其接口更适合table泛型编程。因此,它们不会缩短插入时间。
关于哈希函数
就插入时间而言,您的散列函数有多完美并不重要——重要的是它计算密钥散列的速度有多快。但是,当通过键查找散列 table 中的元素时,它变得相关。