我正在尝试制作一个简单的工具,需要对固定键值数据集进行一些查找,因此我尝试将所有数据惰性地扔到头文件中的哈希映射中:
/** main.h */
#include <unordered_map>
#include <cstdint>
using namespace std;
const unordered_map<uint64_t, const char * const> test = {
{0xDEADC0DE, "Some short text less than 50 characters"},
// 46K rows of data
};
我还没有实现任何东西,但仅仅包含这个头文件就足以使编译器崩溃。
主.cpp
/** main.cpp */
#include <iostream>
#include "main.h"
int main() {
return 0;
}
在 CPU 核心达到最大负载 5 分钟后,g++ (cc1plus) 耗尽了所有 32GB RAM 并崩溃。我知道大标头可能会影响编译性能,但我没想到它会耗尽资源并失败。当头文件大小只有 1.9 MB 时,它如何使用 32GB RAM?有人可以帮我解释一下我的案例中的问题吗?
我使用的版本是 g++ (GCC) 13.2.1 20230801,使用命令
/usr/bin/g++ -O3 -DNDEBUG -o CMakeFiles/main.cpp.o -c /home/foo/main.cpp
我还用不同尺寸的地图做了一些实验:
元素编号 | 构建时间 |
---|---|
10 | 00:00:01.043 |
100 | 00:00:01.187 |
1000 | 00:00:05.225 |
2000 | 00:00:10.200 |
5000 | 00:00:25.604 |
10000 | 00:00:52.208 |
20000 | 00:01:48.090 |
大的头文件会降低你的性能。不要欺负你的编译器!
想象一下
include
ing <main.h>
— 正如您所说,每个需要它的源文件中都有超过 四万六千 (46000 + 1) 个元素。这意味着您在包含它的任何地方都复制了大对象test
,并且您的编译器被迫预处理标头并在包含它的任何地方进行编译。这不好!真的很糟糕!!
就像我在评论中提到的那样,这个对象
test
应该位于翻译单元中,应该具有静态存储持续时间并且应该具有外部链接。这样一来,它将被编译一次,直到程序终止为止,并且可以通过使用 extern
关键字引用它来在其他翻译单元中使用。
test.cpp
#include <unordered_map>
#include <cstdint>
const std::unordered_map<uint64_t, const char* const> test {
{0xDEADC0DE, "Some short text less than 50 characters"},
/* 46K rows of data */
};
main.cpp
#include <iostream>
#include <unordered_map>
#include <cstdint>
extern const std::unordered_map<uint64_t, const char* const> test;
int main() {
std::cout << test[0xDEADC0DE] << '\n';
return 0;
};
任何您想使用
test
的地方,只需声明它 extern
即可引用静态存储中的同一个对象。这样,你就可以避免复制test
,还可以节省一些编译成本。