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
这很可能是因为您在Windows系统上运行。Go语言会将任务委托给底层操作系统处理,因此在Linux系统上会使用系统调用,而在Windows系统上则主要使用暴露的C语言API。
在Go程序中,即使没有显式使用cgo,runtime.cgocall出现在pprof CPU性能分析顶部也是正常现象。这是因为Go运行时系统内部会使用cgo调用来执行系统调用和底层操作系统交互。
技术解释
runtime.cgocall是Go运行时用于在Go代码和C代码之间切换的函数。即使CGO_ENABLED=0,Go运行时仍然会在以下情况下使用cgo调用:
- 系统调用 - 文件I/O、网络操作、时间相关函数
- 调度器操作 - 线程管理和调度
- 垃圾收集 - 内存管理相关操作
- 定时器处理 - 特别是长时间运行的程序中的定时器
示例分析
对于使用定时器的长时间运行程序,这种情况尤其常见。例如:
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 pprof的web命令或list命令来查看更详细的调用链,确定哪些具体的操作导致了大量的cgo调用:
go tool pprof cpu.prof
(pprof) list main.main
(pprof) web
这样可以查看具体的函数调用路径,确认是否是预期的系统调用导致的性能特征。


