improve JIT impersonator property predicate/accessor fast path

Faster for the case that an impersonator has a small number of
impersonator properties. To enable hand-coded search, a small number
of properties are now kept in a vector instead of a hash tree.
This commit is contained in:
Matthew Flatt 2017-12-22 21:30:29 -07:00
parent fe1d9ee517
commit 36d563d7ce
8 changed files with 216 additions and 51 deletions

View File

@ -281,7 +281,23 @@
(err/rt-test (get1 v7) handler)
(err/rt-test (get1 v8) handler)
(err/rt-test (get1 v9) handler)
(err/rt-test (get1 v10) handler)))
(err/rt-test (get1 v10) handler)
;; Make sure it works to have lots of impersontaors:
(let loop ([v v1] [i 100] [preds null] [sels null] [vals null])
(unless (zero? i)
(for ([pred (in-list preds)]
[sel (in-list sels)]
[val (in-list vals)])
(test #t pred v)
(test val sel v)
(test val sel v 'no))
(define-values (p has get) (make-impersonator-property 'p))
(loop (chaperone-vector v #f #f p i)
(sub1 i)
(cons has preds)
(cons get sels)
(cons i vals))))))
;; check property-only chaperones

View File

@ -3535,7 +3535,7 @@ static Scheme_Object *do_chaperone_procedure(const char *name, const char *whati
{
Scheme_Chaperone *px, *px2;
Scheme_Object *val = argv[0], *orig, *naya, *r, *app_mark;
Scheme_Hash_Tree *props;
Scheme_Object *props;
if (SCHEME_CHAPERONEP(val))
val = SCHEME_CHAPERONE_VAL(val);
@ -3569,13 +3569,10 @@ static Scheme_Object *do_chaperone_procedure(const char *name, const char *whati
props = scheme_parse_chaperone_props(name, 2, argc, argv);
if (props) {
app_mark = scheme_hash_tree_get(props, scheme_app_mark_impersonator_property);
app_mark = scheme_chaperone_props_get(props, scheme_app_mark_impersonator_property);
if (app_mark) {
/* don't need to keep the property */
if (props->count == 1)
props = NULL;
else
props = scheme_hash_tree_set(props, scheme_app_mark_impersonator_property, NULL);
props = scheme_chaperone_props_remove(props, scheme_app_mark_impersonator_property);
} else
app_mark = scheme_false;
} else
@ -4555,7 +4552,7 @@ Scheme_Object *do_chaperone_continuation_mark_key (const char *name, int is_impe
Scheme_Chaperone *px;
Scheme_Object *val = argv[0];
Scheme_Object *redirects;
Scheme_Hash_Tree *props;
Scheme_Object *props;
if (SCHEME_CHAPERONEP(val))
val = SCHEME_CHAPERONE_VAL(val);
@ -6495,7 +6492,7 @@ Scheme_Object *do_chaperone_prompt_tag (const char *name, int is_impersonator, i
Scheme_Chaperone *px;
Scheme_Object *val = argv[0];
Scheme_Object *redirects;
Scheme_Hash_Tree *props;
Scheme_Object *props;
int ppos;
if (SCHEME_CHAPERONEP(val))

View File

@ -2072,7 +2072,7 @@ static int common4b(mz_jit_state *jitter, void *_data)
for (i = 0; i < 3; i++) {
for (ii = 0; ii < 3; ii++) { /* single, multi, or tail */
void *code;
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3, *ref4, *refno, *refslow, *refloop;
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3, *ref4, *ref5, *refno, *refslow, *refloop, *refchap;
int prim_other_type;
code = jit_get_ip();
@ -2158,13 +2158,15 @@ static int common4b(mz_jit_state *jitter, void *_data)
jit_ldr_p(JIT_V1, JIT_RUNSTACK);
/* If the failure argument is not a procedure, we can
return it directly, otherwise take slow path. */
jit_ldxi_s(JIT_R2, JIT_V1, &((Scheme_Object *)0x0)->type);
__START_INNER_TINY__(1);
ref5 = jit_bmsi_ul(jit_forward(), JIT_V1, 0x1);
jit_ldxi_s(JIT_R2, JIT_V1, &((Scheme_Object *)0x0)->type);
ref4 = jit_blti_i(jit_forward(), JIT_R2, scheme_prim_type);
__END_INNER_TINY__(1);
(void)jit_blei_i(refslow, JIT_R2, scheme_proc_chaperone_type);
__START_INNER_TINY__(1);
mz_patch_branch(ref4);
mz_patch_branch(ref5);
__END_INNER_TINY__(1);
jit_movr_p(JIT_R0, JIT_V1);
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
@ -2173,6 +2175,58 @@ static int common4b(mz_jit_state *jitter, void *_data)
} else
refno = refslow;
{
/* Chaperone case: if we're looking for an impersonator property,
maybe we can find it here; otherwise, take the slow path. */
refchap = jit_get_ip();
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Primitive_Closure *)0x0)->val);
(void)mz_bnei_t(refslow, JIT_V1, scheme_chaperone_property_type, JIT_R2); /* slow path if not impersonator property */
jit_ldxi_p(JIT_R2, JIT_R1, &((Scheme_Chaperone *)0x0)->props); /* check chaperone's props */
(void)jit_beqi_p(refslow, JIT_R2, NULL);
(void)mz_bnei_t(refslow, JIT_R2, scheme_vector_type, JIT_V1); /* slow path if not represented a vector */
CHECK_LIMIT();
/* Search a vector for the property: */
(void)jit_ldxi_l(JIT_V1, JIT_R2, &SCHEME_VEC_SIZE(0x0)); /* get vector size */
jit_lshi_ul(JIT_V1, JIT_V1, JIT_LOG_WORD_SIZE); /* convert to bytes */
jit_addi_l(JIT_V1, JIT_V1, (int)&SCHEME_VEC_ELS(0x0)); /* bytes at offset */
refloop = jit_get_ip();
(void)jit_beqi_l(refslow, JIT_V1, (int)&SCHEME_VEC_ELS(0x0)); /* index at 0 => not found, so slow path */
jit_subi_l(JIT_V1, JIT_V1, (2 * JIT_WORD_SIZE)); /* step back by two words for key & value */
mz_set_local_p(JIT_V1, JIT_LOCAL3); /* save current index, because we need the register */
jit_ldxi_p(JIT_R2, JIT_R1, &((Scheme_Chaperone *)0x0)->props); /* get vector again */
jit_ldxr_p(JIT_R2, JIT_R2, JIT_V1); /* load a key from the vector */
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Primitive_Closure *)0x0)->val); /* get the property again */
CHECK_LIMIT();
__START_INNER_TINY__(1);
ref5 = jit_beqr_p(jit_forward(), JIT_R2, JIT_V1); /* key matches property? */
__END_INNER_TINY__(1);
CHECK_LIMIT();
mz_get_local_p(JIT_V1, JIT_LOCAL3); /* no match, so reload index and recur */
(void)jit_jmpi(refloop);
__START_INNER_TINY__(1);
mz_patch_branch(ref5);
__END_INNER_TINY__(1);
/* found match, so return #t or extract and return the value */
if (i == 2) {
(void)jit_movi_p(JIT_R0, scheme_true);
} else {
mz_get_local_p(JIT_V1, JIT_LOCAL3); /* reload matching index */
jit_addi_l(JIT_V1, JIT_V1, JIT_WORD_SIZE); /* up by one word, to get value insteda of key */
jit_ldxi_p(JIT_R2, JIT_R1, &((Scheme_Chaperone *)0x0)->props); /* get vector again */
jit_ldxr_p(JIT_R0, JIT_R2, JIT_V1); /* extract value */
}
if (i == 1)
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
mz_epilog(JIT_V1); /* return */
CHECK_LIMIT();
}
/* Continue trying fast path: check proc */
mz_patch_branch(ref);
(void)mz_bnei_t(refslow, JIT_R0, scheme_prim_type, JIT_R2);
@ -2187,9 +2241,9 @@ static int common4b(mz_jit_state *jitter, void *_data)
__START_INNER_TINY__(1);
ref2 = jit_beqi_i(jit_forward(), JIT_R2, scheme_structure_type);
__END_INNER_TINY__(1);
(void)jit_beqi_i(refchap, JIT_R2, scheme_proc_chaperone_type);
(void)jit_beqi_i(refchap, JIT_R2, scheme_chaperone_type);
if (i != 0) { /* for i == 0 mode, `refno` is already `refslow` */
(void)jit_beqi_i(refslow, JIT_R2, scheme_proc_chaperone_type);
(void)jit_beqi_i(refslow, JIT_R2, scheme_chaperone_type);
(void)jit_beqi_i(refslow, JIT_R2, scheme_struct_type_type);
}
(void)jit_bnei_i(refno, JIT_R2, scheme_proc_struct_type);

View File

@ -1966,7 +1966,7 @@ static Scheme_Object *do_chaperone_box(const char *name, int is_impersonator, in
Scheme_Chaperone *px;
Scheme_Object *val = argv[0];
Scheme_Object *redirects;
Scheme_Hash_Tree *props;
Scheme_Object *props;
if (SCHEME_CHAPERONEP(val))
val = SCHEME_CHAPERONE_VAL(val);
@ -3177,7 +3177,7 @@ static Scheme_Object *do_chaperone_hash(const char *name, int is_impersonator, i
Scheme_Chaperone *px;
Scheme_Object *val = argv[0];
Scheme_Object *redirects, *clear, *equal_key_wrap;
Scheme_Hash_Tree *props;
Scheme_Object *props;
int start_props = 5;
if (SCHEME_CHAPERONEP(val))

View File

@ -1159,7 +1159,7 @@ typedef struct Scheme_Chaperone {
Scheme_Inclhash_Object iso; /* 0x1 => impersonator, rather than a checking chaperone */
Scheme_Object *val; /* root object */
Scheme_Object *prev; /* immediately chaperoned object */
Scheme_Hash_Tree *props;
Scheme_Object *props; /* NULL, a vector, or a hash tree */
Scheme_Object *redirects; /* specific to the type of chaperone and root object */
} Scheme_Chaperone;
@ -1218,7 +1218,9 @@ void scheme_chaperone_vector_set(Scheme_Object *o, int i, Scheme_Object *v);
Scheme_Object *scheme_apply_chaperone(Scheme_Object *o, int argc, Scheme_Object **argv,
Scheme_Object *auto_val, int checks);
Scheme_Hash_Tree *scheme_parse_chaperone_props(const char *who, int start_at, int argc, Scheme_Object **argv);
Scheme_Object *scheme_parse_chaperone_props(const char *who, int start_at, int argc, Scheme_Object **argv);
Scheme_Object *scheme_chaperone_props_get(Scheme_Object *props, Scheme_Object *prop);
Scheme_Object *scheme_chaperone_props_remove(Scheme_Object *props, Scheme_Object *prop);
Scheme_Object *scheme_chaperone_hash_get(Scheme_Object *table, Scheme_Object *key);
Scheme_Object *scheme_chaperone_hash_traversal_get(Scheme_Object *table, Scheme_Object *key, Scheme_Object **alt_key);

View File

@ -1202,7 +1202,7 @@ Scheme_Object *do_chaperone_channel(const char *name, int is_impersonator, int a
Scheme_Chaperone *px;
Scheme_Object *val = argv[0];
Scheme_Object *evt;
Scheme_Hash_Tree *props;
Scheme_Object *props;
if (SCHEME_CHAPERONEP(val))
val = SCHEME_CHAPERONE_VAL(val);

View File

@ -1059,6 +1059,43 @@ static Scheme_Object *current_code_inspector(int argc, Scheme_Object *argv[])
/* properties */
/*========================================================================*/
Scheme_Object *scheme_chaperone_props_get(Scheme_Object *props, Scheme_Object *prop)
{
if (!props)
return NULL;
else if (SCHEME_VECTORP(props)) {
int i;
for (i = SCHEME_VEC_SIZE(props); i > 0; ) {
i -= 2;
if (SAME_OBJ(SCHEME_VEC_ELS(props)[i], prop))
return SCHEME_VEC_ELS(props)[i+1];
}
return NULL;
} else
return (Scheme_Object *)scheme_hash_tree_get((Scheme_Hash_Tree *)props, prop);
}
Scheme_Object *scheme_chaperone_props_remove(Scheme_Object *props, Scheme_Object *prop)
/* assumes that `prop` is currently set in `props` */
{
if (SCHEME_VECTORP(props)) {
Scheme_Object *vec;
int i, j;
j = SCHEME_VEC_SIZE(props);
if (j == 2) return NULL;
vec = scheme_make_vector(j - 2, NULL);
for (i = SCHEME_VEC_SIZE(props), j = 0; i > 0; ) {
i -= 2;
if (!SAME_OBJ(SCHEME_VEC_ELS(props)[i], prop)) {
SCHEME_VEC_ELS(vec)[j++] = SCHEME_VEC_ELS(props)[i];
SCHEME_VEC_ELS(vec)[j++] = SCHEME_VEC_ELS(props)[i+1];
}
}
return vec;
} else
return (Scheme_Object *)scheme_hash_tree_set((Scheme_Hash_Tree *)props, prop, NULL);
}
static Scheme_Object *prop_pred(int argc, Scheme_Object **args, Scheme_Object *prim)
{
Scheme_Struct_Type *stype;
@ -1071,10 +1108,7 @@ static Scheme_Object *prop_pred(int argc, Scheme_Object **args, Scheme_Object *p
if (SCHEME_CHAPERONEP(v)) {
/* Check for property at chaperone level: */
px = (Scheme_Chaperone *)v;
if (px->props)
v = scheme_hash_tree_get(px->props, prop);
else
v = NULL;
v = scheme_chaperone_props_get(px->props, prop);
if (v)
return scheme_true;
v = px->val;
@ -1187,11 +1221,9 @@ static Scheme_Object *do_chaperone_prop_accessor(const char *who, Scheme_Object
Scheme_Object *v;
Scheme_Hash_Tree *ht;
if (px->props) {
v = scheme_hash_tree_get(px->props, prop);
if (v)
return v;
}
v = scheme_chaperone_props_get(px->props, prop);
if (v)
return v;
if (!SCHEME_REDIRECTS_STRUCTP(px->redirects)
|| SCHEME_FALSEP(SCHEME_VEC_ELS(px->redirects)[0]))
@ -4056,7 +4088,7 @@ Scheme_Object *scheme_do_chaperone_evt(const char *name, int is_impersonator, in
{
Scheme_Chaperone *px;
Scheme_Object *o, *val, *a[1];
Scheme_Hash_Tree *props;
Scheme_Object *props;
val = argv[0];
if (SCHEME_CHAPERONEP(val))
@ -6212,12 +6244,12 @@ static Scheme_Object *do_chaperone_struct(const char *name, int is_impersonator,
{
Scheme_Chaperone *px;
Scheme_Struct_Type *stype, *st;
Scheme_Object *val = argv[0], *proc;
Scheme_Object *val = argv[0], *proc, *props = NULL;
Scheme_Object *redirects, *prop, *si_chaperone = scheme_false;
Scheme_Object *a[1], *inspector, *getter_positions = scheme_null;
int i, offset, arity, non_applicable_op, repeat_op;
const char *kind;
Scheme_Hash_Tree *props = NULL, *red_props = NULL, *empty_red_props = NULL, *setter_positions = NULL;
Scheme_Hash_Tree *red_props = NULL, *empty_red_props = NULL, *setter_positions = NULL;
intptr_t field_pos;
int empty_si_chaperone = 0, *empty_redirects = NULL, has_redirect = 0, witnessed = 0;
@ -6555,7 +6587,7 @@ Scheme_Object *scheme_chaperone_not_undefined (Scheme_Object *orig_val)
{
Scheme_Chaperone *px;
Scheme_Object *val, *redirects;
Scheme_Hash_Tree *props;
Scheme_Object *props;
val = orig_val;
@ -6589,7 +6621,7 @@ static Scheme_Object *do_chaperone_struct_type(const char *name, int is_imperson
Scheme_Chaperone *px;
Scheme_Object *val = argv[0];
Scheme_Object *redirects;
Scheme_Hash_Tree *props;
Scheme_Object *props;
int arity;
if (SCHEME_CHAPERONEP(val))
@ -6635,35 +6667,99 @@ static Scheme_Object *chaperone_struct_type(int argc, Scheme_Object **argv)
return do_chaperone_struct_type("chaperone-struct-type", 0, argc, argv);
}
Scheme_Hash_Tree *scheme_parse_chaperone_props(const char *who, int start_at, int argc, Scheme_Object **argv)
#define PROPS_MAX_VECTOR_SIZE 5
Scheme_Object *scheme_parse_chaperone_props(const char *who, int start_at, int argc, Scheme_Object **argv)
{
Scheme_Hash_Tree *ht;
Scheme_Object *v;
Scheme_Object *v, *props;
int pos;
if (SCHEME_CHAPERONEP(argv[0]))
ht = ((Scheme_Chaperone *)argv[0])->props;
props = ((Scheme_Chaperone *)argv[0])->props;
else
ht = NULL;
props = NULL;
while (start_at < argc) {
v = argv[start_at];
if (!SAME_TYPE(SCHEME_TYPE(v), scheme_chaperone_property_type))
scheme_wrong_contract(who, "impersonator-property?", start_at, argc, argv);
if (start_at < argc) {
/* Check */
for (pos = start_at; pos < argc; pos += 2) {
v = argv[pos];
if (!SAME_TYPE(SCHEME_TYPE(v), scheme_chaperone_property_type))
scheme_wrong_contract(who, "impersonator-property?", pos, argc, argv);
if (start_at + 1 >= argc)
scheme_contract_error(who,
"missing value after chaperone property",
"chaperone property", 1, v,
NULL);
if (pos + 1 >= argc)
scheme_contract_error(who,
"missing value after chaperone property",
"chaperone property", 1, v,
NULL);
}
if (!ht)
/* Prepare to add */
if (props && SCHEME_VECTORP(props)
&& (((argc - start_at) + SCHEME_VEC_SIZE(props)) > (2 * PROPS_MAX_VECTOR_SIZE))) {
/* Convert vector to a hash tree */
Scheme_Hash_Tree *ht;
int i;
ht = scheme_make_hash_tree(SCHEME_hashtr_eq);
ht = scheme_hash_tree_set(ht, v, argv[start_at + 1]);
for (i = SCHEME_VEC_SIZE(props); i > 0; ) {
i -= 2;
ht = scheme_hash_tree_set(ht, SCHEME_VEC_ELS(props)[i], SCHEME_VEC_ELS(props)[i+1]);
}
props = (Scheme_Object *)ht;
}
start_at += 2;
if (!props || SCHEME_VECTORP(props)) {
/* Keep as vector, and start by counting new distinct entries */
int count = 0, i, len = (props ? SCHEME_VEC_SIZE(props) : 0);
for (pos = start_at; pos < argc; pos += 2) {
v = argv[pos];
if (props) {
for (i = 0; i < len; i += 2) {
if (SAME_OBJ(v, SCHEME_VEC_ELS(props)[i]))
break;
}
} else
i = 0;
if (i >= len) {
for (i = start_at; i < pos; i += 2) {
if (SAME_OBJ(v, argv[i]))
break;
}
if (i >= pos)
count++;
}
}
if (props) {
/* Copy vector, possibly making it larger */
Scheme_Object *old_props = props;
props = scheme_make_vector(SCHEME_VEC_SIZE(old_props) + 2 * count, NULL);
memcpy(SCHEME_VEC_ELS(props), SCHEME_VEC_ELS(old_props), sizeof(Scheme_Object *)*SCHEME_VEC_SIZE(old_props));
} else {
props = scheme_make_vector(2 * count, NULL);
}
len = SCHEME_VEC_SIZE(props);
/* Fill in vector */
for (pos = start_at; pos < argc; pos += 2) {
v = argv[pos];
for (i = 0; i < len; i += 2) {
if (!SCHEME_VEC_ELS(props)[i] || SAME_OBJ(v, SCHEME_VEC_ELS(props)[i])) {
SCHEME_VEC_ELS(props)[i] = v;
SCHEME_VEC_ELS(props)[i+1] = argv[pos+1];
break;
}
}
}
} else {
/* Add to hash tree */
for (pos = start_at; pos < argc; pos += 2) {
props = (Scheme_Object *)scheme_hash_tree_set((Scheme_Hash_Tree *)props, argv[pos], argv[pos+1]);
}
}
}
return ht;
return props;
}
/**********************************************************************/

View File

@ -964,7 +964,7 @@ static Scheme_Object *do_chaperone_vector(const char *name, int is_impersonator,
Scheme_Chaperone *px;
Scheme_Object *val = argv[0];
Scheme_Object *redirects;
Scheme_Hash_Tree *props;
Scheme_Object *props;
if (SCHEME_CHAPERONEP(val)) {
val = SCHEME_CHAPERONE_VAL(val);