閒話不說,上問題:php
一個很大的php數組(1w+),使用array_shfit跟array_pop取數組元素時,性能差距特別大,array_shift慢的沒法忍受,而array_pop就很快了。數組
先不說答案,看段代碼:函數
$arr = array( 0=>123, 3=>132, 2=>987, ); array_shift($arr); //array_pop($arr); var_dump($arr);
輸出會有什麼不一樣呢,
性能
array_shift後,輸出爲:ui
array(2) {spa
[0]=>code
int(132)element
[1]=>get
int(987)hash
}
array_pop後,輸出爲:
array(2) {
[0]=>
int(123)
[3]=>
int(132)
}
有什麼不一樣?
對,就是array_shift操做後,數組的鍵值變了。這就是array_shift爲何慢的緣由了。由於array_shift操做數組會對數字鍵值,從新從0開始重建。每次移出一個元素後,就得遍歷數組中的全部元素。array_pop就不會了。一個是O(1),一個是O(n)的複雜度,數組大了以後,效果就很明顯了。
php裏對應函數的C代碼實現:
/* {{{ void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end) */ static void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end) { zval *stack, /* Input stack */ **val; /* Value to be popped */ char *key = NULL; uint key_len = 0; ulong index; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &stack) == FAILURE) { return; } if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) { return; } /* Get the first or last value and copy it into the return value */ if (off_the_end) { zend_hash_internal_pointer_end(Z_ARRVAL_P(stack)); } else { zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); } zend_hash_get_current_data(Z_ARRVAL_P(stack), (void **)&val); RETVAL_ZVAL(*val, 1, 0); /* Delete the first or last value */ zend_hash_get_current_key_ex(Z_ARRVAL_P(stack), &key, &key_len, &index, 0, NULL); if (key && Z_ARRVAL_P(stack) == &EG(symbol_table)) { zend_delete_global_variable(key, key_len - 1 TSRMLS_CC); } else { zend_hash_del_key_or_index(Z_ARRVAL_P(stack), key, key_len, index, (key) ? HASH_DEL_KEY : HASH_DEL_INDEX); } //就是下面這裏,遍歷全部元素,對是數字鍵的元素從新賦值。pop的off_the_end是1,shift的off_the_end是0 /* If we did a shift... re-index like it did before */ if (!off_the_end) { unsigned int k = 0; int should_rehash = 0; Bucket *p = Z_ARRVAL_P(stack)->pListHead; while (p != NULL) { if (p->nKeyLength == 0) {//鍵值是數字 if (p->h != k) { p->h = k++; should_rehash = 1; } else { k++; } } p = p->pListNext; } Z_ARRVAL_P(stack)->nNextFreeElement = k; if (should_rehash) { //由於從新給鍵賦值,hash事後的位置可能不同了,就得從新hash後,放到對應的位置。 zend_hash_rehash(Z_ARRVAL_P(stack)); } } else if (!key_len && index >= Z_ARRVAL_P(stack)->nNextFreeElement - 1) { Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1; } zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); }
PHP_FUNCTION(array_pop) { _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } PHP_FUNCTION(array_shift) { _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); }