V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
GopherDaily
V2EX  ›  Go 编程语言

约束 GOMAXPROCS 带来的收益

  •  
  •   GopherDaily · 2023-01-18 16:29:39 +08:00 · 2280 次点击
    这是一个创建于 679 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近看了一遍 runtime ,去尝试了之前遗留的一个工作:调整 k8s 中容器的 GOMAXPROCS ,效果还不错。

    在 11:35 左右, 我们将应用的 GOMAXPROCS 从默认的 32/64 主动设置为 4, 对比 14:30 和 11:30 的数据可以看到:

    • go version: 1.17
    • 线程数量从 49 下降到 13, 符合预期
    • 受业务影响, QPS +13%
    • 接口平均响应时间 -9%, CPU -5%
    • GC 的耗时 -59%, STW(StopTheWorld) 的时间 -12%
    • goroutine 在 Runnable 停留的时间 +50%

    gomaxprocs_heavy_gc.png

    第 1 条附言  ·  2023-01-18 17:12:00 +08:00
    GMP 中的 M 和 P 数量多了之后,主要的成本在于:
    - go scheduler 的调度成本
    - go gc 的成本
    - os thread 切换的成本,默认 GOMAXPROCS 等同于 CPU 就是为了尽量一个 M 一个 C ,避免上下文切换
    第 2 条附言  ·  2023-01-18 17:18:11 +08:00
    https://github.com/j2gg0s/j2gg0s/blob/main/20230118_go_%E7%BA%A6%E6%9D%9FGOMAXPROCS%E5%B8%A6%E6%9D%A5%E7%9A%84%E6%94%B6%E7%9B%8A.md

    GOMAXPROCS 限制了用来并发执行代码的系统线程数量, 默认等于 CPU 的数量. 使用 CPU 做为 GOMAXPROCS 的默认值, 是为了平衡执行效率和调度成本. 我们即希望有尽可能多的线程来提高并行程度, 又不希望系统线程被频繁的切换. 所以在假设基本独占机器资源的前提下, 我们选择 CPU 的数量做为默认的线程数量.

    当我们的代码运行在 k8s 中, 基本面发生了一些改变. 一方面, 几十个应用共享一个宿主机(Node); 另一方面 Go 直接使用宿主机的 CPU 数量做为 GOMAXPROCS 的默认值. 于是出现了一些类似, 应用假设的 CPU 数量是 1, 但是默认创建了 64 个线程的情况.

    这种 k8s 下的默认行为是否合理, 我们很难直接从代码中得出一个结论. 通过控制变量的方式来观察结果似一个不错的选择
    23 条回复    2023-02-09 10:30:38 +08:00
    julyclyde
        1
    julyclyde  
       2023-01-18 16:48:13 +08:00
    那就需要分析一下原因了
    降低并发度之后反而提高了处理能力,说明并发的时候内耗比较大?
    kiwi95
        2
    kiwi95  
       2023-01-18 16:51:41 +08:00 via Android
    说明当前场景下并发调度开销比较大?
    loveuer
        3
    loveuer  
       2023-01-18 16:52:12 +08:00
    可以细说一下主要啥类型的业务么🧐
    chenqh
        4
    chenqh  
       2023-01-18 16:55:33 +08:00
    你这是 k8s 吧,golang 这种不应该是一个进程吃满的吗,又不是 python 那种
    GopherDaily
        5
    GopherDaily  
    OP
       2023-01-18 17:10:25 +08:00
    @julyclyde go scheduler 调度的成本,和 os thread 上下文切换的成本
    julyclyde
        6
    julyclyde  
       2023-01-18 17:13:52 +08:00
    是容器运行吗?
    runtime.NumCPU()和容器的核数是否一致?
    Maboroshii
        7
    Maboroshii  
       2023-01-18 17:15:06 +08:00
    k8s node 上还有其他 pod 吧,都想吃满机器 ,肯定有反效果
    GopherDaily
        8
    GopherDaily  
    OP
       2023-01-18 17:18:26 +08:00
    @loveuer 典型的 web 服务呗
    GopherDaily
        9
    GopherDaily  
    OP
       2023-01-18 17:19:21 +08:00
    @Maboroshii pod 是坑定有的,吃满不至于,我们一般是控制实际使用率在 40%以内,突然的情况下会到 60%
    Aoang
        10
    Aoang  
       2023-01-18 21:24:25 +08:00 via iPhone
    要压榨单机性能最好就别用 k8s (:

    个人实践,丢在 k8s 上的东西都是无状态的应用,开了自动扩缩容其他的就不需要操心了。
    对于有状态或者需要压榨单机性能的,例如数据库都不在 k8s 内。

    另,试着升下版本,GOGC 和 GOMEMLIMIT 还能对程序有进一步的提升。
    Dreamacro
        11
    Dreamacro  
       2023-01-18 22:47:31 +08:00 via iPad
    GopherDaily
        12
    GopherDaily  
    OP
       2023-01-18 23:06:33 +08:00
    @Aoang 自然,但是资源能节约要节约,成本核算都是 OKR
    GopherDaily
        13
    GopherDaily  
    OP
       2023-01-18 23:07:09 +08:00
    @Dreamacro CI/CD 里面直接注入环境变量也行,可控性更高
    hopingtop
        14
    hopingtop  
       2023-01-19 09:39:27 +08:00
    针对 Docker 运行 Go 确实有取宿主机的 CPU 来设置 MAXPROCS.
    当使用 Pod 限制资源的配置时,就不建议手动去调整 MAXPROCS , 这样做不好运维,所以可以选择 Uber 的一个库
    hopingtop
        15
    hopingtop  
       2023-01-19 09:43:21 +08:00
    @hopingtop
    https://github.com/uber-go/automaxprocs

    这库的原理就是取读取 容器里 /sys/fs/cgroup/cpu 里面的值,然后计算出一个合理的 MAXPROCS
    在你 main 函数执行之前通过 import 这个过程去初始化 MAXPROCS
    GopherDaily
        16
    GopherDaily  
    OP
       2023-01-19 10:51:22 +08:00
    @hopingtop 你是站在业务开发的角度来看
    liuxu
        17
    liuxu  
       2023-01-19 12:07:03 +08:00
    嗯,k8s 里面进程数是要手动设置下,除非单 pod ,不然 pod 自动扩展物理机进程数 X^n 上升

    物理机和 k8s 还是有很多细微的差别要处理
    GopherDaily
        18
    GopherDaily  
    OP
       2023-01-19 13:47:43 +08:00
    @liuxu 一直还没去关注,k8s/vm 分配 cpu 时间片的时候会不会主动尽量将单个 pod 聚集到某个 物理 c 上
    liuxu
        19
    liuxu  
       2023-01-19 14:24:47 +08:00
    @GopherDaily 物理 C 是物理 node 还是物理 cpu core ,前者可以用 statefulset 、node selector 和 affility 处理,后者 k8s 没有这个能力也不应该有这个能力
    GopherDaily
        20
    GopherDaily  
    OP
       2023-01-19 14:45:27 +08:00
    @liuxu 真实的、物理的 CPU
    情理上我也觉得 k8s 没有这个能力,但是 resources limit 也在 pod spec 中,所以可能还是可以揣测下
    eudore
        21
    eudore  
       2023-01-22 23:34:39 +08:00
    没配置对,uber 有个库就可以根据 limit 去设置 maxprocs ,jvm 也有相识的内置参数。
    paceewang1
        22
    paceewang1  
       2023-02-09 09:50:35 +08:00
    op 这用的是什么监控呢?
    GopherDaily
        23
    GopherDaily  
    OP
       2023-02-09 10:30:38 +08:00
    @paceewang1 prometheus+grafana
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3364 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 11:35 · PVG 19:35 · LAX 03:35 · JFK 06:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.