Skip to content

Commit 0bfe05d

Browse files
committed
Merge pull request #8699 from JuliaLang/ob/gengc
Generational behavior for the garbage collector
2 parents b75638c + 830a6dc commit 0bfe05d

30 files changed

+2270
-595
lines changed

base/base.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ end
154154

155155
finalize(o::ANY) = ccall(:jl_finalize, Void, (Any,), o)
156156

157-
gc() = ccall(:jl_gc_collect, Void, ())
157+
gc(full = true) = ccall(:jl_gc_collect, Void, (Int,), full ? 1 : 0)
158158
gc_enable() = ccall(:jl_gc_enable, Void, ())
159159
gc_disable() = ccall(:jl_gc_disable, Void, ())
160160

base/util.jl

+17-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ gc_time_ns() = ccall(:jl_gc_total_hrtime, UInt64, ())
1212
# total number of bytes allocated so far
1313
gc_bytes() = ccall(:jl_gc_total_bytes, Int64, ())
1414

15+
gc_num_pause() = ccall(:jl_gc_num_pause, Int64, ())
16+
gc_num_full_sweep() = ccall(:jl_gc_num_full_sweep, Int64, ())
17+
1518
function tic()
1619
t0 = time_ns()
1720
task_local_storage(:TIMERS, (t0, get(task_local_storage(), :TIMERS, ())))
@@ -36,12 +39,17 @@ function toc()
3639
end
3740

3841
# print elapsed time, return expression value
39-
40-
function time_print(t, b, g)
42+
const _units = ["bytes", "kB", "MB"]
43+
function time_print(t, b, g, np, nfs)
44+
i = 1
45+
while b > 1024 && i < length(_units)
46+
b = div(b, 1024)
47+
i += 1
48+
end
4149
if 0 < g
42-
@printf("elapsed time: %s seconds (%d bytes allocated, %.2f%% gc time)\n", t/1e9, b, 100*g/t)
50+
@printf("elapsed time: %s seconds (%d %s allocated, %.2f%% gc time in %d pauses with %d full sweep)\n", t/1e9, b, _units[i], 100*g/t, np, nfs)
4351
else
44-
@printf("elapsed time: %s seconds (%d bytes allocated)\n", t/1e9, b)
52+
@printf("elapsed time: %s seconds (%d %s allocated)\n", t/1e9, b, _units[i])
4553
end
4654
end
4755

@@ -50,11 +58,15 @@ macro time(ex)
5058
local b0 = gc_bytes()
5159
local t0 = time_ns()
5260
local g0 = gc_time_ns()
61+
local n0 = gc_num_pause()
62+
local nfs0 = gc_num_full_sweep()
5363
local val = $(esc(ex))
64+
local nfs1 = gc_num_full_sweep()
65+
local n1 = gc_num_pause()
5466
local g1 = gc_time_ns()
5567
local t1 = time_ns()
5668
local b1 = gc_bytes()
57-
time_print(t1-t0, b1-b0, g1-g0)
69+
time_print(t1-t0, b1-b0, g1-g0, n1-n0, nfs1-nfs0)
5870
val
5971
end
6072
end

doc/manual/embedding.rst

+17
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,23 @@ Several Julia values can be pushed at once using the ``JL_GC_PUSH2`` , ``JL_GC_P
107107
// Do something with args (e.g. call jl_... functions)
108108
JL_GC_POP();
109109

110+
The garbage collector also operates under the assumption that it is aware of every old-generation object pointing to a young-generation one. Any time a pointer is updated breaking that assumption, it must be signaled to the collector with the ``gc_wb`` (write barrier) function like so::
111+
112+
jl_value_t *parent = some_old_value, *child = some_young_value;
113+
((some_specific_type*)parent)->field = child;
114+
gc_wb(parent, child);
115+
116+
It is in general impossible to predict which values will be old at runtime, so the write barrier must be inserted after all explicit stores. One notable exception is if the ``parent`` object was just allocated and garbage collection was not run since then. Remember that most ``jl_...`` functions can sometimes invoke garbage collection.
117+
118+
The write barrier is also necessary for arrays of pointers when updating their data directly. For example::
119+
120+
jl_array_t *some_array = ...; // e.g. a Vector{Any}
121+
void **data = (void**)jl_array_data(some_array);
122+
jl_value_t *some_value = ...;
123+
data[0] = some_value;
124+
gc_wb(some_array, some_value);
125+
126+
110127
Manipulating the Garbage Collector
111128
---------------------------------------------------
112129

src/alloc.c

+20-8
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs)
278278
size_t offs = jl_field_offset(st,i) + sizeof(void*);
279279
if (st->fields[i].isptr) {
280280
*(jl_value_t**)((char*)v + offs) = rhs;
281+
if(rhs != NULL) gc_wb(v, rhs);
281282
}
282283
else {
283284
jl_assign_bits((char*)v + offs, rhs);
@@ -521,17 +522,18 @@ static jl_sym_t *mk_symbol(const char *str)
521522
static void unmark_symbols_(jl_sym_t *root)
522523
{
523524
while (root != NULL) {
524-
root->type = (jl_value_t*)(((uptrint_t)root->type)&~1UL);
525+
root->type = (jl_value_t*)(((uptrint_t)root->type)&~3UL);
525526
unmark_symbols_(root->left);
526527
root = root->right;
527528
}
528529
}
529530

530531
void jl_unmark_symbols(void) { unmark_symbols_(symtab); }
531532

532-
static jl_sym_t **symtab_lookup(jl_sym_t **ptree, const char *str)
533+
static jl_sym_t **symtab_lookup(jl_sym_t **ptree, const char *str, jl_sym_t **parent)
533534
{
534535
int x;
536+
if (parent != NULL) *parent = NULL;
535537
uptrint_t h = hash_symbol(str, strlen(str));
536538

537539
// Tree nodes sorted by major key of (int(hash)) and minor key o (str).
@@ -542,6 +544,7 @@ static jl_sym_t **symtab_lookup(jl_sym_t **ptree, const char *str)
542544
if (x == 0)
543545
return ptree;
544546
}
547+
if (parent != NULL) *parent = *ptree;
545548
if (x < 0)
546549
ptree = &(*ptree)->left;
547550
else
@@ -553,16 +556,19 @@ static jl_sym_t **symtab_lookup(jl_sym_t **ptree, const char *str)
553556
jl_sym_t *jl_symbol(const char *str)
554557
{
555558
jl_sym_t **pnode;
556-
557-
pnode = symtab_lookup(&symtab, str);
558-
if (*pnode == NULL)
559+
jl_sym_t *parent;
560+
pnode = symtab_lookup(&symtab, str, &parent);
561+
if (*pnode == NULL) {
559562
*pnode = mk_symbol(str);
563+
if (parent != NULL)
564+
gc_wb(parent, *pnode);
565+
}
560566
return *pnode;
561567
}
562568

563569
jl_sym_t *jl_symbol_lookup(const char *str)
564570
{
565-
return *symtab_lookup(&symtab, str);
571+
return *symtab_lookup(&symtab, str, NULL);
566572
}
567573

568574
DLLEXPORT jl_sym_t *jl_symbol_n(const char *str, int32_t len)
@@ -696,12 +702,15 @@ jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_datatype_t *super,
696702
t = jl_new_uninitialized_datatype(jl_tuple_len(fnames));
697703
else
698704
tn = t->name;
699-
700705
// init before possibly calling jl_new_typename
701706
t->super = super;
707+
if(super != NULL) gc_wb(t, t->super);
702708
t->parameters = parameters;
709+
gc_wb(t, t->parameters);
703710
t->names = fnames;
711+
gc_wb(t, t->names);
704712
t->types = ftypes;
713+
if(ftypes != NULL) gc_wb(t, t->types);
705714
t->abstract = abstract;
706715
t->mutabl = mutabl;
707716
t->pointerfree = 0;
@@ -718,10 +727,13 @@ jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_datatype_t *super,
718727
else
719728
tn = jl_new_typename((jl_sym_t*)name);
720729
t->name = tn;
730+
gc_wb(t, t->name);
721731
}
722732

723-
if (t->name->primary == NULL)
733+
if (t->name->primary == NULL) {
724734
t->name->primary = (jl_value_t*)t;
735+
gc_wb(t->name, t);
736+
}
725737

726738
if (abstract || jl_tuple_len(parameters) > 0) {
727739
t->uid = 0;

src/array.c

+16-5
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims,
9999
memset(data, 0, tot);
100100
JL_GC_POP();
101101
}
102+
a->pooled = tsz <= 2048;
102103

103104
a->data = data;
104105
if (elsz == 1) ((char*)data)[tot-1] = '\0';
@@ -147,8 +148,10 @@ jl_array_t *jl_reshape_array(jl_value_t *atype, jl_array_t *data, jl_tuple_t *di
147148
size_t ndims = jl_tuple_len(dims);
148149

149150
int ndimwords = jl_array_ndimwords(ndims);
150-
a = (jl_array_t*)allocobj((sizeof(jl_array_t) + sizeof(void*) + ndimwords*sizeof(size_t) + 15)&-16);
151+
int tsz = (sizeof(jl_array_t) + sizeof(void*) + ndimwords*sizeof(size_t) + 15)&-16;
152+
a = (jl_array_t*)allocobj(tsz);
151153
a->type = atype;
154+
a->pooled = tsz <= 2048;
152155
a->ndims = ndims;
153156
a->offset = 0;
154157
a->data = NULL;
@@ -211,8 +214,9 @@ jl_array_t *jl_ptr_to_array_1d(jl_value_t *atype, void *data, size_t nel,
211214
elsz = jl_datatype_size(el_type);
212215
else
213216
elsz = sizeof(void*);
214-
215-
a = (jl_array_t*)allocobj((sizeof(jl_array_t)+jl_array_ndimwords(1)*sizeof(size_t)+15)&-16);
217+
int tsz = (sizeof(jl_array_t)+jl_array_ndimwords(1)*sizeof(size_t)+15)&-16;
218+
a = (jl_array_t*)allocobj(tsz);
219+
a->pooled = tsz <= 2048;
216220
a->type = atype;
217221
a->data = data;
218222
#ifdef STORE_ARRAY_LEN
@@ -226,6 +230,7 @@ jl_array_t *jl_ptr_to_array_1d(jl_value_t *atype, void *data, size_t nel,
226230
if (own_buffer) {
227231
a->how = 2;
228232
jl_gc_track_malloced_array(a);
233+
jl_gc_count_allocd(nel*elsz + (elsz == 1 ? 1 : 0));
229234
}
230235
else {
231236
a->how = 0;
@@ -260,7 +265,9 @@ jl_array_t *jl_ptr_to_array(jl_value_t *atype, void *data, jl_tuple_t *dims,
260265
elsz = sizeof(void*);
261266

262267
int ndimwords = jl_array_ndimwords(ndims);
263-
a = (jl_array_t*)allocobj((sizeof(jl_array_t) + ndimwords*sizeof(size_t)+15)&-16);
268+
int tsz = (sizeof(jl_array_t) + ndimwords*sizeof(size_t)+15)&-16;
269+
a = (jl_array_t*)allocobj(tsz);
270+
a->pooled = tsz <= 2048;
264271
a->type = atype;
265272
a->data = data;
266273
#ifdef STORE_ARRAY_LEN
@@ -275,6 +282,7 @@ jl_array_t *jl_ptr_to_array(jl_value_t *atype, void *data, jl_tuple_t *dims,
275282
if (own_buffer) {
276283
a->how = 2;
277284
jl_gc_track_malloced_array(a);
285+
jl_gc_count_allocd(nel*elsz + (elsz == 1 ? 1 : 0));
278286
}
279287
else {
280288
a->how = 0;
@@ -502,6 +510,7 @@ void jl_arrayset(jl_array_t *a, jl_value_t *rhs, size_t i)
502510
}
503511
else {
504512
((jl_value_t**)a->data)[i] = rhs;
513+
gc_wb(a, rhs);
505514
}
506515
}
507516

@@ -545,7 +554,7 @@ static void array_resize_buffer(jl_array_t *a, size_t newlen, size_t oldlen, siz
545554
if (a->how == 2) {
546555
// already malloc'd - use realloc
547556
newdata = (char*)jl_gc_managed_realloc((char*)a->data - oldoffsnb, nbytes,
548-
oldnbytes+oldoffsnb, a->isaligned);
557+
oldnbytes+oldoffsnb, a->isaligned, (jl_value_t*)a);
549558
if (offs != a->offset) {
550559
memmove(&newdata[offsnb], &newdata[oldoffsnb], oldnbytes);
551560
}
@@ -574,6 +583,8 @@ static void array_resize_buffer(jl_array_t *a, size_t newlen, size_t oldlen, siz
574583
a->isshared = 0;
575584
if (a->ptrarray || es==1)
576585
memset(newdata+offsnb+oldnbytes, 0, nbytes-oldnbytes-offsnb);
586+
if (a->how == 1)
587+
gc_wb_buf(a, newdata);
577588
a->maxsize = newlen;
578589
}
579590

src/ast.c

+21-16
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ static jl_value_t *copy_ast(jl_value_t *expr, jl_tuple_t *sp, int do_sp)
731731
// of a top-level thunk that gets type inferred.
732732
li->def = li;
733733
li->ast = jl_prepare_ast(li, li->sparams);
734+
gc_wb(li, li->ast);
734735
JL_GC_POP();
735736
return (jl_value_t*)li;
736737
}
@@ -749,17 +750,18 @@ static jl_value_t *copy_ast(jl_value_t *expr, jl_tuple_t *sp, int do_sp)
749750
jl_expr_t *ne = jl_exprn(e->head, jl_array_len(e->args));
750751
JL_GC_PUSH1(&ne);
751752
if (e->head == lambda_sym) {
752-
jl_exprarg(ne, 0) = copy_ast(jl_exprarg(e,0), sp, 0);
753-
jl_exprarg(ne, 1) = copy_ast(jl_exprarg(e,1), sp, 0);
754-
jl_exprarg(ne, 2) = copy_ast(jl_exprarg(e,2), sp, 1);
753+
jl_exprargset(ne, 0, copy_ast(jl_exprarg(e,0), sp, 0));
754+
jl_exprargset(ne, 1, copy_ast(jl_exprarg(e,1), sp, 0));
755+
jl_exprargset(ne, 2, copy_ast(jl_exprarg(e,2), sp, 1));
755756
}
756757
else if (e->head == assign_sym) {
757-
jl_exprarg(ne, 0) = copy_ast(jl_exprarg(e,0), sp, 0);
758-
jl_exprarg(ne, 1) = copy_ast(jl_exprarg(e,1), sp, 1);
758+
jl_exprargset(ne, 0, copy_ast(jl_exprarg(e,0), sp, 0));
759+
jl_exprargset(ne, 1, copy_ast(jl_exprarg(e,1), sp, 1));
759760
}
760761
else {
761-
for(size_t i=0; i < jl_array_len(e->args); i++)
762-
jl_exprarg(ne, i) = copy_ast(jl_exprarg(e,i), sp, 1);
762+
for(size_t i=0; i < jl_array_len(e->args); i++) {
763+
jl_exprargset(ne, i, copy_ast(jl_exprarg(e,i), sp, 1));
764+
}
763765
}
764766
JL_GC_POP();
765767
return (jl_value_t*)ne;
@@ -780,10 +782,12 @@ DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr)
780782
ne = jl_exprn(e->head, l);
781783
if (l == 0) {
782784
ne->args = jl_alloc_cell_1d(0);
785+
gc_wb(ne, ne->args);
783786
}
784787
else {
785-
for(i=0; i < l; i++)
786-
jl_exprarg(ne, i) = jl_copy_ast(jl_exprarg(e,i));
788+
for(i=0; i < l; i++) {
789+
jl_exprargset(ne, i, jl_copy_ast(jl_exprarg(e,i)));
790+
}
787791
}
788792
JL_GC_POP();
789793
return (jl_value_t*)ne;
@@ -820,17 +824,18 @@ static jl_value_t *dont_copy_ast(jl_value_t *expr, jl_tuple_t *sp, int do_sp)
820824
else if (jl_is_expr(expr)) {
821825
jl_expr_t *e = (jl_expr_t*)expr;
822826
if (e->head == lambda_sym) {
823-
jl_exprarg(e, 0) = dont_copy_ast(jl_exprarg(e,0), sp, 0);
824-
jl_exprarg(e, 1) = dont_copy_ast(jl_exprarg(e,1), sp, 0);
825-
jl_exprarg(e, 2) = dont_copy_ast(jl_exprarg(e,2), sp, 1);
827+
jl_exprargset(e, 0, dont_copy_ast(jl_exprarg(e,0), sp, 0));
828+
jl_exprargset(e, 1, dont_copy_ast(jl_exprarg(e,1), sp, 0));
829+
jl_exprargset(e, 2, dont_copy_ast(jl_exprarg(e,2), sp, 1));
826830
}
827831
else if (e->head == assign_sym) {
828-
jl_exprarg(e, 0) = dont_copy_ast(jl_exprarg(e,0), sp, 0);
829-
jl_exprarg(e, 1) = dont_copy_ast(jl_exprarg(e,1), sp, 1);
832+
jl_exprargset(e, 0, dont_copy_ast(jl_exprarg(e,0), sp, 0));
833+
jl_exprargset(e, 1, dont_copy_ast(jl_exprarg(e,1), sp, 1));
830834
}
831835
else {
832-
for(size_t i=0; i < jl_array_len(e->args); i++)
833-
jl_exprarg(e, i) = dont_copy_ast(jl_exprarg(e,i), sp, 1);
836+
for(size_t i=0; i < jl_array_len(e->args); i++) {
837+
jl_exprargset(e, i, dont_copy_ast(jl_exprarg(e,i), sp, 1));
838+
}
834839
}
835840
return (jl_value_t*)e;
836841
}

0 commit comments

Comments
 (0)