std::map调用clear()耗时过长的原因及优化方案咨询
问题描述
我创建了一个存储自定义结构体的大型std::map,该结构体包含多个std::vector成员。调用该map对象的clear()方法时耗时极长。
我运行的示例代码占用了超过80%的内存,我怀疑交换内存与此问题有关,但即便如此,我仍不清楚为何删除std::map的内容需要这么长时间。
能否有人告知这是怎么回事?
以下是示例代码,在我的机器上运行耗时约100秒(波动较大):
#include <iostream> #include <map> #include <vector> #include <string> #include <chrono> struct MyStruct { std::vector<int64_t> vi01; std::vector<int64_t> vi02; std::vector<int64_t> vi03; std::vector<int64_t> vi04; std::vector<int64_t> vi05; std::vector<int64_t> vi06; std::vector<int64_t> vi07; std::vector<int64_t> vi08; std::vector<int64_t> vi09; std::vector<int64_t> vi10; std::vector<int64_t> vi11; std::vector<int64_t> vi12; std::vector<int64_t> vi13; std::vector<int64_t> vi14; std::vector<int64_t> vi15; std::vector<int64_t> vi16; std::vector<int64_t> vi17; std::vector<int64_t> vi18; std::vector<int64_t> vi19; std::vector<int64_t> vi20; std::vector<std::string> vs01; std::vector<std::string> vs02; std::vector<std::string> vs03; std::vector<std::string> vs04; std::vector<std::string> vs05; std::vector<std::string> vs06; std::vector<std::string> vs07; std::vector<std::string> vs08; std::vector<std::string> vs09; std::vector<std::string> vs10; std::vector<std::string> vs11; std::vector<std::string> vs12; std::vector<std::string> vs13; std::vector<std::string> vs14; std::vector<std::string> vs15; std::vector<std::string> vs16; std::vector<std::string> vs17; std::vector<std::string> vs18; std::vector<std::string> vs19; std::vector<std::string> vs20; }; int main() { std::map<std::string, MyStruct> sId_MyStruct{}; // roughly 500 * 7'000 * 2'000 = 7'000'000'000 bytes (7GB) // but std::vector's capacity() would be bigger. for (int64_t i=10'000ULL; i < 10'000ULL+7'000ULL ; ++i) { std::string sId = std::to_string(i); MyStruct & myStruct = sId_MyStruct[sId]; for (int64_t j=10'000ULL; j<(10'000ULL+2'000ULL); ++j) { // Figures for simple reference only. // Not considered the actual capacity of the vectors. // 8 bytes per element x 20 = 160 bytes. myStruct.vi01.push_back(j); myStruct.vi02.push_back(j); myStruct.vi03.push_back(j); myStruct.vi04.push_back(j); myStruct.vi05.push_back(j); myStruct.vi06.push_back(j); myStruct.vi07.push_back(j); myStruct.vi08.push_back(j); myStruct.vi09.push_back(j); myStruct.vi10.push_back(j); myStruct.vi11.push_back(j); myStruct.vi12.push_back(j); myStruct.vi13.push_back(j); myStruct.vi14.push_back(j); myStruct.vi15.push_back(j); myStruct.vi16.push_back(j); myStruct.vi17.push_back(j); myStruct.vi18.push_back(j); myStruct.vi19.push_back(j); myStruct.vi20.push_back(j); // 15 bytes(string's capacity) per element x 20 = 300bytes. std::string sj = std::to_string(j); myStruct.vs01.push_back(sj); myStruct.vs02.push_back(sj); myStruct.vs03.push_back(sj); myStruct.vs04.push_back(sj); myStruct.vs05.push_back(sj); myStruct.vs06.push_back(sj); myStruct.vs07.push_back(sj); myStruct.vs08.push_back(sj); myStruct.vs09.push_back(sj); myStruct.vs10.push_back(sj); myStruct.vs11.push_back(sj); myStruct.vs12.push_back(sj); myStruct.vs13.push_back(sj); myStruct.vs14.push_back(sj); myStruct.vs15.push_back(sj); myStruct.vs16.push_back(sj); myStruct.vs17.push_back(sj); myStruct.vs18.push_back(sj); myStruct.vs19.push_back(sj); myStruct.vs20.push_back(sj); } if (i % 1'000 == 0) std::cout << "sId:" << sId << std::endl; } std::cout << "std::map object creation finished." << std::endl; std::cout << "press anything and enter to continue."; std::string sIn; std::cin >> sIn; //std::cout << sIn << '\n'; std::cout << "std::map clear() start." << std::endl; auto beg = std::chrono::steady_clock::now(); sId_MyStruct.clear(); auto fin = std::chrono::steady_clock::now(); auto dur = fin - beg; auto sec = (dur).count() / 1'000'000'000; std::cout << sec << " seconds taken." << std::endl; std::cout << "======main fin=======" << std::endl; }
这是相关问题的后续提问,因要求提供最小可复现示例而发布。
后续测试结论
阅读评论和回答后,我进行了不同配置的额外测试,得出以下结论:
- 操作系统与磁盘的内存交换并非直接原因,即使内存(RAM)占用不高(约40%)时,
clear()仍耗时较久。 std::map的clear()与std::unordered_map的clear()性能差异显著,尤其是在仅使用int64_t作为vector元素类型时。- 将vector元素类型从
std::string改为int64_t可提升clear()性能,符合预期,但提升幅度不大。 - 未来优化方向:
- 避免堆分配;
- 若使用堆分配,采用“扁平”数据结构;
- 编写自定义分配器。
额外测试结果
(结果因机器、配置等不同而异,仅供参考)
- 使用
std::map(与原配置一致)- 共40个
vector<int64_t>,无vector<string>:耗时减少约30%(约70秒)。 - 共60个
vector<int64_t>,无vector<string>:耗时增加约40%(约140秒)。
- 共40个
- 使用
std::unordered_map替代std::map- 共20个
vector<int64_t>和20个vector<string>(与原std::map配置一致):耗时减少约5%(约96秒)。 - 共40个
vector<int64_t>,无vector<string>:耗时减少约70%(约30秒)。 - 共60个
vector<int64_t>,无vector<string>:耗时减少约40%(约64秒)。
- 共20个
内容的提问来源于stack exchange,提问作者starriet 차주녕
相关产品推荐
相关产品推荐

