Redis CPU 分析
CPU 上分析和跟踪的性能工程指南
Redis 堆栈 | Redis 社区版 |
---|
填写性能检查清单
Redis 的开发非常重视性能。我们尽最大努力 每个版本,以确保您体验到非常稳定和快速的产品。
不过,如果你正在寻找提高 Redis 或 正在进行性能回归调查,您将需要一个简洁的 监控和分析 Redis 性能的方法。
为此,您可以依赖不同的方法(有些方法比其他方法更适合 取决于我们打算制作的问题/分析的类别)。精选列表 的方法及其步骤由 Brendan Greg 在以下链接中列举。
我们建议使用利用率饱和和错误 (USE) 方法进行回答 你的瓶颈是什么的问题。检查以下 系统资源、指标和工具进行实际深入研究:USE 方法。
确保 CPU 是您的瓶颈
本指南假定您已按照上述方法之一执行 完成系统运行状况检查,并确定瓶颈是 CPU。如果您已确定大部分时间都花在 I/O 上,则锁定、 timers、paging/swapping 等,本指南不适合您。
构建先决条件
为了进行适当的 CPU 上分析,Redis(以及任何动态加载的库,如 Redis Modules)要求堆栈跟踪可供跟踪器使用,您可以 需要先修复。
默认情况下,Redis 是使用-O2
switch 的
在分析期间)。这意味着启用了编译器优化。多
编译器省略帧指针作为运行时优化(保存寄存器),
从而打破了基于帧指针的堆栈遍历。这使得 Redis
executable 更快,但同时它使 Redis(就像任何其他程序一样)
更难追踪,可能会错误地将 CPU 上的时间精确定位到最后一个
可用的帧指针,该指针可以变得更深(但
无法追踪)。
请务必确保:
- 存在调试信息:编译选项
-g
- 存在帧指针寄存器:
-fno-omit-frame-pointer
- 我们仍然使用优化来准确表示生产运行时间,这意味着我们将保留:
-O2
您可以在 redis main repo 中执行以下作:
$ make REDIS_CFLAGS="-g -fno-omit-frame-pointer"
一组用于识别性能回归和/或潜在的 CPU 性能改进的工具
本文档特别关注 CPU 上的资源瓶颈分析。 这意味着我们有兴趣了解线程在哪些方面花费了 CPU 周期 在 CPU 上运行时,同样重要的是,这些周期是否有效 用于计算或停止等待(未阻塞)内存 I/O, 和缓存未命中等。
为此,我们将依赖工具包(perf、bcc 工具)和特定于硬件的 PMC (性能监控计数器),以继续:
-
热点分析(perf 或 bcc 工具):分析代码执行并确定哪些函数消耗的时间最多,因此是优化的目标。我们将提供两个选项来使用 perf 或 bcc/BPF 跟踪工具收集、报告和可视化热点。
-
调用计数分析:对包括函数调用在内的事件进行计数,使我们能够一次关联多个调用/组件,依靠 bcc/BPF 跟踪工具。
-
硬件事件采样:对于了解 CPU 行为至关重要,包括内存 I/O、停顿周期和缓存未命中。
工具先决条件
以下步骤依赖于 Linux perf_events(又名“perf”)、bcc/BPF 跟踪工具和 Brendan Greg 的 FlameGraph 存储库。
我们假设您事先有:
- 已在系统上安装 perf 工具。大多数 Linux 发行版可能会将其打包为与内核相关的包。有关 perf 工具的更多信息,请访问 perf wiki。
- 按照安装 bcc/BPF 说明在您的计算机上安装 bcc toolkit。
- 克隆了 Brendan Greg 的 FlameGraph 存储库,并使其可访问
difffolded.pl
和flamegraph.pl
文件生成折叠的堆栈轨迹和火焰图。
使用 perf 或 eBPF 进行热点分析(堆栈跟踪采样)
通过定时对堆栈跟踪进行采样来分析 CPU 使用率是一种快速且 识别性能关键型代码段 (热点) 的简单方法。
使用 perf 对堆栈跟踪进行采样
要分析特定 redis-server 的用户级和内核级堆栈 采样频率为 999 个样本的时间长度,例如 60 秒 每秒:
$ perf record -g --pid $(pgrep redis-server) -F 999 -- sleep 60
使用 perf report 显示记录的配置文件信息
默认情况下,perf 记录会在当前 目录。
然后,您可以使用调用图输出(调用链、堆栈回溯)、 最小调用图包含阈值为 0.5%,其中:
$ perf report -g "graph,0.5,caller"
请参阅 perf report 文档,了解高级筛选、排序和聚合功能。
使用 Flame Graphs 可视化记录的配置文件信息
火焰图允许快速 以及频繁代码路径的准确可视化。它们可以使用 Brendan Greg 在 github 上的开源程序, ,它们从折叠的堆栈文件创建交互式 SVG。
具体来说,对于 perf,我们需要将生成的 perf.data 转换为 捕获的堆栈,并将其每个堆栈折叠成单行。然后,您可以渲染 CPU 上的火焰图,其中包含:
$ perf script > redis.perf.stacks
$ stackcollapse-perf.pl redis.perf.stacks > redis.folded.stacks
$ flamegraph.pl redis.folded.stacks > redis.svg
默认情况下,perf 脚本会在当前 worker 中生成一个 perf.data 文件 目录。请参阅 perf 脚本文档 了解高级用法。
请参阅 FlameGraph 使用选项,了解更高级的堆栈跟踪可视化效果(如差分可视化效果)。
存档和共享录制的配置文件信息
这样就可以在 other 机器上分析 perf.data 内容 而不是发生集合的那个,您需要与 perf.data 文件,并在记录数据文件中找到具有 build-ID 的所有对象文件。 这可以在 perf-archive.sh 脚本的帮助下轻松完成:
$ perf-archive.sh perf.data
现在请运行:
$ tar xvf perf.data.tar.bz2 -C ~/.debug
在需要运行的机器上perf report
.
使用 bcc/BPF 的配置文件对堆栈跟踪进行采样
与 perf 类似,从 Linux 内核 4.9 开始,BPF 优化的分析现在完全 可用,并承诺降低 CPU 开销(因为堆栈跟踪是 frequency 在内核上下文中计数)和磁盘 I/O 资源。
除此之外,仅依靠 bcc/BPF 的配置文件工具,我们还 删除了 perf.data 和 intermediate 步骤(如果 Stack Traces 分析是我们的 主要目标。您可以使用 bcc 的配置文件工具直接输出折叠格式,对于 火焰图生成:
$ /usr/share/bcc/tools/profile -F 999 -f --pid $(pgrep redis-server) --duration 60 > redis.folded.stacks
通过这种方式,我们删除了所有预处理,并可以渲染 CPU 上的火焰 graph 替换为单个命令:
$ flamegraph.pl redis.folded.stacks > redis.svg
使用 Flame Graphs 可视化记录的配置文件信息
使用 bcc/BPF 进行呼叫计数分析
函数可能会消耗大量 CPU 周期,因为其代码速度较慢
或者因为它经常被调用。要回答函数的速率是多少
调用的,您可以依赖使用 BCC 的funccount
工具:
$ /usr/share/bcc/tools/funccount 'redis-server:(call*|*Read*|*Write*)' --pid $(pgrep redis-server) --duration 60
Tracing 64 functions for "redis-server:(call*|*Read*|*Write*)"... Hit Ctrl-C to end.
FUNC COUNT
call 334
handleClientsWithPendingWrites 388
clientInstallWriteHandler 388
postponeClientRead 514
handleClientsWithPendingReadsUsingThreads 735
handleClientsWithPendingWritesUsingThreads 735
prepareClientToWrite 1442
Detaching...
上面的输出显示,在跟踪时,Redis 的 call() 函数是 调用 334 次,handleClientsWithPendingWrites() 388 次,等等。
使用性能监控计数器 (PMC) 进行硬件事件计数
许多现代处理器都包含性能监控单元 (PMU) 公开 性能监控计数器 (PMC)。PMC 对于了解 CPU 至关重要 行为,包括内存 I/O、停顿周期和缓存未命中,并提供 其他任何地方都无法获得的低级 CPU 性能统计数据。
PMU 的设计和功能是特定于 CPU 的,您应该评估
使用 CPU 支持的计数器和功能perf list
.
要计算每个周期的指令数,微作数 executed 的 Cycle 数,则表示未调度 Micro Ops 的周期数, 内存上停止的周期数(包括每个内存类型的停止),对于 持续时间为 60 秒,特别是对于 Redis 进程:
$ perf stat -e "cpu-clock,cpu-cycles,instructions,uops_executed.core,uops_executed.stall_cycles,cache-references,cache-misses,cycle_activity.stalls_total,cycle_activity.stalls_mem_any,cycle_activity.stalls_l3_miss,cycle_activity.stalls_l2_miss,cycle_activity.stalls_l1d_miss" --pid $(pgrep redis-server) -- sleep 60
Performance counter stats for process id '3038':
60046.411437 cpu-clock (msec) # 1.001 CPUs utilized
168991975443 cpu-cycles # 2.814 GHz (36.40%)
388248178431 instructions # 2.30 insn per cycle (45.50%)
443134227322 uops_executed.core # 7379.862 M/sec (45.51%)
30317116399 uops_executed.stall_cycles # 504.895 M/sec (45.51%)
670821512 cache-references # 11.172 M/sec (45.52%)
23727619 cache-misses # 3.537 % of all cache refs (45.43%)
30278479141 cycle_activity.stalls_total # 504.251 M/sec (36.33%)
19981138777 cycle_activity.stalls_mem_any # 332.762 M/sec (36.33%)
725708324 cycle_activity.stalls_l3_miss # 12.086 M/sec (36.33%)
8487905659 cycle_activity.stalls_l2_miss # 141.356 M/sec (36.32%)
10011909368 cycle_activity.stalls_l1d_miss # 166.736 M/sec (36.31%)
60.002765665 seconds time elapsed
重要的是要知道 PMC 可以通过两种截然不同的方式 (计数和采样),我们只关注 PMC 计数 为了这个分析。Brendan Greg 在下面的链接中清楚地解释了这一点。