go 编码/十六进制:解码字符串浪费内存

webghufk  于 6个月前  发布在  Go
关注(0)|答案(7)|浏览(47)

你使用的Go版本是什么?

go version go1.11.4 windows/amd64

你做了什么?

https://play.golang.org/p/pbZJx5AZHpJ

你想看到什么?

hex.DecodeString返回的字节切片有len == cap个元素。

你看到了什么?

hex.DecodeString返回的字节切片比严格必要的大小大一倍。
查看this rillig/pkglint commit了解我发现这个的实际例子。在一个commit message shortly before that中,我在堆转储中发现了奇怪的十六进制数字序列。

6tdlim6h

6tdlim6h1#

我不确定使用较小的缓冲区(即使用较少的内存)是否值得所需的代码重复。在返回的切片(return src[:n:n])中设置cap==len可能是值得的,尽管这会对任何向返回值追加内容的人造成性能损失。

rggaifut

rggaifut2#

关于这个问题:

func DecodeString(s string) ([]byte, error) {
	dstLen := DecodedLen(len(s))
	dst := make([]byte, dstLen, dstLen)
	n, err := Decode(dst, []byte(s))
	return dst[:n], err
}

这里没有代码重复,但输出切片的大小与预期相符(对我来说)。
Go编译器应该足够聪明,能够判断在这种情况下 []byte(s) 可以是一个无操作。
使用此函数的后续用户可能已经使用了 Decode(dst, src)。将内容追加到 DecodeString 的结果似乎对我来说是一个边缘情况,至少在Go源代码树中我没有找到一个例子。因此,当我看到 DecodeString 在输出预期完整的情况下浪费内存时,我感到很惊讶。

kx1ctssn

kx1ctssn3#

Go编译器应该足够聪明,能够判断在这种情况下[]byte(s)可以是一个无操作。
我使用runtime.ReadMemStats来确认当我传入一个2048字节的字符串时,它会进行两次分配,总大小为3072字节,使用go version go1.11.4 darwin/amd64
因此,至少在go1.11.4中,您的选项中的Go编译器不够智能。

avkwfej4

avkwfej44#

@josharian 使用 src[:n:n] 修剪返回值并不能帮助垃圾回收器,因为它仍然没有释放字节数组的后半部分。我使用以下程序测试了这一点:

package main

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"runtime/debug"
)

func main() {
	large := make([]byte, 0, 1<<24)

	dump("001.heapdump") // 16 MB

	small := large[0 : 1<<10 : 1<<10]
	large = nil

	dump("002.heapdump") // still 16 MB, even though large is not accessible anymore

	fmt.Println(small[0:1])
	small = nil

	dump("003.heapdump") // 272 kB
}

func dump(filename string) {
	f, err := os.Create(filename)
	if err != nil {
		log.Fatalln(err)
	}

	runtime.GC()
	debug.WriteHeapDump(f.Fd())

	err = f.Close()
	if err != nil {
		log.Fatalln(err)
	}
}
js5cn81o

js5cn81o5#

Go编译器应该足够聪明,能够判断在这种情况下[]byte(s)可以是一个无操作。但它不是。我为这个问题提交了#29810
使用src[:n:n]修剪返回值并不能帮助垃圾回收。
确实如此。这样做唯一的好处是避免了这种问题的意外惊喜。@rillig也对“泄漏数据”表示担忧,但考虑到调用者已经有了十六进制输入,这对我来说并不是一个主要问题。总的来说,我认为我们不应该做出这个改变。
我使用runtime.ReadMemStats来确认。
顺便说一下,这种事情更容易通过基准测试来衡量。

vbkedwbf

vbkedwbf6#

一个附注:encoding/hex几乎毫无用处。Printf和它的朋友们在仔细管理内存的情况下可以很好地进行十六进制转换。

mwg9r5ms

mwg9r5ms7#

另一个附带的评论:如果标准encoding/*包具有与strconv.Append*语义相似的Append*(dst []byte, args...) []byte方法,那就太好了。这样的方法可以很容易地在零分配模式下使用。有一个过时的关于encoding/base64的提议在#19366

相关问题