LS支持持续性能数据采集与监控

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 小王所在的公司最近在做降本增效,要求上半年每个人的微服务接口性能提升至少30%。小王思虑很久也没想到优化点,于是开始向ChatGPT寻求帮助。

遇事不慌,先问ChatGPT

小王提问“如何定位性能问题”,ChatGPT 给出了3个答案。

第二个与第三个答案是本质同样使用时间模块记录程序函数的运行时时间,而这对于开发者来说,这可能是最为熟悉的方式,如下所示,使用4条Log 来定位性能瓶颈出现的性能范围,但这对于线上服务来说,不断地Log定位方式也意味着不断地重启,风险是可能带来不断地业务损失,但是更坏的一个问题是如果线上产生死锁等问题,重启也意味着”案发现场“的丢失,这也为下次问题的再次出现埋下隐患。

2023-03-17 15:55:01 [INF] outer function start
2023-03-17 15:55:10 [INF] inner function start
2023-03-17 15:55:13 [INF] inner function end
2023-03-17 15:55:30 [INF] outer function end

而ChatGPT 的首推答案也指出可以使用性能分析工具来帮助开发者定位瓶颈问题,随着软件工程的演进,性能分析工具的地位也愈加上升,对于一些新出现的编程语言,如Golang,性能诊断已经成为语言标准库的一部分。

什么是性能采集?

对于前端开发者来说,Chrome 浏览器中的性能分析可能是最为直观的性能采集,下图中Chrom记录加载网页每个事件的时间戳和持续时间,包括JavaScript函数执行、网络请求和渲染过程等。而这些数据也为前端开发者寻找网页性能瓶颈与定位优化点提供了依据。

以下内容将介绍常见的CPU 与内存 性能采集的基本原理,了解为什么性能采集是“统计值”而不是精确值。

CPU 性能采集基本原理

以CPU 性能Perf采集为例,如下图所示,实线代表某单核CPU 的持续运行所经过的时间,而pid/func1、pid/func2 以及pid/func3 表示在此段运行时间内此单核所经过的函数,当每隔一段时间产生一个中断(箭头处),此单核CPU必然执行于某一具体的函数。对于下图来说,可以产生如下统计结果,根据命中率计算可知,在采集时间段内pid/fun3花费了此核CPU的62.5%。对于一般的程序CPU 中断的间隔一般为100ns,从上述基本原理可知,当CPU 采样时间越大,中断间隔忽略的函数越多,因此CPU 性能采集是统计结果,可能存在误差。

函数

中断命中次数

占用CPU比率

pid/fun1

1

6.25%

pid/fun2

5

31.25%

pid/fun3

10

62.5%

内存性能采集基本原理

与 CPU 性能采集原理不同的是,内存性能数据因为要拿到具体的分配数量,因此内存性能采集的通常做法是将工具集成到内存的分配器中,当语言进行内存分配时,拿到当前的函数调用栈,最终将样本聚合,便可推算出函数的内存分配了。以下代码为GO语言的内置Profiling行为,当开启Profling后,每次功能mallocgc 分配512K大小内存,便触发一次profilealloc函数记录分配行为。

func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
    ...
    if rate := MemProfileRate; rate > 0 {
     // Note cache c only valid while m acquired; see #47302
     if rate != 1 && size < c.nextSample {
      c.nextSample -= size
  } else {
      profilealloc(mp, x, size)
  }
 }
    ...
}
func profilealloc(mp *m, x unsafe.Pointer, size uintptr) {
    c := getMCache()
    if c == nil {
     throw("profilealloc called without a P or outside bootstrapping")
 }
    c.nextSample = nextSample()
    mProf_Malloc(x, size)
}

如下图所示,实线代表某单核CPU 的持续运行所经过的时间,而pid/func1、pid/func2 以及pid/func3 表示在此段运行时间内此单核所经过的函数,每个箭头处表示一次内存分配,但由于默认512K记录一次分配行为,因此可以产生如下统计结果,第一次与第二次malloc行为因为分配不满足512K记录大小规则,因此跳该过记录,如将MemProfileRate设置为1,可以完全精确的记录每一个函数的内存分配,但是性能开销带来的成本是巨大的,因此内存性能采集也同样是统计结果,可能存在误差,因此Profling程序一般并不是采用上述512K的固定值进行,而是以512K为平均值的指数分布中随机取一个值,降低误差影响。

函数

分配次数

pid/fun1

1

pid/fun2

1

pid/fun3

2

为什么需要持续性能分析?

性能采集带给开发者分析程序性能的工具,开发者可以清楚的了解性能采集时刻的CPU 或内存的分布,通过分析这些数据,开发者可以快速定位程序性能瓶颈问题。但现实情况可能更加复杂,应用的部署结构已经从单体结构走向微服务结构,并且随着云化浪潮,部署也存在着多云、多Region、多架构机器等诸多行为。因此宏观的角度分析服务的性能变得更加重要,这也就意味着性能采集多了一个新的维度“时间”,当有了时间维度,性能采集便成为了持续的性能分析,这也意味着分析从单体分析变成了高纬度的对比分析,也让以下场景的性能分析成为了可能。

场景

性能采集

持续性能分析

对比某服务测试环境与生产环境的性能差异

N

Y

对比某服务版本1.0 与版本2.0 性能差异

N

Y

对比某服务在Arm 服务器与Amd 服务器性能差异

N

Y

对比某服务在北京地域与杭州地域服务的性能差异

N

Y

......

SLS 持续性能数据采集与分析

SLS 持续性能分析为开发者提供开放、一站式性能持续分析平台,目前已经对于Pyroscope 开源SDK 或Agent 完全兼容,后续SLS 持续性能分析会持续拓展接入兼容性,如支持对原生Golang Profile接口的采集或对Datadog Profling SDK 等接入的兼容。

性能数据存在着不同的协议,如pprof、JFR、Tire 等,不同协议对性能数据的承载能力不同,如Tire 协议一般仅承载CPU 性能数据,而pprof 可承载cpu、memory、mutex、goroutines 等多种性能数据,下文将使用基于pprof 协议的Go 语言样例介绍SLS持续分析的功能。

类型

Go

Java

NodeJs

Python

CPU时间

分配堆

争用

线程

实际时间

数据探索

在SLS 持续性能分析的数据查询界面,开发者可以选择关注服务性能类别与时间后,整个数据查询探索界面会通过火焰图与表格两种方式显示性能数据,下图展示了Go的一个demo 程序在CPU性能分析下的样例。但对于持续性能分析更重要的是价值是更高维度的数据探索,因此SLS 持续性能分析支持通过动态标签组合满足开发者对性能数据的高纬度探索需求,如下图所示的性能查询条件为进行sls-mall 集群下、test环境、version1版本的性能分析。

对于持续性能分析,时间维度的加入也就意味着可以进行更多的性能数据的对比,在在SLS 持续性能分析探索界面,开发者可以轻松的选择对比性能的时间范围,通过火焰图或者表格方式进行数据探索,如下图为对比Go Demo 程序过去一小时性能与当前性能的差异,通过灰色颜色可以快速得知,服务性能没有明显变化。

  • 灰色: 代表性能性能相近
  • 红色:代表性能下降,颜色越深代表性能下降越严重
  • 蓝色:代表性能提升,颜色约深代表性能上升越多

当然性能对比界面也同样支持利用动态标签组合的方式满足满足开发者对性能数据更高的对比需求,如下图展示了相同服务不同版本的性能差异。

优化案例

小王公司待优化的服务为一个gRPC服务,在限定资源(CPU限制为250m核,内存限制为50M),小王使用日志方式打印了压测下此服务待优化接口性能,发现QPS 仅仅在30左右。

CPU 优化

  1. 定位问题

小王将此服务接入了SLS 持续性能分析发现有一个内部库函数regexp.MatchString消耗了应用程序的大量CPU性能。

  1. 分析问题

小王发现此程序的业务逻辑为计算存储文件行包含输入字符串的数量,使用regexp.MatchString进行匹配,但此行为并未涉及复杂的正则提取,字符串包含操作已经可以满足业务需求,且相比于正则匹配,字符串匹配的算法复杂度更低,因此小王进行了下面的优化。

  1. 性能对比

小王将此优化版本标记版本为version2,通过SLS 持续性能对比界面分析此次"小" 改动的带来的性能差异,下图展示了 version :1version:2(CPU 优化后)的差别,浅蓝色代表部分优化,深蓝色代表全部优化,小王发现经过对正则表达式的优化,上层函数耗时竟然下降77.29%。

  1. 验证优化

小王再次打开自己最初打印的QPS 统计日志,发现同等资源条件下,程序10次平均QPS 提升到1399左右,接口性能得到接近40倍的性能提升,小王出色的完成了公司提出的降本增效任务。

内存优化

  1. 定位问题

小王出色的完成了公司降本增效的认为后,公司领导很高兴,提出能不能把程序的内存也优化一下。小王公司的程序为Go程序,GO的内存性能指标分为alloc_objectsalloc_spaceinuse_objects以及inuse_spacealloc_*指标为从程序启动到目前分配的大小或数量,可以帮助定位频繁分配对象的方法,降低Go程序GC 压力,inuse_* 方法是内存中存在的对象大小或数量,可以帮助定位内存泄漏等问题。小王继续通过SLS 持续性能分析来分析上述4类指标。小王通过对不同指标的查看,发现服务alloc_space指标存在异常,大量内存分配行为都存在于标准库strings.split函数。

  1. 分析问题

小王通过对业务逻辑的梳理,基本逻辑为从本地文件读取文件全量数据,之后按行进行切割,接着与输入进行字符串进行匹配比较,按行切割时存在大量小字符串分配,通过slice 偏移量截取字符串对比,可以避免大量小字符串的分配与释放,因此小王进行了如下优化。

  1. 性能对比

小王将此优化版本标记版本为version3,通过SLS 持续性能对比界面分析此次改动的带来的内存性能差异,下图展示了 version :3version:2(CPU 优化后)的差别,浅蓝色代表部分优化,深蓝色代表全部优化,小王发现经过对本次字符串优化,核心函数空间分配下降59.46%。

小王推测内存分配空间的减少也就意味对象分配时间与GC 时间的减少,因此推测本次优化可能还会带来性能的提升,因此继续使用CPU性能对比查看CPU 性能的变化,小王发现CPU 性能确实得到了19.71%的性能提升。

  1. 验证优化

小王再次打开自己的QPS 统计日志,发现同等资源条件下,验证第二次性能优化的结果,程序10次平均QPS 提升到1840左右,性能再次获得了30%的提升。

优化回顾

上述案例中小王的2次优化,第一次是对正则的优化,第二次是对字符串使用的优化,都是对标准库的使用优化,这样的问题可能也潜藏在每一位开发者的程序中,通过SLS持续性能分析的查询、聚合、对比能力,可以快速的定位应用程序潜藏的顽疾。

展望

SLS性能持续分析基于开放的接入生态与持续性能分析的理念所构建(开放接入部分已在iLogtail开源),基于SLS 性能持续分析,将为广大开发者提供开箱即用、一站式的的性能观测体验,助力开发者轻松面对多云、多Region、多版本、微服务等场景下的性能分析需求,目前已经发布部分地域, 欢迎大家试用与交流。在后续的版本中,我们将为开发者提供更为便捷、丰富的性能监控接入渠道,更强大的性能分析体验。

  • 更多开源Agent与SDK 兼容,如Datadog 等。
  • 更多无需集成SDK 的接入方式,如Golang下的主动抓取采集等。
  • 在交互分析上,提供更便捷、性能更高的迷你图划线对比。
  • ...

参考文档

Linux下做性能分析:perf

Google-Wide Profiling: A Continuous Profiling Infrastructure for Data Centers

Profiling concepts bookmark_border

What is continuous profiling?

相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
目录
相关文章
|
Java Spring 容器
【二十二】springboot整合拦截器实战并对比过滤器
【二十二】springboot整合拦截器实战并对比过滤器
226 0
|
传感器 数据采集
振弦采集仪结合无线中继扩展应用岩土工程监测
随着岩土工程规模不断扩大和复杂化,对于结构物和土壤的变形、裂缝、沉降等方面的监测越来越重要。传统的监测方式需要现场人员进行检测,费时费力,而且数据采集不及时,易受天气等外界因素影响,因此需要一种更加便捷、准确、实时的监测方式。振弦采集仪结合无线中继扩展应用岩土工程监测是一种非常有效的解决方案。
振弦采集仪结合无线中继扩展应用岩土工程监测
|
Kubernetes 容器 Perl
在K8S中,Pod的重启策略是什么?
在K8S中,Pod的重启策略是什么?
|
5G 网络架构
计算机网络与技术——物理层
物理层是计算机网络OSI模型中最低的一层,它规定了为传输数据所需要的物理链路的创建、维持、拆除,以及提供具有机械的、电子的、功能的和规范的特性。物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流,而不是具体的传输媒体。物理层的主要任务是为数据传输提供可靠的环境,它为设备之间的数据通信提供传输媒体及互连设备,确保原始的数据可在各种物理媒体上传输。物理层的基本概念包括物理层的作用、物理层的主要任务、数据在传输媒体上的传输方式等。用于物理层的协议也常称为物理层规程(procedure),其实物理层规程就是物理层协议。
计算机网络与技术——物理层
|
人工智能
[AI Google] TimesFM:AI预测股市价格,能否助我财务自由?
探索谷歌TimesFM模型,看看它能否通过预测股票价格帮助我们实现财务自由。
[AI Google] TimesFM:AI预测股市价格,能否助我财务自由?
|
JSON 数据格式
将json格式的数据快速转换为excel,使用在线工具轻松搞定
将json格式的数据快速转换为excel,使用在线工具轻松搞定
892 0
|
算法 数据挖掘 数据库
【数据挖掘】关联规则、频繁项集、闭项集详解(图文解释 超详细)
【数据挖掘】关联规则、频繁项集、闭项集详解(图文解释 超详细)
2385 1
|
机器学习/深度学习 算法 Java
普林斯顿算法讲义(二)(4)
普林斯顿算法讲义(二)
297 1
|
XML 缓存 安全
HTTP状态码I
HTTP状态码I
975 1
|
XML 存储 数据格式
【JavaWeb】HTTP状态码100、200、300、400、500、600的含义
【JavaWeb】HTTP状态码100、200、300、400、500、600的含义
1678 0