Skip to main content
Version: nightly 🚧

框架性能

概述

基准测试是使用 CMake 通过 this 脚本完成的。有3种基准测试场景:

使用的编译器:

  • Windows:Microsoft Visual Studio 社区 2017 - 版本 15.8.1+28010.2003
  • WINDOWS:gcc 8.1.0(x86_64-posix-seh-rev0,由 MinGW-W64 项目构建)
  • Linux:gcc 6.3.0 20170406(Ubuntu 6.3.0-12ubuntu2)
  • LINUX:clang 4.0.0-1(标签/RELEASE_400/rc1)目标:x86_64-pc-linux-gnu

使用环境(Intel i7 3770k,16g RAM):

  • Windows 7 - 在 SSD 上
  • VirtualBox VM 中的 Ubuntu 17.04 - 在 HDD 上

doctest版本:2.2.0(2018年12月2日发布)

Catch version: 2.3.0 (released on 2018.07.22)

编译时间基准

包含标题的成本

这是一个仅与单个标头和仅标头框架相关的基准 - 例如 doctestCatch

该脚本生成 201 个源文件,其中 200 个以 int f135() { return 135; 的形式创建一个函数。 } 并在 main.cpp 中向前声明了所有 200 个这样的虚拟函数,并累积它们的结果以从 main() 函数返回。这样做是为了确保构建所有源文件并且链接器不会删除/优化任何内容。

  • 基线 - 使用 msbuild/make 进行单线程构建源文件需要多少时间
  • + Implement - 仅在 main.cpp 中,标头包含在其前面的 #define 中,以便测试运行器得以实现:
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
  • + header everywhere - the framework header is also included in all the other source files
  • + disabled - remove everything testing-related from the binary
doctestbaseline+ implement+ header everywhere+ disabled
MSVC Debug4.896.218.336.39
MSVC Release4.386.398.716.02
MinGW GCC Debug8.1210.8614.7310.17
MinGW GCC Release8.2111.1115.0310.71
Linux GCC Debug4.206.239.816.24
Linux GCC Release4.296.9311.056.76
Linux Clang Debug8.7010.0214.4311.13
Linux Clang Release9.3011.6816.2011.58
Catchbaseline+ implement+ header everywhere+ disabled
MSVC Debug4.827.8388.8588.72
MSVC Release4.389.9787.1788.35
MinGW GCC Debug8.0057.28137.28132.73
MinGW GCC Release8.3822.9497.1797.22
Linux GCC Debug4.4215.5797.9497.18
Linux GCC Release4.5019.5999.48100.75
Linux Clang Debug8.7615.60107.99110.61
Linux Clang Release9.3225.75118.67117.11

Conclusion

doctest
  • 在一个源文件中实例化测试运行器大约需要 1-3 秒实现 - 基线
  • 在一个源文件中包含“doctest.h”会花费 11 毫秒 - 23 毫秒“(header_everywhere - 实现)/ 200”
  • 包括随处可见的库,但所有禁用的内容大约需要 2 秒“禁用 - 基线”,对于 200 个文件
Catch
  • 在一个源文件中实例化测试运行器大约需要 3-50 秒实现 - 基线
  • 在一个源文件中包含 catch.hpp 需要 380 毫秒 - 470 毫秒 (header_everywhere - 实现)/ 200
  • 使用配置选项禁用库(CATCH_CONFIG_DISABLE)对标头成本没有影响

因此,如果“doctest.h”在 MSVC 上花费 11 毫秒,“catch.hpp”花费 400 毫秒 - 那么 doctest 标头为>> 36<<轻十倍(对于 MSVC)!


结果在几秒钟内就出来了,并且绝不打算抨击Catch - 如果没有doctest框架就不会存在它。

doctest 标头在编译时间上如此之少的原因是因为它向前声明了所有内容并且不会在源文件中拖动任何标头(除了实现测试运行器的源文件)。这是一个关键的设计决策。

Cost of an assertion macro

该脚本生成 11 个 .cpp 文件,其中 10 个文件创建了 50 个测试用例,其中包含 100 个断言(格式为 CHECK(a==b),其中 ab 始终是相同的 int 变量) - 50k 断言!测试框架在“main.cpp”中实现。

  • 基线 - 单线程构建需要多少时间,并且标头包含在各处 - 没有测试用例或断言!
  • CHECK(a==b) - 将添加 CHECK() 断言,使用模板机制分解表达式

doctest specific:

Catch specific:

  • +fast - 将添加 CATCH_CONFIG_FAST_COMPILE 来加快速度正常断言的编译 CHECK(a==b)
  • +disabled - 所有测试用例和断言宏都将被禁用 CATCH_CONFIG_DISABLE
doctestbaselineCHECK(a==b)+fast 1CHECK_EQ(a,b)+fast 2+disabled
MSVC Debug2.6927.3710.3717.174.821.91
MSVC Release3.1558.7320.7326.076.431.83
MinGW GCC Debug3.7897.2943.0559.8611.881.67
MinGW GCC Release4.09286.7095.42156.7318.162.03
Linux GCC Debug2.3991.3641.9252.2610.161.32
Linux GCC Release3.29257.4097.46128.8419.381.79
Linux Clang Debug2.4085.5243.5351.248.321.62
Linux Clang Release3.40160.6579.3481.5211.901.82

这里是 Catch,它只有正常的 CHECK(a==b) 断言:

CatchbaselineCHECK(a==b)+fast+disabled
MSVC Debug8.2031.2225.548.22
MSVC Release10.13448.68168.6710.20
MinGW GCC Debug53.54152.38131.8549.07
MinGW GCC Release19.26590.16466.6918.99
Linux GCC Debug15.05117.3095.3314.79
Linux GCC Release18.77608.94482.7318.96
Linux Clang Debug12.2794.3977.3312.11
Linux Clang Release20.75545.84506.0220.15

Conclusion

doctest:

  • 使用正则表达式分解 CHECK(a==b) 断言时,比 Catch 快 0 到 8 倍
  • 断言形式为CHECK_EQ(a,b),没有表达式分解 - 比CHECK(a==b)快约 31-63%
  • DOCTEST_CONFIG_SUPER_FAST_ASSERTS 标识符使正常断言速度提高 57-68%
  • DOCTEST_CONFIG_SUPER_FAST_ASSERTS 标识符使二进制断言速度又快了 84-91%
  • 使用 DOCTEST_CONFIG_DISABLE 标识符,断言就消失了,就好像它们从未被写入一样 - 甚至低于基线(因为大部分实现也消失了)

Catch:

  • 使用 CATCH_CONFIG_FAST_COMPILE 可以将构建时间加快 10-30%断言(在一种情况下为 73%)。
  • 使用 CATCH_CONFIG_DISABLE 标识符为断言宏提供了与 doctest 版本相同的巨大好处(DOCTEST_CONFIG_DISABLE) -但不包括标头成本

Runtime benchmarks

运行时基准测试由单个测试用例组成,该测试用例具有执行任务的 1000 万次迭代循环 - 单个正常断言(使用表达式分解)或断言 + 循环迭代器“i”的日志记录:

for(int i = 0; i < 10000000; ++i)
CHECK(i == i);

or

for(int i = 0; i < 10000000; ++i) {
INFO(i);
CHECK(i == i);
}

请注意,断言总是通过 - 目标应该是针对常见情况进行优化 - 许多通过的测试用例和一些可能会失败。

doctestassert+ info                               Catchassert+ info
MSVC Debug4.0011.41MSVC Debug5.60213.91
MSVC Release0.401.47MSVC Release0.767.60
MinGW GCC Debug1.052.93MinGW GCC Debug1.179.54
MinGW GCC Release0.341.27MinGW GCC Release0.364.28
Linux GCC Debug1.242.34Linux GCC Debug1.449.69
Linux GCC Release0.290.52Linux GCC Release0.293.60
Linux Clang Debug1.152.38Linux Clang Debug1.219.91
Linux Clang Release0.280.50Linux Clang Release0.323.27

Conclusion

doctest 断言比的 catch 快约 20%,但在记录变量和上下文时快了几倍(在一个特定编译器的情况下快了 18 倍以上)。

条形图是使用此谷歌电子表格通过粘贴表格中的数据生成的。

如果您想要一个非综合的基准测试 - 请查看 Baptiste Wicht 的这篇博客文章,他使用他的表达式模板库测试了 1.1 版本中断言的编译时间!

在阅读这篇文章时 - 请记住,如果流程的一部分花费了 50% 的时间并且速度提高了 10000 倍 - 整个流程仍然只会快大约 50%。