我知道[1]。通过几行代码,我只想从CPU使用率最高的前n个进程中提取当前的CPU使用率。或多或少都是前5排的顶部。使用github.com/shirou/gopsutil/process这很简单:
// file: gotop.go
package main
import (
"log"
"time"
"sort"
"github.com/shirou/gopsutil/process"
)
type ProcInfo struct{
Name string
Usage float64
}
type ByUsage []ProcInfo
func (a ByUsage) Len() int { return len(a) }
func (a ByUsage) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByUsage) Less(i, j int) bool {
return a[i].Usage > a[j].Usage
}
func main() {
for {
processes, _ := process.Processes()
var procinfos []ProcInfo
for _, p := range processes{
a, _ := p.CPUPercent()
n, _ := p.Name()
procinfos = append(procinfos, ProcInfo{n, a})
}
sort.Sort(ByUsage(procinfos))
for _, p := range procinfos[:5]{
log.Printf(" %s -> %f", p.Name, p.Usage)
}
time.Sleep(3 * time.Second)
}
}
Run Code Online (Sandbox Code Playgroud)
虽然此实现gotop中的刷新率与top一样为 3 秒,但gotop 的刷新率约为 3 秒。像top那样获取这些值对 CPU 使用率的要求高出 5 倍。有什么技巧可以更有效地读取 5 个最消耗的进程吗?我还尝试找到top的实现,看看它是如何实现的。
psutils是造成这种速度减慢的原因吗?我发现cpustat也在GO 中实现。但甚至sudo ./cpustat -i 3000 -s 1似乎没有那么高效top。
主要动机是通过相当少量的计算工作来监视当前机器的使用情况,以便它可以作为服务在后台运行。
看来,即使htop也只读取 /proc/stat。
按照评论中的建议进行编辑 是分析时的结果
Showing top 10 nodes out of 46 (cum >= 70ms)
flat flat% sum% cum cum%
40ms 40.00% 40.00% 40ms 40.00% syscall.Syscall
10ms 10.00% 50.00% 30ms 30.00% github.com/shirou/gopsutil/process.(*Process).fillFromStatusWithContext
10ms 10.00% 60.00% 30ms 30.00% io/ioutil.ReadFile
10ms 10.00% 70.00% 10ms 10.00% runtime.slicebytetostring
10ms 10.00% 80.00% 20ms 20.00% strings.FieldsFunc
10ms 10.00% 90.00% 10ms 10.00% syscall.Syscall6
10ms 10.00% 100% 10ms 10.00% unicode.IsSpace
0 0% 100% 10ms 10.00% bytes.(*Buffer).ReadFrom
0 0% 100% 70ms 70.00% github.com/shirou/gopsutil/process.(*Process).CPUPercent
0 0% 100% 70ms 70.00% github.com/shirou/gopsutil/process.(*Process).CPUPercentWithContext
Run Code Online (Sandbox Code Playgroud)
似乎系统调用需要永远。树转储在这里: https://gist.github.com/PatWie/4fa528b7d7b1d0b5c1b665c056671477
这将问题变成: - 系统调用是问题吗?- 该程序有任何 c 源吗top?我刚刚找到了 htop 的实现 - 有一个简单的修复方法吗?我考虑用c写它然后把它包装起来供go使用。
github.com/shirou/gopsutil/process使用ioutil.ReadFile它访问文件系统的效率低于 top。尤其,ReadFile:
Stat会增加额外不必要的系统调用。os.Open而不是unix.Openat+ ,这会在解析路径时os.NewFile导致额外的内核时间遍历。仍然有点低效,因为它总是检查文件描述符是否是非阻塞的。直接使用或包可以避免这种情况。/procos.NewFilegolang.org/x/sys/unixsyscall一般来说,在 Linux 下检索进程详细信息的效率相当低(大量文件系统扫描、编组文本数据)。但是,您可以top通过修复文件系统访问来实现与 Go 类似的性能(如上所述)。