为什么我的日志logging库会使性能testing运行得更快?

我花了一年的时间开发一个C ++的日志logging库,并且考虑到性能。 为了评估性能,我开发了一套基准testing程序 ,将我的代码与其他库进行比较,其中包括根本不执行任何日志logging的基本情况。

在我的上一个基准testing中,我测量了一个CPU密集型任务的总运行时间。 然后我可以比较一下时间来确定我的图书馆有多less开销。 这个条形图显示了与我的非测井基础案例相比的差异。

性能图表

正如你所看到的,我的库(“鲁莽”)增加了负面开销 (除非所有4个CPU核心都忙)。 当启用日志function时,程序运行速度比停用时快大约半秒。

我知道我应该尝试把这个问题分解成一个简单的案例,而不是问一个4000线的项目。 但是有那么多的地方要去掉什么东西,没有一个假设,当我试图隔离它的时候,我会让问题消失。 我可能再花一年时间来做这件事。 我希望堆栈溢出的集体专业知识会使这个问题变得更加简单,或者对于那些比我更有经验的人来说,这个原因是显而易见的。

关于我的图书馆和基准的一些事实:

  • 该库由一个将日志参数推送到无锁队列(Boost.Lockless)的前端API和一个执行string格式化并将日志条目写入磁盘的后端线程组成。
  • 时间是基于简单地在程序的开始和结束处调用std::chrono::steady_clock::now() ,并打印差异。
  • 该基准testing是在一个4核英特尔CPU(i7-3770K)上运行的。
  • 基准程序计算一个1024×1024 Mandelbrot分形并logging每个像素的统计信息,即它写入大约一百万条日志条目。
  • 单个工作线程的总运行时间约为35秒。 所以速度增加大概是1.5%。
  • 基准testing产生一个输出文件(这不是定时代码的一部分),它包含了生成的Mandelbrot分形。 我已经validation,日志打开和closures时产生相同的输出。
  • 基准testing运行了100次(所有基准testing库都需要大约10个小时)。 条形图显示平均时间,误差棒显示四分位间距。
  • Mandelbrot计算的源代码
  • 基准的源代码 。
  • 代码库和文档的根 。

我的问题是,我怎样才能解释当我的日志库启用明显的速度增加?

编辑 :这是在尝试了评论中给出的build议后解决的。 我的日志对象是在基准testing的第24行创build的。 显然,当LOG_INIT()触及日志对象时,它会触发页面错误,导致图像缓冲区的一些或全部页面被映射到物理内存。 我仍然不确定为什么这会将性能提高近半秒; 即使没有日志对象,在mandelbrot_thread()函数中发生的第一件事是写入图像缓冲区的底部,这应该具有类似的效果。 但是,在任何情况下,在开始基准testing之前用memset()清除缓冲区会使得一切更加理智。 目前的基准在这里

其他我试过的事情是:

  • 用oprofile分析器运行它。 即使在将工作放大10分钟左右之后,我也从来没有能够在任何时间注册。 几乎所有的时间都在Mandelbrot计算的内部循环中。 但是,也许现在我知道页面错误,我可以用不同的方式解释它们。 我没有想到要检查图像写入是否花费了不成比例的时间。
  • 卸下锁。 这确实对性能有显着的影响,但结果仍然很奇怪,无论如何,我无法对任何multithreading变体进行更改。
  • 比较生成的汇编代码。 有差异,但日志build设显然是做更多的事情。 没有什么是一个明显的表演杀手。

首次访问未初始化的内存时,页面错误将影响时间。

所以,在第一次调用std::chrono::steady_clock::now() ,通过在你的sample_buffer上运行memset()来初始化内存。