From 0df50fd5c0a7545ef1fb8162c581f4766bb2d0c3 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 14 Dec 2011 01:17:24 -0500 Subject: [PATCH] storing ASTs serialized to save memory part of issue #261 significantly cuts the number of live objects, making GC much faster --- j/inference.j | 44 +++++++++++----- src/codegen.cpp | 12 ++++- src/dump.c | 134 +++++++++++++++++++++++++++++++++++++---------- src/gc.c | 15 +++--- src/jltypes.c | 2 +- src/julia.expmap | 2 + 6 files changed, 159 insertions(+), 50 deletions(-) diff --git a/j/inference.j b/j/inference.j index 3e0dc026de46a..d4243a853cc47 100644 --- a/j/inference.j +++ b/j/inference.j @@ -837,6 +837,10 @@ function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop) tf = def.tfunc while !is(tf,()) if typeseq(tf[1],atypes) + if isa(tf[2],Tuple) + # compressed tree format + return (tf[2], tf[2][3]) + end # if the frame above this one recurred, rerun type inf # here instead of returning, and update the cache, until the new # inferred type equals the cached type (fixed point) @@ -885,11 +889,12 @@ function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop) #print("typeinf ", linfo.name, " ", atypes, "\n") if redo - elseif cop - sparams = append(sparams, linfo.sparams) - ast = ccall(:jl_prepare_ast, Any, (Any,Any), linfo.ast, sparams)::Expr else ast = linfo.ast + if cop + sparams = append(sparams, linfo.sparams) + ast = ccall(:jl_prepare_ast, Any, (Any,Any), ast, sparams)::Expr + end end assert(is(ast.head,:lambda), "inference.j:745") @@ -1059,14 +1064,19 @@ function typeinf(linfo::LambdaStaticData,atypes::Tuple,sparams::Tuple, def, cop) fulltree = type_annotate(ast, s, sv, rec ? RecPending{frame.result} : frame.result, vars) - if !redo - def.tfunc = (atypes, fulltree, def.tfunc) - end if !rec fulltree.args[3] = inlining_pass(fulltree.args[3], s[1]) tuple_elim_pass(fulltree) linfo.inferred = true end + if !redo + vals = {} + treedata = ccall(:jl_compress_ast, Any, (Any, Any), fulltree, vals) + compressed = (treedata, vals, frame.result) + fulltree = compressed + #compressed = fulltree + def.tfunc = (atypes, compressed, def.tfunc) + end inference_stack = (inference_stack::CallStack).prev return (fulltree, frame.result) end @@ -1295,17 +1305,21 @@ function inlineable(f, e::Expr, vars) return NF end end - for vi = meth[3].ast.args[2].args[2] + (ast, ty) = typeinf(meth[3], meth[1], meth[2], meth[3]) + if is(ast,()) + return NF + end + if isa(ast,Tuple) + ast = ccall(:jl_uncompress_ast, Any, (Any,), ast) + end + ast = ast::Expr + for vi = ast.args[2].args[2] if (vi[3]&1)!=0 # captures variables (TODO) return NF end end - (ast, ty) = typeinf(meth[3], meth[1], meth[2], meth[3]) - if is(ast,()) - return NF - end - body = without_linenums(ast.args[3].args) + body = without_linenums(ast.args[3].args)::Array{Any,1} # see if body is only "return " if length(body) != 1 return NF @@ -1543,7 +1557,11 @@ end function finfer(f, types) x = getmethods(f,types)[1] - typeinf(x[3], x[1], x[2])[1] + (tree, ty) = typeinf(x[3], x[1], x[2]) + if isa(tree,Tuple) + return ccall(:jl_uncompress_ast, Any, (Any,), tree) + end + tree end tfunc(f,t) = (getmethods(f,t)[1][3]).tfunc diff --git a/src/codegen.cpp b/src/codegen.cpp index 1735cd64bb8e0..959f73cc6d9c2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -143,7 +143,6 @@ static Function *to_function(jl_lambda_info_t *li) JL_SIGATOMIC_BEGIN(); Function *f = Function::Create(jl_func_sig, Function::ExternalLinkage, li->name->name, jl_Module); - assert(jl_is_expr(li->ast)); assert(!li->inInference); li->functionObject = (void*)f; BasicBlock *old = nested_compile ? builder.GetInsertBlock() : NULL; @@ -356,11 +355,14 @@ static void make_gcroot(Value *v, jl_codectx_t *ctx) // --- lambda --- +extern "C" jl_value_t *jl_uncompress_ast(jl_tuple_t *data); + static Value *emit_lambda_closure(jl_value_t *expr, jl_codectx_t *ctx) { assert(jl_is_lambda_info(expr)); size_t i; - jl_array_t *capt = jl_lam_capt((jl_expr_t*)((jl_lambda_info_t*)expr)->ast); + jl_value_t *ast = ((jl_lambda_info_t*)expr)->ast; + jl_array_t *capt = jl_lam_capt((jl_expr_t*)ast); if (capt->length == 0) { // no captured vars; lift jl_value_t *fun = jl_new_closure_internal((jl_lambda_info_t*)expr, @@ -1327,6 +1329,11 @@ extern "C" jl_tuple_t *jl_tuple_tvars_to_symbols(jl_tuple_t *t); static void emit_function(jl_lambda_info_t *lam, Function *f) { jl_expr_t *ast = (jl_expr_t*)lam->ast; + if (jl_is_tuple(ast)) { + ast = (jl_expr_t*)jl_uncompress_ast((jl_tuple_t*)ast); + } + assert(jl_is_expr(ast)); + jl_gc_preserve((jl_value_t*)ast); //jl_print((jl_value_t*)ast); //ios_printf(ios_stdout, "\n"); BasicBlock *b0 = BasicBlock::Create(jl_LLVMContext, "top", f); @@ -1700,6 +1707,7 @@ static void emit_function(jl_lambda_info_t *lam, Function *f) //used_roots += ctx.maxDepth; //JL_GC_POP(); jl_gc_unpreserve(); + jl_gc_unpreserve(); } // --- initialization --- diff --git a/src/dump.c b/src/dump.c index 27a00aceb91be..7db06409db5a3 100644 --- a/src/dump.c +++ b/src/dump.c @@ -24,11 +24,15 @@ static htable_t id_to_fptr; static const ptrint_t LongSymbol_tag = 23; static const ptrint_t LongTuple_tag = 24; static const ptrint_t LongExpr_tag = 25; +static const ptrint_t LiteralVal_tag = 26; static const ptrint_t Null_tag = 254; static const ptrint_t BackRef_tag = 255; static ptrint_t VALUE_TAGS; +// pointers to non-AST-ish objects in a compressed tree +static jl_array_t *tree_literal_values=NULL; + #define write_uint8(s, n) ios_putc((n), (s)) #define read_uint8(s) ((uint8_t)ios_getc(s)) #define write_int8(s, n) write_uint8(s, n) @@ -67,9 +71,9 @@ static void write_as_tag(ios_t *s, uint8_t tag) #define jl_serialize_value(s, v) jl_serialize_value_(s,(jl_value_t*)(v)) -void jl_serialize_value_(ios_t *s, jl_value_t *v); +static void jl_serialize_value_(ios_t *s, jl_value_t *v); -void jl_serialize_fptr(ios_t *s, void *fptr) +static void jl_serialize_fptr(ios_t *s, void *fptr) { void **pbp = ptrhash_bp(&fptr_to_id, fptr); if (*pbp == HT_NOTFOUND) @@ -77,7 +81,7 @@ void jl_serialize_fptr(ios_t *s, void *fptr) write_int32(s, *(ptrint_t*)pbp); } -void jl_serialize_tag_type(ios_t *s, jl_value_t *v) +static void jl_serialize_tag_type(ios_t *s, jl_value_t *v) { if (jl_is_struct_type(v)) { writetag(s, (jl_value_t*)jl_struct_kind); @@ -119,7 +123,7 @@ void jl_serialize_tag_type(ios_t *s, jl_value_t *v) } } -void jl_serialize_methlist(ios_t *s, jl_methlist_t *ml) +static void jl_serialize_methlist(ios_t *s, jl_methlist_t *ml) { while (ml != NULL) { jl_serialize_value(s, ml->sig); @@ -134,7 +138,7 @@ void jl_serialize_methlist(ios_t *s, jl_methlist_t *ml) jl_serialize_value(s, NULL); } -void jl_serialize_typecache(ios_t *s, jl_typename_t *tn) +static void jl_serialize_typecache(ios_t *s, jl_typename_t *tn) { typekey_stack_t *tc = (typekey_stack_t*)tn->cache; while (tc != NULL) { @@ -150,7 +154,27 @@ void jl_serialize_typecache(ios_t *s, jl_typename_t *tn) jl_serialize_value(s, NULL); } -void jl_serialize_value_(ios_t *s, jl_value_t *v) +static int is_ast_node(jl_value_t *v) +{ + return jl_is_symbol(v) || jl_is_expr(v) || + jl_typeis(v, jl_array_any_type) || + jl_is_tuple(v) || jl_is_typevar(v) || + jl_is_symbolnode(v) || jl_is_bool(v) || + jl_is_topnode(v) || jl_is_quotenode(v) || jl_is_gotonode(v) || + jl_is_labelnode(v) || jl_is_linenode(v); +} + +static int literal_val_id(jl_value_t *v) +{ + for(int i=0; i < jl_array_len(tree_literal_values); i++) { + if (jl_cellref(tree_literal_values,i) == v) + return i; + } + jl_cell_1d_push(tree_literal_values, v); + return jl_array_len(tree_literal_values)-1; +} + +static void jl_serialize_value_(ios_t *s, jl_value_t *v) { if (v == NULL) { write_uint8(s, Null_tag); @@ -163,13 +187,23 @@ void jl_serialize_value_(ios_t *s, jl_value_t *v) return; } - bp = ptrhash_bp(&backref_table, v); - if (*bp != HT_NOTFOUND) { - write_uint8(s, BackRef_tag); - write_int32(s, (ptrint_t)*bp); - return; + if (tree_literal_values) { + // compressing tree + if (!is_ast_node(v)) { + writetag(s, (jl_value_t*)LiteralVal_tag); + write_int32(s, literal_val_id(v)); + return; + } + } + else { + bp = ptrhash_bp(&backref_table, v); + if (*bp != HT_NOTFOUND) { + write_uint8(s, BackRef_tag); + write_int32(s, (ptrint_t)*bp); + return; + } + ptrhash_put(&backref_table, v, (void*)(ptrint_t)ios_pos(s)); } - ptrhash_put(&backref_table, v, (void*)(ptrint_t)ios_pos(s)); size_t i; if (jl_is_tuple(v)) { @@ -254,7 +288,8 @@ void jl_serialize_value_(ios_t *s, jl_value_t *v) jl_function_t *f = (jl_function_t*)v; jl_serialize_value(s, (jl_value_t*)f->linfo); jl_serialize_value(s, f->env); - if (f->linfo && f->linfo->ast && jl_is_expr(f->linfo->ast) && + if (f->linfo && f->linfo->ast && + (jl_is_expr(f->linfo->ast) || jl_is_tuple(f->linfo->ast)) && f->fptr != &jl_trampoline) { write_int32(s, 0); } @@ -310,7 +345,7 @@ void jl_serialize_value_(ios_t *s, jl_value_t *v) } } -void jl_serialize_module(ios_t *s, jl_module_t *m) +static void jl_serialize_module(ios_t *s, jl_module_t *m) { size_t i; void **table = m->bindings.table; @@ -336,8 +371,8 @@ void jl_serialize_module(ios_t *s, jl_module_t *m) } htable_t *jl_gc_get_finalizer_table(); - -void jl_serialize_finalizers(ios_t *s) +/* +static void jl_serialize_finalizers(ios_t *s) { htable_t *finalizer_table = jl_gc_get_finalizer_table(); int i; @@ -349,12 +384,12 @@ void jl_serialize_finalizers(ios_t *s) } jl_serialize_value(s, NULL); } - +*/ // --- deserialize --- -jl_value_t *jl_deserialize_value(ios_t *s); +static jl_value_t *jl_deserialize_value(ios_t *s); -jl_fptr_t jl_deserialize_fptr(ios_t *s) +static jl_fptr_t jl_deserialize_fptr(ios_t *s) { int fptr = read_int32(s); if (fptr == 0) @@ -365,7 +400,7 @@ jl_fptr_t jl_deserialize_fptr(ios_t *s) return *(jl_fptr_t*)pbp; } -jl_value_t *jl_deserialize_tag_type(ios_t *s, jl_struct_type_t *kind, int pos) +static jl_value_t *jl_deserialize_tag_type(ios_t *s, jl_struct_type_t *kind, int pos) { if (kind == jl_struct_kind) { jl_struct_type_t *st = @@ -428,7 +463,7 @@ jl_value_t *jl_deserialize_tag_type(ios_t *s, jl_struct_type_t *kind, int pos) return NULL; } -jl_methlist_t *jl_deserialize_methlist(ios_t *s) +static jl_methlist_t *jl_deserialize_methlist(ios_t *s) { jl_methlist_t *ml = NULL; jl_methlist_t **pnext = &ml; @@ -451,7 +486,7 @@ jl_methlist_t *jl_deserialize_methlist(ios_t *s) return ml; } -typekey_stack_t *jl_deserialize_typecache(ios_t *s) +static typekey_stack_t *jl_deserialize_typecache(ios_t *s) { typekey_stack_t *tk = NULL; typekey_stack_t **pnext = &tk; @@ -480,7 +515,7 @@ typekey_stack_t *jl_deserialize_typecache(ios_t *s) jl_struct_type_t *jl_idtable_type=NULL; void jl_idtable_rehash(jl_array_t **pa, size_t newsz); -jl_value_t *jl_deserialize_value(ios_t *s) +static jl_value_t *jl_deserialize_value(ios_t *s) { int pos = ios_pos(s); int32_t tag = read_uint8(s); @@ -491,6 +526,7 @@ jl_value_t *jl_deserialize_value(ios_t *s) return (jl_value_t*)ptrhash_get(&deser_tag, (void*)(ptrint_t)tag); } if (tag == BackRef_tag) { + assert(tree_literal_values == NULL); ptrint_t offs = read_int32(s); void **bp = ptrhash_bp(&backref_table, (void*)(ptrint_t)offs); assert(*bp != HT_NOTFOUND); @@ -564,6 +600,9 @@ jl_value_t *jl_deserialize_value(ios_t *s) } return (jl_value_t*)e; } + else if (vtag == (jl_value_t*)LiteralVal_tag) { + return jl_cellref(tree_literal_values, read_int32(s)); + } else if (vtag == (jl_value_t*)jl_typename_type) { jl_sym_t *name = (jl_sym_t*)jl_deserialize_value(s); jl_typename_t *tn = jl_new_typename(name); @@ -682,7 +721,7 @@ jl_value_t *jl_deserialize_value(ios_t *s) return NULL; } -void jl_deserialize_module(ios_t *s, jl_module_t *m) +static void jl_deserialize_module(ios_t *s, jl_module_t *m) { while (1) { jl_value_t *name = jl_deserialize_value(s); @@ -702,8 +741,8 @@ void jl_deserialize_module(ios_t *s, jl_module_t *m) (jl_function_t*)jl_deserialize_value(s)); } } - -void jl_deserialize_finalizers(ios_t *s) +/* +static void jl_deserialize_finalizers(ios_t *s) { htable_t *finalizer_table = jl_gc_get_finalizer_table(); while (1) { @@ -714,7 +753,7 @@ void jl_deserialize_finalizers(ios_t *s) *bp = jl_deserialize_value(s); } } - +*/ // --- entry points --- DLLEXPORT @@ -822,6 +861,44 @@ void jl_restore_system_image(char *fname) JL_GC_POP(); } +DLLEXPORT +jl_value_t *jl_compress_ast(jl_value_t *ast, jl_array_t *vals) +{ + ios_t dest; + jl_ios_mem(&dest, 0); + int en = jl_gc_is_enabled(); + jl_gc_disable(); + + tree_literal_values = vals; + jl_serialize_value(&dest, ast); + tree_literal_values = NULL; + + //ios_printf(ios_stderr, "%d bytes, %d values\n", dest.size, vals->length); + + jl_value_t *v = (jl_value_t*)jl_takebuf_array(&dest); + if (en) + jl_gc_enable(); + return v; +} + +DLLEXPORT +jl_value_t *jl_uncompress_ast(jl_tuple_t *data) +{ + jl_array_t *bytes = (jl_array_t*)jl_tupleref(data, 0); + tree_literal_values = (jl_array_t*)jl_tupleref(data, 1); + ios_t src; + jl_ios_mem(&src, 0); + ios_setbuf(&src, bytes->data, bytes->length, 0); + src.size = bytes->length; + int en = jl_gc_is_enabled(); + jl_gc_disable(); + jl_value_t *v = jl_deserialize_value(&src); + if (en) + jl_gc_enable(); + tree_literal_values = NULL; + return v; +} + // --- init --- void jl_init_serializer(void) @@ -835,7 +912,8 @@ void jl_init_serializer(void) void *tags[] = { jl_symbol_type, jl_tag_kind, jl_bits_kind, jl_struct_kind, jl_func_kind, jl_tuple_type, jl_array_type, jl_expr_type, (void*)LongSymbol_tag, (void*)LongTuple_tag, - (void*)LongExpr_tag, jl_intrinsic_type, jl_methtable_type, + (void*)LongExpr_tag, (void*)LiteralVal_tag, + jl_intrinsic_type, jl_methtable_type, jl_typename_type, jl_lambda_info_type, jl_tvar_type, jl_null, jl_any_type, jl_symbol("Any"), diff --git a/src/gc.c b/src/gc.c index d3febc5176992..2f012fb52eb3a 100644 --- a/src/gc.c +++ b/src/gc.c @@ -20,7 +20,7 @@ //#define MEMPROFILE //#define GCTIME -#define GC_PAGE_SZ (2048*sizeof(void*))//bytes +#define GC_PAGE_SZ (1536*sizeof(void*))//bytes typedef struct _gcpage_t { union { @@ -91,7 +91,7 @@ static bigval_t *big_objects = NULL; static pool_t pools[N_POOLS]; static size_t allocd_bytes = 0; -static size_t collect_interval = 4096*1024*sizeof(void*); +static size_t collect_interval = 3200*1024*sizeof(void*); static htable_t finalizer_table; static arraylist_t to_finalize; @@ -836,13 +836,16 @@ static size_t pool_stats(pool_t *p, size_t *pwaste) static void all_pool_stats(void) { int i; - size_t nb=0, w, tw=0; + size_t nb=0, w, tw=0, no=0, b; for(i=0; i < N_POOLS; i++) { - nb += pool_stats(&pools[i], &w); + b = pool_stats(&pools[i], &w); + nb += b; + no += (b/pools[i].osize); tw += w; } - ios_printf(ios_stdout, "%d total allocated, %d total fragments\n", - nb, tw); + ios_printf(ios_stdout, + "%d objects, %d total allocated, %d total fragments\n", + no, nb, tw); } static void big_obj_stats(void) diff --git a/src/jltypes.c b/src/jltypes.c index aaff2173bce84..6d05e49ae5b7a 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2491,7 +2491,7 @@ void jl_init_types(void) jl_symbol(""), jl_symbol(""), jl_symbol(""), jl_symbol(""), jl_symbol("inferred")), - jl_tuple(9, jl_expr_type, jl_tuple_type, + jl_tuple(9, jl_any_type, jl_tuple_type, jl_any_type, jl_sym_type, jl_any_type, jl_tuple_type, jl_function_type, jl_tuple_type, diff --git a/src/julia.expmap b/src/julia.expmap index 4575bde823d97..30fc26b976443 100644 --- a/src/julia.expmap +++ b/src/julia.expmap @@ -153,6 +153,8 @@ jl_start_io_thread; jl_save_system_image; jl_restore_system_image; + jl_compress_ast; + jl_uncompress_ast; jl_current_task; jl_get_current_task; jl_enter_handler;