Skip to content

Commit fca77ff

Browse files
committed
runtime: Simplify slice growing/appending code
Refactor the slice appending function to rely on the slice growing function, and remove branches/loops to use a branchfree variant. Signed-off-by: L. Pereira <[email protected]>
1 parent 725518f commit fca77ff

File tree

2 files changed

+18
-43
lines changed

2 files changed

+18
-43
lines changed

src/runtime/slice.go

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,25 @@ package runtime
33
// This file implements compiler builtins for slices: append() and copy().
44

55
import (
6+
"math/bits"
67
"unsafe"
78
)
89

910
// Builtin append(src, elements...) function: append elements to src and return
1011
// the modified (possibly expanded) slice.
11-
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) {
12-
if elemsLen == 0 {
13-
// Nothing to append, return the input slice.
14-
return srcBuf, srcLen, srcCap
15-
}
12+
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) {
13+
newLen := srcLen + elemsLen
1614

17-
if srcLen+elemsLen > srcCap {
18-
// Slice does not fit, allocate a new buffer that's large enough.
19-
srcCap = srcCap * 2
20-
if srcCap == 0 { // e.g. zero slice
21-
srcCap = 1
22-
}
23-
for srcLen+elemsLen > srcCap {
24-
// This algorithm may be made more memory-efficient: don't multiply
25-
// by two but by 1.5 or something. As far as I can see, that's
26-
// allowed by the Go language specification (but may be observed by
27-
// programs).
28-
srcCap *= 2
29-
}
30-
buf := alloc(srcCap*elemSize, nil)
15+
if elemsLen != 0 {
16+
// Allocate a new slice with capacity for elemsLen more elements, if necessary;
17+
// otherwise, reuse the passed slice.
18+
srcBuf, srcLen, srcCap = sliceGrow(srcBuf, srcLen, srcCap, newLen, elemSize)
3119

32-
// Copy the old slice to the new slice.
33-
if srcLen != 0 {
34-
memmove(buf, srcBuf, srcLen*elemSize)
35-
}
36-
srcBuf = buf
20+
// Append the new elements in-place.
21+
memmove(unsafe.Add(srcBuf, srcLen*elemSize), elemsBuf, elemsLen*elemSize)
3722
}
3823

39-
// The slice fits (after possibly allocating a new one), append it in-place.
40-
memmove(unsafe.Add(srcBuf, srcLen*elemSize), elemsBuf, elemsLen*elemSize)
41-
return srcBuf, srcLen + elemsLen, srcCap
24+
return srcBuf, newLen, srcCap
4225
}
4326

4427
// Builtin copy(dst, src) function: copy bytes from dst to src.
@@ -54,29 +37,21 @@ func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr
5437

5538
// sliceGrow returns a new slice with space for at least newCap elements
5639
func sliceGrow(oldBuf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) {
57-
58-
// TODO(dgryski): sliceGrow() and sliceAppend() should be refactored to share the base growth code.
59-
6040
if oldCap >= newCap {
6141
// No need to grow, return the input slice.
6242
return oldBuf, oldLen, oldCap
6343
}
6444

65-
// allow nil slice
66-
if oldCap == 0 {
67-
oldCap++
68-
}
69-
70-
// grow capacity
71-
for oldCap < newCap {
72-
oldCap *= 2
73-
}
45+
// This can be made more memory-efficient by multiplying by some other constant, such as 1.5,
46+
// which seems to be allowed by the Go language specification (but this can be observed by
47+
// programs).
48+
newCap = 1 << (32 - bits.LeadingZeros32(uint32(newCap)))
7449

75-
buf := alloc(oldCap*elemSize, nil)
50+
buf := alloc(newCap*elemSize, nil)
7651
if oldLen > 0 {
7752
// copy any data to new slice
7853
memmove(buf, oldBuf, oldLen*elemSize)
7954
}
8055

81-
return buf, oldLen, oldCap
56+
return buf, oldLen, newCap
8257
}

testdata/slice.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ bar: len=3 cap=5 data: 1 2 4
88
slice is nil? true true
99
grow: len=0 cap=0 data:
1010
grow: len=1 cap=1 data: 42
11-
grow: len=3 cap=4 data: 42 -1 -2
11+
grow: len=3 cap=8 data: 42 -1 -2
1212
grow: len=7 cap=8 data: 42 -1 -2 1 2 4 5
1313
grow: len=7 cap=8 data: 42 -1 -2 1 2 4 5
1414
grow: len=14 cap=16 data: 42 -1 -2 1 2 4 5 42 -1 -2 1 2 4 5
15-
bytes: len=6 cap=6 data: 1 2 3 102 111 111
15+
bytes: len=6 cap=8 data: 1 2 3 102 111 111
1616
slice to array pointer: 1 -2 20 4
1717
unsafe.Add array: 1 5 8 4
1818
unsafe.Slice array: 3 3 9 15 4

0 commit comments

Comments
 (0)