@@ -16,58 +16,64 @@ const preloadLimit LNumber = 128
16
16
var _fv float64
17
17
var _uv uintptr
18
18
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
+
19
27
// allocator is a fast bulk memory allocator for the LValue.
20
28
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
29
35
}
30
36
31
37
func newAllocator (size int ) * allocator {
32
38
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 ,
40
42
}
41
- al .nheader = (* reflect .SliceHeader )(unsafe .Pointer (& al .nptrs ))
42
43
al .fheader = (* reflect .SliceHeader )(unsafe .Pointer (& al .fptrs ))
44
+ al .scratchValue = LNumber (0 )
45
+ al .scratchValueP = (* iface )(unsafe .Pointer (& al .scratchValue ))
43
46
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
- }
50
47
return al
51
48
}
52
49
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.
53
57
func (al * allocator ) LNumber2I (v LNumber ) LValue {
58
+ // first check for shared preloaded numbers
54
59
if v >= 0 && v < preloadLimit && float64 (v ) == float64 (int64 (v )) {
55
- return al . preloads [int (v )]
60
+ return preloads [int (v )]
56
61
}
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 )
62
66
al .fheader = (* reflect .SliceHeader )(unsafe .Pointer (& al .fptrs ))
63
67
}
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 ++
67
68
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
73
79
}
0 commit comments