对一些知名或以低延时为目标的 C/C++ 日志库做的 Benchmark - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
weidaizi
V2EX    程序员

对一些知名或以低延时为目标的 C/C++ 日志库做的 Benchmark

  •  1
     
  •   weidaizi
    MuggleWei 2023-10-17 11:44:49 +08:00 2589 次点击
    这是一个创建于 727 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目地址:https://github.com/MuggleWei/cc_log_benchmark

    概述

    当前已经有许多开源的 C/C++ 日志库, 本项目对一些知名或以低延时为目标的日志库做一个 Benchmark, 力求发现当面对如 MMORPG Server 或是金融交易系统这类低延时的场景下, 哪些日志库是较好的选择.

    省流,将结果放在前面

    提醒: 有一些图表之外需要注意的内容, 感兴趣的读者可在最后看到

    日志库列表

    此库对几个 C/C++ 日志库做 benchmark, 被测的库如下所示(按字母顺序排序)

    • easyloggingpp: C++ logging library. It is extremely powerful, extendable, light-weight, fast performing, thread and type safe and consists of many built-in features. It provides ability to write logs in your own customized format. It also provide support for logging your classes, third-party libraries, STL and third-party containers etc.
    • fmtlog: fmtlog is a performant fmtlib-style logging library with latency in nanoseconds.
    • glog: C++ implementation of the Google logging module
    • haclog: Haclog(Happy Aync C log) is an extremely fast plain C logging library
    • loguru: A lightweight C++ logging library
    • Nanolog: Nanolog is an extremely performant nanosecond scale logging system for C++ that exposes a simple printf-like API.
    • quill: Asynchronous Low Latency C++ Logging Library
    • reckless: Reckless logging. Low-latency, high-throughput, asynchronous logging library for C++.
    • spdlog: Fast C++ logging library

    Benchmark 说明

    使用 google benchmark 进行测试, 测试分为两部分场景

    1. 场景 1: 设定最小的测试时间 (设置 google benchmark 中的 MinTime), 在这时间中尽可能的压力测试写日志, 此场景主要针对异步日志, 可以反映日志库的吞吐量, 以及当缓冲区高度紧张的情况下日志前端的写入效率如何. 在此场景下测试的线程数量为: 1/2/4/8
    2. 场景 2: 设定迭代和重复次数 (设置 google benchmark 中的 Iterations + Repetitions), 反映在非压力测试的情况下, 各个日志库的表现如何. 在此场景下测试的线程数量为: 1/${0.5 * CPU 数量}/${1 * CPU 数量}/${2 * CPU 数量}

    基准测试

    测试日期: 2023-10-14
    被测库版本, 详见: CMakeLists.txt
    单次写入数据:

    struct LogMsg { uint64_t u64; uint32_t u32; int64_t i64; int32_t i32; char s[128]; }; 

    日志输出格式:

    ${Level}|${datetime}|${filename}.${line_no}|${func_name}|${thread_id} - u64: msg.u64, i64: msg.i64, u32: msg.u32, i32: msg.i32, s: msg.s 

    测试机器

    类型: 笔记本
    机器: 20R10002CD ThinkPad X1 Carbon 7th
    系统: Arch Linux x86_64
    Kernel: 6.5.6-arch2-1
    CPU: Intel i7-10710U (12) @ 4.700GHz
    GPU: Intel Comet Lake UHD Graphics
    Memory: 15659MiB
    gcc: (GCC) 13.2.1 20230801
    ldd: (GNU libc) 2.38

    额外说明

    由于我本地机器的测试中遇到一些问题, 导致 fmtlog, quill 和 reckless 没有做到测试场景完全的覆盖

    • 当 fmtlog 加入 #define FMTLOG_BLOCK 1 时, 测试进程会被卡死而无法继续, 所以 fmtlog 只测试了缓冲区满选择丢弃日志的模式
    • 当 quill 使用 UNBOUNDED 时, 在 场景 1 中, 会导致内存使用一直增长而失败, 所以在测试 quill_unbounded 的时候, 跳过 场景 1 的测试
    • reckless 在 场景 1 中会卡住, 所以 reckless 跳过 场景 1 的测试
    • fmtlog 和 Nanolog 我并没有找到设置默认缓冲区的接口, 所以都只使用了默认的缓冲区大小

    由于个人水平所限, 若有错误和纰漏, 亦或是考虑欠妥之处, 还请不吝指正!

    结果分析

    通过上述图表不难看出

    • 在场景 1 中, 较快的日志库有(按字母顺序排序): fmtlog, haclog, Nanolog, quill_bounded
    • 在场景 2 中, 较快的日志库为(按字母顺序排序): fmtlog, haclog, Nanolog

    那么上面提到的这四个日志库为什么更快?他们有什么优劣势,以及使用中有什么要注意的坑呢?

    以上 4 个日志库均为异步日志库, 设计上也都是多缓冲区队列写, 消费者负责轮询的模式, 整体思路相似, 代码细节各有各的趣味. 但作为用户来说, 有一点要特别注意

    特别注意!!!
    特别注意!!!
    特别注意!!!

    既然都使用了缓冲区, 那么就一定要考虑缓冲区被写满的情况, 此时有三种不同的应对方式

    1. 阻塞: 生产者线程阻塞, 等待缓冲区有足够空间后再写入
    2. 丢弃: 放弃写入本条日志, 或者直接套圈写入本条日志但是放弃一整个缓冲区的日志
    3. 扩展: 动态增加缓冲区的长度, 继续写入

    haclog 与 Nanolog 选择了方案 1, fmtlog 可选方案 1/2, quill 可选方案 2/3
    而由于 额外说明 中提到的情况, 导致 fmtlog 只可选择方案 2, quill 在 场景 1 中仅讨论 bounded 模式

    fmtlog

    优点

    • 在两种场景下都表现出良好的速度, 其中在 场景 1 中速度与 quill_bounded 旗鼓相当, 并列最快速度
    • 在两种场景下, 速度表现的稳定性足够好, 波动较小
    • 采用了 format 格式化风格

    缺点

    • 场景 1 中出现了大量丢失日志的情况
    • 场景 2 中也出现了日志丢失的情况
    • 设置 #define FMTLOG_BLOCK 1 情况时, 无法完成 场景 1 的测试

    haclog

    优点

    • 在两种场景下大部分情况都表现出良好的速度, 其中 场景 2 中多线程的情况下, 速度仅略逊于 Nanolog
    • 缓冲区满时采用了阻塞模式, 不会丢日志

    缺点

    • 当吞吐量超过了某个阈值, 缓冲区大量被写满的情况下, 效率会出现大幅下跌

    Nanolog

    优点

    • 在两种场景下都表现出良好的速度, 其中在 场景 2 中多线程的情况下, 速度排名第一
    • 很高的吞吐量, 无论在 场景 1 还是 场景 2 中, 都没有出现效率大幅下跌的情况
    • 缓冲区满时采用了阻塞模式, 不会丢日志

    缺点

    • 日志无法直接阅读, 需要使用附带的 decompressor 程序进行解码
    • 仅支持 Linux

    quill

    优点

    • 场景 1 中速度与 fmtlog 旗鼓相当, 并列最快速度
    • 采用了 format 格式化风格

    缺点

    • 在 bounded 模式下会出现日志丢失的情况
    • 在 unbounded 模式下, 有可能导致内存一直增长, 无法完成 场景 1 的测试

    额外的一点考虑

    haclog 使用纯 C 开发, 而 fmtlog, Nanolog 和 quill 采用 C++ 开发; 当就日志库这个场景来说, 理论上 C++ 实现速度上限会更高一点点, 表现为以下两个方面

    • 编译期计算: C++ 能在编译期预计算好日志参数信息, 而纯 C 实现的日志库需要在第一次运行时计算
    • 日志前端序列化:
      • C 需要在运行时遍历 va_list, 生成的汇编代码是通过循环来逐个参数序列化, 无论是通过 switch 判断预计算好的类型,或是调用预先设置好的函数指针, 都可能会带来一点额外的开销
      • C++ 通过变参模板来进行序列化, 汇编代码可实现为平铺展开的形式, 以空间换时间

    综上所述

    • fmtlog 和 quill 采用 format 格式化风格, 书写方便, 且两者速度稳定性较强, fmtlog 和 quill_bounded 在 场景 1 表现出色. 但是如上一小节所分析的, 他们的速度部分是通过日志丢失换取的, 这在使用场景上需要特别注意
    • haclog 在两种场景下也都表现出色, 在 场景 2 中的表现仅略逊与 Nanolog, 但是在 场景 1 当缓冲区被压满的情况下, 效率大幅度的降低. 这正好与 fmtlog 以及 quill 相反, 他以速度降低来换取了不丢日志
    • Nanolog 在两种场景下都表现出极高的效率, 以及超高的吞吐量. 在 场景 2 下的王者, 在 场景 1 中面临压力的情况下, 保证不丢日志也不用拿太多的效率去换. 不过其超高的吞吐量, 是拿日志非实时可读换取的, 这也导致诸如 tail 一类的工具无法使用

    可以看出, 当前这几个异步日志并没有一个能在全方位碾压其他日志库, 而是都在某一方面做了权衡取舍

    8 条回复    2023-10-18 23:14:48 +08:00
    F281M6Dh8DXpD1g2
        1
    F281M6Dh8DXpD1g2  
       2023-10-17 11:56:55 +08:00
    你用了一个带睿频的 cpu 测试的么?
    cnbatch
        2
    cnbatch  
       2023-10-17 12:08:43 +08:00
    看完这个测试后,OP 的测试应该是测出了日志库的不可能三角?
    高吞吐、不丢日志、可实时读换
    weidaizi
        3
    weidaizi  
    OP
       2023-10-17 13:15:58 +08:00
    @liprais 是的,支持睿频,在测试之前,我已经直接 cpupower 设置成了 performance
    weidaizi
        4
    weidaizi  
    OP
       2023-10-17 13:17:55 +08:00
    @cnbatch 哈哈哈,这位朋友总结的专业!
    更严谨一点,应该是: 高吞吐 -> 低时延且高吞吐
    kkocdko
        5
    kkocdko  
       2023-10-17 19:25:56 +08:00
    一楼的意思是你应该把 boost=0 ,睿频会有很大的性能波动。benchmark 这种事情本身就是求稳定,才能对各个库都公平。
    weidaizi
        6
    weidaizi  
    OP
       2023-10-17 23:18:55 +08:00
    @kkocdko 谢谢您的提醒!
    我之前一直认为,设置了 `cpupower frequency-set -g performance`, 那么默认都应该是固定在最高频率上跑,频率应该是稳定的;您要表达的意思是,如果 turbo boost 开着,我这样设置之后频率依然是不稳定的吗?
    weidaizi
        7
    weidaizi  
    OP
       2023-10-18 00:29:19 +08:00   1
    @kkocdko @liprais 查了一番,我之前的理解的确不到位,通过 cpupower 设置为 performance 在睿频开启的情况下的确还是有波动,谢谢两位!之后我会重新出一个关闭了 Turbo Boost 的结果
    weidaizi
        8
    weidaizi  
    OP
       2023-10-18 23:14:48 +08:00
    @kkocdko @liprais 更新了一下 benchmark ,增加了 `CPU 频率设置与监控`,固定在 3.2GHz 上测试,再次感谢两位的提醒!
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     6028 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 01:49 PVG 09:49 LAX 18:49 JFK 21:49
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86