- 后端
- 网络/通信
- 数据库
- 密码学
- Web框架
【免费下载链接】poco
The POCO C++ Libraries are powerful cross-platform C++ libraries for building network- and internet-based applications that run on desktop, server, mobile, IoT, and embedded systems.
本指南围绕 POCO C++ 库(The POCO C++ Libraries)自带的 Benchmark 性能基准测试模块展开,它基于 Google Benchmark 对 POCO 的日志、队列等核心组件进行量化评测。阅读本文后,你将掌握该基准应用的安装、构建、命令行用法,理解其内置测试场景与自定义SummaryReporter对比汇总机制,并能按规范编写新的基准测试用例并接入构建系统。
模块定位与依赖
Benchmark 模块是 POCO 仓库中一个可选构建的基准测试应用(对应目录 Benchmark/),用于对 POCO 组件进行性能评测,底层测量引擎是 Google Benchmark 库。根据 Benchmark/README.md 的说明,其依赖为:
- Poco Foundation:提供
Message、Logger、PatternFormatter、Channel、NotificationQueue等被测组件; - Poco Util:
BenchmarkApp本身继承自Poco::Util::Application,并使用Option、HelpFormatter等解析命令行参数(见 Benchmark/src/BenchmarkApp.cpp); - Google Benchmark library:提供基准测试运行框架、计时与统计。
从源码结构看,模块还通过 Benchmark/include/Poco/Benchmark/Benchmark.h 提供了POCO_BENCHMARK_DO_NOT_OPTIMIZE与POCO_BENCHMARK_CLOBBER_MEMORY两个便捷宏,分别对应benchmark::DoNotOptimize与benchmark::ClobberMemory。
安装 Google Benchmark
Benchmark 模块本身不捆绑 Google Benchmark,需要提前在系统中安装。官方 README 给出了各平台推荐方式:
macOS(Homebrew):
brew install google-benchmarkDebian/Ubuntu:
apt install libbenchmark-devFedora/RHEL:
dnf install google-benchmark-develWindows(vcpkg):
vcpkg install benchmark:x64-windows源码编译安装(Linux/macOS):
git clone https://github.com/google/benchmark.git cd benchmark cmake -DBENCHMARK_DOWNLOAD_DEPENDENCIES=on -DCMAKE_BUILD_TYPE=Release -S . -B build cmake --build build --config Release sudo cmake --install build源码编译安装(Windows):
git clone https://github.com/google/benchmark.git cd benchmark cmake -DBENCHMARK_DOWNLOAD_DEPENDENCIES=on -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=C:\local -S . -B build cmake --build build --config Release cmake --install build --config Release值得说明的是,Benchmark/Makefile 在 Make 构建路径下会依次尝试pkg-config探测和常见安装路径探测(macOS Homebrew 的/opt/homebrew(Apple Silicon)与/usr/local(Intel)、Linux 的/usr/include与/usr/local);若均未找到,则输出提示并跳过编译,而不是报错中断。
构建 Benchmark 应用
使用 Make(Linux/macOS)
Make 构建依赖环境变量POCO_BASE指向仓库根目录,随后在仓库根目录执行:
make -C Benchmark成功后会生成benchmark可执行文件(见 Benchmark/Makefile 中的target = benchmark)。
使用 CMake
根级 CMakeLists.txt 中定义了开关:
option(ENABLE_BENCHMARK "Set to OFF|ON (default is OFF) to enable Benchmark application (requires Google Benchmark library)" OFF)即ENABLE_BENCHMARK默认关闭,需显式开启;开启后 CMakeLists.txt 会自动强制打开ENABLE_UTIL(因为应用依赖 Poco Util),随后 CMakeLists.txt 会find_package(benchmark QUIET)探测 Google Benchmark,找到后才会add_subdirectory(Benchmark)并注册Benchmark组件。
Linux/macOS:
cmake -B build -DENABLE_BENCHMARK=ON cmake --build build --target BenchmarkWindows:
cmake -B build -G Ninja -DENABLE_BENCHMARK=ON -DCMAKE_PREFIX_PATH=C:\local cmake --build build --target BenchmarkREADME 特别提示:Windows 上请使用 Ninja 生成器以保证 target 构建可靠,且CMAKE_PREFIX_PATH需指向 Google Benchmark 的安装位置(对应上面cmake --install时设置的-DCMAKE_INSTALL_PREFIX=C:\local)。子级 Benchmark/CMakeLists.txt 中通过poco_add_executable(Benchmark ...)构建目标,并链接Poco::Foundation、Poco::Util与benchmark::benchmark。
运行与命令行选项
构建完成后按平台目录运行可执行文件:
./Benchmark/bin/$(uname)/$(arch)/benchmark [OPTIONS]选项一览
| Option | Description |
|---|---|
-h, --help | Display help information |
-l, --list | List all available benchmarks |
-f, --filter=<regex> | Run only benchmarks matching regex |
-t, --min-time=<seconds> | Minimum time to run each benchmark |
-r, --repetitions=<num> | Number of times to repeat each benchmark |
-a, --aggregates-only | Report only aggregates (mean, median, stddev) |
--format=<fmt> | Output format: console, json, or csv |
-o, --output=<file> | Write results to file |
--output-format=<fmt> | Format for output file |
--tabular | Display counters in tabular format |
选项与 Google Benchmark 的映射原理
从 Benchmark/src/BenchmarkApp.cpp 的main()实现可以看出,应用通过Poco::Util::OptionSet解析上述参数后,会将其逐一翻译为 Google Benchmark 的原生参数再交给benchmark::Initialize处理:
-l→--benchmark_list_tests=true-f <regex>→--benchmark_filter=<regex>-t <seconds>→--benchmark_min_time=<seconds>-r <num>→--benchmark_repetitions=<num>-a→--benchmark_report_aggregates_only=true--format=<fmt>→--benchmark_format=<fmt>(console/json/csv)-o <file>→--benchmark_out=<file>--output-format=<fmt>→--benchmark_out_format=<fmt>--tabular→--benchmark_counters_tabular=true
另外,main()会预先检查是否存在未识别的--前缀参数(Windows 上误用双横线是常见问题),并提示 Windows 应改用/filter或-f形式,随后通过benchmark::ReportUnrecognizedArguments校验剩余参数。因此,README 中-h展示的帮助文本、示例均与实际行为一致。
运行示例
列出所有可用的基准测试:
./benchmark --list只运行匹配某个正则的测试(例如仅运行 PatternFormatter 相关用例):
./benchmark --filter="PatternFormatter.*"重复运行并只输出统计聚合结果(均值、中位数、标准差):
./benchmark --repetitions=5 --aggregates-only以 JSON 格式导出结果到文件:
./benchmark --format=json --output=results.json内置基准测试场景
在编写自己的测试之前,可以先--list查看当前模块内置的用例。仓库src/下现包含三个测试文件,覆盖 POCO 三组核心组件:
PatternFormatter 格式化基准
Benchmark/src/PatternFormatterBench.cpp 以BM_PatternFormatter_*命名,通过不同复杂度的时间/优先级/来源/线程/源码位置格式串,度量PatternFormatter::format()的格式化开销:
TextOnly:"%t",仅消息文本;SourceText:"%s: %t";DateTime:"%Y-%m-%d %H:%M:%S";DateTimeMillis:带毫秒"%Y-%m-%d %H:%M:%S.%i";Typical:生产环境常见模式"%Y-%m-%d %H:%M:%S.%i [%p] %s: %t";Full:追加线程信息[%T:%I];Debug:追加源码文件与行号%s(%U:%u);Basename:使用%O输出文件基名;NodeName:含节点名%N;LocalTime:以%L前缀触发本地时区换算。
每个用例都通过benchmark::DoNotOptimize(result.data())防止编译器消除结果,并用state.SetBytesProcessed(state.iterations() * result.size())上报吞吐量,可用来对比"纯文本 vs 完整时间戳模式"之间真实的格式化成本差异。
Logger 与日志管线基准
Benchmark/src/LoggerBench.cpp 是内容最丰富的测试文件,命名遵循Logger_*、FastLogger_*、Loggers_*、Message_*、String_*分组,主要覆盖:
- 级别检查快路径:
Logger_LevelCheck_Enabled/Disabled,验证日志级别被禁用时调用几乎无开销; - Logger 查找:
Logger_Get_Existing/Hierarchical,度量Logger::get()的按名查找成本; - Message 构造与拷贝/移动:
Message_Create_Copy/Move、Message_Copy/Move,并单独测Process::id()、Thread::currentOsTid()、Timestamp等Message::init()内部系统调用成本; - NullChannel 下的纯管线开销:
Logger_Log_NullChannel_Copy/Move/CopyFresh/Literal/WithSrcLocation/Disabled,对比传 const 引用、移动、字面量与源码位置参数下的日志调用成本; - FormattingChannel 全管线:
Logger_FormattingChannel_Simple/Typical/Full/Debug,度量"格式化 + 输出"整体开销; - SplitterChannel 扇出:
Logger_Splitter->Arg(1)->Arg(2)->Arg(4)->Arg(8),测试日志通道数量增长对日志线程开销的影响(每个子通道在日志线程上独立格式化一次,成本随通道数线性增长); - 变参格式化:
Logger_Format_OneArg/ThreeArgs/Disabled,验证级别禁用时格式化参数不会被求值; - 宏日志:
Logger_Macro_Enabled/Disabled、Logger_Macro_Format_Enabled/Disabled(poco_information/poco_information_f),验证宏在级别检查处短路; - 静态格式化与 Hex 转储:
Logger_StaticFormat_*、Logger_FormatDump_Small/Medium/Large; - AsyncChannel 异步路径:
Logger_AsyncChannel_NullChannel/WithFormat/ToFile,其中ToFile通过TemporaryFile落盘真实文件并在结束后清理; - FastLogger(Quill 后端)对比:在
POCO_ENABLE_FASTLOGGER宏保护下(见 Foundation/include/Poco/FastLogger.h),包含FastLogger_Direct_*、FastLogger_Bridge_*、FastLogger_Direct_Splitter、FastLogger_Bridge_Splitter等用例,并与Loggers_AsyncChannel_File、Loggers_FastLogger_File、Loggers_AsyncChannel_ShortMsg/LongMsg、Loggers_FastLogger_ShortMsg/LongMsg形成"同步/异步、短消息/长消息"的成组对照。
源码注释中对"AsyncChannel 的 wall time ≈ CPU time"给出了明确的原理说明:AsyncChannel::log()在调用线程内同步完成了MessageNotification堆分配、Message拷贝与互斥锁入队,因此生产线程的 CPU 时间已反映绝大部分工作;而 FastLogger 的 CPU 时间远小于 wall time,因为它只在调用线程向预分配的无锁环形缓冲区写入少量字节便立即返回,格式化与 I/O 全部在后台线程完成。需要留意的是,FastLogger 相关用例还通过静态初始化器setBackendOption("enableYieldWhenIdle", "false")关闭空闲让出,以获得更一致的计时。
队列基准
Benchmark/src/NotificationQueueBench.cpp 以Queues_*命名,对比 POCO 的三种队列在同一测试下的原始开销:
NotificationQueue:互斥锁 + 条件变量实现的通知队列(enqueueNotification/dequeueNotification/waitDequeueNotification);SPSCQueue:单生产者单消费者无锁队列;MPSCQueue:多生产者单消费者无锁队列。
测试维度包括单线程往返(RoundTrip)、纯入队(Enqueue,用 1M 容量避免填满)、带消费线程的多线程吞吐(Threaded,通过state.counters["items_per_second"]上报吞吐率)、字符串负载(String)以及Message负载(MessageMove)。由于命名统一为Queues_<实现>_<测试>,自定义的SummaryReporter可以按前缀分组、按实现列并排对比。
编写新的基准测试
基准模板
在 Benchmark/src/ 新建一个.cpp文件,例如src/MyComponentBench.cpp:
// src/MyComponentBench.cpp #include <benchmark/benchmark.h> #include "Poco/MyComponent.h" using Poco::MyComponent; static void BM_MyOperation(benchmark::State& state) { MyComponent component; for (auto _ : state) { // Code to benchmark - runs many iterations auto result = component.doSomething(); benchmark::DoNotOptimize(result); } // Optional: report throughput state.SetBytesProcessed(state.iterations() * sizeof(result)); } BENCHMARK(BM_MyOperation); // Parameterized benchmark static void BM_MyOperationSized(benchmark::State& state) { int size = state.range(0); std::vector<int> data(size); for (auto _ : state) { // Benchmark with different sizes processData(data); } } BENCHMARK(BM_MyOperationSized)->Range(8, 8<<10);注册到构建系统
CMakeLists.txt(即 Benchmark/CMakeLists.txt):
set(SRCS src/BenchmarkApp.cpp src/PatternFormatterBench.cpp src/MyComponentBench.cpp # Add this )Makefile(即 Benchmark/Makefile):
objects = BenchmarkApp PatternFormatterBench MyComponentBench注意:当前 Benchmark/CMakeLists.txt 的SRCS实际包含BenchmarkApp.cpp、PatternFormatterBench.cpp与LoggerBench.cpp(README 示例略有出入,以仓库实际为准);而 Benchmark/Makefile 的objects为BenchmarkApp PatternFormatterBench LoggerBench NotificationQueueBench。新增用例时需在所选构建方式的对应列表中追加。若希望自定义汇总逻辑,可参考BenchmarkApp.cpp中注册新用例的方式:Google Benchmark 会通过链接期自动注册BENCHMARK(...)宏生成的静态对象,因此只需把新源文件加入编译即可。
编写基准测试的实用技巧
README 末尾给出的一组建议是保证测量有效性的关键,结合仓库代码逐一说明:
- 使用
benchmark::DoNotOptimize()防止编译器优化掉结果:这是所有内置用例的标准写法,如BenchmarkApp.cpp与各 Bench 文件中的benchmark::DoNotOptimize(result.data()); - 需要强制内存写可见时使用
benchmark::ClobberMemory():可经由POCO_BENCHMARK_CLOBBER_MEMORY()宏调用; - 将初始化/设置代码放在
for (auto _ : state)循环之外:避免把构建成本计入测量,例如PatternFormatterBench.cpp将PatternFormatter与Message建在循环外,循环内只执行format; - 循环内昂贵的设置用
state.PauseTiming()/state.ResumeTiming()排除计时:典型例子是LoggerBench.cpp的Message_Move用例在循环内先PauseTiming()构造原始 Message,再ResumeTiming()后只测量移动构造;SPSCQueue_Enqueue在队列满时也用该机制暂停计时排空部分条目; - 用
state.SetBytesProcessed()或state.SetItemsProcessed()上报吞吐量:如PatternFormatterBench用SetBytesProcessed(state.iterations() * result.size())报告格式化吞吐,队列的Threaded用例则通过state.counters["items_per_second"]以benchmark::Counter::kIsRate上报每秒处理条目数。
结果解读:SummaryReporter 与耗时指标
BenchmarkApp的一个亮点是内置自定义报表器SummaryReporter(继承benchmark::ConsoleReporter,见 Benchmark/src/BenchmarkApp.cpp)。它除了输出 Google Benchmark 的标准结果外,还会在Finalize()时打印分组对比汇总:
- 按前缀分组:从基准名中提取第一个下划线前的类别(如
Queues_、Loggers_),同一类别内再按"测试后缀"与"实现名"整理; - 自动选择基线:按字母序排在第一位的实现作为基线,其他实现显示耗时及其相对基线的倍数(快于基线显示绿色
Nx,慢于基线显示红色-Nx); - 同时输出 Wall Time 与 CPU Time 两套汇总:会根据耗量级自动换算为
ns/us/ms/s单位。
因此命名规范对可读性影响很大——遵循类别_实现_测试(如Loggers_AsyncChannel_File、Queues_SPSCQueue_RoundTrip)的三段式命名,即可自动获得并排对比表。解读结果时需区分两个指标:CPU Time反映调用线程实际消耗,是衡量"调用方延迟/热路径工作量"的关键;Wall Time包含后台线程处理,用于观察端到端总耗时。对异步日志(AsyncChannel / FastLogger)而言,两者差异本身就是评估后端设计的重要线索,具体原理已在 LoggerBench 源码注释中说明。
小结
POCO 的 Benchmark 模块为组件性能评测提供了"开箱即用的可执行应用 + 规范的基准编写范式":通过ENABLE_BENCHMARK=ON接入 CMake 构建,用-f过滤、-r重复、--format=json导出即可开展日常性能回归;内置的 PatternFormatter、Logger/FastLogger、队列三类用例覆盖了格式化、日志管线、并发队列等核心路径;自定义SummaryReporter让成组对比无需额外脚本。编写新基准时,遵循命名规范并正确使用DoNotOptimize、PauseTiming、吞吐量上报等技巧,即可与现有测试无缝衔接,形成可持续的性能回归体系。
- 后端
- 网络/通信
- 数据库
- 密码学
- Web框架
【免费下载链接】poco
The POCO C++ Libraries are powerful cross-platform C++ libraries for building network- and internet-based applications that run on desktop, server, mobile, IoT, and embedded systems.
相关推荐
缓存 localStorage 与 Cookie 读取:Plate 仓库中的 js-cache-storage 存储性能优化实践
缓存 localStorage 与 Cookie 读取:Plate 仓库中的 js cache storage 存储性能优化实践 在富文本编辑器等客户端应用里,
后端网络/通信数据库密码学Web框架Nix 性能基准测试完全指南:基于 Google Benchmark 的构建、运行与编写方法
Nix 性能基准测试完全指南:基于 Google Benchmark 的构建、运行与编写方法 本指南基于 Nix 官方手册中的 benchmarking.md
包管理器开发工具CLI构建工具Go性能基准测试:基于gopher-reading-list的benchmark实践
Go性能基准测试:基于gopher reading list的benchmark实践 你是否还在为Go程序的性能优化而烦恼?不知道如何科学地评估代码改动对性能的
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考