在标头中初始化大地图会导致 g++ 崩溃

问题描述 投票:0回答:1

我正在尝试制作一个简单的工具,需要对固定键值数据集进行一些查找,因此我尝试将所有数据惰性地扔到头文件中的哈希映射中:

/** 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
c++ compilation g++ header-files
1个回答
3
投票

大的头文件会降低你的性能。不要欺负你的编译器!

想象一下

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
,还可以节省一些编译成本。

© www.soinside.com 2019 - 2024. All rights reserved.