Go有没有真正缩小切片的方法?这是个问题吗?

huangapple go评论124阅读模式
英文:

Does Go have no real way to shrink a slice? Is that an issue?

问题

我已经尝试了一段时间的Go语言,这个问题一直困扰着我。假设我在一个切片中构建了一个相当大的数据集(比如说,1000万个int64类型的数据)。

但是后来我决定我不想要其中的大部分数据,我只想要其中的10个数据。我尝试过在Go的维基上使用切片和删除技巧,但是似乎都无法减少切片的容量。

所以我的问题是:Go语言是否没有真正缩小切片容量的方法,类似于在C语言中使用比之前调用时的较小大小参数进行realloc()操作?这是一个问题,应该如何处理?

英文:

I've been trying out Go for some time and this question keeps bugging me. Say I build up a somewhat large dataset in a slice (say, 10 million int64s).

package main

import (
	"math"
	"fmt"
)

func main() {
	var a []int64
	var i int64;
	upto := int64(math.Pow10(7))
	for i = 0; i < upto; i++ {
		a = append(a, i)
	}
	fmt.Println(cap(a))
}

But then I decide I don't want most of them so I want to end up with a slice of just 10 of those. I've tried both slicing and delete techniques on Go's wiki but none of them seem to reduce the slice's capacity.

So that's my question: does Go has no real way of shrinking the capacity of a slice that would be similar to realloc()-ing with a smaller size argument than in your previous call on the same pointer in C? Is that an issue and how should one deal with it?

答案1

得分: 34

为了执行一个实际上是对切片进行重新分配的操作:

a = append([]T(nil), a[:newSize]...) // 感谢 @Dijkstra 指出了省略号的缺失。

编译器完全可以自行决定是将 newSize 个元素复制到新的内存位置,还是像 realloc(3) 一样实际地进行 原地 调整大小。如果您对当前状态进行调查,并且发现有改进的空间,可以提出一个 问题

然而,这可能只是微小的优化。提高性能的第一个来源几乎总是选择更好的算法和/或更好的数据结构。使用一个巨大的向量最终只保留几个元素可能不是关于内存消耗的最佳选择。

编辑:上述内容只是部分正确的。编译器在一般情况下 无法 推断是否有其他指针指向切片的后备数组。因此,重新分配是不适用的。上述代码片段实际上 保证 执行一个复制 'newSize' 个元素的操作。对可能造成的任何困惑表示抱歉。

英文:

To perform an, in effect, a realloc of a slice:

a = append([]T(nil), a[:newSize]...) // Thanks to @Dijkstra for pointing out the missing ellipsis.

If it does a copy of newSize elements to a new memory place or if it does an actual in place resize as in realloc(3) is at complete discretion of the compiler. You might want to investigate the current state and perhaps raise an issue if there's a room for improvement in this.

However, this is likely a micro-optimization. The first source of performance enhancements lies almost always in selecting a better algorithm and/or a better data structure. Using a hugely sized vector to finally keep a few items only is probably not the best option wrt to memory consumption.

EDIT: The above is only partially correct. The compiler cannot, in the general case, derive if there are other pointers to the slice's backing array. Thus the realloc is not applicable. The above snippet is actually guaranteed to peform a copy of 'newSize' elements. Sorry for any confusion possibly created.

答案2

得分: 9

Go没有缩小切片的方法。在大多数情况下这不是一个问题,但是如果你分析了内存使用情况发现使用太多,你可以采取一些措施:

首先,你可以创建一个所需大小的切片,并将数据复制到其中。垃圾回收器将释放大的切片。Copy内置函数

其次,每次生成大切片时,你可以重复使用它,这样你就不会多次分配它。

最后,你可以使用1e7代替math.Pow10(7)

英文:

Go does not have a way of shrinking slices. This isn't a problem in most cases, but if you profile your memory use and find you're using too much, you can do something about it:

Firstly, you can just create a slice of the size you need and copy your data into it. The garbage collector will then free the large slice. Copy built-in

Secondly, you could re-use the big slice each time you wish to generate it, so you never allocate it more than once.

On a final note, you can use 1e7 instead of math.Pow10(7).

答案3

得分: 5

让我们看一个例子:

func main() {
    s := []string{"A", "B", "C", "D", "E", "F", "G", "H"}
    fmt.Println(s, len(s), cap(s))  // 切片,长度,容量

    t := s[2:4]
    fmt.Println(t, len(t), cap(t))

    u := make([]string, len(t))
    copy(u, t)
    fmt.Println(u, len(u), cap(u))
}

它产生以下输出:

[A B C D E F G H] 8 8
[C D] 2 6
[C D] 2 2

s 是一个包含8个字符串的切片。t 是一个保留了部分 [C D] 的切片。t 的长度是2,但由于它使用了与 s 相同的隐藏数组,它的容量是6(从 "C" 到 "H")。问题是:如何拥有一个与 s 的隐藏数组独立的 [C D] 切片?只需创建一个长度为2的新字符串切片(切片 u),并将 t 的内容复制到 uu 的底层隐藏数组与 s 的隐藏数组不同。

最初的问题是:你有一个大的切片,并在其上创建一个新的较小的切片。由于较小的切片使用了相同的隐藏数组,垃圾回收器不会删除隐藏数组。

有关更多信息,请参阅此帖子底部:http://blog.golang.org/go-slices-usage-and-internals 。

英文:

Let's see this example:

func main() {
    s := []string{"A", "B", "C", "D", "E", "F", "G", "H"}
    fmt.Println(s, len(s), cap(s))  // slice, length, capacity

    t := s[2:4]
    fmt.Println(t, len(t), cap(t))

    u := make([]string, len(t))
    copy(u, t)
    fmt.Println(u, len(u), cap(u))
}

It produces the following output:

[A B C D E F G H] 8 8
[C D] 2 6
[C D] 2 2

s is a slice that holds 8 pieces of strings. t is a slice that keeps the part [C D]. The length of t is 2, but since it uses the same hidden array of s, its capacity is 6 (from "C" to "H"). The question is: how to have a slice of [C D] that is independent from the hidden array of s? Simply create a new slice of strings with length 2 (slice u) and copy the content of t to u. u's underlying hidden array is different from the hidden array of s.

The initial problem was this: you have a big slice and you create a new smaller slice on it. Since the smaller slice uses the same hidden array, the garbage collector won't delete the hidden array.

See the bottom of this post for more info: http://blog.golang.org/go-slices-usage-and-internals .

答案4

得分: 3

此外,您可以在您的应用程序工作期间重复使用大部分分配的内存,请参阅:bufs package

如果您为较小的切片重新分配新的内存,旧的内存可能不会在同一时间被释放,它将在垃圾收集器决定时被释放。

英文:

Additionally you can re-use most of the allocated memory during work of yours app, take a look at: bufs package

PS if you re-alocate new memory for smaller slice, old memory may not be freed in same time, it will be freed when garbage collector decides to.

答案5

得分: 1

你可以通过将切片的值重新分配给其自身的一部分来实现这一点。

  a := []int{1,2,3}
  fmt.Println(len(a), a) // 3 [1 2 3]

  a = a[:len(a)-1]
  fmt.Println(len(a), a) //2 [1 2]
英文:

You can do that by re-assigning the slice's value to a portion of itself

  a := []int{1,2,3}
  fmt.Println(len(a), a) // 3 [1 2 3]

  a = a[:len(a)-1]
  fmt.Println(len(a), a) //2 [1 2]

答案6

得分: 1

在Go 1.2中有一个名为3-index slice的新功能,它可以通过以下方式获取切片的一部分:

slice[a:b:c]

返回的切片的len将为b-a,新切片的cap将为c-a

提示:整个过程中没有进行复制操作,它只返回一个指向&slice[a]的新切片,其lenb-acapc-a

你只需要执行以下操作:

slice= slice[0:len(slice):len(slice)];

然后切片的cap将被更改为len(slice) - 0,与其len相同,并且没有进行复制操作。

英文:

There is a new feature called 3-index slice in Go 1.2, which means to get part of a slice in this way:

slice[a:b:c]

In which the len for the returned slice whould be b-a, and the cap for the new slice would be c-a.

Tips: no copy is down in the whole process, it only returns a new slice which points to &slice[a] and has the len as b-a and cav as c-a.

And that's the only thing you have to do:

slice= slice[0:len(slice):len(slice)];

Then the cap of the slice would be changed to len(slice) - 0, which is the same as the len of it, and no copy is done.

huangapple
  • 本文由 发表于 2013年5月25日 17:49:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/16748330.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定