Browse Source

Merge pull request #1596 from svaarala/newobj-init-size

Add initial alloc size to NEWOBJ opcode (object literals)
pull/1597/head
Sami Vaarala 7 years ago
committed by GitHub
parent
commit
4688c177c2
  1. 3
      RELEASES.rst
  2. 1
      debugger/duk_opcodes.yaml
  3. 2
      src-input/duk_heap.h
  4. 2
      src-input/duk_heap_markandsweep.c
  5. 33
      src-input/duk_hobject.h
  6. 28
      src-input/duk_hobject_props.c
  7. 21
      src-input/duk_js_compiler.c
  8. 3
      src-input/duk_js_executor.c
  9. 122
      tests/perf/test-object-literal-100.js
  10. 0
      tests/perf/test-object-literal-20.js
  11. 25
      tests/perf/test-object-literal-3.js

3
RELEASES.rst

@ -2981,7 +2981,8 @@ Planned
* Miscellaneous performance improvements: move rare/large opcodes into
NOINLINE helpers (GH-1510); duk_harray fast path for internal array
unpack for bound functions, .call, and .apply (GH-1525); unsafe internal
value stack pops where safe (GH-1583, GH-1584)
value stack pops where safe (GH-1583, GH-1584); initial object property
table size estimate for NEWOBJ opcode (object literals) (GH-1596)
3.0.0 (XXXX-XX-XX)
------------------

1
debugger/duk_opcodes.yaml

@ -926,6 +926,7 @@ opcodes:
- BC_R
- name: NEWOBJ
args:
- A_I # property count init size
- BC_R
- name: NEWARR
args:

2
src-input/duk_heap.h

@ -574,6 +574,8 @@ struct duk_heap {
duk_int_t stats_ms_emergency_count;
duk_int_t stats_intern_hit;
duk_int_t stats_intern_miss;
duk_int_t stats_object_realloc_props;
duk_int_t stats_object_abandon_array;
duk_int_t stats_getprop_all;
duk_int_t stats_getprop_arrayidx;
duk_int_t stats_getprop_bufobjidx;

2
src-input/duk_heap_markandsweep.c

@ -1065,6 +1065,8 @@ DUK_LOCAL void duk__dump_stats(duk_heap *heap) {
(long) heap->stats_ms_emergency_count));
DUK_D(DUK_DPRINT("stats string intern: hit=%ld, miss=%ld",
(long) heap->stats_intern_hit, (long) heap->stats_intern_miss));
DUK_D(DUK_DPRINT("stats object: realloc_props=%ld, abandon_array=%ld",
(long) heap->stats_object_realloc_props, (long) heap->stats_object_abandon_array));
DUK_D(DUK_DPRINT("stats getprop: all=%ld, arrayidx=%ld, bufobjidx=%ld, "
"bufferidx=%ld, bufferlen=%ld, stringidx=%ld, stringlen=%ld, "
"proxy=%ld, arguments=%ld",

33
src-input/duk_hobject.h

@ -868,6 +868,9 @@ DUK_INTERNAL_DECL void duk_hobject_realloc_props(duk_hthread *thr,
duk_uint32_t new_a_size,
duk_uint32_t new_h_size,
duk_bool_t abandon_array);
DUK_INTERNAL_DECL void duk_hobject_resize_props(duk_hthread *thr,
duk_hobject *obj,
duk_uint32_t new_e_size);
/* low-level property functions */
DUK_INTERNAL_DECL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *e_idx, duk_int_t *h_idx);
@ -903,22 +906,20 @@ DUK_INTERNAL_DECL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_hobject *obj
#endif
/* helpers for defineProperty() and defineProperties() */
DUK_INTERNAL_DECL
void duk_hobject_prepare_property_descriptor(duk_context *ctx,
duk_idx_t idx_in,
duk_uint_t *out_defprop_flags,
duk_idx_t *out_idx_value,
duk_hobject **out_getter,
duk_hobject **out_setter);
DUK_INTERNAL_DECL
duk_bool_t duk_hobject_define_property_helper(duk_context *ctx,
duk_uint_t defprop_flags,
duk_hobject *obj,
duk_hstring *key,
duk_idx_t idx_value,
duk_hobject *get,
duk_hobject *set,
duk_bool_t throw_flag);
DUK_INTERNAL_DECL void duk_hobject_prepare_property_descriptor(duk_context *ctx,
duk_idx_t idx_in,
duk_uint_t *out_defprop_flags,
duk_idx_t *out_idx_value,
duk_hobject **out_getter,
duk_hobject **out_setter);
DUK_INTERNAL_DECL duk_bool_t duk_hobject_define_property_helper(duk_context *ctx,
duk_uint_t defprop_flags,
duk_hobject *obj,
duk_hstring *key,
duk_idx_t idx_value,
duk_hobject *get,
duk_hobject *set,
duk_bool_t throw_flag);
/* Object built-in methods */
DUK_INTERNAL_DECL void duk_hobject_object_get_own_property_descriptor(duk_context *ctx, duk_idx_t obj_idx);

28
src-input/duk_hobject_props.c

@ -537,6 +537,8 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr,
DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));
DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
DUK_STATS_INC(thr->heap, stats_object_realloc_props);
/*
* Pre resize assertions.
*/
@ -692,6 +694,8 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr,
*/
DUK_ASSERT(new_a_size == 0);
DUK_STATS_INC(thr->heap, stats_object_abandon_array);
for (i = 0; i < DUK_HOBJECT_GET_ASIZE(obj); i++) {
duk_tval *tv1;
duk_tval *tv2;
@ -956,6 +960,30 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr,
* Helpers to resize properties allocation on specific needs.
*/
DUK_INTERNAL void duk_hobject_resize_props(duk_hthread *thr,
duk_hobject *obj,
duk_uint32_t new_e_size) {
duk_uint32_t old_e_size;
duk_uint32_t new_a_size;
duk_uint32_t new_h_size;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
old_e_size = DUK_HOBJECT_GET_ESIZE(obj);
if (old_e_size > new_e_size) {
new_e_size = old_e_size;
}
#if defined(DUK_USE_HOBJECT_HASH_PART)
new_h_size = duk__get_default_h_size(new_e_size);
#else
new_h_size = 0;
#endif
new_a_size = DUK_HOBJECT_GET_ASIZE(obj);
duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
}
/* Grow entry part allocation for one additional entry. */
DUK_LOCAL void duk__grow_props_for_new_entry_item(duk_hthread *thr, duk_hobject *obj) {
duk_uint32_t old_e_used; /* actually used, non-NULL entries */

21
src-input/duk_js_compiler.c

@ -2955,6 +2955,7 @@ typedef struct {
duk_reg_t reg_obj;
duk_reg_t temp_start;
duk_small_uint_t num_pairs;
duk_small_uint_t num_total_pairs;
} duk__objlit_state;
DUK_LOCAL void duk__objlit_flush_keys(duk_compiler_ctx *comp_ctx, duk__objlit_state *st) {
@ -2975,6 +2976,7 @@ DUK_LOCAL void duk__objlit_flush_keys(duk_compiler_ctx *comp_ctx, duk__objlit_st
st->reg_obj,
st->temp_start,
st->num_pairs * 2);
st->num_total_pairs += st->num_pairs;
st->num_pairs = 0;
}
DUK__SETTEMP(comp_ctx, st->temp_start);
@ -3006,6 +3008,10 @@ DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r
duk_small_uint_t max_init_pairs; /* max # of key-value pairs initialized in one MPUTOBJ set */
duk_bool_t first; /* first value: comma must not precede the value */
duk_bool_t is_set, is_get; /* temps */
duk_int_t pc_newobj;
#if !defined(DUK_USE_PREFER_SIZE)
duk_compiler_instr *instr;
#endif
DUK_ASSERT(comp_ctx->prev_token.t == DUK_TOK_LCURLY);
@ -3014,8 +3020,10 @@ DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r
st.reg_obj = DUK__ALLOCTEMP(comp_ctx); /* target object */
st.temp_start = DUK__GETTEMP(comp_ctx); /* start of MPUTOBJ argument list */
st.num_pairs = 0; /* number of key/value pairs emitted for current MPUTOBJ set */
st.num_total_pairs = 0; /* number of key/value pairs emitted overall */
duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, st.reg_obj); /* XXX: patch initial size hint afterwards? */
pc_newobj = duk__get_current_pc(comp_ctx);
duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, st.reg_obj);
/*
* Emit initializers in sets of maximum max_init_pairs keys.
@ -3203,6 +3211,17 @@ DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r
DUK_ASSERT(st.num_pairs == 0);
DUK_ASSERT(DUK__GETTEMP(comp_ctx) == st.temp_start);
/* Update initial size for NEWOBJ. The init size doesn't need to be
* exact as the purpose is just to avoid object resizes in common
* cases. The size is capped to field A limit, and will be too high
* if the object literal contains duplicate keys (this is harmless but
* increases memory traffic if the object is compacted later on).
*/
#if !defined(DUK_USE_PREFER_SIZE)
instr = duk__get_instr_ptr(comp_ctx, pc_newobj);
instr->ins |= DUK_ENC_OP_A(0, st.num_total_pairs > 255 ? 255 : st.num_total_pairs);
#endif
DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RCURLY);
duk__advance(comp_ctx);

3
src-input/duk_js_executor.c

@ -4741,6 +4741,9 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread *
case DUK_OP_NEWOBJ: {
duk_context *ctx = (duk_context *) thr;
duk_push_object(ctx);
#if !defined(DUK_USE_PREFER_SIZE)
duk_hobject_resize_props(thr, duk_known_hobject(ctx, -1), DUK_DEC_A(ins));
#endif
DUK__REPLACE_TOP_BC_BREAK();
}

122
tests/perf/test-object-literal-100.js

@ -0,0 +1,122 @@
/*
* Create Object using a literal
*/
if (typeof print !== 'function') { print = console.log; }
function test() {
var obj;
var i;
for (i = 0; i < 1e6; i++) {
obj = {
key1: 'val1',
key2: 'val2',
key3: 'val3',
key4: 'val4',
key5: 'val5',
key6: 'val6',
key7: 'val7',
key8: 'val8',
key9: 'val9',
key10: 'val10',
key11: 'val11',
key12: 'val12',
key13: 'val13',
key14: 'val14',
key15: 'val15',
key16: 'val16',
key17: 'val17',
key18: 'val18',
key19: 'val19',
key20: 'val20',
key21: 'val21',
key22: 'val22',
key23: 'val23',
key24: 'val24',
key25: 'val25',
key26: 'val26',
key27: 'val27',
key28: 'val28',
key29: 'val29',
key30: 'val30',
key31: 'val31',
key32: 'val32',
key33: 'val33',
key34: 'val34',
key35: 'val35',
key36: 'val36',
key37: 'val37',
key38: 'val38',
key39: 'val39',
key40: 'val40',
key41: 'val41',
key42: 'val42',
key43: 'val43',
key44: 'val44',
key45: 'val45',
key46: 'val46',
key47: 'val47',
key48: 'val48',
key49: 'val49',
key50: 'val50',
key51: 'val51',
key52: 'val52',
key53: 'val53',
key54: 'val54',
key55: 'val55',
key56: 'val56',
key57: 'val57',
key58: 'val58',
key59: 'val59',
key60: 'val60',
key61: 'val61',
key62: 'val62',
key63: 'val63',
key64: 'val64',
key65: 'val65',
key66: 'val66',
key67: 'val67',
key68: 'val68',
key69: 'val69',
key70: 'val70',
key71: 'val71',
key72: 'val72',
key73: 'val73',
key74: 'val74',
key75: 'val75',
key76: 'val76',
key77: 'val77',
key78: 'val78',
key79: 'val79',
key80: 'val80',
key81: 'val81',
key82: 'val82',
key83: 'val83',
key84: 'val84',
key85: 'val85',
key86: 'val86',
key87: 'val87',
key88: 'val88',
key89: 'val89',
key90: 'val90',
key91: 'val91',
key92: 'val92',
key93: 'val93',
key94: 'val94',
key95: 'val95',
key96: 'val96',
key97: 'val97',
key98: 'val98',
key99: 'val99',
key100: 'val100'
};
}
}
try {
test();
} catch (e) {
print(e.stack || e);
throw e;
}

0
tests/perf/test-object-literal.js → tests/perf/test-object-literal-20.js

25
tests/perf/test-object-literal-3.js

@ -0,0 +1,25 @@
/*
* Create Object using a literal
*/
if (typeof print !== 'function') { print = console.log; }
function test() {
var obj;
var i;
for (i = 0; i < 1e6; i++) {
obj = {
key1: 'val1',
key2: 'val2',
key3: 'val3'
};
}
}
try {
test();
} catch (e) {
print(e.stack || e);
throw e;
}
Loading…
Cancel
Save