Skip to content

Commit 417a26d

Browse files
lpereiradgryski
andauthored
runtime: Simplify slice growing/appending code (#4287)
* reflect: rawFieldByNameFunc: copy index slice to avoid later overwrites * 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]> * runtime: Remove one branch in sliceAppend() Both branches were equivalent, so guard the overall logic in sliceAppend() with the more general condition. Signed-off-by: L. Pereira <[email protected]> * runtime: Simplify slice growing calculation Use `bits.Len()` rather than `32 - bits.LeadingZeros32()`. They're equivalent, but the Len version is a bit easier to read. Signed-off-by: L. Pereira <[email protected]> * reflect: Always call sliceGrow() in extendSlice() sliceGrow() will return the old slice if its capacity is large enough. Signed-off-by: L. Pereira <[email protected]> --------- Signed-off-by: L. Pereira <[email protected]> Co-authored-by: Damian Gryski <[email protected]>
1 parent 88f9fc3 commit 417a26d

File tree

4 files changed

+23
-59
lines changed

4 files changed

+23
-59
lines changed

src/reflect/type.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ func (t *rawType) rawFieldByNameFunc(match func(string) bool) (rawStructField, [
774774
if match(name) {
775775
found = append(found, result{
776776
rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset),
777-
append(ll.index, int(i)),
777+
append(ll.index[:len(ll.index):len(ll.index)], int(i)),
778778
})
779779
}
780780

@@ -787,7 +787,7 @@ func (t *rawType) rawFieldByNameFunc(match func(string) bool) (rawStructField, [
787787

788788
nextlevel = append(nextlevel, fieldWalker{
789789
t: embedded,
790-
index: append(ll.index, int(i)),
790+
index: append(ll.index[:len(ll.index):len(ll.index)], int(i)),
791791
})
792792
}
793793

src/reflect/value.go

+1-12
Original file line numberDiff line numberDiff line change
@@ -1712,18 +1712,7 @@ func extendSlice(v Value, n int) sliceHeader {
17121712
old = *(*sliceHeader)(v.value)
17131713
}
17141714

1715-
var nbuf unsafe.Pointer
1716-
var nlen, ncap uintptr
1717-
1718-
if old.len+uintptr(n) > old.cap {
1719-
// we need to grow the slice
1720-
nbuf, nlen, ncap = sliceGrow(old.data, old.len, old.cap, old.cap+uintptr(n), v.typecode.elem().Size())
1721-
} else {
1722-
// we can reuse the slice we have
1723-
nbuf = old.data
1724-
nlen = old.len
1725-
ncap = old.cap
1726-
}
1715+
nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), v.typecode.elem().Size())
17271716

17281717
return sliceHeader{
17291718
data: nbuf,

src/runtime/slice.go

+18-43
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,24 @@ 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
12+
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) {
13+
newLen := srcLen + elemsLen
14+
if elemsLen > 0 {
15+
// Allocate a new slice with capacity for elemsLen more elements, if necessary;
16+
// otherwise, reuse the passed slice.
17+
srcBuf, _, srcCap = sliceGrow(srcBuf, srcLen, srcCap, newLen, elemSize)
18+
19+
// Append the new elements in-place.
20+
memmove(unsafe.Add(srcBuf, srcLen*elemSize), elemsBuf, elemsLen*elemSize)
1521
}
1622

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)
31-
32-
// Copy the old slice to the new slice.
33-
if srcLen != 0 {
34-
memmove(buf, srcBuf, srcLen*elemSize)
35-
}
36-
srcBuf = buf
37-
}
38-
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
23+
return srcBuf, newLen, srcCap
4224
}
4325

4426
// Builtin copy(dst, src) function: copy bytes from dst to src.
@@ -54,29 +36,22 @@ func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr
5436

5537
// sliceGrow returns a new slice with space for at least newCap elements
5638
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-
6039
if oldCap >= newCap {
6140
// No need to grow, return the input slice.
6241
return oldBuf, oldLen, oldCap
6342
}
6443

65-
// allow nil slice
66-
if oldCap == 0 {
67-
oldCap++
68-
}
69-
70-
// grow capacity
71-
for oldCap < newCap {
72-
oldCap *= 2
73-
}
44+
// This can be made more memory-efficient by multiplying by some other constant, such as 1.5,
45+
// which seems to be allowed by the Go language specification (but this can be observed by
46+
// programs); however, due to memory fragmentation and the current state of the TinyGo
47+
// memory allocators, this causes some difficult to debug issues.
48+
newCap = 1 << bits.Len(uint(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

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ copy foo -> bar: 3
77
bar: len=3 cap=5 data: 1 2 4
88
slice is nil? true true
99
grow: len=0 cap=0 data:
10-
grow: len=1 cap=1 data: 42
10+
grow: len=1 cap=2 data: 42
1111
grow: len=3 cap=4 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)