It's been a concept stuck in my mind since I first looked into ZE2.1 and saw what compiled variables were, and a recent blog post (http://php100.wordpress.com/2006/11/24/optimizations/) (which got picked up by planet PHP) got my wheels spinning again. Tonight I decided to actually sit down and see what benefit could be milked from such an endeavor and what it's maintenance cost would be.

The attached patch shows what I've tossed together for HEAD and the benchmark I've run against it do show some appreciable gains...

Based on this code snippet, I saw a consistent gain of aproximately 18%:
for($i = 0; $i < 10000000; $i++) pi();

I chose the pi() function as it (A) would actually favor the unpatched approach, being quick to hash and strcmp. It also (B) performs minimal actual work, simply returning a constant value, thus leaving as much of the execution time to the matter being tested as possible.

Of course, this isn't a "normal use" example, and it doesn't address dynamic function calls or method calls. I also havn't looked into class resolution yet, but this is a good spot to begin discussion from.

Thoughts anyone?

-Sara
Index: Zend/zend_compile.c
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.c,v
retrieving revision 1.728
diff -u -p -r1.728 zend_compile.c
--- Zend/zend_compile.c 17 Nov 2006 10:48:53 -0000      1.728
+++ Zend/zend_compile.c 4 Dec 2006 05:24:16 -0000
@@ -293,6 +293,33 @@ static int lookup_cv(zend_op_array *op_a
        return i;
 }
 
+static int lookup_cf(zend_op_array *op_array, zend_uchar type, zstr name, int 
name_len)
+{
+       int i = 0;
+       ulong hash_value = zend_u_inline_hash_func(type, name, name_len+1);
+
+       while (i < op_array->last_func) {
+               if (op_array->funcs[i].hash_value == hash_value &&
+                   op_array->funcs[i].name_len == name_len &&
+                   !memcmp(op_array->funcs[i].name.v, name.v, 
type==IS_UNICODE?UBYTES(name_len):name_len)) {
+                 efree(name.v);
+                 return i;
+               }
+               i++;
+       }
+       i = op_array->last_func;
+       op_array->last_func++;
+       if (op_array->last_func > op_array->size_func) {
+               op_array->size_func += 16; /* FIXME */
+               op_array->funcs = erealloc(op_array->funcs, 
op_array->size_func*sizeof(zend_compiled_function));
+       }
+       op_array->funcs[i].type = type;
+       op_array->funcs[i].name = name; /* estrndup(name, name_len); */
+       op_array->funcs[i].name_len = name_len;
+       op_array->funcs[i].hash_value = hash_value;
+       return i;
+}
+
 
 void zend_do_binary_op(zend_uchar op, znode *result, znode *op1, znode *op2 
TSRMLS_DC)
 {
@@ -1615,8 +1642,13 @@ void zend_do_end_function_call(znode *fu
        } else {
                opline = get_next_op(CG(active_op_array) TSRMLS_CC);
                if (!is_method && !is_dynamic_fcall && 
function_name->op_type==IS_CONST) {
+                       /* Static function call:  foo(...) */
                        opline->opcode = ZEND_DO_FCALL;
-                       opline->op1 = *function_name;
+                       opline->op1.op_type = IS_CONST;
+                       Z_TYPE(opline->op1.u.constant) = IS_LONG;
+                       Z_LVAL(opline->op1.u.constant) = 
lookup_cf(CG(active_op_array), Z_TYPE(function_name->u.constant),
+                                                                       
Z_UNIVAL(function_name->u.constant),
+                                                                       
Z_UNILEN(function_name->u.constant));
                } else {
                        opline->opcode = ZEND_DO_FCALL_BY_NAME;
                        SET_UNUSED(opline->op1);
@@ -3441,10 +3473,8 @@ void zend_do_shell_exec(znode *result, z
        opline->opcode = ZEND_DO_FCALL;
        opline->result.u.var = get_temporary_variable(CG(active_op_array));
        opline->result.op_type = IS_VAR;
-       Z_STRVAL(opline->op1.u.constant) = estrndup("shell_exec", 
sizeof("shell_exec")-1);
-       Z_STRLEN(opline->op1.u.constant) = sizeof("shell_exec")-1;
-       INIT_PZVAL(&opline->op1.u.constant);
-       Z_TYPE(opline->op1.u.constant) = IS_STRING;
+       Z_TYPE(opline->op1.u.constant) = IS_LONG;
+       Z_LVAL(opline->op1.u.constant) = lookup_cf(CG(active_op_array), 
IS_STRING, ZSTR("shell_exec"), sizeof("shell_exec") - 1);
        opline->op1.op_type = IS_CONST;
        opline->extended_value = 1;
        SET_UNUSED(opline->op2);
Index: Zend/zend_compile.h
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.h,v
retrieving revision 1.350
diff -u -p -r1.350 zend_compile.h
--- Zend/zend_compile.h 17 Nov 2006 10:48:53 -0000      1.350
+++ Zend/zend_compile.h 4 Dec 2006 05:24:16 -0000
@@ -176,6 +176,13 @@ typedef struct _zend_compiled_variable {
        ulong hash_value;
 } zend_compiled_variable;
 
+typedef struct _zend_compiled_function {
+       zend_uchar type; /* IS_STRING/IS_UNICODE */
+       zstr name;
+       int name_len;
+       ulong hash_value;
+} zend_compiled_function;
+
 struct _zend_op_array {
        /* Common elements */
        zend_uchar type;
@@ -198,6 +205,9 @@ struct _zend_op_array {
        zend_compiled_variable *vars;
        int last_var, size_var;
 
+       zend_compiled_function *funcs;
+       int last_func, size_func;
+
        zend_uint T;
 
        zend_brk_cont_element *brk_cont_array;
@@ -301,6 +311,7 @@ struct _zend_execute_data {
        zval *object;
        union _temp_variable *Ts;
        zval ***CVs;
+       zend_function **CFs;
        zend_bool original_in_execution;
        HashTable *symbol_table;
        struct _zend_execute_data *prev_execute_data;
Index: Zend/zend_execute.c
===================================================================
RCS file: /repository/ZendEngine2/zend_execute.c,v
retrieving revision 1.753
diff -u -p -r1.753 zend_execute.c
--- Zend/zend_execute.c 7 Nov 2006 20:28:40 -0000       1.753
+++ Zend/zend_execute.c 4 Dec 2006 05:24:16 -0000
@@ -147,6 +147,9 @@ static inline void zend_pzval_unlock_fre
 #define CV_OF(i)     (EG(current_execute_data)->CVs[i])
 #define CV_DEF_OF(i) (EG(active_op_array)->vars[i])
 
+#define CF_OF(i)     (EG(current_execute_data)->CFs[i])
+#define CF_DEF_OF(i) (EG(active_op_array)->funcs[i])
+
 ZEND_API zval** zend_get_compiled_variable_value(zend_execute_data 
*execute_data_ptr, zend_uint var)
 {
        return execute_data_ptr->CVs[var];
@@ -247,6 +250,22 @@ static inline zval *_get_zval_ptr_cv(zno
        return **ptr;
 }
 
+static inline zend_function *_get_compiled_function_ptr(long cfid TSRMLS_DC)
+{
+       zend_function **ptr = &CF_OF(cfid);
+
+       if (!*ptr) {
+               zend_compiled_function *cf = &CF_DEF_OF(cfid);
+
+               if (zend_u_hash_quick_find(EG(function_table), cf->type, 
cf->name, cf->name_len+1, cf->hash_value, (void **)ptr)==FAILURE) {
+                       /* Leave *ptr alone in case the function gets defined 
later */
+                       return NULL;
+               }
+       }
+
+       return *ptr;
+}
+
 static inline zval *_get_zval_ptr(znode *node, temp_variable *Ts, zend_free_op 
*should_free, int type TSRMLS_DC)
 {
 /*     should_free->is_var = 0; */
@@ -1384,6 +1403,7 @@ ZEND_API void execute_internal(zend_exec
 
 #define ZEND_VM_RETURN_FROM_EXECUTE_LOOP() \
        free_alloca(EX(CVs)); \
+       free_alloca(EX(CFs)); \
        if (EX(op_array)->T < TEMP_VAR_STACK_LIMIT) { \
                free_alloca(EX(Ts)); \
        } else { \
Index: Zend/zend_opcode.c
===================================================================
RCS file: /repository/ZendEngine2/zend_opcode.c,v
retrieving revision 1.124
diff -u -p -r1.124 zend_opcode.c
--- Zend/zend_opcode.c  17 Nov 2006 10:48:53 -0000      1.124
+++ Zend/zend_opcode.c  4 Dec 2006 05:24:16 -0000
@@ -73,6 +73,10 @@ void init_op_array(zend_op_array *op_arr
        op_array->last_var = 0;
        op_array->vars = NULL;
 
+       op_array->size_func = 0;
+       op_array->last_func = 0;
+       op_array->funcs = NULL;
+
        op_array->T = 0;
 
        op_array->function_name.v = NULL;
@@ -267,6 +271,15 @@ ZEND_API void destroy_op_array(zend_op_a
                efree(op_array->vars);
        }
 
+       if (op_array->funcs) {
+               i = op_array->last_func;
+               while (i > 0) {
+                       i--;
+                       efree(op_array->funcs[i].name.v);
+               }
+               efree(op_array->funcs);
+       }
+
        while (opline<end) {
                if (opline->op1.op_type==IS_CONST) {
 #if DEBUG_ZEND>2
Index: Zend/zend_vm_def.h
===================================================================
RCS file: /repository/ZendEngine2/zend_vm_def.h,v
retrieving revision 1.149
diff -u -p -r1.149 zend_vm_def.h
--- Zend/zend_vm_def.h  30 Oct 2006 11:04:47 -0000      1.149
+++ Zend/zend_vm_def.h  4 Dec 2006 05:24:16 -0000
@@ -2069,9 +2069,11 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, CONST
 
        zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL);
 
-       if (zend_u_hash_find(EG(function_table), Z_TYPE_P(fname), 
Z_UNIVAL_P(fname), Z_UNILEN_P(fname)+1, (void **) 
&EX(function_state).function)==FAILURE) {
+       if (!(EX(function_state).function = 
_get_compiled_function_ptr(Z_LVAL_P(fname) TSRMLS_CC))) {
                /* FIXME: output identifiers properly */
-               zend_error_noreturn(E_ERROR, "Call to undefined function %R()", 
Z_TYPE_P(fname), Z_UNIVAL_P(fname));
+               zend_compiled_function *cf = &CF_DEF_OF(Z_LVAL_P(fname));
+
+               zend_error_noreturn(E_ERROR, "Call to undefined function %R()", 
cf->type, cf->name.v);
        }
        EX(object) = NULL;
 
Index: Zend/zend_vm_execute.skl
===================================================================
RCS file: /repository/ZendEngine2/zend_vm_execute.skl,v
retrieving revision 1.6
diff -u -p -r1.6 zend_vm_execute.skl
--- Zend/zend_vm_execute.skl    19 Sep 2006 10:38:30 -0000      1.6
+++ Zend/zend_vm_execute.skl    4 Dec 2006 05:24:16 -0000
@@ -22,6 +22,8 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_
        }
        EX(CVs) = (zval***)do_alloca(sizeof(zval**) * op_array->last_var);
        memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var);
+       EX(CFs) = (zend_function**)do_alloca(sizeof(zend_function*) * 
op_array->last_func);
+       memset(EX(CFs), 0, sizeof(zend_function*) * op_array->last_func);
        EX(op_array) = op_array;
        EX(original_in_execution) = EG(in_execution);
        EX(symbol_table) = EG(active_symbol_table);

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to