我们有使用 gcc 和制作文件的项目。项目还包含一个大子项目 (SDK) 和许多使用该 SDK 和一些共享框架的相对较小的子项目。
我们使用预编译的头文件,但这仅有助于重新编译更快。
是否有任何已知的技术和工具可以帮助进行构建时优化?或者,也许你知道一些关于这个或相关主题的文章/资源?
我们有使用 gcc 和制作文件的项目。项目还包含一个大子项目 (SDK) 和许多使用该 SDK 和一些共享框架的相对较小的子项目。
我们使用预编译的头文件,但这仅有助于重新编译更快。
是否有任何已知的技术和工具可以帮助进行构建时优化?或者,也许你知道一些关于这个或相关主题的文章/资源?
您可以从两个方面解决这个问题:重构代码以降低编译器所看到的复杂性,或者加快编译器的执行速度。
无需接触代码,您就可以在其中添加更多的编译能力。使用 ccache 避免重新编译您已经编译的文件,并使用 distcc 在更多机器之间分配构建时间。使用 make -j 其中 N 是核心数+1,如果你在本地编译,或者更大的数字用于分布式构建。该标志将并行运行多个编译器。
重构代码。更喜欢前向声明而不是包含(简单)。尽可能多地解耦以避免依赖(使用 PIMPL 习惯用法)。
模板实例化很昂贵,它们在每个使用它们的编译单元中重新编译。如果您可以重构模板以转发声明它们,然后仅在一个编译单元中实例化它们。
我能想到的最好的make
就是-j
选项。这告诉make
并行运行尽可能多的作业:
make -j
如果要将并发作业的数量限制为n,可以使用:
make -j
n
确保依赖项是正确的,这样make
就不会运行它不需要的作业。
另一件需要考虑的事情是gcc
与-O
交换机相关的优化。您可以指定各种优化级别。优化程度越高,编译和链接时间越长。我使用的一个项目运行需要 2 分钟来链接 . -O3
,半分钟-O1
. 您应该确保您的优化没有超出您的需要。您可以在不优化开发构建和优化部署构建的情况下构建。
使用调试信息 ( ) 进行编译gcc -g
可能会增加可执行文件的大小并可能影响构建时间。如果您不需要它,请尝试删除它以查看它是否会影响您。
链接的类型(静态与动态)应该有所不同。据我了解静态链接需要更长的时间(尽管我在这里可能错了)。您应该看看这是否会影响您的构建。
从项目的描述中,我猜你每个目录都有一个 Makefile 并且经常使用递归 make。在这种情况下,“Recursive Make Considered Harmful”中的技术应该会很有帮助。
此外,您可能希望使源代码文件尽可能小且自包含/可行,即更喜欢许多较小的目标文件而不是一个巨大的单个目标文件。
这也将有助于避免不必要的重新编译,此外,您可以为每个源代码目录或模块拥有一个带有目标文件的静态库,基本上允许编译器尽可能多地重用以前编译的代码。
之前的任何回复中尚未提及的其他内容正在使符号链接尽可能“私有”,即如果代码不必在外部可见,则更喜欢代码的静态链接(函数、变量)。
此外,您可能还想研究使用GNU gold 链接器,它在为 ELF 目标编译 C++ 代码时效率更高。
基本上,我建议您仔细分析构建过程并检查花费最多的时间,这将为您提供一些关于如何优化构建过程或项目源代码结构的提示。
您可以考虑切换到不同的构建系统(显然不适用于所有人),例如 SCons。SCons 比 make 聪明得多。它会自动扫描头依赖项,因此您始终拥有最小的重建依赖项集。通过将这一行添加Decider('MD5-timestamp')
到您的 SConstruct 文件中,SCons 将首先查看文件的时间戳,如果它比先前构建的时间戳更新,它将使用文件的 MD5 来确保您确实更改了某些内容。这不仅适用于源文件,也适用于目标文件。这意味着如果您更改评论,例如,您不必重新链接。
头文件的自动扫描还确保我永远不必键入 scons --clean。它总是做正确的事。
如果您有一个带有开发人员机器的 LAN,也许您应该尝试实现一个分布式编译器解决方案,例如distcc。
如果构建过程中的所有时间都花在分析依赖关系或执行一些单一的串行任务上,这可能无济于事。正如 Nathan 所建议的(在单台机器上),对于将许多源文件编译为目标文件的原始紧缩,并行构建显然会有所帮助。跨多台机器并行化可以更进一步。
http://ccache.samba.org/大大加快了速度。
我从事一个中等规模的项目,这是我们加快编译时间的唯一方法。
如果您可以访问多台机器,则可以使用distcc分布式编译器来减少构建时间。这是来自 IBM developerWorks 的一篇与 distcc 相关的文章以及如何使用它: http ://www.ibm.com/developerworks/linux/library/l-distcc.html
另一种减少构建时间的方法是使用预编译头文件。这是gcc 的起点。
如果您的机器有多个 cpu/core(2x cores/cpus 的数量就可以了),也不要忘记在使用 make 构建时使用 -j。
使用小文件可能并不总是一个好的建议。磁盘的最小扇区大小为 32 或 64K,文件至少占用一个扇区。所以 1024 个 3K 大小的文件(内部的小代码)实际上会占用 32 或 64 Meg 的磁盘空间,而不是预期的 3 meg。需要由驱动器读取的 32/64 meg。如果文件分散在磁盘上,则寻道时间会增加读取时间。显然,这对磁盘缓存有一定的帮助。预编译的头文件也可以很好地帮助缓解这种情况。
因此,考虑到编码指南,将每个 strcuct、typedef 或实用程序类放入单独的文件中是没有意义的。