Go性能分析文件为空。

huangapple go评论109阅读模式
英文:

Go profiling profile is empty

问题

我正在按照这个教程上的指导进行Go性能分析:

flag.Parse()
if *cpuprofile != "" {
    f, err := os.Create(*cpuprofile)
    if err != nil {
        log.Fatal(err)
    }
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()
}

然后我使用 -cpuprofile=myprogram.prof 标志启动了我的代码,并且文件被创建了。然后我使用以下命令启动了 pprof 工具

go tool pprof myprogram myprogram.prof

嗯,myprogram 读取一个大的 JSON 文件,并将其映射到一个大的 map[string]string 中,所以我的程序中有很多操作,但是当我在 pprof 中执行 top10 命令时,我得到了以下结果:

Entering interactive mode (type "help" for commands)
(pprof) top10
profile is empty
英文:

I'm following this tutorial on Go profiling and did as advised:

flag.Parse()
if *cpuprofile != "" {
    f, err := os.Create(*cpuprofile)
    if err != nil {
        log.Fatal(err)
    }
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()
}

I then started my code with the flag -cpuprofile=myprogram.prof and the file got created. Then I started the pprof tool with

go tool pprof myprogram myprogram.prof

Well, myprogram reads a big json file and maps it to a big map[string]string, so there is a lot going on in my program, but when I do like top10 in pprof, I get:

Entering interactive mode (type "help" for commands)
(pprof) top10
profile is empty

答案1

得分: 5

很可能你的代码执行得太快了,即使你认为它做了很多事情。我也遇到过这种情况。

你可以通过更改采样率来调整,使用runtime.SetCPUProfileRate函数。将其设置为默认值100以上的值,单位是Hz。请注意,Go语言的作者不建议超过500的值-详见解释

pprof.StartCPUProfile之前执行这个操作。你还会看到警告信息runtime: cannot set cpu profile rate until previous profile has finished-请参考这个答案了解解释。

希望对你有帮助。

英文:

Most probably your code is executing too fast, even if you think it's doing a lot. Happened to me several times.

You can play with changing the sampling rate via runtime.SetCPUProfileRate. -
set it to the value above default 100, unit is Hz. Please note the Go authors don't recommend values above 500 - see explanation.

Do it just before pprof.StartCPUProfile. You will also see the warning runtime: cannot set cpu profile rate until previous profile has finished - please see this answer for explanation.

HTH

答案2

得分: 3

很可能你没有处理系统中断信号。为了使"pprof.StopCPUProfile()"能够成功写入配置文件数据,你应该显式地处理它,否则当使用"ctrl+c"退出程序时,程序会退出得太快。

以下是一个示例解决方案:

var f *os.File

func main() {
    flag.Parse()
    if *cpuProfile != "" {
        cpuProfileFile, err := os.Create(*cpuProfile)
        if err != nil {
            log.Fatal(err)
        }
        defer f.Close()
        pprof.StartCPUProfile(cpuProfileFile)
    }
    
    c := make(chan os.Signal, 2)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM) // 订阅系统信号
    onKill := func(c chan os.Signal) {
        select {
        case <-c:
            defer f.Close()
            defer pprof.StopCPUProfile()
            defer os.Exit(0)
        }
    }

    // 尝试处理操作系统中断信号(终止信号)
    go onKill(c)
}

希望对你有帮助!

英文:

Most probably you are not handling the System Interrupt signal. You should explicitly handle it in order for "pprof.StopCPUProfile()" to write the profile data successfully, otherwise, the program exits to fast when exited with "ctrl+c".
Here is an example solution:

var f *os.File

func main() {
	flag.Parse()
	if *cpuProfile != &quot;&quot; {
		cpuProfileFile, err := os.Create(*cpuProfile)
		if err != nil {
			log.Fatal(err)
	    }
	   defer f.Close()
	   pprof.StartCPUProfile(cpuProfileFile)
    }
	    
    c := make(chan os.Signal, 2)                    
	signal.Notify(c, os.Interrupt, syscall.SIGTERM) // subscribe to system signals
	onKill := func(c chan os.Signal) {
		select {
		case &lt;-c: 
            defer f.Close()
            defer pprof.StopCPUProfile()
			defer os.Exit(0)
        }
    }

	// try to handle os interrupt(signal terminated)
    go onKill(c)
}

答案3

得分: 2

用于分析 Go 程序的性能的方法是使用 pprof 作为一个 Web 服务器。你需要在你的 Go 程序/应用的主文件中添加一些代码来启动 pprof 服务器,该服务器将持续为你的程序提供服务器上的资源使用情况详情,并且你可以轻松获取所有相关的详情。如果你按照下面的代码,你可以在浏览器上查看你的程序的详情,地址是 http://localhost:6060/debug/pprof/(需要刷新页面以查看更新的数据)。

你可以查看下面的代码片段,或者访问以下链接获取完整的代码:github.com/arbaaz-khan/GoLangLearning/blob/master/ProfilingGoProgram/profile_go_prog.go

go func() {
    log.Printf("Starting Server! \t Go to http://localhost:6060/debug/pprof/\n")
    err := http.ListenAndServe("localhost:6060", nil)
    if err != nil {
        log.Printf("Failed to start the server! Error: %v", err)
        wg.Done()
    }
}()

希望对你有帮助!
1: https://github.com/arbaaz-khan/GoLangLearning/blob/master/ProfilingGoProgram/profile_go_prog.go

英文:

For profiling go programs you can use pprof as a web server. You need to add a bit of code to your main file of your go program/application to start the pprof server which will continuously serve the resource usage details for your program on the server and you can easily get all the relevant details. If you follow the code below the you can see the details of your program on your browser at http://localhost:6060/debug/pprof/
(Need to refresh the page to see the updated data)

You may see the code snippet below or go to the following link for the complete code:
github.com/arbaaz-khan/GoLangLearning/blob/master/ProfilingGoProgram/profile_go_prog.go

go func() {
	log.Printf(&quot;Starting Server! \t Go to http://localhost:6060/debug/pprof/\n&quot;)
	err := http.ListenAndServe(&quot;localhost:6060&quot;, nil)
	if err != nil {
		log.Printf(&quot;Failed to start the server! Error: %v&quot;, err)
		wg.Done()
	}
}()

Hope it helps!
1: https://github.com/arbaaz-khan/GoLangLearning/blob/master/ProfilingGoProgram/profile_go_prog.go

答案4

得分: 2

如果您使用Ctrl-C停止程序,请确保在profile.Start()中传递profile.NoShutdownHook参数。

英文:

If you use ctrl-c to stop the program, make sure you pass in profile.NoShutdownHook param in profile.Start().

答案5

得分: 0

你处理了Ctrl-C信号吗?
如果没有处理,程序会被操作系统停止。你必须确保程序正常退出,才能将配置文件写入文件中。
你也可以检查netprof模块。

英文:

did you handle ctrl-c signal?
if you haven't, the program is stopped by OS. you must make sure the program is exit normally, only then the profile will be written to file.
you can also check netprof module.

答案6

得分: 0

对我来说,问题是我的代码执行得太快了。我所做的是使用runtime.SetCPUProfileRate来更改采样率。请注意,在runtime.pprof.StartCPUProfile中,采样率为100 Hz,建议最多为500 Hz。

func StartCPUProfile(w io.Writer) error {
    // 运行时例程允许可变的分析率,
    // 但实际上操作系统不能以超过大约500 Hz的频率触发信号,
    // 而我们处理信号的成本不低(主要是获取堆栈跟踪)。
    // 100 Hz是一个合理的选择:它足够频繁以产生有用的数据,
    // 不会拖慢系统,并且是一个很好的整数,使得将样本计数转换为秒很容易。
    // 我们不要求每个客户端都指定频率,而是将其硬编码。
    const hz = 100

    cpu.Lock()
    defer cpu.Unlock()
    if cpu.done == nil {
        cpu.done = make(chan bool)
    }
    // 再次检查。
    if cpu.profiling {
        return fmt.Errorf("cpu profiling already in use")
    }
    cpu.profiling = true
    runtime.SetCPUProfileRate(hz)
    go profileWriter(w)
    return nil
}

但在我的情况下,将其设置为500 Hz还不够快。在查看runtime.SetCPUProfileRate的代码后,似乎可以提供高达1000000 Hz的频率。将其设置为足够大的值后,问题得到解决。

// SetCPUProfileRate将CPU分析率设置为每秒hz个样本。
// 如果hz <= 0,则SetCPUProfileRate将关闭分析。
// 如果分析器已经打开,则不能在不先关闭它的情况下更改速率。
//
// 大多数客户端应该使用runtime/pprof包或
// 测试包的-test.cpuprofile标志,而不是直接调用SetCPUProfileRate。
func SetCPUProfileRate(hz int) {
    // 将hz限制在合理的范围内。
    if hz < 0 {
        hz = 0
    }
    if hz > 1000000 {
        hz = 1000000
    }

    lock(&cpuprof.lock)
    if hz > 0 {
        if cpuprof.on || cpuprof.log != nil {
            print("runtime: cannot set cpu profile rate until previous profile has finished.\n")
            unlock(&cpuprof.lock)
            return
        }

        cpuprof.on = true
        cpuprof.log = newProfBuf(1, 1<<17, 1<<14)
        hdr := [1]uint64{uint64(hz)}
        cpuprof.log.write(nil, nanotime(), hdr[:], nil)
        setcpuprofilerate(int32(hz))
    } else if cpuprof.on {
        setcpuprofilerate(0)
        cpuprof.on = false
        cpuprof.addExtra()
        cpuprof.log.close()
    }
    unlock(&cpuprof.lock)
}
英文:

For me, the problem was my code was executing too fast. What I did is changing the sampling rate using runtime.SetCPUProfileRate. Note that in runtime.pprof.StartCPUProfile the sampling rate is 100 Hz and recommended to be 500 Hz at most.

func StartCPUProfile(w io.Writer) error {
    // The runtime routines allow a variable profiling rate,
    // but in practice operating systems cannot trigger signals
    // at more than about 500 Hz, and our processing of the
    // signal is not cheap (mostly getting the stack trace).
    // 100 Hz is a reasonable choice: it is frequent enough to
    // produce useful data, rare enough not to bog down the
    // system, and a nice round number to make it easy to
    // convert sample counts to seconds. Instead of requiring
    // each client to specify the frequency, we hard code it.
    const hz = 100

    cpu.Lock()
    defer cpu.Unlock()
    if cpu.done == nil {
	     cpu.done = make(chan bool)
    }
    // Double-check.
    if cpu.profiling {
 	     return fmt.Errorf(&quot;cpu profiling already in use&quot;)
     }
    cpu.profiling = true
    runtime.SetCPUProfileRate(hz)
    go profileWriter(w)
    return nil
}

But setting it to 500 Hz wasn't fast enough in my case. After looking into the code of runtime.SetCPUProfileRate it seems that you can provide frequencies up to 1000000 Hz. After setting it to a large enough value it solved my issue.

// SetCPUProfileRate sets the CPU profiling rate to hz samples per second.
// If hz &lt;= 0, SetCPUProfileRate turns off profiling.
// If the profiler is on, the rate cannot be changed without first turning it off.
//
// Most clients should use the runtime/pprof package or
// the testing package&#39;s -test.cpuprofile flag instead of calling
// SetCPUProfileRate directly.

func SetCPUProfileRate(hz int) {

    // Clamp hz to something reasonable.
    if hz &lt; 0 {
    	hz = 0
    }
    if hz &gt; 1000000 {
	    hz = 1000000
    }

    lock(&amp;cpuprof.lock)
    if hz &gt; 0 {
	    if cpuprof.on || cpuprof.log != nil {
		    print(&quot;runtime: cannot set cpu profile rate until previous profile has finished.\n&quot;)
		    unlock(&amp;cpuprof.lock)
		    return
	    }

	    cpuprof.on = true
	    cpuprof.log = newProfBuf(1, 1&lt;&lt;17, 1&lt;&lt;14)
	    hdr := [1]uint64{uint64(hz)}
	    cpuprof.log.write(nil, nanotime(), hdr[:], nil)
	    setcpuprofilerate(int32(hz))
    } else if cpuprof.on {
	    setcpuprofilerate(0)
	    cpuprof.on = false
	    cpuprof.addExtra()
	    cpuprof.log.close()
    }
    unlock(&amp;cpuprof.lock)
}

huangapple
  • 本文由 发表于 2017年2月17日 21:12:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/42298890.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定