Skip to content

Instantly share code, notes, and snippets.

@hiboma
Last active December 27, 2015 02:19
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hiboma/7251089 to your computer and use it in GitHub Desktop.
Save hiboma/7251089 to your computer and use it in GitHub Desktop.
array_push と [] との比較

array_push と $array[] = $val って何が違うの

  • array_push で配列の要素足すのと、$array[] = $val で配列の要素足すのと何が違うのかわからんので調べた
  • http://pecl.php.net/package/vld を入れると処理系の OPコードを見れる

環境

[vagrant@localhost ~]$ rpm -q php
php-5.3.3-23.el6_4.x86_64

[vagrant@localhost ~]$ php -v
PHP 5.3.3 (cli) (built: Jul 12 2013 20:35:47) 
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies

array_push

<?php

$numbers = array();
array_push($numbers, 100);
array_push($numbers, 200);
array_push($numbers, 300);
$ php -dvld.active=1 test.php
Finding entry points
Branch analysis from position: 0
Return found
filename:       /home/vagrant/test.php
function name:  (null)
number of ops:  12
compiled vars:  !0 = $numbers
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   3     0  >   INIT_ARRAY                                       ~0      
         1      ASSIGN                                                   !0, ~0
   4     2      SEND_REF                                                 !0
         3      SEND_VAL                                                 100
         4      DO_FCALL                                      2          'array_push'
   5     5      SEND_REF                                                 !0
         6      SEND_VAL                                                 200
         7      DO_FCALL                                      2          'array_push'
   6     8      SEND_REF                                                 !0
         9      SEND_VAL                                                 300
        10      DO_FCALL                                      2          'array_push'
   7    11    > RETURN                                                   1

branch: #  0; line:     3-    7; sop:     0; eop:    11
path #1: 0, 
  • SEND_REF, SEND_VAL, DO_FCALL で 'array_push' を関数呼び出しする
    • function_table ハッシュテーブルから関数名探したりとかしてる
ZEND_VM_HANDLER(60, ZEND_DO_FCALL, CONST, ANY)
{
	zend_op *opline = EX(opline);
	zend_free_op free_op1;
	zval *fname = GET_OP1_ZVAL_PTR(BP_VAR_R);

	zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope));

	if (zend_hash_quick_find(EG(function_table), fname->value.str.val, fname->value.str.len+1, Z_LVAL(opline->op2.u.constant), (void **) &EX(function_state).function)==FAILURE) {
		zend_error_noreturn(E_ERROR, "Call to undefined function %s()", fname->value.str.val);
	}
	EX(object) = NULL;

	FREE_OP1();

	ZEND_VM_DISPATCH_TO_HELPER(zend_do_fcall_common_helper);

さらに ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY) に続く様子

$array[] = $val の場合

<?php

$numbers = array();
$numbers[] = 100;
$numbers[] = 200;
$numbers[] = 300;
Finding entry points
Branch analysis from position: 0
Return found
filename:       /home/vagrant/test.php
function name:  (null)
number of ops:  9
compiled vars:  !0 = $numbers
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   3     0  >   INIT_ARRAY                                       ~0      
         1      ASSIGN                                                   !0, ~0
   4     2      ASSIGN_DIM                                               !0
         3      OP_DATA                                                  100, $3
   5     4      ASSIGN_DIM                                               !0
         5      OP_DATA                                                  200, $5
   6     6      ASSIGN_DIM                                               !0
         7      OP_DATA                                                  300, $7
   7     8    > RETURN                                                   1

branch: #  0; line:     3-    7; sop:     0; eop:     8
path #1: 0, 
  • ASSIGN_DIM という OPコードで処理されてる
 ZEND_VM_HANDLER(147, ZEND_ASSIGN_DIM, VAR|CV, CONST|TMP|VAR|UNUSED|CV)
{
	zend_op *opline = EX(opline);
	zend_op *op_data = opline+1;
	zend_free_op free_op1;
	zval **object_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);

	if (OP1_TYPE == IS_VAR && !object_ptr) {
		zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
	}
	if (Z_TYPE_PP(object_ptr) == IS_OBJECT) {
		zend_free_op free_op2;
		zval *property_name = GET_OP2_ZVAL_PTR(BP_VAR_R);

		if (IS_OP2_TMP_FREE()) {
			MAKE_REAL_ZVAL_PTR(property_name);
		}
		zend_assign_to_object(&opline->result, object_ptr, property_name, &op_data->op1, EX(Ts), ZEND_ASSIGN_DIM TSRMLS_CC);
		if (IS_OP2_TMP_FREE()) {
			zval_ptr_dtor(&property_name);
		} else {
			FREE_OP2();
		}
	} else {
		zend_free_op free_op2, free_op_data1, free_op_data2;
		zval *value;
		zval *dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
		zval **variable_ptr_ptr;

		zend_fetch_dimension_address(&EX_T(op_data->op2.u.var), object_ptr, dim, IS_OP2_TMP_FREE(), BP_VAR_W TSRMLS_CC);
		FREE_OP2();

		value = get_zval_ptr(&op_data->op1, EX(Ts), &free_op_data1, BP_VAR_R);
		variable_ptr_ptr = _get_zval_ptr_ptr_var(&op_data->op2, EX(Ts), &free_op_data2 TSRMLS_CC);
		if (!variable_ptr_ptr) {
			if (zend_assign_to_string_offset(&EX_T(op_data->op2.u.var), value, op_data->op1.op_type TSRMLS_CC)) {
				if (!RETURN_VALUE_UNUSED(&opline->result)) {
					EX_T(opline->result.u.var).var.ptr_ptr = &EX_T(opline->result.u.var).var.ptr;
					ALLOC_ZVAL(EX_T(opline->result.u.var).var.ptr);
					INIT_PZVAL(EX_T(opline->result.u.var).var.ptr);
					ZVAL_STRINGL(EX_T(opline->result.u.var).var.ptr, Z_STRVAL_P(EX_T(op_data->op2.u.var).str_offset.str)+EX_T(op_data->op2.u.var).str_offset.offset, 1, 1);
				}
			} else if (!RETURN_VALUE_UNUSED(&opline->result)) {
				AI_SET_PTR(EX_T(opline->result.u.var).var, EG(uninitialized_zval_ptr));
				PZVAL_LOCK(EG(uninitialized_zval_ptr));
			}
		} else {
		 	value = zend_assign_to_variable(variable_ptr_ptr, value, IS_TMP_FREE(free_op_data1) TSRMLS_CC);
			if (!RETURN_VALUE_UNUSED(&opline->result)) {
				AI_SET_PTR(EX_T(opline->result.u.var).var, value);
				PZVAL_LOCK(value);
			}
		}
		FREE_OP_VAR_PTR(free_op_data2);
	 	FREE_OP_IF_VAR(free_op_data1);
	}
 	FREE_OP1_VAR_PTR();
	/* assign_dim has two opcodes! */
	ZEND_VM_INC_OPCODE();
	ZEND_VM_NEXT_OPCODE();
}
@kurotaky
Copy link

kurotaky commented Nov 1, 2013

👀

@hiboma
Copy link
Author

hiboma commented Nov 1, 2013

🍣

@hiboma
Copy link
Author

hiboma commented Nov 1, 2013

oprofile の結果

array_push

CPU: CPU with timer interrupt, speed 0 MHz (estimated)
Profiling through timer interrupt
samples  %        symbol name
12       15.0000  zend_do_fcall_common_helper_SPEC
11       13.7500  _zend_mm_alloc_int
10       12.5000  _zend_mm_free_int
10       12.5000  zend_parse_va_args
9        11.2500  zend_hash_rehash
6         7.5000  zend_mm_remove_from_free_list
5         6.2500  zend_mm_add_to_free_list
4         5.0000  ZEND_SEND_REF_SPEC_CV_HANDLER
3         3.7500  ZEND_POST_INC_SPEC_CV_HANDLER
2         2.5000  _safe_emalloc
2         2.5000  _zend_hash_index_update_or_next_insert
1         1.2500  ZEND_DO_FCALL_SPEC_CONST_HANDLER
1         1.2500  ZEND_FREE_SPEC_TMP_HANDLER
1         1.2500  ZEND_IS_SMALLER_SPEC_CV_CONST_HANDLER
1         1.2500  execute
1         1.2500  gc_zval_possible_root
1         1.2500  zend_hash_destroy

[]

samples  %        symbol name
5        14.7059  _zend_mm_alloc_int
5        14.7059  zend_fetch_dimension_address
4        11.7647  _zend_hash_index_update_or_next_insert
3         8.8235  zend_hash_rehash
3         8.8235  zend_mm_add_to_free_list
3         8.8235  zend_mm_remove_from_free_list
2         5.8824  ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_HANDLER
2         5.8824  ZEND_JMPZNZ_SPEC_TMP_HANDLER
2         5.8824  execute
1         2.9412  _efree
1         2.9412  _zend_mm_free_int
1         2.9412  _zval_ptr_dtor
1         2.9412  zend_register_functions
1         2.9412  zend_shutdown_fpu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment