Redis CPU 性能分析

关于 CPU 性能分析和跟踪的性能工程指南

填写性能清单

Redis 的开发非常重视性能。我们尽最大努力在每次发布时确保您体验到非常稳定且快速的产品。

但是,如果您发现有改进 Redis 效率的空间,或者正在进行性能回归调查,那么您将需要一种简洁的方法来监控和分析 Redis 性能。

要做到这一点,您可以依赖不同的方法(根据我们打算进行的分析类型,有些方法比其他方法更适合)。Brendan Greg 在以下链接中列举了方法及其步骤的精选清单。

我们建议使用利用饱和度和错误 (USE) 方法来回答什么是您的瓶颈的问题。查看以下系统资源、指标和工具之间的映射,以便进行实际的深入分析:USE 方法

确保 CPU 是您的瓶颈

本指南假设您已按照上述方法之一执行了完整的系统健康检查,并确定了瓶颈是 CPU。如果您发现大部分时间都花在了 I/O、锁、计时器、分页/交换等阻塞上,本指南不适合您

构建先决条件

为了进行适当的 On-CPU 分析,Redis(以及任何动态加载的库,如 Redis 模块)需要将堆栈跟踪提供给跟踪器,您可能需要先修复此问题。

默认情况下,Redis 使用-O2开关编译(我们打算在分析期间保持此开关)。这意味着启用了编译器优化。许多编译器将帧指针省略为运行时优化(节省了一个寄存器),从而破坏了基于帧指针的堆栈遍历。这使 Redis 可执行文件更快,但同时也使 Redis(像任何其他程序一样)更难跟踪,可能会错误地将 On-CPU 时间定位到调用堆栈的最后一个可用帧指针,该调用堆栈可能更深(但无法跟踪)。

确保以下几点很重要:

  • 存在调试信息:编译选项-g
  • 存在帧指针寄存器:-fno-omit-frame-pointer
  • 我们仍然使用优化来获得生产运行时间的准确表示,这意味着我们将保留:-O2

您可以在 redis 主仓库中按如下方式进行操作:

$ make REDIS_CFLAGS="-g -fno-omit-frame-pointer"

一组用于识别性能回归和/或潜在On-CPU 性能改进的工具

本文档专门针对On-CPU资源瓶颈分析,这意味着我们感兴趣的是了解线程在 On-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 工具包。
  • 克隆 Brendan Greg 的 FlameGraph 仓库,并使difffolded.plflamegraph.pl文件可访问,以生成折叠的堆栈跟踪和火焰图。

使用 perf 或 eBPF 进行热点分析(堆栈跟踪采样)

通过在定时间隔对堆栈跟踪进行采样来分析 CPU 使用情况是一种快速简便的方法,可以识别性能关键的代码部分(热点)。

使用 perf 采样堆栈跟踪

要分析 redis-server 的用户级和内核级堆栈在特定时间段内(例如 60 秒)的 CPU 使用情况,采样频率为每秒 999 次

$ perf record -g --pid $(pgrep redis-server) -F 999 -- sleep 60

使用 perf report 显示记录的配置文件信息

默认情况下,perf record 会在当前工作目录中生成一个 perf.data 文件。

然后,您可以使用调用图输出(调用链、堆栈回溯)进行报告,最小调用图包含阈值为 0.5%,使用

$ perf report -g "graph,0.5,caller"

请参阅perf report 文档以了解高级过滤、排序和聚合功能。

使用火焰图可视化记录的配置文件信息

火焰图 允许快速准确地可视化频繁的代码路径。可以使用 Brendan Greg 在 github 上的开源程序生成它们,这些程序会从折叠的堆栈文件中创建交互式 SVG。

具体来说,对于 perf,我们需要将生成的 perf.data 转换为捕获的堆栈,并将每个堆栈折叠成单行。然后,您可以使用以下命令渲染 On-CPU 火焰图:

$ perf script > redis.perf.stacks
$ stackcollapse-perf.pl redis.perf.stacks > redis.folded.stacks
$ flamegraph.pl redis.folded.stacks > redis.svg

默认情况下,perf script 会在当前工作目录中生成一个 perf.data 文件。有关高级用法的文档,请参阅perf script

有关更高级的堆栈跟踪可视化(如差异可视化),请参阅FlameGraph 用法选项

存档和共享记录的配置文件信息

为了能够在与收集记录数据的机器不同的机器上分析 perf.data 内容,您需要与 perf.data 文件一起导出记录数据文件中找到的所有具有构建 ID 的目标文件。这可以使用perf-archive.sh脚本轻松完成

$ perf-archive.sh perf.data

现在请运行

$ tar xvf perf.data.tar.bz2 -C ~/.debug

在您需要运行perf report的机器上。

使用 bcc/BPF 的 profile 采样堆栈跟踪

与 perf 类似,从 Linux 内核 4.9 开始,BPF 优化后的分析现在完全可用,并且承诺在 CPU 上的开销更低(因为堆栈跟踪是在内核上下文中频繁统计的),并且在分析期间的磁盘 I/O 资源更低。

除此之外,仅依赖于 bcc/BPF 的 profile 工具,我们还删除了 perf.data 和中间步骤,如果堆栈跟踪分析是我们的主要目标。您可以使用 bcc 的 profile 工具直接输出折叠格式,以生成火焰图

$ /usr/share/bcc/tools/profile -F 999 -f --pid $(pgrep redis-server) --duration 60 > redis.folded.stacks

通过这种方式,我们消除了任何预处理,并且可以使用单个命令渲染 On-CPU 火焰图:

$ flamegraph.pl redis.folded.stacks > redis.svg

使用火焰图可视化记录的配置文件信息

使用 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 的,您应该使用perf list评估您的 CPU 支持的计数器和功能。

要计算每个周期的指令数、执行的微操作数、没有调度微操作的周期数、在内存上停滞的周期数(包括按内存类型划分的停滞数),持续时间为 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 在以下链接中对此进行了清晰的解释。

RATE THIS PAGE
Back to top ↑