用googletest进行基准测试?

17

背景(如果不感兴趣,请跳到下面的问题)

我有一个模拟器,经历了三个阶段:

  1. 单线程启动(I/O正常)
  2. 多线程内存CPU密集型模拟阶段(I/O出现问题)
  3. 模拟后,单线程阶段完成(I/O正常)

这是什么鬼! 在标准测试中,CPU使用率从100%降至20%,总运行时间比正常情况下慢了约30倍(130秒对比4.2秒)

Callgrind没有发现任何可疑的情况时,我开始想要回滚到上一个提交,以便丢失所有的错误修复。

失望的是,我走进服务器房间,并听到刺耳的金属声,后来证实是在/proc/PID/fd中写入Mysql sockets引起的!!! 这表明在第2阶段深层次的Mysql代码引起了问题。

所学到的教训

  1. 意外的I/O对实时应用程序可能会造成致命的影响
  2. 单元测试不够:我还需要基准测试

解决方法 我将引入线程本地存储IOSentinels,并在ReadAllowed()和WriteAllowed()上使用assert()来确保Stage 2线程永远不会进行任何IO。

问题

有人成功地使用googletest附加/编写基准测试框架吗?

遗憾的是,这次所有的googletest都通过了。如果我稍微离开一下,回来时没有注意到运行时间,那么这将是一个灾难性的提交,可能更难修复。

我希望 googletest 失败,如果运行时间比上一次运行时间长2或3倍:这个最后的部分很棘手,因为对于非常快速的运行,系统状态可能导致某些操作需要两倍的时间,但仍然是可接受的。但对于长时间的模拟运行/测试,我不希望运行时间有很大变化(>50%将会是不寻常的)。

我在这里开放建议,但希望有一个低维护的检查,能够与自动化测试一起使用,这样即使所有输出看起来都正常,如果系统突然变慢,也很容易发现。

4个回答

5

我在v1.5.0中这样做:

BENCHMARK(SomeBenchmark);
BENCHMARK(AnotherBenchmark);

TEST(MyTest, Benchmarks)
{
    ::benchmark::RunSpecifiedBenchmarks();
}

也就是说,我只需在 gtest 的一个测试中直接调用 RunSpecifiedBenchmarks。

4

Google Test框架默认提供了一个经过时间测量的度量方式。它由一个环境变量GTEST_PRINT_TIME控制,默认值为1

那么,为什么不使用Google Test平台的这个特性来监控经过的时间呢?

这里有一个关于Google Test中经过时间变量的说明

默认情况下,googletest会打印每个测试运行所需的时间。要禁用此功能,请使用--gtest_print_time=0命令行标志运行测试程序,或将GTEST_PRINT_TIME环境变量设置为0。


3
+1 这几乎是我想要的 - 如果我能够通过某些验证方式实际访问每个测试的结果,那就完美了:例如 EXPECT_LT( TEST_RUNTIME, 1000) 表示少于一千毫秒... - kfmfe04

4

关于这个问题的一些更新(2016年):

  1. 这里是Nick Brunn的一个不错的博客文章,介绍了他的Hayai基准测试框架。(2012年)

    • 它没有提供指定运行时间要求的可能性。
    • 它与Google Test非常相似。语法等方面。
    • 它向用户或持续集成框架提供基准测试结果。还可以查看MojaveWastelander的分支以进行活动开发和MSVC支持。
  2. Google在2014年发布了'Benchmark'。这提供了与上述Hayai类似的行为。据我所知,无法定义要求。同样,语法受到GoogleTest的启发。

    • 甚至有测量复杂度(大O)等高级功能。
  3. GoogleTest在Github上有这个问题的开放功能。有一个基本的实现,但它还不是GoogleTest的一部分。


3
“难道这不就是这么简单吗?”
const clock_t t0 = clock(); // or gettimeofday or whatever
int res = yourFunction();
const clock_t t1 = clock();
const double elapsedSec = (t1 - t0) / (double)CLOCKS_PER_SEC;
EXPECT_EQ(EXPECTED, res);
EXPECT_GT(10.0, elapsedSec);

在这里,您需要根据任务手动更改10.0的值。

当然,您可以进一步进行类似以下的操作:

double prev = -1;
{
  ifstream ifs("/var/tmp/time_record.txt");
  ifs >> prev;
}
if (prev < 0) prev = DEFAULT_VALUE;
// ...
EXPECT_GT(2 * prev, elapsedSec);
{
  ofstream ofs("/var/tmp/time_record.txt");
  ofs << elapsedSec << endl;
}

但我想知道这种额外的复杂性是否真的有正当理由。

2
或者,如果可能的话,使用C++11中的std::chrono库。googletest库在编译C++11时也可以正常工作。 - Hans

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接