Golang中Runtime.cgocall为何总是出现在pprof CPU性能分析顶部

Golang中Runtime.cgocall为何总是出现在pprof CPU性能分析顶部 我决定试用一下pprof工具,因为之前从未使用过。我在程序代码周围添加了https://golang.org/pkg/runtime/pprof/中展示的代码。CPU性能分析文件生成正确,于是我使用go tool pprof cpu.prof打开它,得到以下信息:

Main binary filename not available.
Type: cpu
Time: Jul 25, 2018 at 10:24am (CEST)
Duration: 26.90s, Total samples = 32.03s (119.06%)
Entering interactive mode (type "help" for commands, "o" for options)

到目前为止一切看起来都不错,于是我继续执行(pprof) top10

结果正确显示,但我非常惊讶地看到:

Showing nodes accounting for 29.21s, 91.20% of 32.03s total
Dropped 198 nodes (cum <= 0.16s)
Showing top 10 nodes out of 45
      flat  flat%   sum%        ■■■   ■■■%
    26.70s 83.36% 83.36%     26.70s 83.36%  runtime.cgocall
     0.77s  2.40% 85.76%      1.49s  4.65%  runtime.scanobject
     0.46s  1.44% 87.20%      0.46s  1.44%  runtime.stdcall1
     0.39s  1.22% 88.42%      0.39s  1.22%  runtime.memclrNoHeapPointers
     0.22s  0.69% 89.10%      0.35s  1.09%  runtime.greyobject
     0.17s  0.53% 89.63%      0.66s  2.06%  runtime.sweepone
     0.14s  0.44% 90.07%      0.39s  1.22%  runtime.lock
     0.13s  0.41% 90.48%      0.19s  0.59%  runtime.heapBitsForObject
     0.12s  0.37% 90.85%      0.17s  0.53%  runtime.scanblock
     0.11s  0.34% 91.20%      1.88s  5.87%  runtime.gcDrain

问题是,我在程序中从未使用过cgo。

$ go env
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\Sander\AppData\Local\go-build
set GOEXE=.exe
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=C:\Users\Sander\go
set GORACE=
set GOROOT=C:\Go
set GOTMPDIR=
set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64
set GCCGO=gccgo
set CC=gcc
set CXX=g++
set CGO_ENABLED=0
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\Sander\AppData\Local\Temp\go-build411961950=/tmp/go-build -gno-record-gcc-switches

如你所见,CGO_ENABLED设置为0,但runtime.cgocall仍然出现在我的CPU性能分析结果的顶部。

有没有人知道为什么会发生这种情况,以及这是否需要关注?这也是一个使用定时器的长时间运行程序。


更多关于Golang中Runtime.cgocall为何总是出现在pprof CPU性能分析顶部的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

谢谢,我会在Linux系统上尝试一下,看看是否还会得到相同的结果!

更多关于Golang中Runtime.cgocall为何总是出现在pprof CPU性能分析顶部的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这很可能是因为您在Windows系统上运行。Go语言会将任务委托给底层操作系统处理,因此在Linux系统上会使用系统调用,而在Windows系统上则主要使用暴露的C语言API。

在Go程序中,即使没有显式使用cgo,runtime.cgocall出现在pprof CPU性能分析顶部也是正常现象。这是因为Go运行时系统内部会使用cgo调用来执行系统调用和底层操作系统交互。

技术解释

runtime.cgocall是Go运行时用于在Go代码和C代码之间切换的函数。即使CGO_ENABLED=0,Go运行时仍然会在以下情况下使用cgo调用:

  1. 系统调用 - 文件I/O、网络操作、时间相关函数
  2. 调度器操作 - 线程管理和调度
  3. 垃圾收集 - 内存管理相关操作
  4. 定时器处理 - 特别是长时间运行的程序中的定时器

示例分析

对于使用定时器的长时间运行程序,这种情况尤其常见。例如:

package main

import (
    "fmt"
    "runtime/pprof"
    "os"
    "time"
)

func main() {
    f, _ := os.Create("cpu.prof")
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()

    ticker := time.NewTicker(100 * time.Millisecond)
    defer ticker.Stop()
    
    for i := 0; i < 1000; i++ {
        select {
        case <-ticker.C:
            // 定时器处理会触发系统调用
            fmt.Printf("Tick %d\n", i)
        }
    }
}

在这个例子中,定时器的处理会通过系统调用实现,导致runtime.cgocall出现在性能分析中。

是否需要关注

通常情况下,这不需要特别关注runtime.cgocall的高占用率表明:

  • 程序正在进行大量的系统调用
  • 程序有频繁的I/O操作
  • 定时器处理频繁

如果程序性能符合预期,这属于正常现象。只有当程序性能出现问题时,才需要进一步分析具体的系统调用来源。

可以通过go tool pprofweb命令或list命令来查看更详细的调用链,确定哪些具体的操作导致了大量的cgo调用:

go tool pprof cpu.prof
(pprof) list main.main
(pprof) web

这样可以查看具体的函数调用路径,确认是否是预期的系统调用导致的性能特征。

回到顶部