ID: 47815 User updated by: basant dot kukreja at gmail dot com Reported By: basant dot kukreja at gmail dot com Status: Open Bug Type: Performance problem Operating System: Solaris 10 PHP Version: 5.2.9 New Comment:
diff -r 00438f7eebe4 php-5.2.9RC3/Zend/zend_compile.h --- a/php-5.2.9RC3/Zend/zend_compile.h Tue Mar 17 11:27:02 2009 -0700 +++ b/php-5.2.9RC3/Zend/zend_compile.h Fri Mar 27 10:18:13 2009 -0700 @@ -83,6 +83,7 @@ znode op1; znode op2; ulong extended_value; + ulong hval; uint lineno; zend_uchar opcode; }; diff -r 00438f7eebe4 php-5.2.9RC3/Zend/zend_execute.c --- a/php-5.2.9RC3/Zend/zend_execute.c Tue Mar 17 11:27:02 2009 -0700 +++ b/php-5.2.9RC3/Zend/zend_execute.c Fri Mar 27 10:18:13 2009 -0700 @@ -930,11 +930,12 @@ return NULL; } -static inline zval **zend_fetch_dimension_address_inner(HashTable *ht, zval *dim, int type TSRMLS_DC) +static inline zval **zend_fetch_dimension_address_inner(HashTable *ht, zval *dim, int type, zend_ulong hval, int usehval TSRMLS_DC) { zval **retval; char *offset_key; int offset_key_length; + int ret; switch (dim->type) { case IS_NULL: @@ -948,7 +949,13 @@ offset_key_length = dim->value.str.len; fetch_string_dim: - if (zend_symtable_find(ht, offset_key, offset_key_length+1, (void **) &retval) == FAILURE) { + if (usehval) { + ret = zend_symtable_quick_find(ht, offset_key, offset_key_length+1, hval, (void **) &retval); + } + else { + ret = zend_symtable_find(ht, offset_key, offset_key_length+1, (void **) &retval); + } + if (ret == FAILURE) { switch (type) { case BP_VAR_R: zend_error(E_NOTICE, "Undefined index: %s", offset_key); @@ -1023,7 +1030,7 @@ return retval; } -static void zend_fetch_dimension_address(temp_variable *result, zval **container_ptr, zval *dim, int dim_is_tmp_var, int type TSRMLS_DC) +static void zend_fetch_dimension_address(temp_variable *result, zval **container_ptr, zval *dim, int dim_is_tmp_var, int type, zend_ulong hval, int usehval TSRMLS_DC) { zval *container; @@ -1078,7 +1085,7 @@ new_zval->refcount--; } } else { - retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, type TSRMLS_CC); + retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, type, hval, usehval TSRMLS_CC); } if (result) { result->var.ptr_ptr = retval; diff -r 00438f7eebe4 php-5.2.9RC3/Zend/zend_hash.h --- a/php-5.2.9RC3/Zend/zend_hash.h Tue Mar 17 11:27:02 2009 -0700 +++ b/php-5.2.9RC3/Zend/zend_hash.h Fri Mar 27 10:18:13 2009 -0700 @@ -354,6 +354,12 @@ return zend_hash_find(ht, arKey, nKeyLength, pData); } +static inline int zend_symtable_quick_find(HashTable *ht, char *arKey, uint nKeyLength, ulong h, void **pData) +{ + HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_find(ht, idx, pData)); + return zend_hash_quick_find(ht, arKey, nKeyLength, h, pData); +} + static inline int zend_symtable_exists(HashTable *ht, char *arKey, uint nKeyLength) { diff -r 00438f7eebe4 php-5.2.9RC3/Zend/zend_object_handlers.c --- a/php-5.2.9RC3/Zend/zend_object_handlers.c Tue Mar 17 11:27:02 2009 -0700 +++ b/php-5.2.9RC3/Zend/zend_object_handlers.c Fri Mar 27 10:18:13 2009 -0700 @@ -175,24 +175,11 @@ return 0; } -ZEND_API struct _zend_property_info *zend_get_property_info(zend_class_entry *ce, zval *member, int silent TSRMLS_DC) +static struct _zend_property_info *zend_get_property_info_hval(zend_class_entry *ce, zval *member, int silent, ulong h TSRMLS_DC) { zend_property_info *property_info = NULL; zend_property_info *scope_property_info; zend_bool denied_access = 0; - ulong h; - - if (Z_STRVAL_P(member)[0] == '\0') { - if (!silent) { - if (Z_STRLEN_P(member) == 0) { - zend_error(E_ERROR, "Cannot access empty property"); - } else { - zend_error(E_ERROR, "Cannot access property started with '\\0'"); - } - } - return NULL; - } - h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1); if (zend_hash_quick_find(&ce->properties_info, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, h, (void **) &property_info)==SUCCESS) { if(property_info->flags & ZEND_ACC_SHADOW) { /* if it's a shadow - go to access it's private */ @@ -244,6 +231,31 @@ return property_info; } +static int test_valid_string(const zval* member, int silent) +{ + if (Z_STRVAL_P(member)[0] == '\0') { + if (!silent) { + if (Z_STRLEN_P(member) == 0) { + zend_error(E_ERROR, "Cannot access empty property"); + } else { + zend_error(E_ERROR, "Cannot access property started with '\\0'"); + } + } + return FAILURE; + } + return SUCCESS; +} + +ZEND_API struct _zend_property_info *zend_get_property_info(zend_class_entry *ce, zval *member, int silent TSRMLS_DC) +{ + ulong h; + + if (test_valid_string(member, silent) != SUCCESS) + return NULL; + h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1); + return zend_get_property_info_hval(ce, member, silent, h TSRMLS_CC); +} + ZEND_API int zend_check_property_access(zend_object *zobj, char *prop_info_name, int prop_info_name_len TSRMLS_DC) { @@ -293,7 +305,7 @@ return zend_hash_quick_add(zobj->guards, property_info->name, property_info->name_length+1, property_info->h, (void**)&stub, sizeof(stub), (void**) pguard); } -zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC) +zval *zend_std_read_property_hval(zval *object, zval *member, ulong hval, int type TSRMLS_DC) { zend_object *zobj; zval *tmp_member = NULL; @@ -312,14 +324,24 @@ zval_copy_ctor(tmp_member); convert_to_string(tmp_member); member = tmp_member; + hval = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member)); } + + if (test_valid_string(member, silent) != SUCCESS) { + if (!silent) { + zend_error(E_NOTICE,"Undefined property type for class: %s", zobj->ce->name); + } + retval = &EG(uninitialized_zval_ptr); + /* Unexpected */ + return *retval; + } + property_info = zend_get_property_info_hval(zobj->ce, member, (zobj->ce->__get != NULL), hval TSRMLS_CC); #if DEBUG_OBJECT_HANDLERS fprintf(stderr, "Read object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member)); #endif /* make zend_get_property_info silent if we have getter - we may want to use it */ - property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__get != NULL) TSRMLS_CC); if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &retval) == FAILURE) { zend_guard *guard; @@ -367,6 +389,15 @@ (*retval)->refcount--; } return *retval; +} + +zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC) +{ + ulong hval = 0; + if (member->type == IS_STRING) { + hval = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member)); + } + return zend_std_read_property_hval(object, member, hval, type TSRMLS_CC); } diff -r 00438f7eebe4 php-5.2.9RC3/Zend/zend_opcode.c --- a/php-5.2.9RC3/Zend/zend_opcode.c Tue Mar 17 11:27:02 2009 -0700 +++ b/php-5.2.9RC3/Zend/zend_opcode.c Fri Mar 27 10:18:13 2009 -0700 @@ -397,6 +397,32 @@ case ZEND_JMPNZ_EX: opline->op2.u.jmp_addr = &op_array->opcodes[opline->op2.u.opline_num]; break; + case ZEND_FETCH_OBJ_R: + case ZEND_FETCH_OBJ_IS: + case ZEND_FETCH_OBJ_FUNC_ARG: + case ZEND_FETCH_DIM_R: + case ZEND_FETCH_DIM_W: + case ZEND_FETCH_DIM_RW: + case ZEND_FETCH_DIM_IS: + case ZEND_FETCH_DIM_UNSET: + case ZEND_ASSIGN_DIM: + if ((opline->op2.op_type == IS_CONST) && + (opline->op2.u.constant.type == IS_STRING) + ) { + zval* zop2= &opline->op2.u.constant; + opline->hval = zend_get_hash_value(Z_STRVAL_P(zop2), + Z_STRLEN_P(zop2) +1); + } + break; + case ZEND_DO_FCALL: + if ((opline->op1.op_type == IS_CONST) && + (opline->op1.u.constant.type == IS_STRING) + ) { + zval* zop1= &opline->op1.u.constant; + opline->hval = zend_get_hash_value(Z_STRVAL_P(zop1), + Z_STRLEN_P(zop1) +1); + } + break; } ZEND_VM_SET_OPCODE_HANDLER(opline); opline++; diff -r 00438f7eebe4 php-5.2.9RC3/Zend/zend_vm_def.h --- a/php-5.2.9RC3/Zend/zend_vm_def.h Tue Mar 17 11:27:02 2009 -0700 +++ b/php-5.2.9RC3/Zend/zend_vm_def.h Fri Mar 27 10:18:13 2009 -0700 @@ -418,7 +418,7 @@ zend_op *op_data = opline+1; zval *dim = GET_OP2_ZVAL_PTR(BP_VAR_R); - zend_fetch_dimension_address(&EX_T(op_data->op2.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_RW), dim, IS_OP2_TMP_FREE(), BP_VAR_RW TSRMLS_CC); + zend_fetch_dimension_address(&EX_T(op_data->op2.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_RW), dim, IS_OP2_TMP_FREE(), BP_VAR_RW, 0, 0 TSRMLS_CC); value = get_zval_ptr(&op_data->op1, EX(Ts), &free_op_data1, BP_VAR_R); var_ptr = get_zval_ptr_ptr(&op_data->op2, EX(Ts), &free_op_data2, BP_VAR_RW); increment_opline = 1; @@ -1040,13 +1040,17 @@ zend_op *opline = EX(opline); zend_free_op free_op1, free_op2; zval *dim = GET_OP2_ZVAL_PTR(BP_VAR_R); + int usehval = 0; + if (OP2_TYPE == IS_CONST) { + usehval = 1; + } if (opline->extended_value == ZEND_FETCH_ADD_LOCK && OP1_TYPE != IS_CV && EX_T(opline->op1.u.var).var.ptr_ptr) { PZVAL_LOCK(*EX_T(opline->op1.u.var).var.ptr_ptr); } - zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_R), dim, IS_OP2_TMP_FREE(), BP_VAR_R TSRMLS_CC); + zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_R), dim, IS_OP2_TMP_FREE(), BP_VAR_R, opline->hval, usehval TSRMLS_CC); FREE_OP2(); FREE_OP1_VAR_PTR(); ZEND_VM_NEXT_OPCODE(); @@ -1058,7 +1062,11 @@ zend_free_op free_op1, free_op2; zval *dim = GET_OP2_ZVAL_PTR(BP_VAR_R); - zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_W), dim, IS_OP2_TMP_FREE(), BP_VAR_W TSRMLS_CC); + int usehval = 0; + if (OP2_TYPE == IS_CONST) { + usehval = 1; + } + zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_W), dim, IS_OP2_TMP_FREE(), BP_VAR_W, opline->hval, usehval TSRMLS_CC); FREE_OP2(); if (OP1_TYPE == IS_VAR && OP1_FREE && READY_TO_DESTROY(free_op1.var) && @@ -1079,7 +1087,11 @@ zend_free_op free_op1, free_op2; zval *dim = GET_OP2_ZVAL_PTR(BP_VAR_R); - zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_RW), dim, IS_OP2_TMP_FREE(), BP_VAR_RW TSRMLS_CC); + int usehval = 0; + if (OP2_TYPE == IS_CONST) { + usehval = 1; + } + zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_RW), dim, IS_OP2_TMP_FREE(), BP_VAR_RW, opline->hval, usehval TSRMLS_CC); FREE_OP2(); if (OP1_TYPE == IS_VAR && OP1_FREE && READY_TO_DESTROY(free_op1.var) && @@ -1099,8 +1111,12 @@ zend_op *opline = EX(opline); zend_free_op free_op1, free_op2; zval *dim = GET_OP2_ZVAL_PTR(BP_VAR_R); + int usehval = 0; + if (OP2_TYPE == IS_CONST) { + usehval = 1; + } - zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_IS), dim, IS_OP2_TMP_FREE(), BP_VAR_IS TSRMLS_CC); + zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_IS), dim, IS_OP2_TMP_FREE(), BP_VAR_IS, opline->hval, usehval TSRMLS_CC); FREE_OP2(); FREE_OP1_VAR_PTR(); ZEND_VM_NEXT_OPCODE(); @@ -1117,7 +1133,7 @@ zend_error_noreturn(E_ERROR, "Cannot use [] for reading"); } dim = GET_OP2_ZVAL_PTR(BP_VAR_R); - zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(type), dim, IS_OP2_TMP_FREE(), type TSRMLS_CC); + zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(type), dim, IS_OP2_TMP_FREE(), type, 0, 0 TSRMLS_CC); FREE_OP2(); if (OP1_TYPE == IS_VAR && type == BP_VAR_W && OP1_FREE && READY_TO_DESTROY(free_op1.var) && @@ -1138,6 +1154,10 @@ zend_free_op free_op1, free_op2; zval **container = GET_OP1_ZVAL_PTR_PTR(BP_VAR_UNSET); zval *dim = GET_OP2_ZVAL_PTR(BP_VAR_R); + int usehval = 0; + if (OP2_TYPE == IS_CONST) { + usehval = 1; + } /* Not needed in DIM_UNSET if (opline->extended_value == ZEND_FETCH_ADD_LOCK) { @@ -1149,7 +1169,7 @@ SEPARATE_ZVAL_IF_NOT_REF(container); } } - zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), container, dim, IS_OP2_TMP_FREE(), BP_VAR_UNSET TSRMLS_CC); + zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), container, dim, IS_OP2_TMP_FREE(), BP_VAR_UNSET, opline->hval, usehval TSRMLS_CC); FREE_OP2(); if (OP1_TYPE == IS_VAR && OP1_FREE && READY_TO_DESTROY(free_op1.var) && @@ -1176,6 +1196,9 @@ ZEND_VM_NEXT_OPCODE(); } +extern zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC); +extern zval *zend_std_read_property_hval(zval *object, zval *member, ulong hval, int type TSRMLS_DC); + ZEND_VM_HELPER_EX(zend_fetch_property_address_read_helper, VAR|UNUSED|CV, CONST|TMP|VAR|CV, int type) { zend_op *opline = EX(opline); @@ -1216,7 +1239,14 @@ } /* here we are sure we are dealing with an object */ - *retval = Z_OBJ_HT_P(container)->read_property(container, offset, type TSRMLS_CC); + if ((OP2_TYPE == IS_CONST) && Z_OBJ_HT_P(container)->read_property == zend_std_read_property) + { + *retval = zend_std_read_property_hval(container, offset, + EX(opline)->hval, + type TSRMLS_CC); + } else { + *retval = Z_OBJ_HT_P(container)->read_property(container, offset, type TSRMLS_CC); + } if (RETURN_VALUE_UNUSED(&opline->result) && ((*retval)->refcount == 0)) { zval_dtor(*retval); @@ -1398,7 +1428,7 @@ zend_free_op free_op2; zval *dim = GET_OP2_ZVAL_PTR(BP_VAR_R); - EX_T(opline->result.u.var).var.ptr_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, BP_VAR_R TSRMLS_CC); + EX_T(opline->result.u.var).var.ptr_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, BP_VAR_R, 0, 0 TSRMLS_CC); SELECTIVE_PZVAL_LOCK(*EX_T(opline->result.u.var).var.ptr_ptr, &opline->result); FREE_OP2(); } @@ -1426,6 +1456,10 @@ zend_op *op_data = opline+1; zend_free_op free_op1; zval **object_ptr; + int usehval = 0; + if (OP2_TYPE == IS_CONST) { + usehval = 1; + } if (OP1_TYPE == IS_CV || EX_T(opline->op1.u.var).var.ptr_ptr) { /* not an array offset */ @@ -1441,7 +1475,7 @@ zval *value; zval *dim = GET_OP2_ZVAL_PTR(BP_VAR_R); - zend_fetch_dimension_address(&EX_T(op_data->op2.u.var), object_ptr, dim, IS_OP2_TMP_FREE(), BP_VAR_W TSRMLS_CC); + zend_fetch_dimension_address(&EX_T(op_data->op2.u.var), object_ptr, dim, IS_OP2_TMP_FREE(), BP_VAR_W, opline->hval, usehval TSRMLS_CC); FREE_OP2(); value = get_zval_ptr(&op_data->op1, EX(Ts), &free_op_data1, BP_VAR_R); @@ -2098,7 +2132,7 @@ zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); - if (zend_hash_find(EG(function_table), fname->value.str.val, fname->value.str.len+1, (void **) &EX(function_state).function)==FAILURE) { + if (zend_hash_quick_find(EG(function_table), fname->value.str.val, fname->value.str.len+1, opline->hval, (void **) &EX(function_state).function)==FAILURE) { zend_error_noreturn(E_ERROR, "Call to undefined function %s()", fname->value.str.val); } EX(object) = NULL; Previous Comments: ------------------------------------------------------------------------ [2009-03-27 22:58:50] basant dot kukreja at gmail dot com Description: ------------ In specweb ecommerce php benchmark, hash lookup consumes a lot of time. For a 30 minutes measurement : Excl. Incl. Name User CPU User CPU sec. sec. 434.304 914.980 zend_fetch_dimension_address 136.195 427.119 zend_get_property_info 914 seconds (13 % of user time) are spent in zend_fetch_dimension_address out of total 6947 seconds. Reproduce code: --------------- specweb benchmark. Expected result: ---------------- Low time spent in zend_fetch_dimension_address and zend_get_property_info Actual result: -------------- Time spend in zend_fetch_dimension_address and zend_get_property_info is very high. ------------------------------------------------------------------------ -- Edit this bug report at http://bugs.php.net/?id=47815&edit=1