Skip to content

Commit 72a807a

Browse files
einsiedlerspielkanaka
authored andcommitted
Hare: Use hashmap to keep track of symbols in gc
This fixes the performance issues with symbol creation.
1 parent b09b4dc commit 72a807a

File tree

3 files changed

+108
-55
lines changed

3 files changed

+108
-55
lines changed

impls/hare/mal/gc.ha

Lines changed: 95 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
type memory = struct {
44
envs: []*env,
55
hashs: []hashmap,
6-
symbols: []symbol,
6+
symbols: (void | hashmap),
77
funcs: []function,
88
lists: []list,
99
vecs: []vector,
@@ -19,25 +19,53 @@ type garbage_collector = struct {
1919

2020
let gc = garbage_collector {
2121
marked = memory {
22+
symbols = void,
2223
funcs = [],
2324
...
2425
},
2526
memory = memory {
27+
symbols = void,
2628
funcs = [],
2729
...
2830
},
2931
};
3032

33+
fn reset_memory(memory: *memory) void = {
34+
35+
memory.envs = memory.envs[..0];
36+
memory.hashs = memory.hashs[..0];
37+
memory.funcs = memory.funcs[..0];
38+
memory.lists = memory.lists[..0];
39+
memory.vecs = memory.vecs[..0];
40+
memory.strings = memory.strings[..0];
41+
memory.atoms = memory.atoms[..0];
42+
memory.intrinsics = memory.intrinsics[..0];
43+
44+
match(memory.symbols){
45+
case let hm: hashmap =>
46+
hm.data = hm.data[..0];
47+
case void =>
48+
void;
49+
};
50+
};
51+
3152
fn finish_memory(memory: memory) void = {
53+
3254
free(memory.envs);
3355
free(memory.hashs);
34-
free(memory.symbols);
3556
free(memory.funcs);
3657
free(memory.lists);
3758
free(memory.vecs);
3859
free(memory.strings);
3960
free(memory.atoms);
4061
free(memory.intrinsics);
62+
63+
match(memory.symbols){
64+
case let hm: hashmap =>
65+
hm_free(hm);
66+
case void =>
67+
void;
68+
};
4169
};
4270

4371
fn mark_hash(hm: hashmap) void = {
@@ -75,6 +103,12 @@ fn mark_col(col: []MalType) void = {
75103

76104
fn mark (val: MalType) void = {
77105

106+
match(gc.marked.symbols){
107+
case void =>
108+
gc.marked.symbols = hm_init(false);
109+
case => void;
110+
};
111+
78112
match(val){
79113
case let v: vector =>
80114
for(let x .. gc.marked.vecs){
@@ -121,10 +155,11 @@ fn mark (val: MalType) void = {
121155
};
122156
mark_hash(h);
123157
case let s: symbol =>
124-
for(let x .. gc.marked.symbols){
125-
if(x == s) return void;
158+
match(hm_get(gc.marked.symbols: hashmap, s)){
159+
case undefined_key =>
160+
hm_add(gc.marked.symbols: hashmap, s, s);
161+
case => void;
126162
};
127-
append(gc.marked.symbols, s);
128163
case let s: string =>
129164
for(let x .. gc.marked.strings){
130165
if(x == s) return void;
@@ -143,83 +178,93 @@ fn mark (val: MalType) void = {
143178

144179
fn sweep() void ={
145180

146-
// since we're only ever comparing pointers and never dereferencing here
147-
// it should be ok to not call delete(gc.memory.<t>[i-1]) on the after
148-
// freeing the value.
149-
//
150-
// This means doing anything with gc.memory after calling this function
151-
// is extremely unsafe.
181+
const marked_symbols = match(gc.marked.symbols){
182+
case void =>
183+
gc.marked.symbols = hm_init(false);
184+
yield gc.marked.symbols: hashmap;
185+
case let hm: hashmap =>
186+
yield hm;
187+
};
152188

153-
for :sweep (let i: size = len(gc.memory.symbols); i > 0; i -= 1) {
154-
for(let x .. gc.marked.symbols){
155-
if(x == gc.memory.symbols[i-1]) continue :sweep;
189+
const memory_symbols = match(gc.memory.symbols){
190+
case void =>
191+
gc.memory.symbols = hm_init(false);
192+
yield gc.memory.symbols: hashmap;
193+
case let hm: hashmap =>
194+
yield hm;
195+
};
196+
197+
for (let i: size = 0; len(memory_symbols.data) > i; i += 1) {
198+
match(hm_get(marked_symbols, memory_symbols.data[i].key)){
199+
case undefined_key =>
200+
free(memory_symbols.data[i].key: symbol);
201+
case =>
202+
void;
156203
};
157-
free(gc.memory.symbols[i-1]);
158204
};
159-
for :sweep (let i: size = len(gc.memory.atoms); i > 0; i -= 1) {
205+
for :sweep (let i: size = 0; len(gc.memory.atoms) > i; i += 1) {
160206
for(let x .. gc.marked.atoms){
161-
if(x == gc.memory.atoms[i-1]) continue :sweep;
207+
if(x == gc.memory.atoms[i]) continue :sweep;
162208
};
163-
free(gc.memory.atoms[i-1]);
209+
free(gc.memory.atoms[i]);
164210
};
165-
for :sweep (let i: size = len(gc.memory.strings); i > 0; i -= 1) {
211+
for :sweep (let i: size = 0; len(gc.memory.strings) > i; i += 1) {
166212
for(let x .. gc.marked.strings){
167-
if(x == gc.memory.strings[i-1]) continue :sweep;
213+
if(x == gc.memory.strings[i]) continue :sweep;
168214
};
169-
free_string(gc.memory.strings[i-1]);
215+
free_string(gc.memory.strings[i]);
170216
};
171-
for :sweep (let i: size = len(gc.memory.hashs); i > 0; i -= 1) {
217+
for :sweep (let i: size = 0; len(gc.memory.hashs) > i; i += 1) {
172218
for(let x .. gc.marked.hashs){
173-
if(x == gc.memory.hashs[i-1]) continue :sweep;
219+
if(x == gc.memory.hashs[i]) continue :sweep;
174220
};
175-
hm_free(gc.memory.hashs[i-1]);
221+
hm_free(gc.memory.hashs[i]);
176222
};
177-
for :sweep (let i: size = len(gc.memory.envs); i > 0; i -= 1) {
223+
for :sweep (let i: size = 0; len(gc.memory.envs) > i; i += 1) {
178224
for(let x .. gc.marked.envs){
179-
if(x == gc.memory.envs[i-1]) continue :sweep;
225+
if(x == gc.memory.envs[i]) continue :sweep;
180226
};
181-
free(gc.memory.envs[i-1]); //.data is collected as a hashmap
227+
free(gc.memory.envs[i]); //.data is collected as a hashmap
182228
};
183-
for :sweep (let i: size = len(gc.memory.vecs); i > 0; i -= 1) {
229+
for :sweep (let i: size = 0; len(gc.memory.vecs) > i; i += 1) {
184230
for(let x .. gc.marked.vecs){
185-
if(x == gc.memory.vecs[i-1]) continue :sweep;
231+
if(x == gc.memory.vecs[i]) continue :sweep;
186232
};
187-
free_vec(gc.memory.vecs[i-1]);
233+
free_vec(gc.memory.vecs[i]);
188234
};
189-
for :sweep (let i: size = len(gc.memory.lists); i > 0; i -= 1) {
235+
for :sweep (let i: size = 0; len(gc.memory.lists) > i; i += 1) {
190236
for(let x .. gc.marked.lists){
191-
if(x == gc.memory.lists[i-1]) continue :sweep;
237+
if(x == gc.memory.lists[i]) continue :sweep;
192238
};
193-
free_list(gc.memory.lists[i-1]);
239+
free_list(gc.memory.lists[i]);
194240
};
195-
for :sweep (let i: size = len(gc.memory.funcs); i > 0; i -= 1) {
241+
for :sweep (let i: size = 0; len(gc.memory.funcs) > i; i += 1) {
196242
for(let x .. gc.marked.funcs){
197-
if(x == gc.memory.funcs[i-1]) continue :sweep;
243+
if(x == gc.memory.funcs[i]) continue :sweep;
198244
};
199-
free_func(gc.memory.funcs[i-1]);
245+
free_func(gc.memory.funcs[i]);
200246
};
201-
for :sweep (let i: size = len(gc.memory.intrinsics); i > 0; i -= 1) {
247+
for :sweep (let i: size = 0; len(gc.memory.intrinsics) > i; i += 1) {
202248
for(let x .. gc.marked.intrinsics){
203-
if(x == gc.memory.intrinsics[i-1]) continue :sweep;
249+
if(x == gc.memory.intrinsics[i]) continue :sweep;
204250
};
205-
free(gc.memory.intrinsics[i-1]);
251+
free(gc.memory.intrinsics[i]);
252+
};
253+
254+
reset_memory(&gc.memory);
255+
256+
gc = garbage_collector {
257+
marked = gc.memory,
258+
memory = gc.marked,
206259
};
207260
};
208261

209-
// it doesn't make sense to call this with anything but the global repl_env.
262+
// it doesn't make sense to call this with anything but the global repl_env,
263+
// because as of this version there's no way to keep track of objects reachable
264+
// through the ast of the current evaluation and it's possible continuations.
210265

211266
export fn run_gc(envi: *env) void = {
212267

213268
mark_env(envi);
214269
sweep();
215-
finish_memory(gc.memory);
216-
217-
gc = garbage_collector {
218-
marked = memory {
219-
funcs = [],
220-
...
221-
},
222-
memory = gc.marked,
223-
};
224270
};
225-

impls/hare/mal/hashmap.ha

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ fn hm_print(
212212
};
213213
};
214214

215-
216215
fn hash_cmp(hm1: hashmap, hm2: hashmap) bool = {
217216

218217
if(len(hm1.data) != len(hm2.data)){

impls/hare/mal/types.ha

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,22 @@ fn free_vec(v: vector) void = {
130130

131131
export fn make_symbol(name: str) symbol = {
132132

133-
// this is slow
134-
for(let s .. gc.memory.symbols){
135-
if(s == name) return s;
133+
let hm: hashmap = match(gc.memory.symbols){
134+
case void =>
135+
gc.memory.symbols = hm_init(false);
136+
yield gc.memory.symbols: hashmap;
137+
case let hm: hashmap =>
138+
yield hm;
139+
};
140+
141+
match(hm_get(hm, name: symbol)) {
142+
case undefined_key => void;
143+
case let s: symbol =>
144+
return s;
136145
};
137146

138147
const new = strings::dup(name): symbol;
139-
append(gc.memory.symbols, new);
148+
hm_add(gc.memory.symbols: hashmap, new, new);
140149

141150
return new;
142151
};

0 commit comments

Comments
 (0)