"const unordered_map" 作为全局变量的编译速度慢

Slow compilation speed for "const unordered_map" as global variable

我遇到的编译时间真的很慢,可能是因为存在 std::unordered_map 这种全局变量。您可以在下面找到代码行,它们位于名为 correspondance.h

的文件中
    using Entry_t = std::tuple<Cfptr_t, short int, short int>;
    ...
    #include "map_content.h"
        inline const std::unordered_map<std::string, Entry_t> squaredampl{MAP_CONTENT};
    #undef MAP_CONTENT

并且,在map_content.h中有地图的内容:

     #define MAP_CONTENT \
    { {EMPTYCHAR+corr::su_R,ANTICHAR,EMPTYCHAR+corr::sd_L,EMPTYCHAR+corr::A,EMPTYCHAR+corr::W},{    &mssm2to2::sumSqAmpl_su_R_anti_sd_L_to_A_W, 9,2}},\
    { {EMPTYCHAR+corr::su_L,ANTICHAR,EMPTYCHAR+corr::sd_R,EMPTYCHAR+corr::A,EMPTYCHAR+corr::W},{  &mssm2to2::sumSqAmpl_su_L_anti_sd_R_to_A_W, 9,2}},\
    ...

这样下去大约有3500行。 注意

  1. Cfptr_t是函数指针
  2. EMPTYCHARcorr::something 类型的所有变量都是整数,所以它们定义了键值 string

我遇到的问题是,每个包含 correspondace.h 的文件的 makefile 需要 10 分钟,因为确实有这么多行代码要处理。

正如您可能已经理解的那样,需要这个映射来允许用户在不知道函数名称的情况下访问函数,而只需要一个字符串。出于这个原因,我目前无法消除此功能,因为它是基本功能。

尽我所能,我尝试将此文件包含在尽可能少的文件中。无论如何,它不可避免地包含在每个将生成可执行文件的文件中,因此,总的来说,这迫使我每次必须调试某些东西时都要等待很长时间。

如果您对如何加速编译保持此功能有任何建议,那将对我很有帮助:)

改变

#include "map_content.h"
inline const std::unordered_map<std::string, Entry_t> squaredampl{MAP_CONTENT};
#undef MAP_CONTENT

extern const std::unordered_map<std::string, Entry_t> squaredampl;

并在新的 .cpp 文件中:

#include "correspondance.h"
#include "map_content.h"
const std::unordered_map<std::string, Entry_t> squaredampl{MAP_CONTENT};
#undef MAP_CONTENT

这将导致只发出一个地图定义(与 inline 对比,后者将在每个翻译单元 odr-using 中发出)。

只要在输入 main 之前在另一个静态存储持续时间对象的构造过程中不使用地图,它就是安全的。如果需要,最好使映射成为返回它的函数中的局部静态变量 by-reference 以供调用者使用。这将避免“静态初始化顺序失败”。


它也可能有助于创建一个函数来为每个条目使用 insert 语句来初始化映射,而不是在初始化程序中全部完成,即有一个函数 initSquaredampl 返回一个 std::unordered_map<std::string, Entry_t> by-value 然后:

auto initSquaredampl() {
    std::unordered_map<std::string, Entry_t> squaredampl;
    // Insert items one-by-one
    return squaredampl;
}

const std::unordered_map<std::string, Entry_t> squaredampl = initSquaredampl();

可能有帮助,因为编译器可能没有针对编译非常长的表达式或初始值设定项进行优化。但是,他们也可能在处理非常长的函数时遇到麻烦。

我要感谢@Kaldrr 和@user17732522 的精彩见解。我通过以下方式解决了我的问题。在 correspondance.h 中,我只是输入:

inline std::unordered_map<std::string, Entry_t> squaredampl;

即空地图的简单初始化。 然后,我使用以下行创建了一个新的 header init_map.h

#include "correspondance.h"

int Initialise();

当然还有对应的 initialise_map.cpp 文件,其中包含函数的定义

#include "initialise_map.h"

int Initialise()
{
  if (!corr::squaredampl.empty()) return 0;
  corr::squaredampl.reserve(3500);
  corr::squaredampl = {
#include "map_content_body.h"
  };
  return 0;
}

其中 map_content_body.h 是地图内容的线条。这样:

  1. 地图的代码只在一个文件中
  2. 我避免定义宏并复制它
  3. 一旦我有了 object 文件 initialise_map.o,所有将生成可执行文件的 .cpp 文件将不包含代码,但函数将在链接阶段存在

因此,除非我删除 initialise_map.o 或更新 initialise_map.cpp,否则我的代码将以正常速度编译,这是应该的。

当然,我注意在每个 header 文件中有一个 #pragma once 或类似的文件以避免无用的代码副本。

也非常感谢@JaMiT 的鼓励话语:)

编辑:正如我在评论中所建议的那样,我最终通过@user17732522 实现了解决方案。这样我就不必每次都调用一个函数来进行初始化。