Skip to content

Commit

Permalink
Merge pull request #21620 from JuliaLang/jb/nonleaf_fields
Browse files Browse the repository at this point in the history
optimize field access when field types don't depend on parameters
  • Loading branch information
JeffBezanson authored May 2, 2017
2 parents 8d93b4d + f11dc15 commit 3a181fe
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 33 deletions.
35 changes: 19 additions & 16 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2455,8 +2455,8 @@ static jl_cgval_t emit_getfield(jl_value_t *expr, jl_sym_t *name, jl_codectx_t *
JL_GC_PUSH1(&sty);
if (jl_is_type_type((jl_value_t*)sty) && jl_is_leaf_type(jl_tparam0(sty)))
sty = (jl_datatype_t*)jl_typeof(jl_tparam0(sty));
if (jl_is_structtype(sty) && sty != jl_module_type && sty->uid != 0 &&
jl_is_leaf_type((jl_value_t*)sty)) {
sty = (jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)sty);
if (jl_is_structtype(sty) && sty != jl_module_type && sty->layout) {
unsigned idx = jl_field_index(sty, name, 0);
if (idx != (unsigned)-1) {
jl_cgval_t strct = emit_expr(expr, ctx);
Expand Down Expand Up @@ -2971,8 +2971,7 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args,
JL_GC_POP();
return true;
}
jl_datatype_t *stt = (jl_datatype_t*)expr_type(args[1], ctx);
jl_value_t *fldt = expr_type(args[2], ctx);
jl_value_t *fldt = expr_type(args[2], ctx);

// VA tuple
if (ctx->vaStack && slot_eq(args[1], ctx->vaSlot)) {
Expand All @@ -2989,31 +2988,34 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args,
return true;
}

if (fldt == (jl_value_t*)jl_long_type && jl_is_leaf_type((jl_value_t*)stt)) {
if ((jl_is_structtype(stt) || jl_is_tuple_type(stt)) && !jl_subtype((jl_value_t*)jl_module_type, (jl_value_t*)stt)) {
size_t nfields = jl_datatype_nfields(stt);
jl_datatype_t *stt = (jl_datatype_t*)expr_type(args[1], ctx);
jl_value_t *utt = jl_unwrap_unionall((jl_value_t*)stt);

if (fldt == (jl_value_t*)jl_long_type && jl_is_datatype(utt) && ((jl_datatype_t*)utt)->layout) {
if ((jl_is_structtype(utt) || jl_is_tuple_type(utt)) && !jl_subtype((jl_value_t*)jl_module_type, (jl_value_t*)stt)) {
size_t nfields = jl_datatype_nfields(utt);
jl_cgval_t strct = emit_expr(args[1], ctx);
// integer index
size_t idx;
if (jl_is_long(args[2]) && (idx=jl_unbox_long(args[2])-1) < nfields) {
// known index
*ret = emit_getfield_knownidx(strct, idx, stt, ctx);
*ret = emit_getfield_knownidx(strct, idx, (jl_datatype_t*)utt, ctx);
JL_GC_POP();
return true;
}
else {
// unknown index
Value *vidx = emit_unbox(T_size, emit_expr(args[2], ctx), (jl_value_t*)jl_long_type);
if (emit_getfield_unknownidx(ret, strct, vidx, stt, ctx)) {
if (emit_getfield_unknownidx(ret, strct, vidx, (jl_datatype_t*)utt, ctx)) {
if (ret->typ == (jl_value_t*)jl_any_type) // improve the type, if known from the expr
ret->typ = expr_type(expr, ctx);
JL_GC_POP();
return true;
}
}
}
} else {
jl_value_t *utt = jl_unwrap_unionall((jl_value_t*)stt);
}
else {
if (jl_is_tuple_type(utt) && is_tupletype_homogeneous(((jl_datatype_t*)utt)->types, true)) {
// For tuples, we can emit code even if we don't know the exact
// type (e.g. because we don't know the length). This is possible
Expand Down Expand Up @@ -3045,25 +3047,26 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args,
else if (f==jl_builtin_setfield && nargs==3) {
jl_datatype_t *sty = (jl_datatype_t*)expr_type(args[1], ctx);
rt1 = (jl_value_t*)sty;
if (jl_is_structtype(sty) && sty != jl_module_type) {
jl_datatype_t *uty = (jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)sty);
if (jl_is_structtype(uty) && uty != jl_module_type) {
size_t idx = (size_t)-1;
if (jl_is_quotenode(args[2]) && jl_is_symbol(jl_fieldref(args[2],0))) {
idx = jl_field_index(sty, (jl_sym_t*)jl_fieldref(args[2],0), 0);
idx = jl_field_index(uty, (jl_sym_t*)jl_fieldref(args[2],0), 0);
}
else if (jl_is_long(args[2]) || (jl_is_quotenode(args[2]) && jl_is_long(jl_fieldref(args[2],0)))) {
ssize_t i;
if (jl_is_long(args[2]))
i = jl_unbox_long(args[2]);
else
i = jl_unbox_long(jl_fieldref(args[2],0));
if (i > 0 && i <= jl_datatype_nfields(sty))
if (i > 0 && i <= jl_datatype_nfields(uty))
idx = i-1;
}
if (idx != (size_t)-1) {
jl_value_t *ft = jl_svecref(sty->types, idx);
jl_value_t *ft = jl_svecref(uty->types, idx);
jl_value_t *rhst = expr_type(args[3], ctx);
rt2 = rhst;
if (jl_is_leaf_type((jl_value_t*)sty) && jl_subtype(rhst, ft)) {
if (uty->layout && jl_subtype(rhst, ft)) {
// TODO: attempt better codegen for approximate types
jl_cgval_t strct = emit_expr(args[1], ctx); // emit lhs
*ret = emit_expr(args[3], ctx);
Expand Down
45 changes: 32 additions & 13 deletions src/datatype.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,37 @@ void jl_compute_field_offsets(jl_datatype_t *st)
size_t sz = 0, alignm = 1;
int homogeneous = 1;
jl_value_t *lastty = NULL;

uint64_t max_offset = (((uint64_t)1) << 32) - 1;
uint64_t max_size = max_offset >> 1;

if (st->name->wrapper) {
// If layout doesn't depend on type parameters, it's stored in st->name->wrapper
// and reused by all subtypes.
jl_datatype_t *w = (jl_datatype_t*)jl_unwrap_unionall(st->name->wrapper);
if (st != w && // this check allows us to re-compute layout for some types during init
w->layout) {
st->layout = w->layout;
st->size = w->size;
return;
}
}
if (st->types == NULL)
return;
uint32_t nfields = jl_svec_len(st->types);
if (nfields == 0 && st != jl_sym_type && st != jl_simplevector_type) {
// reuse the same layout for all singletons
static const jl_datatype_layout_t singleton_layout = {0, 1, 0, 0, 0};
st->layout = &singleton_layout;
return;
}
if (!jl_is_leaf_type((jl_value_t*)st)) {
// compute layout whenever field types have no free variables
for (size_t i = 0; i < nfields; i++) {
if (jl_has_free_typevars(jl_field_type(st, i)))
return;
}
}

size_t descsz = nfields * sizeof(jl_fielddesc32_t);
jl_fielddesc32_t *desc;
if (descsz < jl_page_size)
Expand Down Expand Up @@ -372,18 +398,11 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_datatype_t *super
}
jl_precompute_memoized_dt(t);

if (abstract || jl_svec_len(parameters) > 0) {
t->uid = 0;
}
else {
t->uid = jl_assign_type_uid();
if (t->types != NULL && t->isleaftype) {
static const jl_datatype_layout_t singleton_layout = {0, 1, 0, 0, 0};
if (fnames == jl_emptysvec)
t->layout = &singleton_layout;
else
jl_compute_field_offsets(t);
}
t->uid = 0;
if (!abstract) {
if (jl_svec_len(parameters) == 0)
t->uid = jl_assign_type_uid();
jl_compute_field_offsets(t);
}
JL_GC_POP();
return t;
Expand Down
5 changes: 1 addition & 4 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,7 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s)
b->value = temp;
jl_rethrow();
}
if (dt->name->names == jl_emptysvec)
dt->layout = jl_void_type->layout; // reuse the same layout for all singletons
else if (jl_is_leaf_type((jl_value_t*)dt))
jl_compute_field_offsets(dt);
jl_compute_field_offsets(dt);
if (para == (jl_value_t*)jl_emptysvec && jl_is_datatype_make_singleton(dt)) {
dt->instance = jl_gc_alloc(ptls, 0, dt);
jl_gc_wb(dt, dt->instance);
Expand Down

2 comments on commit 3a181fe

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing the daily benchmark build, I will reply here when finished:

@nanosoldier runbenchmarks(ALL, isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @jrevels

Please sign in to comment.