diff --git a/src-input/duk_forwdecl.h b/src-input/duk_forwdecl.h index 46f0719b28..9a2a73a673 100644 --- a/src-input/duk_forwdecl.h +++ b/src-input/duk_forwdecl.h @@ -43,6 +43,7 @@ struct duk_breakpoint; struct duk_activation; struct duk_catcher; struct duk_strcache; +struct duk_slotcache_entry; struct duk_ljstate; struct duk_strtab_entry; @@ -101,6 +102,7 @@ typedef struct duk_breakpoint duk_breakpoint; typedef struct duk_activation duk_activation; typedef struct duk_catcher duk_catcher; typedef struct duk_strcache duk_strcache; +typedef struct duk_slotcache_entry duk_slotcache_entry; typedef struct duk_ljstate duk_ljstate; typedef struct duk_strtab_entry duk_strtab_entry; diff --git a/src-input/duk_heap.h b/src-input/duk_heap.h index efbd2efdb3..10d62d1c70 100644 --- a/src-input/duk_heap.h +++ b/src-input/duk_heap.h @@ -141,6 +141,11 @@ #define DUK_HEAP_STRCACHE_SIZE 4 #define DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT 16 /* strings up to the this length are not cached */ +/* Slotcache is used to speeding up object property lookups without + * caching a property value. + */ +#define DUK_HEAP_SLOTCACHE_SIZE 8192 + /* helper to insert a (non-string) heap object into heap allocated list */ #define DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap,hdr) duk_heap_insert_into_heap_allocated((heap),(hdr)) @@ -293,6 +298,15 @@ struct duk_strcache { duk_uint32_t cidx; }; +/* + * Property slot cache. Best effort data structure to cache property + * table slots for recently accessed object/key combinations. + */ + +struct duk_slotcache_entry { + duk_uint8_t slot; +}; + /* * Longjmp state, contains the information needed to perform a longjmp. * Longjmp related values are written to value1, value2, and iserror. @@ -461,6 +475,11 @@ struct duk_heap { */ duk_strcache strcache[DUK_HEAP_STRCACHE_SIZE]; + /* property slot cache, global structure for speeding up commonly + * referenced property access. + */ + duk_slotcache_entry slotcache[DUK_HEAP_SLOTCACHE_SIZE]; + /* built-in strings */ #if defined(DUK_USE_ROM_STRINGS) /* No field needed when strings are in ROM. */ @@ -514,6 +533,9 @@ DUK_INTERNAL void duk_heap_strtable_dump(duk_heap *heap); DUK_INTERNAL_DECL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h); DUK_INTERNAL_DECL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_uint_fast32_t char_offset); +DUK_INTERNAL_DECL duk_uint32_t duk_heap_slotcache_lookup(duk_heap *heap, duk_hobject *obj, duk_hstring *key); +DUK_INTERNAL_DECL void duk_heap_slotcache_insert(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_uint32_t slot); + #if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS) DUK_INTERNAL_DECL void *duk_default_alloc_function(void *udata, duk_size_t size); DUK_INTERNAL_DECL void *duk_default_realloc_function(void *udata, void *ptr, duk_size_t newsize); diff --git a/src-input/duk_heap_alloc.c b/src-input/duk_heap_alloc.c index f41cf5c480..2ebbc398c2 100644 --- a/src-input/duk_heap_alloc.c +++ b/src-input/duk_heap_alloc.c @@ -595,6 +595,7 @@ DUK_LOCAL void duk__dump_type_sizes(void) { DUK__DUMPSZ(duk_activation); DUK__DUMPSZ(duk_catcher); DUK__DUMPSZ(duk_strcache); + DUK__DUMPSZ(duk_slotcache_entry); DUK__DUMPSZ(duk_ljstate); DUK__DUMPSZ(duk_fixedbuffer); DUK__DUMPSZ(duk_bitdecoder_ctx); @@ -932,6 +933,12 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, } #endif + /* + * Init slotcache + */ + + /* Nothing to init now. */ + /* XXX: error handling is incomplete. It would be cleanest if * there was a setjmp catchpoint, so that all init code could * freely throw errors. If that were the case, the return code diff --git a/src-input/duk_heap_slotcache.c b/src-input/duk_heap_slotcache.c new file mode 100644 index 0000000000..6390949238 --- /dev/null +++ b/src-input/duk_heap_slotcache.c @@ -0,0 +1,34 @@ +/* + * Object property table slot cache. + */ + +#include "duk_internal.h" + +DUK_INTERNAL duk_uint32_t duk_heap_slotcache_lookup(duk_heap *heap, duk_hobject *obj, duk_hstring *key) { + duk_uint32_t idx; + duk_slotcache_entry *ent; + + DUK_ASSERT(heap != NULL); + DUK_ASSERT(obj != NULL); + DUK_ASSERT(key != NULL); + + /* FIXME: assert: hashlimit <= 256 */ + + idx = ((duk_uint32_t) obj) ^ DUK_HSTRING_GET_HASH(key); + ent = heap->slotcache + (idx & (DUK_HEAP_SLOTCACHE_SIZE - 1)); + return ent->slot; +} + +DUK_INTERNAL void duk_heap_slotcache_insert(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_uint32_t slot) { + duk_uint32_t idx; + duk_slotcache_entry *ent; + + /* FIXME: assert: hashlimit <= 256 */ + /* FIXME: skip check if hash part present and hashlimit correct */ + if (DUK_UNLIKELY(slot >= DUK_UINT16_MAX)) { + return; + } + idx = ((duk_uint32_t) obj) ^ DUK_HSTRING_GET_HASH(key); + ent = heap->slotcache + (idx & (DUK_HEAP_SLOTCACHE_SIZE - 1)); + ent->slot = (duk_uint16_t) slot; +} diff --git a/src-input/duk_hobject_props.c b/src-input/duk_hobject_props.c index 20226659e3..6f93c96b13 100644 --- a/src-input/duk_hobject_props.c +++ b/src-input/duk_hobject_props.c @@ -1159,6 +1159,8 @@ DUK_INTERNAL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj) */ DUK_INTERNAL 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) { + duk_uint32_t slot; + DUK_ASSERT(obj != NULL); DUK_ASSERT(key != NULL); DUK_ASSERT(e_idx != NULL); @@ -1177,10 +1179,22 @@ DUK_INTERNAL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject *o duk_hstring **h_keys_base; DUK_DDD(DUK_DDDPRINT("duk_hobject_find_existing_entry() using linear scan for lookup")); + slot = duk_heap_slotcache_lookup(heap, obj, key); + if (slot < DUK_HOBJECT_GET_ENEXT(obj)) { + if (DUK_HOBJECT_E_GET_KEY(heap, obj, slot) == key) { + DUK_D(DUK_DPRINT("slot cache hit: %p %p -> %ld", (void *) obj, (void *) key, (long) slot)); + *e_idx = slot; + *h_idx = -1; + return; + } + } + h_keys_base = DUK_HOBJECT_E_GET_KEY_BASE(heap, obj); n = DUK_HOBJECT_GET_ENEXT(obj); for (i = 0; i < n; i++) { if (h_keys_base[i] == key) { + DUK_D(DUK_DPRINT("slot cache insert: %p %p -> %ld", (void *) obj, (void *) key, (long) i)); + duk_heap_slotcache_insert(heap, obj, key, i); *e_idx = i; *h_idx = -1; return;