欲了解更多排错与调试,请参见:https://github.com/pyroscope-io/pyroscope

python中计算火焰区域形状的无序变化性 python火焰图_python


图源:原作者

“调试就像在犯罪电影中扮演侦探,而你同时也是凶手。”— Filipe Fortes

以本人经验来讲,在Python服务器上调试性能上的问题可令人沮丧。通常,流量的增加或暂时的故障都可能导致终端用户提交错误报告。

通常,因为基本不可能复现故障场景,所以很难找出是哪一部分代码或者架构导致了服务器的性能问题。

本文介绍如何使用火焰图持续剖析你的代码,并准确定位是哪些代码导致的性能问题。

为什么您必须关心中央处理器性能

通常公司使用云端(如AWS,谷歌云端等)运行软件时会将中央处理器的利用率作为程序性能的一个指标。

“Netflix非常关注中央处理器的利用率,并且将其作为我们云端规模的关键指标。如果,中央处理器利用率能下降1%或者5%就是一个很大的胜利。”—
Brendan Gregg,Netflix高级性能架构师

不管怎样,较小的公司也可看到提升性能的好处。因为无论大小,在软件运行时,中央处理器时常与以下两个重要方面直接相关:

1.服务器的花费 : 所需的中央处理器资源越多,运行服务器的成本越高。

2.终端用户经验 : 服务器的中央处理器负荷越大,网站或服务器的运行速度越慢。

因此,当你看到如下中央处理器利用率图表时:

python中计算火焰区域形状的无序变化性 python火焰图_服务器_02


如你的中央处理器利用率看起来如上,极可能性能表现不好

在中央处理器利用率为100%,你可假设:

  • 终端用户的经验并不好(如:应用程序/网站加载速度很慢)
  • 配置新服务器处理额外负荷后,服务器成本将增加

关键是哪部分代码增加了中央处理器的利用率?火焰图这时就派上了用场!

如何使用火焰图调试性能问题(并在服务器上节省下6.6万美元)

假设下面的火焰图对应呈现上图中央处理器利用率飙升的时段。在此高峰期间,服务器的中央处理器的使用情况如下:

  • Foo()消耗的时间是75%
  • Bar()消耗的时间是25%
  • 10万美元的服务器成本

您可把火焰图视为超详细的饼图,其中:

  • 火焰图的宽度代表着整个时段
  • 每个节点代表一个功能
  • 最大的节点占用了大部分中央处理器资源
  • 每个节点被其上方的节点调用

在这种情况下,foo()占据了整时间范围的75%,因此我们可以改进foo()及其调用的函数来减少中央处理器的利用率(并节省$$)。

用Pyroscope工具创建火焰图和表格

为了用代码重现上文的例子,我们将使用Pyroscope工具 — 专门针对性能调试问题提供持续的性能分析,并且是开源。

为了模拟服务器,我写了 work(duration) 函数,该函数在该持续时间段内模拟工作。这样,我们就可以通过下述代码构建火焰图,来复现foo() 所用的75%时间和 bar()所用的25%时间:

python中计算火焰区域形状的无序变化性 python火焰图_服务器_03

# 模拟每次迭代中央处理器的时间
def work(n):
    i = 0
    while i < n:
        i += 1

# 模拟中央处理器运行7.5秒
def foo():
    work(75000)

# 模拟中央处理器运行2.5秒
def bar():
    work(25000)

profiling_functions.py

然后,假设你进行了代码优化,把 foo() 的时间从75,000降到8000,但其它代码不变。新代码及火焰图所示如下:

python中计算火焰区域形状的无序变化性 python火焰图_服务器_04

# 模拟中央处理器运行0.8秒
def foo():
    # work(75000)
    work(8000)

# 模拟中央处理器运行2.5秒
def bar():
    work(25000)

profiling_functions_reversed.py

优化foo () 为我们节省了6.6万美金

火焰图帮助我们能立即发现foo() 是我们代码中的瓶颈。进行优化之后,我们大幅降低中央处理器的利用率。

python中计算火焰区域形状的无序变化性 python火焰图_cpu_05

这代表你的中央处理器的总利用率下降了66%。若你之前为了服务器支付10万美元,那现在只需要3.4万美元就能处理等量的负荷。