『每周译Go』Go 语言的 goroutine 性能分析

本文档最后一次更新时所用的 Go 版本是 1.15.6,但是大多数情况下,新老版本都适用。

描述

Go 运行时在一个称为 allgs 简单切片追踪所有的 goroutines。这里面包含了活跃的和死亡的 goroutine 。死亡的 goroutine 保留下来,等到生成新的 goroutine 时重用。

Go 有各种 API 来监测 allgs中活跃的 goroutine 和这些 goroutines 当前的堆栈跟踪信息,以及各种其他属性。一些 API 将这些信息公开为统计摘要,而另外一些 API 则给每个单独的 goroutine 信息提供查询接口。

尽管 API 之间有差异,但是活跃的 goroutine 都有如下共同 定义

  • 非死

  • 不是系统 goroutine,也不是 finalizer goroutine。

换句话说,正在运行的 goroutine 和那些等待 i/o、锁、通道、调度的 goroutine 一样,都被认为是活跃的。尽管人们可能会天真的认为后面那几种等待的 goroutine 是不活跃的。

开销

Go 中 所有可用的 goroutine 分析都需要一个 O(N) stop-the-world 阶段。这里的 N 是指已分配 goroutine 的数量。一个简单的基准测试 表明,当使用 runtime.GoroutineProfile() API 时,每个 goroutine 的世界会停止约 1 个µs。但是这个数字可能会随着诸如程序的平均堆栈深度、死掉的 goroutines 数量等因素的变化而波动。

根据经验,对于延迟非常敏感并使用数千个活跃 goroutine 的应用程序,在生产中使用 goroutine 分析可能需要谨慎一些。因此,对于包含大量的 goroutine ,甚至 Go 本身这样的应用程序来说,使用 goroutine 分析可能不是一个好主意。

大多数应用程序不会产生大量的 goroutine,并且可以容忍几毫秒的额外延迟,在生产中持续 goroutine 性能分析应该没有问题。

Goroutine 属性

Goroutines 有很多属性 可以帮助调试 Go 应用程序。下面的属性非常有趣,并且可以通过文章后面描述的 API 不同程度地暴露。

  • goid: goroutine 的唯一 id, 主 goroutine 的 id 为1.

  • atomicstatus: goroutine 的状态如下:

    • idle: 刚分配

    • runnable: 在运行队列上,等待调度

    • running: 在操作系统线程上执行

    • syscall: 在系统调用时阻塞

    • waiting: 等待调度,见g.waitreason

    • dead: 刚刚退出或被重新初始化

    • copystack: 堆栈当前正在移动

    • preempted: 抢占

  • waitreason:goroutine 等待的原因,比如 sleep、channel 操作、i/o、gc 等等。

  • waitsince: goroutine 进入 waiting 或者 syscall 状态的大约时间戳,由等待启动后第一个 GC 确定。

  • labels: 可以附加到 goroutines 上的一系列 键/值分析标签。

  • stack trace: 当前正在执行的函数及其调用者。要么是文件名、函数名和行号的纯文本输出,要么是程序计数器地址的一个切片 (pcs)。你也可以进一步研究更多的细节比如:文件名、函数名和行号的纯文本可以转换成 pcs 吗?

  • gopc: go ...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值