所以我有一个简单的 net/http 网络服务器。它所做的只是提供 100MB 的随机字节,我打算将其用于网络速度测试。我的 100mb 端点处理程序非常简单(粘贴在下面)。代码工作正常,我得到了我的随机字节文件,问题是当我运行它并且有人下载了这 100 兆字节时,这个程序的 CPU 占用率高达 150% 并一直保持到这个处理程序完成运行。我在这里做错了什么吗?我可以做些什么来提高这个处理程序的性能?
func downloadHandler(w http.ResponseWriter, r *http.Request) {
str := RandStringBytes(8192); //generates 8192 bytes of randomness
sz := 1000*1000*100; //100Megabytes
iter := sz/len(str)+1;
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Length", strconv.Itoa( sz ))
for i := 0; i < iter ; i++ {
fmt.Fprintf(w, str )
}
}
最佳答案
问题是 fmt.Fprintf()
需要一个格式字符串:
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
然后您将一个 8 KB 的大格式字符串传递给它。 fmt
包必须分析格式字符串,它不是按原样到达输出的东西。最肯定的是,这就是占用您 CPU 的原因。
如果随机字符串包含特殊的 %
符号,那甚至会使您的情况变得更糟,因为那时 fmt.Fprintf()
可能会期待更多您不希望的参数“交付”,因此 fmt
包还必须(将)在输出中包含错误消息,例如:
fmt.Fprintf(os.Stdout, "aaa%bbb%d")
输出:
aaa%!b(MISSING)bb%!d(MISSING)
使用fmt.Fprint()
相反,它不需要格式字符串:
fmt.Fprint(w, str)
或者更好的是,将随机字符串转换为字节 slice 一次,然后继续写入:
data := []byte(str)
for i := 0; i < iter; i++ {
if _, err := w.Write(data); err != nil {
// Handle error, e.g. return
}
}
传送大量数据——没有比在循环中编写准备好的字节 slice 更快的解决方案了(如果改变 slice 的大小,速度可能会稍微快一些)。如果您的解决方案仍然“缓慢”,那可能是由于您的 RandStringBytes()
函数我们一无所知,或者如果您使用其他处理程序或您的输出可能被压缩(gzipped)一些框架(确实使用相对较高的 CPU)。此外,如果接收响应的客户端也在您的计算机上(例如浏览器),它或防火墙/防病毒软件可能会检查/分析恶意代码的响应(这也可能是资源密集型的)。
关于performance - 简单网络服务器上的 Golang 高 CPU 使用率无法理解为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40342340/