From 912ab38760faa68e77a6b863ec5ccb26e8aebd50 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Tue, 4 Feb 2020 20:35:00 +0300 Subject: [PATCH] Added fast path in [[Get]] and [[Set]] for arrays and typed-arrays. --- src/njs_array.c | 1 + src/njs_number.h | 12 ++++- src/njs_object.c | 3 ++ src/njs_typed_array.c | 1 + src/njs_value.c | 108 +++++++++++++++++++++++++++++++++++++-- src/njs_value.h | 1 + src/njs_vmcode.c | 2 +- src/test/njs_unit_test.c | 4 +- 8 files changed, 123 insertions(+), 9 deletions(-) diff --git a/src/njs_array.c b/src/njs_array.c index 75e94a52..3378115a 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -67,6 +67,7 @@ njs_array_alloc(njs_vm_t *vm, uint64_t length, uint32_t spare) array->object.type = NJS_ARRAY; array->object.shared = 0; array->object.extensible = 1; + array->object.fast_array = 1; array->size = size; array->length = length; diff --git a/src/njs_number.h b/src/njs_number.h index b8348144..5d5bec1f 100644 --- a/src/njs_number.h +++ b/src/njs_number.h @@ -30,16 +30,24 @@ njs_int_t njs_number_parse_float(njs_vm_t *vm, njs_value_t *args, njs_inline njs_bool_t -njs_number_is_integer_index(double num, const njs_value_t *value) +njs_number_is_integer_index(double num) { uint32_t u32; u32 = num; - return (u32 == num && u32 != 0xffffffff) + return (u32 == num && u32 != 0xffffffff); +} + + +njs_inline njs_bool_t +njs_key_is_integer_index(double num, const njs_value_t *value) +{ + return (njs_number_is_integer_index(num)) && !(njs_is_string(value) && num == 0 && signbit(num)); } + njs_inline int64_t njs_number_to_int64(double num) { diff --git a/src/njs_object.c b/src/njs_object.c index b2e941df..34a8cc28 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -51,6 +51,8 @@ njs_object_alloc(njs_vm_t *vm) object->shared = 0; object->extensible = 1; object->error_data = 0; + object->fast_array = 0; + return object; } @@ -108,6 +110,7 @@ njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value, njs_uint_t type) ov->object.type = njs_object_value_type(type); ov->object.shared = 0; ov->object.extensible = 1; + ov->object.fast_array = 0; index = njs_primitive_prototype_index(type); ov->object.__proto__ = &vm->prototypes[index].object; diff --git a/src/njs_typed_array.c b/src/njs_typed_array.c index aa8689a5..8751c62c 100644 --- a/src/njs_typed_array.c +++ b/src/njs_typed_array.c @@ -179,6 +179,7 @@ njs_typed_array_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, array->object.type = NJS_TYPED_ARRAY; array->object.shared = 0; array->object.extensible = 1; + array->object.fast_array = 1; njs_set_typed_array(&vm->retval, array); diff --git a/src/njs_value.c b/src/njs_value.c index aff4dd98..eb95b822 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -549,7 +549,7 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value, case NJS_STRING: if (njs_fast_path(!njs_is_null_or_undefined_or_boolean(key))) { num = njs_key_to_index(key); - if (njs_fast_path(njs_number_is_integer_index(num, key))) { + if (njs_fast_path(njs_key_is_integer_index(num, key))) { return njs_string_property_query(vm, pq, value, num); } } @@ -654,7 +654,7 @@ njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq, switch (proto->type) { case NJS_ARRAY: num = njs_key_to_index(key); - if (njs_fast_path(njs_number_is_integer_index(num, key))) { + if (njs_fast_path(njs_key_is_integer_index(num, key))) { array = (njs_array_t *) proto; return njs_array_property_query(vm, pq, array, num); } @@ -663,7 +663,7 @@ njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq, case NJS_TYPED_ARRAY: num = njs_key_to_index(key); - if (njs_fast_path(njs_number_is_integer_index(num, key))) { + if (njs_fast_path(njs_key_is_integer_index(num, key))) { tarray = (njs_typed_array_t *) proto; return njs_typed_array_property_query(vm, pq, tarray, num); } @@ -676,7 +676,7 @@ njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq, case NJS_OBJECT_STRING: num = njs_key_to_index(key); - if (njs_fast_path(njs_number_is_integer_index(num, key))) { + if (njs_fast_path(njs_key_is_integer_index(num, key))) { ov = (njs_object_value_t *) proto; ret = njs_string_property_query(vm, pq, &ov->value, num); @@ -1021,10 +1021,60 @@ njs_int_t njs_value_property(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *retval) { + double num; + uint32_t index; njs_int_t ret; + njs_array_t *array; njs_object_prop_t *prop; + njs_typed_array_t *tarray; njs_property_query_t pq; + if (njs_fast_path(njs_is_number(key))) { + num = njs_number(key); + + if (njs_slow_path(!njs_number_is_integer_index(num))) { + goto slow_path; + } + + index = (uint32_t) num; + + if (njs_is_typed_array(value)) { + tarray = njs_typed_array(value); + + if (njs_slow_path(index >= njs_typed_array_length(tarray))) { + goto slow_path; + } + + njs_set_number(retval, njs_typed_array_get(tarray, index)); + + return NJS_OK; + } + + if (njs_slow_path(!(njs_is_object(value) + && njs_object(value)->fast_array))) + { + goto slow_path; + } + + /* NJS_ARRAY */ + + array = njs_array(value); + + if (njs_slow_path(index >= array->length)) { + goto slow_path; + } + + *retval = array->start[index]; + + if (njs_slow_path(!njs_is_valid(retval))) { + njs_set_undefined(retval); + } + + return NJS_OK; + } + +slow_path: + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0); ret = njs_property_query(vm, &pq, value, key); @@ -1093,16 +1143,66 @@ njs_int_t njs_value_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *setval) { + double num; + uint32_t index; njs_int_t ret; + njs_array_t *array; njs_object_prop_t *prop; + njs_typed_array_t *tarray; njs_property_query_t pq; + if (njs_fast_path(njs_is_number(key))) { + num = njs_number(key); + + if (njs_slow_path(!njs_number_is_integer_index(num))) { + goto slow_path; + } + + index = (uint32_t) num; + + if (njs_is_typed_array(value)) { + tarray = njs_typed_array(value); + + if (njs_fast_path(index < njs_typed_array_length(tarray))) { + ret = njs_value_to_number(vm, setval, &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + njs_typed_array_set(tarray, index, num); + } + + return NJS_OK; + } + + if (njs_slow_path(!(njs_is_object(value) + && njs_object(value)->fast_array))) + { + goto slow_path; + } + + /* NJS_ARRAY */ + + array = njs_array(value); + + if (njs_slow_path(index >= array->length)) { + goto slow_path; + } + + array->start[index] = *setval; + + return NJS_OK; + } + +slow_path: + if (njs_is_primitive(value)) { njs_type_error(vm, "property set on primitive %s type", njs_type_string(value->type)); return NJS_ERROR; } + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0); ret = njs_property_query(vm, &pq, value, key); diff --git a/src/njs_value.h b/src/njs_value.h index 304b6bb2..e49d1621 100644 --- a/src/njs_value.h +++ b/src/njs_value.h @@ -215,6 +215,7 @@ struct njs_object_s { uint8_t extensible:1; uint8_t error_data:1; + uint8_t fast_array:1; }; diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index ff0e8a27..2a6f0105 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -1152,7 +1152,7 @@ njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, switch (value->type) { case NJS_ARRAY: num = njs_key_to_index(key); - if (njs_slow_path(!njs_number_is_integer_index(num, key))) { + if (njs_slow_path(!njs_key_is_integer_index(num, key))) { njs_internal_error(vm, "invalid index while property initialization"); return NJS_ERROR; diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index ec4f7a00..bd9727dd 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -17772,8 +17772,8 @@ njs_string_to_index_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat) } } - is_integer_index = njs_number_is_integer_index(njs_number(&vm->retval), - &tests[i].value); + is_integer_index = njs_key_is_integer_index(njs_number(&vm->retval), + &tests[i].value); if (tests[i].is_integer_index != is_integer_index) { njs_string_get(&tests[i].value, &string); -- 2.47.3