@@ -26,52 +26,58 @@ func init() {
26
26
27
27
// allocator is a fast bulk memory allocator for the LValue.
28
28
type allocator struct {
29
- top int
30
- size int
31
- nptrs [] LValue
32
- nheader * reflect.SliceHeader
33
- fptrs [] float64
34
- fheader * reflect. SliceHeader
35
- itabLNumber unsafe. Pointer
29
+ top int
30
+ size int
31
+ fptrs [] float64
32
+ fheader * reflect.SliceHeader
33
+
34
+ scratchValue LValue
35
+ scratchValueP * iface
36
36
}
37
37
38
38
func newAllocator (size int ) * allocator {
39
39
al := & allocator {
40
- top : 0 ,
41
- size : size ,
42
- nptrs : make ([]LValue , size ),
43
- nheader : nil ,
44
- fptrs : make ([]float64 , size ),
45
- fheader : nil ,
46
- itabLNumber : unsafe .Pointer (nil ),
40
+ top : 0 ,
41
+ size : size ,
42
+ fptrs : make ([]float64 , size ),
43
+ fheader : nil ,
47
44
}
48
- al .nheader = (* reflect .SliceHeader )(unsafe .Pointer (& al .nptrs ))
49
45
al .fheader = (* reflect .SliceHeader )(unsafe .Pointer (& al .fptrs ))
46
+ al .scratchValue = LNumber (0 )
47
+ al .scratchValueP = (* iface )(unsafe .Pointer (& al .scratchValue ))
50
48
51
- var v LValue = LNumber (0 )
52
- vp := (* iface )(unsafe .Pointer (& v ))
53
- al .itabLNumber = vp .itab
54
49
return al
55
50
}
56
51
52
+ // LNumber2I takes a number value and returns an interface LValue representing the same number.
53
+ // Converting an LNumber to a LValue naively, by doing:
54
+ // `var val LValue = myLNumber`
55
+ // will result in an individual heap alloc of 8 bytes for the float value. LNumber2I amortizes the cost and memory
56
+ // overhead of these allocs by allocating blocks of floats instead.
57
+ // The downside of this is that all of the floats on a given block have to become eligible for gc before the block
58
+ // as a whole can be gc-ed.
57
59
func (al * allocator ) LNumber2I (v LNumber ) LValue {
60
+ // first check for shared preloaded numbers
58
61
if v >= 0 && v < preloadLimit && float64 (v ) == float64 (int64 (v )) {
59
62
return preloads [int (v )]
60
63
}
61
- if al .top == len (al .nptrs )- 1 {
64
+
65
+ // check if we need a new alloc page
66
+ if al .top == len (al .fptrs )- 1 {
62
67
al .top = 0
63
- al .nptrs = make ([]LValue , al .size )
64
- al .nheader = (* reflect .SliceHeader )(unsafe .Pointer (& al .nptrs ))
65
68
al .fptrs = make ([]float64 , al .size )
66
69
al .fheader = (* reflect .SliceHeader )(unsafe .Pointer (& al .fptrs ))
67
70
}
71
+
72
+ // alloc a new float, and store our value into it
68
73
fptr := (* float64 )(unsafe .Pointer (al .fheader .Data + uintptr (al .top )* unsafe .Sizeof (_fv )))
69
- e := * (* LValue )(unsafe .Pointer (al .nheader .Data + uintptr (al .top )* unsafe .Sizeof (_uv )))
70
74
al .top ++
71
-
72
- ep := (* iface )(unsafe .Pointer (& e ))
73
- ep .itab = al .itabLNumber
74
75
* fptr = float64 (v )
75
- ep .word = unsafe .Pointer (fptr )
76
- return e
76
+
77
+ // hack our scratch LValue to point to our allocated value
78
+ // this scratch lvalue is copied when this function returns meaning the scratch value can be reused
79
+ // on the next call
80
+ al .scratchValueP .word = unsafe .Pointer (fptr )
81
+
82
+ return al .scratchValue
77
83
}
0 commit comments