使用C++ std::unordered_map哪种代码更优?实测结果存疑求解
问题:两段C++嵌套哈希表代码的性能差异疑惑
我原本认为第二种写法性能更优,但实际测试后发现第一种耗时更短,测试数据如下:
- test1:第一种耗时183947微秒,第二种耗时186965微秒
- test2:第一种耗时226307微秒,第二种耗时268437微秒
两段代码分别为:
代码1
std::unordered_map<std::string, std::unordered_map<std::string, double>> ab_map; for (int i = 0; i < 30000; i++) { std::unordered_map<std::string, double>& item = ab_map[std::to_string(i)]; for (int j = 0; j < 10; j++) { item.emplace(std::to_string(j), 10.0); } }
代码2
std::unordered_map<std::string, std::unordered_map<std::string, double>> ab_map; for (int i = 0; i < 30000; i++) { std::unordered_map<std::string, double> item; for (int j = 0; j < 10; j++) { item.emplace(std::to_string(j), 10.0); } ab_map.emplace(std::to_string(i), std::move(item)); }
请问这是什么原因?
解答
两种写法的性能差异核心来自内存分配路径和哈希表初始化的细节差异:
内存分配的直接性
- 代码1中,
ab_map[std::to_string(i)]会直接在ab_map的内部节点内存(堆上)构造空的unordered_map,后续的emplace操作直接在这块已分配好的内存上执行,没有额外的临时对象内存开销。 - 代码2中,先在栈上创建临时
item(若栈空间不足会退化为堆分配),构造完成后再通过std::move转移到ab_map的节点中。这多了一次临时对象的内存分配/释放(栈分配涉及栈帧操作,堆分配则是额外的堆申请),以及哈希表内部数据结构(桶数组、节点指针)的移动操作——单次开销虽小,但3万次累积后就会体现出差异。
- 代码1中,
哈希表的初始化与潜在调整
- 默认构造的
std::unordered_map桶数很小(比如11),插入10个元素不会触发扩容,两段代码的这部分逻辑开销一致。但代码2中,移动临时对象到ab_map时,部分标准库实现可能会对移动后的哈希表做微小的校验或结构调整(标准无强制要求,但不同编译器的libstdc++/libc++实现细节有差异),这会带来额外的累积开销。
- 默认构造的
无差异的共性开销
两段代码都重复调用了std::to_string(i),这部分开销完全一致,不是性能差异的来源。
另外,测试环境的编译器版本、优化等级(如-O2/-O3)也会影响结果:优化等级低时,栈临时对象的开销会更明显;优化等级高时,编译器可能对代码2做部分优化,但依然抵消不了内存分配路径上的本质差异。
需要注意的是,这种差异属于微观性能层面,多数业务场景中可以忽略。若追求极致性能,可提前给ab_map预留桶数(比如ab_map.reserve(30000)),或用更紧凑的存储结构替代嵌套哈希表。
内容的提问来源于stack exchange,提问作者Anna
相关产品推荐
相关产品推荐

