Skip to content

Commit e1787a1

Browse files
authored
Merge pull request yuin#191 from tul/allocator_opto
Allocator opto
2 parents b562f9e + 123714d commit e1787a1

File tree

1 file changed

+42
-36
lines changed

1 file changed

+42
-36
lines changed

alloc.go

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,58 +16,64 @@ const preloadLimit LNumber = 128
1616
var _fv float64
1717
var _uv uintptr
1818

19+
var preloads [int(preloadLimit)]LValue
20+
21+
func init() {
22+
for i := 0; i < int(preloadLimit); i++ {
23+
preloads[i] = LNumber(i)
24+
}
25+
}
26+
1927
// allocator is a fast bulk memory allocator for the LValue.
2028
type allocator struct {
21-
top int
22-
size int
23-
nptrs []LValue
24-
nheader *reflect.SliceHeader
25-
fptrs []float64
26-
fheader *reflect.SliceHeader
27-
itabLNumber unsafe.Pointer
28-
preloads [int(preloadLimit)]LValue
29+
size int
30+
fptrs []float64
31+
fheader *reflect.SliceHeader
32+
33+
scratchValue LValue
34+
scratchValueP *iface
2935
}
3036

3137
func newAllocator(size int) *allocator {
3238
al := &allocator{
33-
top: 0,
34-
size: size,
35-
nptrs: make([]LValue, size),
36-
nheader: nil,
37-
fptrs: make([]float64, size),
38-
fheader: nil,
39-
itabLNumber: unsafe.Pointer(nil),
39+
size: size,
40+
fptrs: make([]float64, 0, size),
41+
fheader: nil,
4042
}
41-
al.nheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.nptrs))
4243
al.fheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.fptrs))
44+
al.scratchValue = LNumber(0)
45+
al.scratchValueP = (*iface)(unsafe.Pointer(&al.scratchValue))
4346

44-
var v LValue = LNumber(0)
45-
vp := (*iface)(unsafe.Pointer(&v))
46-
al.itabLNumber = vp.itab
47-
for i := 0; i < int(preloadLimit); i++ {
48-
al.preloads[i] = LNumber(i)
49-
}
5047
return al
5148
}
5249

50+
// LNumber2I takes a number value and returns an interface LValue representing the same number.
51+
// Converting an LNumber to a LValue naively, by doing:
52+
// `var val LValue = myLNumber`
53+
// will result in an individual heap alloc of 8 bytes for the float value. LNumber2I amortizes the cost and memory
54+
// overhead of these allocs by allocating blocks of floats instead.
55+
// The downside of this is that all of the floats on a given block have to become eligible for gc before the block
56+
// as a whole can be gc-ed.
5357
func (al *allocator) LNumber2I(v LNumber) LValue {
58+
// first check for shared preloaded numbers
5459
if v >= 0 && v < preloadLimit && float64(v) == float64(int64(v)) {
55-
return al.preloads[int(v)]
60+
return preloads[int(v)]
5661
}
57-
if al.top == len(al.nptrs)-1 {
58-
al.top = 0
59-
al.nptrs = make([]LValue, al.size)
60-
al.nheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.nptrs))
61-
al.fptrs = make([]float64, al.size)
62+
63+
// check if we need a new alloc page
64+
if cap(al.fptrs) == len(al.fptrs) {
65+
al.fptrs = make([]float64, 0, al.size)
6266
al.fheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.fptrs))
6367
}
64-
fptr := (*float64)(unsafe.Pointer(al.fheader.Data + uintptr(al.top)*unsafe.Sizeof(_fv)))
65-
e := *(*LValue)(unsafe.Pointer(al.nheader.Data + uintptr(al.top)*unsafe.Sizeof(_uv)))
66-
al.top++
6768

68-
ep := (*iface)(unsafe.Pointer(&e))
69-
ep.itab = al.itabLNumber
70-
*fptr = float64(v)
71-
ep.word = unsafe.Pointer(fptr)
72-
return e
69+
// alloc a new float, and store our value into it
70+
al.fptrs = append(al.fptrs, float64(v))
71+
fptr := (*float64)(unsafe.Pointer(al.fheader.Data + uintptr(len(al.fptrs)-1)*unsafe.Sizeof(_fv)))
72+
73+
// hack our scratch LValue to point to our allocated value
74+
// this scratch lvalue is copied when this function returns meaning the scratch value can be reused
75+
// on the next call
76+
al.scratchValueP.word = unsafe.Pointer(fptr)
77+
78+
return al.scratchValue
7379
}

0 commit comments

Comments
 (0)