Permalink
Browse files

Improved ternary operator performance when returning arrays

  • Loading branch information...
1 parent 41ddca8 commit 07b7ba8b4004596086c0506e2f5a9058aa43ed55 @arnaud-lb arnaud-lb committed Oct 18, 2011
Showing with 460 additions and 15 deletions.
  1. +1 −0 NEWS
  2. +37 −0 Zend/micro_bench.php
  3. +41 −8 Zend/zend_compile.c
  4. +1 −0 Zend/zend_execute_API.c
  5. +1 −0 Zend/zend_opcode.c
  6. +67 −2 Zend/zend_vm_def.h
  7. +310 −5 Zend/zend_vm_execute.h
  8. +2 −0 Zend/zend_vm_opcodes.h
View
1 NEWS
@@ -3,6 +3,7 @@ PHP NEWS
?? ??? 2011, PHP 5.4.0 RC1
- General improvements:
. Improve the warning message of incompatible arguments. (Laruence)
+ . Improve ternary operator performance when returning arrays. (Arnaud, Dmitry)
- Core:
View
@@ -202,6 +202,35 @@ function read_str_offset($n) {
}
}
+function issetor($n) {
+ $val = array(0,1,2,3,4,5,6,7,8,9);
+ for ($i = 0; $i < $n; ++$i) {
+ $x = $val ?: null;
+ }
+}
+
+function issetor2($n) {
+ $f = false; $j = 0;
+ for ($i = 0; $i < $n; ++$i) {
+ $x = $f ?: $j + 1;
+ }
+}
+
+function ternary($n) {
+ $val = array(0,1,2,3,4,5,6,7,8,9);
+ $f = false;
+ for ($i = 0; $i < $n; ++$i) {
+ $x = $f ? null : $val;
+ }
+}
+
+function ternary2($n) {
+ $f = false; $j = 0;
+ for ($i = 0; $i < $n; ++$i) {
+ $x = $f ? $f : $j + 1;
+ }
+}
+
/*****/
function empty_loop($n) {
@@ -318,4 +347,12 @@ function total()
$t = end_test($t, '$x = $hash[\'v\']', $overhead);
read_str_offset(N);
$t = end_test($t, '$x = $str[0]', $overhead);
+issetor(N);
+$t = end_test($t, '$x = $a ?: null', $overhead);
+issetor2(N);
+$t = end_test($t, '$x = $f ?: tmp', $overhead);
+ternary(N);
+$t = end_test($t, '$x = $f ? $f : $a', $overhead);
+ternary2(N);
+$t = end_test($t, '$x = $f ? $f : tmp', $overhead);
total($t0, "Total");
View
@@ -1469,7 +1469,8 @@ void zend_do_free(znode *op1 TSRMLS_DC) /* {{{ */
&& opline->result.var == op1->u.op.var) {
if (opline->opcode == ZEND_FETCH_R ||
opline->opcode == ZEND_FETCH_DIM_R ||
- opline->opcode == ZEND_FETCH_OBJ_R) {
+ opline->opcode == ZEND_FETCH_OBJ_R ||
+ opline->opcode == ZEND_QM_ASSIGN_VAR) {
/* It's very rare and useless case. It's better to use
additional FREE opcode and simplify the FETCH handlers
their selves */
@@ -6308,8 +6309,13 @@ void zend_do_jmp_set(const znode *value, znode *jmp_token, znode *colon_token TS
int op_number = get_next_op_number(CG(active_op_array));
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
- opline->opcode = ZEND_JMP_SET;
- opline->result_type = IS_TMP_VAR;
+ if (value->op_type == IS_VAR || value->op_type == IS_CV) {
+ opline->opcode = ZEND_JMP_SET_VAR;
+ opline->result_type = IS_VAR;
+ } else {
+ opline->opcode = ZEND_JMP_SET;
+ opline->result_type = IS_TMP_VAR;
+ }
opline->result.var = get_temporary_variable(CG(active_op_array));
SET_NODE(opline->op1, value);
SET_UNUSED(opline->op2);
@@ -6326,9 +6332,20 @@ void zend_do_jmp_set_else(znode *result, const znode *false_value, const znode *
{
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
- opline->opcode = ZEND_QM_ASSIGN;
- opline->extended_value = 0;
SET_NODE(opline->result, colon_token);
+ if (colon_token->op_type == IS_TMP_VAR) {
+ if (false_value->op_type == IS_VAR || false_value->op_type == IS_CV) {
+ CG(active_op_array)->opcodes[jmp_token->u.op.opline_num].opcode = ZEND_JMP_SET_VAR;
+ CG(active_op_array)->opcodes[jmp_token->u.op.opline_num].result_type = IS_VAR;
+ opline->opcode = ZEND_QM_ASSIGN_VAR;
+ opline->result_type = IS_VAR;
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ }
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN_VAR;
+ }
+ opline->extended_value = 0;
SET_NODE(opline->op1, false_value);
SET_UNUSED(opline->op2);
@@ -6363,8 +6380,13 @@ void zend_do_qm_true(const znode *true_value, znode *qm_token, znode *colon_toke
CG(active_op_array)->opcodes[qm_token->u.op.opline_num].op2.opline_num = get_next_op_number(CG(active_op_array))+1; /* jmp over the ZEND_JMP */
- opline->opcode = ZEND_QM_ASSIGN;
- opline->result_type = IS_TMP_VAR;
+ if (true_value->op_type == IS_VAR || true_value->op_type == IS_CV) {
+ opline->opcode = ZEND_QM_ASSIGN_VAR;
+ opline->result_type = IS_VAR;
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->result_type = IS_TMP_VAR;
+ }
opline->result.var = get_temporary_variable(CG(active_op_array));
SET_NODE(opline->op1, true_value);
SET_UNUSED(opline->op2);
@@ -6383,8 +6405,19 @@ void zend_do_qm_false(znode *result, const znode *false_value, const znode *qm_t
{
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
- opline->opcode = ZEND_QM_ASSIGN;
SET_NODE(opline->result, qm_token);
+ if (qm_token->op_type == IS_TMP_VAR) {
+ if (false_value->op_type == IS_VAR || false_value->op_type == IS_CV) {
+ CG(active_op_array)->opcodes[colon_token->u.op.opline_num - 1].opcode = ZEND_QM_ASSIGN_VAR;
+ CG(active_op_array)->opcodes[colon_token->u.op.opline_num - 1].result_type = IS_VAR;
+ opline->opcode = ZEND_QM_ASSIGN_VAR;
+ opline->result_type = IS_VAR;
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN;
+ }
+ } else {
+ opline->opcode = ZEND_QM_ASSIGN_VAR;
+ }
SET_NODE(opline->op1, false_value);
SET_UNUSED(opline->op2);
View
@@ -1299,6 +1299,7 @@ void execute_new_code(TSRMLS_D) /* {{{ */
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
+ case ZEND_JMP_SET_VAR:
opline->op2.jmp_addr = &CG(active_op_array)->opcodes[opline->op2.opline_num];
break;
}
View
@@ -529,6 +529,7 @@ ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC)
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
+ case ZEND_JMP_SET_VAR:
opline->op2.jmp_addr = &op_array->opcodes[opline->op2.opline_num];
break;
}
View
@@ -4661,8 +4661,45 @@ ZEND_VM_HANDLER(152, ZEND_JMP_SET, CONST|TMP|VAR|CV, ANY)
if (i_zend_is_true(value)) {
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, value);
- zendi_zval_copy_ctor(EX_T(opline->result.var).tmp_var);
- FREE_OP1();
+ if (!IS_OP1_TMP_FREE()) {
+ zendi_zval_copy_ctor(EX_T(opline->result.var).tmp_var);
+ }
+ FREE_OP1_IF_VAR();
+#if DEBUG_ZEND>=2
+ printf("Conditional jmp to %d\n", opline->op2.opline_num);
+#endif
+ ZEND_VM_JMP(opline->op2.jmp_addr);
+ }
+
+ FREE_OP1();
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_HANDLER(158, ZEND_JMP_SET_VAR, CONST|TMP|VAR|CV, ANY)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *value, *ret;
+
+ SAVE_OPLINE();
+ value = GET_OP1_ZVAL_PTR(BP_VAR_R);
+
+ if (i_zend_is_true(value)) {
+ if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
+ Z_ADDREF_P(value);
+ EX_T(opline->result.var).var.ptr = value;
+ EX_T(opline->result.var).var.ptr_ptr = NULL;
+ } else {
+ ALLOC_ZVAL(ret);
+ INIT_PZVAL_COPY(ret, value);
+ EX_T(opline->result.var).var.ptr = ret;
+ EX_T(opline->result.var).var.ptr_ptr = NULL;
+ if (!IS_OP1_TMP_FREE()) {
+ zval_copy_ctor(EX_T(opline->result.var).var.ptr);
+ }
+ }
+ FREE_OP1_IF_VAR();
#if DEBUG_ZEND>=2
printf("Conditional jmp to %d\n", opline->op2.opline_num);
#endif
@@ -4692,6 +4729,34 @@ ZEND_VM_HANDLER(22, ZEND_QM_ASSIGN, CONST|TMP|VAR|CV, ANY)
ZEND_VM_NEXT_OPCODE();
}
+ZEND_VM_HANDLER(157, ZEND_QM_ASSIGN_VAR, CONST|TMP|VAR|CV, ANY)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *value, *ret;
+
+ SAVE_OPLINE();
+ value = GET_OP1_ZVAL_PTR(BP_VAR_R);
+
+ if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
+ Z_ADDREF_P(value);
+ EX_T(opline->result.var).var.ptr = value;
+ EX_T(opline->result.var).var.ptr_ptr = NULL;
+ } else {
+ ALLOC_ZVAL(ret);
+ INIT_PZVAL_COPY(ret, value);
+ EX_T(opline->result.var).var.ptr = ret;
+ EX_T(opline->result.var).var.ptr_ptr = NULL;
+ if (!IS_OP1_TMP_FREE()) {
+ zval_copy_ctor(EX_T(opline->result.var).var.ptr);
+ }
+ }
+
+ FREE_OP1_IF_VAR();
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
ZEND_VM_HANDLER(101, ZEND_EXT_STMT, ANY, ANY)
{
SAVE_OPLINE();
Oops, something went wrong.

0 comments on commit 07b7ba8

Please sign in to comment.