Thanks. The order of items does matter in my current case, but that
could be useful elsewhere.
You're right. I wrote a benchmark that creates a slice of (b.N+1)
items then deletes the second (i.e., s[1]) item b.N times.
package foo
import (
"rand"
"testing"
)
func BenchmarkAppend(b *testing.B) {
b.StopTimer()
s := make([]interface{}, b.N+1)
for i := 0; i < b.N+1; i++ {
s[i] = rand.Int()
}
b.StartTimer()
for i := 0; i < b.N; i++ {
s = append(s[:1], s[2:]...)
}
}
func BenchmarkCopy(b *testing.B) {
b.StopTimer()
s := make([]interface{}, b.N+1)
for i := 0; i < b.N+1; i++ {
s[i] = rand.Int()
}
b.StartTimer()
for i := 0; i < b.N; i++ {
copy(s[1:], s[2:])
s = s[:len(s)-1]
}
}
And the results:
foo.BenchmarkAppend 100000 315823 ns/op
foo.BenchmarkCopy 100000 310980 ns/op
It looks like append is only slightly slower, and I'm guessing it
doesn't allocate any unnecessary memory since it's not much slower
than copy. But still, a more concrete evidence of append's memory
usage would be better. I'll try playing with the profiler later and
see for myself.
If the capacity of s is not large enough to fit the additional values, append allocates a new, sufficiently large slice that fits both the existing slice elements and the additional values. Thus, the returned slice may refer to a different underlying array.
No. Good for you. I hope your superiority complex has had a decent stroking.
Unfortunately, it only occured as a result of your inability to read
rather than just recognize isolated phrases that you have some
objection to.
What I said was that I thought they were asking a more difficult
question, "does the function allocate memory?" rather than the one
they ended up making a test case for, which is a non-question since
the answer is in the spec. I pointed out that there is a strong
possibility that memory is in fact allocated in order to indicate that
there is still something to investigate if you need to know, not as a
final answer.
> You may want to look at the C source code "src/pkg/runtime/slice.c",
> function "appendslice1".
A perfect place to find an answer, yes.
Well, yes, that's what I was thinking about when I asked, the
possibility of allocating some temporary memory didn't occur to me
until you pointed it out.
> > You may want to look at the C source code "src/pkg/runtime/slice.c",
> > function "appendslice1".
>
> A perfect place to find an answer, yes.
I took a look at the source code, if the underlying array is big
enough (which is always the case when deleting an item), append is
just a memmove[1]. slicecopy (in the same file) is also just a
memmove, but it does fewer checks since it will stop when the
destination slice is full instead of allocating a new underlying
array. That explains why copy is marginally faster than append.
I'll continue to use append for deleting items, the difference isn't
big enough to make a difference, and I find it more readable. Thanks
for your help (also thanks to ⚛ for pointing me to the relevant
runtime source code).
[1] runtime·memmove is an ordinary C memmove, right?