Go 如何减少[]字符串的内存使用?

Go 如何减少[]字符串的内存使用?,go,Go,Go对我来说很陌生,我在理解内存使用方面遇到了一些问题: 我想将一个类似于csv的文件加载到一个行数组中,每行是一个结构,由22个字符上的一个键和一个字符串值数组组成。 我的代码如下所示: 问题是,对于一个450M的文件,它使用了大约2G1的内存。 有没有人有减少内存使用的解决方案 使用SirDarius解决方案更新:仍然使用1G9左右这对我来说效率很低: for _, value := range strings.Split(line[23:], ";") { row.Values =

Go对我来说很陌生,我在理解内存使用方面遇到了一些问题:

我想将一个类似于csv的文件加载到一个行数组中,每行是一个结构,由22个字符上的一个键和一个字符串值数组组成。 我的代码如下所示:

问题是,对于一个450M的文件,它使用了大约2G1的内存。 有没有人有减少内存使用的解决方案


使用SirDarius解决方案更新:仍然使用1G9左右这对我来说效率很低:

for _, value := range strings.Split(line[23:], ";") {
    row.Values = append(row.Values, value)
}
基本上,通过调用string.Split函数获得一个[]字符串,然后在该片上循环,将每个字符串附加到另一个初始为nil的字符串片上

为什么不干脆做:

row.Values = strings.Split(line[23:], ";")
相反


虽然我不能保证,但循环可能会导致复制每个字符串,从而使您的程序使用两倍于所需的内存。

这对我来说似乎效率很低:

for _, value := range strings.Split(line[23:], ";") {
    row.Values = append(row.Values, value)
}
基本上,通过调用string.Split函数获得一个[]字符串,然后在该片上循环,将每个字符串附加到另一个初始为nil的字符串片上

为什么不干脆做:

row.Values = strings.Split(line[23:], ";")
相反


虽然我不能保证,但循环可能会导致复制每个字符串,从而使程序使用所需内存的两倍。

文件中有多少行和字段

你所描述的是使用最小内存量,这似乎是合理的

看看代码,我认为它将使用450MB的内存来存储底层字符串数据

然后,它会将其切分为多个字符串。它们由一个指针和一个在64位平台上占用16字节的长度组成

因此,1.5GB/16=9300万

因此,如果文件中有>5000万个字段,那么内存使用似乎是合理的

还有其他开销,如行数等,因此这不是一个精确的计算

编辑

给定
500万行,每行10列

这是5000万个16字节的字符串头,需要800MB。加上450MB的数据本身,再加上5*8*500万行=200MB,就得到了1.45GB


因此,我认为即使使用完美的内存分配,也无法将使用量降低到1.5GB以下。

文件中有多少行和字段

你所描述的是使用最小内存量,这似乎是合理的

看看代码,我认为它将使用450MB的内存来存储底层字符串数据

然后,它会将其切分为多个字符串。它们由一个指针和一个在64位平台上占用16字节的长度组成

因此,1.5GB/16=9300万

因此,如果文件中有>5000万个字段,那么内存使用似乎是合理的

还有其他开销,如行数等,因此这不是一个精确的计算

编辑

给定
500万行,每行10列

这是5000万个16字节的字符串头,需要800MB。加上450MB的数据本身,再加上5*8*500万行=200MB,就得到了1.45GB

因此,我认为即使使用完美的内存分配,也无法将使用量降低到1.5GB以下。

您将每次迭代获得的值添加到行结构中,考虑到巨大的文件大小,这不是一个合理的好方法。为什么您的服务器不批量处理该文件

查看函数,它返回一个子字符串片段,因此无需覆盖结果片段并将其附加到row.Values中。您可以将结果值直接分配给row.values,然后将其附加到rows切片

将切片s拆分为由sep分隔的所有子字符串,并返回一个 这些分隔符之间的子字符串切片。如果sep为空, 在每个UTF-8序列之后拆分。它相当于SplitN 计数为-1

将每次迭代获得的值追加到行结构中,考虑到巨大的文件大小,这不是一个合理的好方法。为什么您的服务器不批量处理该文件

查看函数,它返回一个子字符串片段,因此无需覆盖结果片段并将其附加到row.Values中。您可以将结果值直接分配给row.values,然后将其附加到rows切片

将切片s拆分为由sep分隔的所有子字符串,并返回一个 这些分隔符之间的子字符串切片。如果sep为空, 在每个UTF-8序列之后拆分。它相当于SplitN 计数为-1


在我看来,这是关于附加函数的。从语言规范

如果s的容量不足以容纳附加值, append分配一个新的、足够大的底层数组

这个新分配的数组的大小足以消耗更多的附加项。因此,为了精确地分配,您应该使用ExpectedCapacity将切片:=生成[]行,0,然后分配切片[n]=而不是追加。如果你不能做到这一点,你至少可以 n尝试压缩

reflect.ValueOf(&slice).Elem().SetCap(len(slice))

有些棘手,但您可以看到它是有效的。

在我看来,它是关于附加函数的。从语言规范

如果s的容量不足以容纳附加值, append分配一个新的、足够大的底层数组

这个新分配的数组的大小足以消耗更多的附加项。因此,为了精确地分配,您应该使用ExpectedCapacity将切片:=生成[]行,0,然后分配切片[n]=而不是追加。如果你做不到这一点,你至少可以尝试压缩反射

reflect.ValueOf(&slice).Elem().SetCap(len(slice))

有些问题很棘手,但您可以看到它是有效的。

您的程序需要解决哪些问题?内存减少技术可能会有很大的不同,这取决于问题类别。逐行读取文件。诀窍就在这里:你需要马上把所有的东西都存储在内存中吗?你不能像@AlexanderTrakhimenok建议的那样逐行处理文件吗?你的程序要解决什么问题?内存减少技术可能会有很大的不同,这取决于问题类别。逐行读取文件。诀窍就在这里:你需要马上把所有的东西都存储在内存中吗?你不能像@AlexanderTrakhimenok建议的那样逐行处理文件吗?事实上,这是非常无用的,我在循环中进行了一些有效性检查,但我可以推迟它们。我刚试过,它降到1g9,不完美,但已经更好了!谢谢事实上,这是非常无用的,我在循环中进行了一些有效性检查,但我可以推迟它们。我刚试过,它降到1g9,不完美,但已经更好了!谢谢500万行,10列每个500万行,10列每个知道我用于测试的文件的大小,所以我尝试手动设置每个阵列的容量,但没有大的结果。。。因此,我想它不会有太大的变化,因为我没有使用附加,但我会尝试!我知道用于测试的文件的大小,所以我尝试手动设置每个阵列的容量,但没有大的结果。。。因此,我想它不会有太大的变化,因为我没有使用附加,但我会尝试!