英文:
Can't get golang pprof working
问题
我尝试对一些 Golang 应用程序进行性能分析,但无法使其正常工作。我按照以下两个教程进行操作:
- http://blog.golang.org/profiling-go-programs
- http://saml.rilspace.org/profiling-and-creating-call-graphs-for-go-programs-with-go-tool-pprof
两篇教程都说,在应用程序中添加一些代码行后,你需要执行你的应用程序。我按照这样做了,屏幕上显示了以下信息:
> 2015/06/16 12:04:00 profile: cpu profiling enabled,
> /var/folders/kg/4fxym1sn0bx02zl_2sdbmrhr9wjvqt/T/profile680799962/cpu.pprof
所以,我理解正在执行性能分析,并将信息发送到文件中。
但是,当我查看文件大小时,无论我测试的程序是什么,它始终为 64 字节。
当我尝试使用 pprof 打开 cpu.pprof 文件,并执行 "top10" 命令时,我发现文件中没有任何内容:
(" ./fact" 是我的应用程序)
> go tool pprof ./fact
> /var/folders/kg/4fxym1sn0bx02zl_2sdbmrhr9wjvqt/T/profile680799962/cpu.pprof
>
> top10 -->
>
> (pprof) top10 0 of 0 total ( 0%)
> flat flat% sum% cum cum%
所以,就像在进行性能分析时什么都没有发生。
我在 Mac(此示例)和 Ubuntu 上进行了测试,使用了三个不同的程序。
你知道我做错了什么吗?
这个示例程序非常简单,以下是代码(是一个非常简单的阶乘程序,我从互联网上找到的):
import "fmt"
import "github.com/davecheney/profile"
func fact(n int) int {
if n == 0 {
return 1
}
return n * fact(n-1)
}
func main() {
defer profile.Start(profile.CPUProfile).Stop()
fmt.Println(fact(30))
}
谢谢,
Fer
英文:
I have tried to profile some golang applications but I couldn't have that working, I have followed these two tutorials:
- http://blog.golang.org/profiling-go-programs
- http://saml.rilspace.org/profiling-and-creating-call-graphs-for-go-programs-with-go-tool-pprof
Both says that after adding some code lines to the application, you have to execute your app, I did that and I receiveed the following message in the screen:
> 2015/06/16 12:04:00 profile: cpu profiling enabled,
> /var/folders/kg/4fxym1sn0bx02zl_2sdbmrhr9wjvqt/T/profile680799962/cpu.pprof
So, I understand that the profiling is being executed, sending info to the file.
But, when I see the file size, in any program that I test, it is always 64bytes.
When I try to open the cpu.pprof file with pprof, and I execute the "top10" command, I see that nothing is in the file:
("./fact" is my app)
> go tool pprof ./fact
> /var/folders/kg/4fxym1sn0bx02zl_2sdbmrhr9wjvqt/T/profile680799962/cpu.pprof
>
> top10 -->
>
> (pprof) top10 0 of 0 total ( 0%)
> flat flat% sum% cum cum%
So, it is like nothing is happening when I am profiling.
I have tested it in mac (this example) and in ubuntu, with three different programs.
Do you know that I am doing wrong?
Then example program is very simple, this is the code (is a very simple factorial program that I take from internet):
import "fmt"
import "github.com/davecheney/profile"
func fact(n int) int {
if n == 0 {
return 1
}
return n * fact(n-1)
}
func main() {
defer profile.Start(profile.CPUProfile).Stop()
fmt.Println(fact(30))
}
Thanks,
Fer
答案1
得分: 10
正如inf已经提到的,你的代码执行得太快了。原因是pprof通过在程序执行期间重复暂停你的程序,查看此时正在运行的函数,并将其记录下来(连同整个函数调用堆栈)。pprof的采样率为每秒100个样本。你可以在runtime/pprof/pprof.go中轻松检查到这一点(参见https://golang.org/src/runtime/pprof/pprof.go第575行及其上方的注释):
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
// Avoid queueing behind StopCPUProfile.
// Could use TryLock instead if we had it.
if cpu.profiling {
return fmt.Errorf("cpu profiling already in use")
}
cpu.Lock()
defer cpu.Unlock()
if cpu.done == nil {
cpu.done = make(chan bool)
}
// Double-check.
if cpu.profiling {
return fmt.Errorf("cpu profiling already in use")
}
cpu.profiling = true
runtime.SetCPUProfileRate(hz)
go profileWriter(w)
return nil
}
你的程序运行的时间越长,采样的样本就越多,也越有可能采样到运行时间较短的函数。如果你的程序在第一个样本生成之前就结束了,那么生成的cpu.pprof文件将是空的。
从上面的代码中可以看出,采样率是通过runtime.SetCPUProfileRate(..)
来设置的。
如果在调用StartCPUProfile()
之前使用另一个值调用runtime.SetCPUProfileRate()
,你可以覆盖采样率。在程序执行期间,你将收到一个警告消息,告诉你"runtime: cannot set cpu profile rate until previous profile has finished.",你可以忽略它。这是因为pprof.go再次调用了SetCPUProfileRate()。由于你已经设置了值,pprof中的值将被忽略。
此外,Dave Cheney发布了他的一个新版本的性能分析工具,你可以在这里找到它:https://github.com/pkg/profile。在那里,你可以指定cpu.pprof写入的路径:
defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop()
你可以在这里阅读更多信息:http://dave.cheney.net/2014/10/22/simple-profiling-package-moved-updated
顺便说一下,即使你将int64作为参数和返回值,你的fact()函数也会很快溢出。30!大约是210^32,而int64只能存储最大值为2^63-1,大约是910^18。
英文:
As inf mentioned already, your code executes too fast. The reason is that pprof works by repeatedly halting your program during its execution, looking at which function is running at that moment in time and writing that down (together with the whole function call stack). Pprof samples with a rate of 100 samples per second. This is hardcoded in runtime/pprof/pprof.go as you can easily check (see https://golang.org/src/runtime/pprof/pprof.go line 575 and the comment above it):
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
// Avoid queueing behind StopCPUProfile.
// Could use TryLock instead if we had it.
if cpu.profiling {
return fmt.Errorf("cpu profiling already in use")
}
cpu.Lock()
defer cpu.Unlock()
if cpu.done == nil {
cpu.done = make(chan bool)
}
// Double-check.
if cpu.profiling {
return fmt.Errorf("cpu profiling already in use")
}
cpu.profiling = true
runtime.SetCPUProfileRate(hz)
go profileWriter(w)
return nil
}
The longer your program runs the more samples can be made and the more probable it will become that also short running functions will be sampled. If your program finishes before even the first sample is made, than the generated cpu.pprof will be empty.
As you can see from the code above, the sampling rate is set with
runtime.SetCPUProfileRate(..)
If you call runtime.SetCPUProfileRate() with another value before you call StartCPUProfile(), you can override the sampling rate. You will receive a warning message during execution of your program telling you "runtime: cannot set cpu profile rate until previous profile has finished." which you can ignore. It results since pprof.go calls SetCPUProfileRate() again. Since you have already set the value, the one from pprof will be ignored.
Also, Dave Cheney has released a new version of his profiling tool, you can find it here: https://github.com/pkg/profile . There, you can, among other changes, specify the path where the cpu.pprof is written to:
defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop()
You can read about it here: http://dave.cheney.net/2014/10/22/simple-profiling-package-moved-updated
By the way, your fact() function will quickly overflow, even if you take int64 as parameter and return value. 30! is roughly 210^32 and an int64 stores values only up to 2^63-1 which is roughly 910^18.
答案2
得分: 4
问题在于你的函数运行得太快,pprof 无法对其进行采样。尝试在 fact
调用周围添加一个循环,并将结果相加,以人为地延长程序的运行时间。
英文:
The problem is that your function is running too fast and pprof can't sample it. Try adding a loop around the fact
call and sum the result to artificially prolong the program.
答案3
得分: 1
我在处理空的pprof文件时遇到了困难,直到我意识到我按照过时的博客文章进行操作。
上游文档很好:https://pkg.go.dev/runtime/pprof
编写一个你想要进行性能分析的测试,然后执行以下命令:
go test -cpuprofile cpu.prof -memprofile mem.prof -bench .
这将创建cpu.prof和mem.prof文件。
你可以像这样分析它们:
go tool pprof cpu.prof
这将给你一个命令行界面。经常使用的命令有"top"和"web"。
内存分析也是一样的:
go tool pprof mem.prof
英文:
I struggled with empty pprof files, until I realized that I followed outdated blog articles.
The upstream docs are good: https://pkg.go.dev/runtime/pprof
Write a test which you want to profile, then:
go test -cpuprofile cpu.prof -memprofile mem.prof -bench .
this creates cpu.prof and mem.prof.
You can analyze them like this:
go tool pprof cpu.prof
This gives you a command-line. The "top" and "web" commands are used often.
Same for memory:
go tool pprof mem.prof
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论