From: Dmitry Stogov Date: Thu, 22 Apr 2010 15:03:17 +0000 (+0000) Subject: Optimized access to global constants using values with pre-calculated hash_values... X-Git-Tag: php-5.4.0alpha1~191^2~1696 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e87d72002b7b173331c073e1e581e69f788d8a9c;p=php Optimized access to global constants using values with pre-calculated hash_values from litersls table --- diff --git a/NEWS b/NEWS index cb4f239c3e..f829f3110d 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,8 @@ . ZEND_FETCH_DIM_? may fetch array and dimension operans in a different order . ZEND_RETURN is splitted into two new instructions ZEND_RETURN and ZEND_RETURN_BY_REF + . optimized access to global constants using values with pre-calculated + hash_values from litersls table - Added concept of interned strings. All strings constants known at compile time are allocated in a single copy and never changed. (Dmitry) - Added an optimization which saves memory and emalloc/efree calls for empty diff --git a/Zend/micro_bench.php b/Zend/micro_bench.php index e07ee88ce5..0d705e942b 100644 --- a/Zend/micro_bench.php +++ b/Zend/micro_bench.php @@ -166,6 +166,14 @@ function create_object($n) { } } +define('TEST', null); + +function read_const($n) { + for ($i = 0; $i < $n; ++$i) { + $x = TEST; + } +} + /*****/ function empty_loop($n) { @@ -272,4 +280,6 @@ $x->read_const(N); $t = end_test($t, '$x = Foo::TEST', $overhead); create_object(N); $t = end_test($t, 'new Foo()', $overhead); +read_const(N); +$t = end_test($t, '$x = TEST', $overhead); total($t0, "Total"); diff --git a/Zend/zend.h b/Zend/zend.h index 8aafd2cf81..7efa9f4825 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -525,11 +525,12 @@ typedef int (*zend_write_func_t)(const char *str, uint str_length); #define IS_CONSTANT_ARRAY 9 /* Ugly hack to support constants as static array indices */ -#define IS_CONSTANT_TYPE_MASK 0x0f -#define IS_CONSTANT_UNQUALIFIED 0x10 -#define IS_CONSTANT_INDEX 0x80 -#define IS_LEXICAL_VAR 0x20 -#define IS_LEXICAL_REF 0x40 +#define IS_CONSTANT_TYPE_MASK 0x00f +#define IS_CONSTANT_UNQUALIFIED 0x010 +#define IS_CONSTANT_INDEX 0x080 +#define IS_LEXICAL_VAR 0x020 +#define IS_LEXICAL_REF 0x040 +#define IS_CONSTANT_IN_NAMESPACE 0x100 /* overloaded elements data types */ #define OE_IS_ARRAY (1<<0) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c523a3dfa0..9909643795 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -420,6 +420,76 @@ int zend_add_class_name_literal(zend_op_array *op_array, const zval *zv TSRMLS_D } /* }}} */ +int zend_add_const_name_literal(zend_op_array *op_array, const zval *zv, int unqualified TSRMLS_DC) /* {{{ */ +{ + int ret, tmp_literal; + char *name, *tmp_name, *ns_separator; + int name_len, ns_len; + zval c; + + if (op_array->last_literal > 0 && + &op_array->literals[op_array->last_literal - 1].constant == zv) { + /* we already have function name as last literal (do nothing) */ + ret = op_array->last_literal - 1; + } else { + ret = zend_add_literal(op_array, zv); + } + + /* skip leading '\\' */ + if (Z_STRVAL_P(zv)[0] == '\\') { + name_len = Z_STRLEN_P(zv) - 1; + name = Z_STRVAL_P(zv) + 1; + } else { + name_len = Z_STRLEN_P(zv); + name = Z_STRVAL_P(zv); + } + ns_separator = zend_memrchr(name, '\\', name_len); + if (ns_separator) { + ns_len = ns_separator - name; + } else { + ns_len = 0; + } + + if (ns_len) { + /* lowercased namespace name & original constant name */ + tmp_name = estrndup(name, name_len); + zend_str_tolower(tmp_name, ns_len); + ZVAL_STRINGL(&c, tmp_name, name_len, 0); + tmp_literal = zend_add_literal(CG(active_op_array), &c); + CALCULATE_LITERAL_HASH(tmp_literal); + + /* lowercased namespace name & lowercased constant name */ + tmp_name = zend_str_tolower_dup(name, name_len); + ZVAL_STRINGL(&c, tmp_name, name_len, 0); + tmp_literal = zend_add_literal(CG(active_op_array), &c); + CALCULATE_LITERAL_HASH(tmp_literal); + } + + if (ns_len) { + if (!unqualified) { + return ret; + } + ns_len++; + name += ns_len; + name_len -= ns_len; + } + + /* original constant name */ + tmp_name = estrndup(name, name_len); + ZVAL_STRINGL(&c, tmp_name, name_len, 0); + tmp_literal = zend_add_literal(CG(active_op_array), &c); + CALCULATE_LITERAL_HASH(tmp_literal); + + /* lowercased constant name */ + tmp_name = zend_str_tolower_dup(name, name_len); + ZVAL_STRINGL(&c, tmp_name, name_len, 0); + tmp_literal = zend_add_literal(CG(active_op_array), &c); + CALCULATE_LITERAL_HASH(tmp_literal); + + return ret; +} +/* }}} */ + #define LITERAL_STRINGL(op, str, len, copy) do { \ zval _c; \ ZVAL_STRINGL(&_c, str, len, copy); \ @@ -4149,14 +4219,20 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con opline->result.var = get_temporary_variable(CG(active_op_array)); GET_NODE(result, opline->result); SET_UNUSED(opline->op1); - if(compound) { + opline->op2_type = IS_CONST; + if (compound) { /* the name is unambiguous */ opline->extended_value = 0; - } else { + opline->op2.constant = zend_add_const_name_literal(CG(active_op_array), &constant_name->u.constant, 0 TSRMLS_CC); + } else { opline->extended_value = IS_CONSTANT_UNQUALIFIED; + if (CG(current_namespace)) { + opline->extended_value |= IS_CONSTANT_IN_NAMESPACE; + opline->op2.constant = zend_add_const_name_literal(CG(active_op_array), &constant_name->u.constant, 1 TSRMLS_CC); + } else { + opline->op2.constant = zend_add_const_name_literal(CG(active_op_array), &constant_name->u.constant, 0 TSRMLS_CC); + } } - SET_NODE(opline->op2, constant_name); - CALCULATE_LITERAL_HASH(opline->op2.constant); break; } } diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 1743ffd2a8..f581842698 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -222,6 +222,31 @@ ZEND_API void zend_register_string_constant(const char *name, uint name_len, cha zend_register_stringl_constant(name, name_len, strval, strlen(strval), flags, module_number TSRMLS_CC); } +static int zend_get_halt_offset_constant(const char *name, uint name_len, zend_constant **c TSRMLS_DC) +{ + int ret; + static char haltoff[] = "__COMPILER_HALT_OFFSET__"; + + if (!EG(in_execution)) { + return 0; + } else if (name_len == sizeof("__COMPILER_HALT_OFFSET__")-1 && + !memcmp(name, "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) { + char *cfilename, *haltname; + int len, clen; + + cfilename = zend_get_executed_filename(TSRMLS_C); + clen = strlen(cfilename); + /* check for __COMPILER_HALT_OFFSET__ */ + zend_mangle_property_name(&haltname, &len, haltoff, + sizeof("__COMPILER_HALT_OFFSET__") - 1, cfilename, clen, 0); + ret = zend_hash_find(EG(zend_constants), haltname, len+1, (void **) c); + efree(haltname); + return (ret == SUCCESS); + } else { + return 0; + } +} + ZEND_API int zend_get_constant(const char *name, uint name_len, zval *result TSRMLS_DC) { @@ -237,29 +262,7 @@ ZEND_API int zend_get_constant(const char *name, uint name_len, zval *result TSR retval=0; } } else { - static char haltoff[] = "__COMPILER_HALT_OFFSET__"; - - if (!EG(in_execution)) { - retval = 0; - } else if (name_len == sizeof("__COMPILER_HALT_OFFSET__")-1 && - !memcmp(name, "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) { - char *cfilename, *haltname; - int len, clen; - - cfilename = zend_get_executed_filename(TSRMLS_C); - clen = strlen(cfilename); - /* check for __COMPILER_HALT_OFFSET__ */ - zend_mangle_property_name(&haltname, &len, haltoff, - sizeof("__COMPILER_HALT_OFFSET__") - 1, cfilename, clen, 0); - if (zend_hash_find(EG(zend_constants), haltname, len+1, (void **) &c) == SUCCESS) { - retval = 1; - } else { - retval=0; - } - pefree(haltname, 0); - } else { - retval=0; - } + retval = zend_get_halt_offset_constant(name, name_len, &c TSRMLS_CC); } efree(lookup_name); } @@ -410,6 +413,41 @@ finish: return zend_get_constant(name, name_len, result TSRMLS_CC); } +int zend_quick_get_constant(const zend_literal *key, zval *result, ulong flags TSRMLS_DC) +{ + zend_constant *c; + + if (zend_hash_quick_find(EG(zend_constants), Z_STRVAL(key->constant), Z_STRLEN(key->constant) + 1, key->hash_value, (void **) &c) == FAILURE) { + key++; + if (zend_hash_quick_find(EG(zend_constants), Z_STRVAL(key->constant), Z_STRLEN(key->constant) + 1, key->hash_value, (void **) &c) == FAILURE || + (c->flags & CONST_CS) != 0) { + if ((flags & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) { + key++; + if (zend_hash_quick_find(EG(zend_constants), Z_STRVAL(key->constant), Z_STRLEN(key->constant) + 1, key->hash_value, (void **) &c) == FAILURE) { + key++; + if (zend_hash_quick_find(EG(zend_constants), Z_STRVAL(key->constant), Z_STRLEN(key->constant) + 1, key->hash_value, (void **) &c) == FAILURE || + (c->flags & CONST_CS) != 0) { + + key--; + if (!zend_get_halt_offset_constant(Z_STRVAL(key->constant), Z_STRLEN(key->constant), &c TSRMLS_CC)) { + return 0; + } + } + } + } else { + key--; + if (!zend_get_halt_offset_constant(Z_STRVAL(key->constant), Z_STRLEN(key->constant), &c TSRMLS_CC)) { + return 0; + } + } + } + } + + INIT_PZVAL_COPY(result, &c->value); + zval_copy_ctor(result); + return 1; +} + ZEND_API int zend_register_constant(zend_constant *c TSRMLS_DC) { char *lowercase_name = NULL; diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 75fb67e2eb..e8fb3f2116 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -69,6 +69,7 @@ ZEND_API void zend_register_stringl_constant(const char *name, uint name_len, ch ZEND_API int zend_register_constant(zend_constant *c TSRMLS_DC); void zend_copy_constants(HashTable *target, HashTable *sourc); void copy_zend_constant(zend_constant *c); +int zend_quick_get_constant(const zend_literal *key, zval *result, ulong flags TSRMLS_DC); END_EXTERN_C() #define ZEND_CONSTANT_DTOR (void (*)(void *)) free_zend_constant diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b9e718fced..953f58a74c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3329,7 +3329,7 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST) SAVE_OPLINE(); if (OP1_TYPE == IS_UNUSED) { /* namespaced constant */ - if (!zend_get_constant_ex(Z_STRVAL_P(opline->op2.zv), Z_STRLEN_P(opline->op2.zv), &EX_T(opline->result.var).tmp_var, NULL, opline->extended_value TSRMLS_CC)) { + if (!zend_quick_get_constant(opline->op2.literal + 1, &EX_T(opline->result.var).tmp_var, opline->extended_value TSRMLS_CC)) { if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) { char *actual = (char *)zend_memrchr(Z_STRVAL_P(opline->op2.zv), '\\', Z_STRLEN_P(opline->op2.zv)); if(!actual) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index e6e4f90cde..e48b807eea 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2981,7 +2981,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCO SAVE_OPLINE(); if (IS_CONST == IS_UNUSED) { /* namespaced constant */ - if (!zend_get_constant_ex(Z_STRVAL_P(opline->op2.zv), Z_STRLEN_P(opline->op2.zv), &EX_T(opline->result.var).tmp_var, NULL, opline->extended_value TSRMLS_CC)) { + if (!zend_quick_get_constant(opline->op2.literal + 1, &EX_T(opline->result.var).tmp_var, opline->extended_value TSRMLS_CC)) { if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) { char *actual = (char *)zend_memrchr(Z_STRVAL_P(opline->op2.zv), '\\', Z_STRLEN_P(opline->op2.zv)); if(!actual) { @@ -11495,7 +11495,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE SAVE_OPLINE(); if (IS_VAR == IS_UNUSED) { /* namespaced constant */ - if (!zend_get_constant_ex(Z_STRVAL_P(opline->op2.zv), Z_STRLEN_P(opline->op2.zv), &EX_T(opline->result.var).tmp_var, NULL, opline->extended_value TSRMLS_CC)) { + if (!zend_quick_get_constant(opline->op2.literal + 1, &EX_T(opline->result.var).tmp_var, opline->extended_value TSRMLS_CC)) { if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) { char *actual = (char *)zend_memrchr(Z_STRVAL_P(opline->op2.zv), '\\', Z_STRLEN_P(opline->op2.zv)); if(!actual) { @@ -19381,7 +19381,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPC SAVE_OPLINE(); if (IS_UNUSED == IS_UNUSED) { /* namespaced constant */ - if (!zend_get_constant_ex(Z_STRVAL_P(opline->op2.zv), Z_STRLEN_P(opline->op2.zv), &EX_T(opline->result.var).tmp_var, NULL, opline->extended_value TSRMLS_CC)) { + if (!zend_quick_get_constant(opline->op2.literal + 1, &EX_T(opline->result.var).tmp_var, opline->extended_value TSRMLS_CC)) { if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) { char *actual = (char *)zend_memrchr(Z_STRVAL_P(opline->op2.zv), '\\', Z_STRLEN_P(opline->op2.zv)); if(!actual) {