[英]Avoiding excessive memory allocation in golang when using an io.Writer
我正在 Go 中开发一个名为redis-mass的命令行工具,它将一堆 redis 命令转换为redis 协议格式。
第一步是将 node.js 版本移植到 Go。 我使用ioutil.ReadFile(inputFileName)
来获取文件的字符串版本,然后返回一个编码的字符串作为输出。
当我在一个包含 2,000,000 个 redis 命令的文件上运行它时,大约需要 8 秒,而节点版本大约需要 16 秒。 我猜测它的速度只有两倍的原因是因为它首先将整个文件读入内存,所以我将编码函数更改为接受一对(raw io.Reader, enc io.Writer)
,它看起来像这样:
func EncodeStream(raw io.Reader, enc io.Writer) {
var args []string
var length int
scanner := bufio.NewScanner(raw)
for scanner.Scan() {
command := strings.TrimSpace(scanner.Text())
args = parse(command)
length = len(args)
if length > 0 {
io.WriteString(enc, fmt.Sprintf("*%d\r\n", length))
for _, arg := range args {
io.WriteString(enc, fmt.Sprintf("$%d\r\n%s\r\n", len(arg), arg))
}
}
}
}
然而,这在200万行的文件上花费了12秒,所以我使用github.com/pkg/profile查看它是如何使用内存的,看起来内存分配的数量是巨大的:
# Alloc = 3162912
# TotalAlloc = 1248612816
# Mallocs = 46001048
# HeapAlloc = 3162912
我可以限制io.Writer
使用固定大小的缓冲区并避免所有这些分配吗?
更一般地说,我怎样才能避免这种方法中的过度分配? 这是更多上下文的完整来源
通过使用 []byte 而不是字符串来减少分配。 fmt.Printf 直接输出而不是 fmt.Sprintf 和 io.WriteString。
func EncodeStream(raw io.Reader, enc io.Writer) {
var args []string
var length int
scanner := bufio.NewScanner(raw)
for scanner.Scan() {
command := bytes.TrimSpace(scanner.Bytes())
args = parse(command)
length = len(args)
if length > 0 {
fmt.Printf(enc, "*%d\r\n", length))
for _, arg := range args {
fmt.Printf(enc, "$%d\r\n%s\r\n", len(arg), arg))
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.