在高性能计算场景下,常使用perf工具进行函数级别的时间分析、使用valgrind工具进行内存泄漏和内存分配异常检测。

valgrind功能非常强大,能追踪每一段内存申请和释放的栈帧。但是valgrind使用相对复杂,最重要的是valgrind效率极其低下,通常为原始程序运行时间长的10倍以上。对于多线程程序,valgrind无法跑满线程,性能退化能到几十甚至上百倍。

大部分场景下,即使精简case后,valgrind依然难以快速定位内存异常问题。

对于上述问题,fastgrind开源库提供一个轻量级的、函数级别监控、可视化的、高效C++内存监控方案。在64核服务器上测试,64个线程能完全跑满。简单case (调用栈深度10以内),性能几乎无退化;复杂case (调用栈深度30+),性能退化4倍以内。

box grouping


fastgrind仓库的testcase中,提供了一个包含bin query、分组算法的box grouping测例,调整线程数量,测试得到的benchmark如上图所示。

简介

fastgrind 是一个仅单一头文件、轻量级、快速、线程安全、类似 Valgrind 的内存分析器,旨在跟踪 C++ 应用程序中的运行时内存分配并分析调用堆栈。Fastgrind 通过自动和手动插桩两种检测方法提供全面的内存使用情况分析。

fastgrind 兼容C++11以上版本,集成到工程中不影响原始仓库中其它第三方内存管理库或glibc内存管理的正常运行。

仓库结构

fastgrind/
├── include/fastgrind.h           # 核心代码 (head only)
|
├── demo/
│   ├── manual_instrument/        # 手动插桩 demos
│   ├── auto_instrument/          # 自动插桩 demos  
|   └── build_all_demo.sh         # 编译所有demo的脚本
|
├── testcase/
│   ├── benchmark_box_grouping/   # 性能测试
│   ├── cpp_feature_test/         # 现代C++特性测试
│   ├── glibc_je_tc_availabe/     # 分配器兼容性测试
│   ├── multi_pkg_compile/        # 多lib编译测试
|   ├── thirdparty_leveldb_test/  # 第三方开源库测试 (https://github.com/google/leveldb)
|   └── thirdparty_zlib_test      # 第三方开源库测试 (https://zlib.net)
|
├── doc/
|   ├── compile.md                # 集成和编译选项说明
|   ├── demo.md                   # demo说明
|   ├── feature_list.md           # fastgrind特性说明
|   ├── querstion_list.md         # fastgrind使用过​​程中出现的问题及解决方案说明
|   └── testcase.md               # testcase说明
|
├── tools/fastgrind.py            # 可视化工具 (使用方法:python fastgrind.py fastgrind.json)
|
├── CMakeList.txt                 # testcase的顶层Cmake
├── Doxyfile                      # Doxyfile生成手册
└── README.md                     # 存库描述

快速开始

编译 testcase

mkdir build && cd build
cmake ..
make -j$(nproc)

运行 testcase

cd build/testcase/benchmark_box_grouping
./benchmark_raw
./benchmark_fastgrind
./run_valgrind.sh

cd build/testcase/cpp_feature_test
./cpp_feature_test

...

cd build/testcase/multi_pkg_compile
./multi_pkg_main

调用堆栈 Report

程序退出时会生成两个报告文件

[FASTGRIND] Start summary memory info
[FASTGRIND] saved: fastgrind.text (size=2335 bytes)
[FASTGRIND] saved: fastgrind.json (size=65952 bytes)

更多细节请看本文段落: fastgrind 输出与分析

如何在你的项目中使用

手动和自动插桩都需要额外的编译标志

有关详细编译和链接选项,请看本文段落: fastgrind 编译选项

手动插桩的使用方法

通过显示的插入__FASTGRIND__::FAST_GRIND宏,选择要监控的函数。

#include "fastgrind.h"

using namespace __FASTGRIND__;

void processData() {
    FAST_GRIND;                       // 启用此函数的调用堆栈跟踪
    
    int* data = new int[1000];
    // ... process data ...
    delete[] data;
}

int main() {
    FAST_GRIND;                       // 启用此函数的调用堆栈跟踪
    processData();
    return 0;
}

自动插桩的使用方法

在任何一个.cpp中包含 fastgrind.h,并通过编译选项,使得目标外的所有函数都会自动监控。

fastgrind 输出与分析

当集成 fastgrind 的应用程序退出时,会自动生成两个文件:fastgrind.textfastgrind.json

fastgrind.text

​fastgrind.text 是类似于 Linux 下 perf report 格式的输出。有函数级别的调用栈内存申请/释放统计,在vscode等editor下可以进行子调用栈折叠。

zlib-ouput示意图

更多推荐