欲了解更多排错与调试,请参见:https://github.com/pyroscope-io/pyroscope
图源:原作者
“调试就像在犯罪电影中扮演侦探,而你同时也是凶手。”— Filipe Fortes
以本人经验来讲,在Python服务器上调试性能上的问题可令人沮丧。通常,流量的增加或暂时的故障都可能导致终端用户提交错误报告。
通常,因为基本不可能复现故障场景,所以很难找出是哪一部分代码或者架构导致了服务器的性能问题。
本文介绍如何使用火焰图持续剖析你的代码,并准确定位是哪些代码导致的性能问题。
为什么您必须关心中央处理器性能
通常公司使用云端(如AWS,谷歌云端等)运行软件时会将中央处理器的利用率作为程序性能的一个指标。
“Netflix非常关注中央处理器的利用率,并且将其作为我们云端规模的关键指标。如果,中央处理器利用率能下降1%或者5%就是一个很大的胜利。”—
Brendan Gregg,Netflix高级性能架构师
不管怎样,较小的公司也可看到提升性能的好处。因为无论大小,在软件运行时,中央处理器时常与以下两个重要方面直接相关:
1.服务器的花费 : 所需的中央处理器资源越多,运行服务器的成本越高。
2.终端用户经验 : 服务器的中央处理器负荷越大,网站或服务器的运行速度越慢。
因此,当你看到如下中央处理器利用率图表时:
如你的中央处理器利用率看起来如上,极可能性能表现不好
在中央处理器利用率为100%,你可假设:
- 终端用户的经验并不好(如:应用程序/网站加载速度很慢)
- 配置新服务器处理额外负荷后,服务器成本将增加
关键是哪部分代码增加了中央处理器的利用率?火焰图这时就派上了用场!
如何使用火焰图调试性能问题(并在服务器上节省下6.6万美元)
假设下面的火焰图对应呈现上图中央处理器利用率飙升的时段。在此高峰期间,服务器的中央处理器的使用情况如下:
- Foo()消耗的时间是75%
- Bar()消耗的时间是25%
- 10万美元的服务器成本
您可把火焰图视为超详细的饼图,其中:
- 火焰图的宽度代表着整个时段
- 每个节点代表一个功能
- 最大的节点占用了大部分中央处理器资源
- 每个节点被其上方的节点调用
在这种情况下,foo()
占据了整时间范围的75%,因此我们可以改进foo()
及其调用的函数来减少中央处理器的利用率(并节省$$)。
用Pyroscope工具创建火焰图和表格
为了用代码重现上文的例子,我们将使用Pyroscope工具 — 专门针对性能调试问题提供持续的性能分析,并且是开源。
为了模拟服务器,我写了 work(duration)
函数,该函数在该持续时间段内模拟工作。这样,我们就可以通过下述代码构建火焰图,来复现foo()
所用的75%时间和 bar()
所用的25%时间:
# 模拟每次迭代中央处理器的时间
def work(n):
i = 0
while i < n:
i += 1
# 模拟中央处理器运行7.5秒
def foo():
work(75000)
# 模拟中央处理器运行2.5秒
def bar():
work(25000)
然后,假设你进行了代码优化,把 foo()
的时间从75,000降到8000,但其它代码不变。新代码及火焰图所示如下:
# 模拟中央处理器运行0.8秒
def foo():
# work(75000)
work(8000)
# 模拟中央处理器运行2.5秒
def bar():
work(25000)
profiling_functions_reversed.py
优化foo ()
为我们节省了6.6万美金
火焰图帮助我们能立即发现foo() 是我们代码中的瓶颈。进行优化之后,我们大幅降低中央处理器的利用率。
这代表你的中央处理器的总利用率下降了66%。若你之前为了服务器支付10万美元,那现在只需要3.4万美元就能处理等量的负荷。