Hom*_*er6 41 c++ benchmarking header-only
我试图用SO找到答案.有许多问题列出了在c ++中构建一个只有头文件库的各种优缺点,但是我无法找到一个以可量化的术语来构建这样的库.
因此,在可量化的术语中,使用传统上分离的c ++标头和实现文件与仅标头之间有什么不同?
为简单起见,我假设不使用模板(因为它们只需要标题).
详细说明,我列出了我从文章中看到的优点和缺点.显然,有些不容易量化(例如易用性),因此无法用于量化比较.我会用可量化的方式标记那些我期望可量化的指标.
仅限标题的优点
仅限标题的缺点
您可以从较大的开源项目(比较类似大小的代码库)中使用的任何示例都非常感激.或者,如果您知道可以在仅标题版本和分离版本之间切换的项目(使用包含两者的第三个文件),那将是理想的.轶事数字也很有用,因为它们给了我一个可以获得一些洞察力的球场.
利弊来源:
提前致谢...
更新:
对于可能稍后阅读并且有兴趣获得关于链接和编译的一些背景信息的任何人,我发现这些资源很有用:
更新:(回应下面的评论)
仅仅因为答案可能不同,并不意味着测量是无用的.你必须开始测量一些点.您拥有的测量值越多,图像就越清晰.我在这个问题上要求的不是整个故事,而是对图片的一瞥.当然,如果他们想要不道德地宣传他们的偏见,任何人都可以使用数字来扭曲争论.但是,如果有人对两个选项之间的差异感到好奇并发布这些结果,我认为这些信息很有用.
没有人对这个话题感到好奇,足以衡量它吗?
我喜欢枪战项目.我们可以从删除大部分变量开始.只在一个版本的linux上使用一个版本的gcc.仅对所有基准测试使用相同的硬件.不要使用多个线程进行编译.
然后,我们可以测量:
Rea*_*law 30
摘要(值得注意的一点):
Box2D基准,数据:
Botan基准,数据:
Box2D摘要(78个单位)

Botan概要(301个单位)

好的图表:
Box2D可执行文件大小:

Box2D编译/链接/构建/运行时间:

Box2D编译/链接/构建/运行最大内存使用情况:

Botan可执行文件大小:

Botan编译/链接/构建/运行时间:

Botan编译/链接/构建/运行最大内存使用情况:

TL; DR
测试的项目,Box2D和Botan之所以被选中,是因为它们的计算成本可能很高,包含大量单元,并且实际上很少或没有错误编译成一个单元.尝试了许多其他项目,但是花费了太多时间来"修复"作为一个单元进行编译.通过定期轮询内存占用并使用最大值来测量内存占用量,因此可能不完全准确.
此外,此基准测试不会执行自动标头依赖关系生成(以检测标头更改).在使用不同构建系统的项目中,这可能会增加所有基准测试的时间.
基准测试中有3个编译器,每个编译器有5个配置.
编译:
编译器配置:
-O3 -march=native-Os-O3 -flto -march=native与clang和gcc,-O3 -ipo -march=native与icpc/icc-Os我觉得这些每个人都可以对之间的单单元和多单元构建比较不同的轴承.我包括LTO/IPO,所以我们可能会看到如何"正确"的方式来实现单单元效益进行比较.
csv字段的说明:
Test Name - 基准的名称.例子:Botan, Box2D.Test Name.Compiler - 使用的编译器的名称.例子:gcc,icc,clang.Compiler Configuration - 使用的编译器选项配置的名称.例:gcc opt nativeCompiler Version String - 编译器本身的编译器版本的第一行输出.示例:在我的系统上g++ --version生成g++ (GCC) 4.6.1.Header only- True如果将此测试用例构建为单个单元,False则将其值构建为多单元项目.Units - 测试用例中的单元数,即使它是作为一个单元构建的.Compile Time,Link Time,Build Time,Run Time - 听起来像.Re-compile Time AVG,Re-compile Time MAX,Re-link Time AVG,Re-link Time MAX,Re-build Time AVG,Re-build Time MAX - 触摸单个文件后重建项目的时间.触摸每个单元,并为每个单元重建项目.这些字段中记录了最大时间和平均时间.Compile Memory,Link Memory,Build Memory,Run Memory,Executable Size - 听起来像.要重现基准:
"units"- c/cpp/cc组成该项目单位的文件列表"executable" - 要编译的可执行文件的名称."link_libs" - 要链接到的已安装库的空格分隔列表."include_directores" - 要包含在项目中的目录列表."command"- 可选的.执行特殊命令以运行基准测试.例如,"command": "botan_test --benchmark"test_base_cases中run.py与信息工程,包括数据文件名.data.csv应包含基准测试结果.要生成条形图:
fields列表以决定生成哪些图形.python chart.py data.csv.test.png现在应该包含结果../configure.py --disable-asm --with-openssl --enable-modules=asn1,benchmark,block,cms,engine,entropy,filters,hash,kdf,mac,bigint,ec_gfp,mp_generic,numbertheory,mutex,rng,ssl,stream,cvc,这会生成头文件和Makefile.grep -o "\./src.*cpp" Makefile和,grep -o "\./checks.*" Makefile以获取.cpp单位并将它们放入botan_bench.data文件中./checks/checks.cpp为不调用x509单元测试,并删除了x509检查,因为Botan typedef和openssl之间存在冲突.Intel(R) Core(TM) i7 CPU Q 720 @ 1.60GHzRea*_*law 28
更新
这是Real Slaw的原始答案.他上面的回答(被接受者)是他的第二次尝试.我觉得他的第二次尝试完全回答了这个问题. - 荷马6
那么,为了比较,你可以查找"统一构建"的想法(与图形引擎无关).基本上,"统一构建"是将所有cpp文件包含在单个文件中,并将它们全部编译为一个编译单元的地方.我认为这应该提供一个很好的比较,因为AFAICT,这相当于只使你的项目标题.你对你列出的第二个"骗局"感到惊讶; "统一构建"的重点是减少编译时间.据说统一构建编译速度更快,因为它们:
..是一种减少构建开销的方法(特别是通过减少生成的目标文件的数量来打开和关闭文件并减少链接时间),因此用于大幅加快构建时间.
编译时间比较(从这里):

"团结建设"的三个主要参考:
我假设您想要列出利弊的原因.
仅限标题的优点
[...]
3)它可能要快得多.(可量化的)代码可能会更好地进行优化.原因是,当单元是分开的时,函数只是一个函数调用,因此必须保留.没有关于此呼叫的信息,例如:
此外,如果函数内部代码是已知的,则可能值得内联它(即将其代码直接转储到调用函数中).内联避免了函数调用开销.内联还允许发生一系列其他优化(例如,常量传播;例如我们调用factorial(10),现在如果编译器不知道代码factorial(),则被迫离开它,但如果我们知道源代码代码factorial(),我们实际上可以变量函数中的变量并用10替换它,如果我们很幸运,我们甚至可以在编译时得到答案,而不在运行时运行任何东西).内联后的其他优化包括死码消除和(可能)更好的分支预测.
4)可以为编译器/链接器提供更好的优化机会(如果可能,可以解释/量化)
我认为这是从(3)开始的.
仅限标题的缺点
1)它使代码膨胀.(可量化的)(这会如何影响执行时间和内存占用量)只有Header可以通过几种方式使代码膨胀,我知道.
首先是模板膨胀; 编译器实例化从未使用过的类型的不必要模板.这不仅仅是标题,而是模板,现代编译器对此进行了改进,使其成为最小的问题.
第二个更明显的方法是(过)内联函数.如果在任何地方使用大型函数,那么这些调用函数的大小会增加.这可能是几年前关于可执行文件大小和可执行映像内存大小的问题,但硬盘驱动器空间和内存已经增长,使得它几乎毫无意义.更重要的问题是,这种增加的函数大小会破坏指令缓存(因此现在更大的函数不适合缓存,现在缓存必须在CPU通过函数执行时重新填充).内联后寄存器压力会增加(寄存器数量有限制,CPU可以直接处理的CPU内存).这意味着编译器必须在现在更大的函数中间处理寄存器,因为变量太多了.
2)编译时间更长.(定量的)
好吧,只有标题编译可以在逻辑上导致更长的编译时间,原因有很多(尽管有"统一构建"的表现;逻辑不一定是现实世界,其他因素也参与其中).一个原因可能是,如果整个项目只是标题,那么我们就会失去增量构建.这意味着项目任何部分的任何更改都意味着必须重建整个项目,而使用单独的编译单元时,一个cpp中的更改只意味着必须重建目标文件,并重新链接项目.
在我的(轶事)经历中,这是一个很大的打击.在某些特殊情况下,标题只能提高性能,但生产效率明显,通常不值得.当您开始获得更大的代码库时,从头开始编译时间每次大约需要10分钟.在微小的变化上重新编译开始变得令人厌烦.你不知道有多少次我忘记了";" 并且不得不等待5分钟才能听到它,只是回去修理它,然后等待另外5分钟,通过修复";"再找到我刚才介绍的其他东西.
性能很好,生产力更好; 它会浪费你很大一部分时间,并使你的编程目标失去动力/分散注意力.
编辑:我应该提一下,过程间优化(参见链接时优化和整个程序优化)试图完成"统一构建"的优化优势.在大多数编译器AFAIK中,这种实现仍然有点摇摇欲坠,但最终这可能会克服性能优势.