
1.2.5 可视化GPM编程
在理解GPM的基本模型和初始化的生命周期及过程的基础上,还能不能通过一些工具来看一下程序的GPM模型在执行过程中的数据呢?Go语言提供了两种方式可以查看一个程序的GPM数据。
1.go tool trace
trace记录了运行时的信息,能提供可视化的Web页面。下面举一个简单的例子,主要的流程是,main()函数创建trace,trace会运行在单独的Goroutine中,然后main()打印"Hello World",最后退出,代码如下:

运行,结果如下:
$go run trace.go Hello World
这里会发现,当前路径下会得到一个trace.out文件,可以用工具go tool打开该文件,结果如下:
$go tool trace trace.out 2021/02/23 10:44:11 Parsing trace... 2021/02/23 10:44:11 Splitting trace... 2021/02/23 10:44:11 Opening browser.Trace viewer is listening on http://127.0.0.1:33479
通过浏览器打开http://127.0.0.1:33479网址,单击view trace能够看见可视化的调度流程,如图1.27所示。
图1.27所示的是一个当前代码运行的进程所包含的与全部Go程序相关的流程和各自的时间轴,其中左侧部分包含的信息菜单如图1.28所示。

图1.27 go tool trace可视化

图1.28 go tool trace可视化左侧菜单
(1)G信息。单击Goroutines那一行可视化的数据条,会看到一些更详细的信息,如图1.29所示。

图1.29 G相关信息
一共有两个G在程序中,一个是特殊的G0,是每个M必须有的一个初始化的G;另一个是G1,即Main Goroutine(执行main()函数的协程),在一段时间内处于可运行和运行的状态。
(2)M信息。单击Threads那一行可视化的数据条,会看到一些更详细的信息,如图1.30所示。

图1.30 M相关信息
一共有两个M在程序中,一个是特殊的M0;另一个是正处于Running执行状态的M1,这个M1是用于承载G1的线程。
(3)P信息。再来看左侧PROCS栏中的信息,这里列举的Proc 0和Proc 1均属于Process(处理器)的信息,如图1.31所示。

图1.31 P相关信息
G1中调用了main.main,创建了trace goroutine G18。G1运行在P1上,G18运行在P0上。这里有两个P,一个P必须绑定一个M才能调度G。接下来是M的信息,如图1.32所示。

图1.32 G18的出现
从图1.32中可以看到,G18在P0上被运行的时候,在Threads行新增了一个M的数据,单击可查看更详细的信息,如图1.33所示。

图1.33 更详细信息
新增的一个M2就是P0为了执行G18而动态创建的。
2.DeBug trace

编译上面的代码的结果如下:
$go build trace2.go
接下来,通过DeBug方式运行,结果如下:

接下来简单分析一下,上述输出结果的大致含义。
(1)SCHED:调试信息输出标志字符串,代表本行是Goroutine调度器的输出。
(2)0ms:从程序启动到输出这行日志的时间。
(3)gomaxprocs:P的数量,本例有两个P,因为默认的P的属性是和CPU核心数量一致,当然也可以通过GOMAXPROCS设置。
(4)idleprocs:处于idle状态的P的数量;通过gomaxprocs和idleprocs的差值,就可知道执行Go代码的P的数量。
(5)threads:os threads/M的数量,包含scheduler使用的m数量,加上runtime自用的类似sysmon这样的thread的数量。
(6)spinningthreads:处于自旋状态的os thread数量。
(7)idlethread:处于idle状态的os thread的数量。
(8)runqueue=0:Scheduler全局队列中G的数量。
(9)[0 0]:分别为两个P的local queue中的G的数量。
查看GPM数据go tool trace方式和DeBug trace方式有所差异。二者均能够看到一个程序运行中的GPM分布情况,go tool trace还提供了本地Web的可视化查看方式,而DeBug trace是通过命令行将结果输出到终端中。