Skip to content

Commit 602d104

Browse files
committed
Optimize array_slice for packed arrays with large offsets
If the offset is 100000, and there are no gaps in the packed/unpacked array, then advance the pointer once by 100000, instead of looping and skipping 100000 times.
1 parent 75279b5 commit 602d104

File tree

1 file changed

+75
-46
lines changed

1 file changed

+75
-46
lines changed

ext/standard/array.c

Lines changed: 75 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3455,6 +3455,32 @@ PHP_FUNCTION(array_splice)
34553455
}
34563456
/* }}} */
34573457

3458+
/* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
3459+
Finds the bucket at the given valid offset */
3460+
static inline Bucket* find_bucket_at_offset(HashTable* ht, zend_long offset)
3461+
{
3462+
zend_long pos;
3463+
Bucket *bucket;
3464+
ZEND_ASSERT(offset >= 0 && offset < ht->nNumOfElements);
3465+
if (HT_IS_WITHOUT_HOLES(ht)) {
3466+
/** There's no need to iterate over the array to filter out holes if there are no holes */
3467+
/** This properly handles both packed and unpacked arrays. */
3468+
return ht->arData + offset;
3469+
}
3470+
/* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
3471+
pos = 0;
3472+
ZEND_HASH_FOREACH_BUCKET(ht, bucket) {
3473+
if (pos >= offset) {
3474+
/** This is the bucket of the array element at the requested offset */
3475+
return bucket;
3476+
}
3477+
++pos;
3478+
} ZEND_HASH_FOREACH_END();
3479+
3480+
/** Return a pointer to the end of the bucket array. */
3481+
return ht->arData + ht->nNumUsed;
3482+
}
3483+
34583484
/* {{{ proto array array_slice(array input, int offset [, int length [, bool preserve_keys]])
34593485
Returns elements specified by offset and length */
34603486
PHP_FUNCTION(array_slice)
@@ -3466,7 +3492,6 @@ PHP_FUNCTION(array_slice)
34663492
zend_bool length_is_null = 1; /* Whether an explicit length has been omitted */
34673493
zend_bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array */
34683494
uint32_t num_in; /* Number of elements in the input array */
3469-
uint32_t pos; /* Current position in the array */
34703495
zend_string *string_key;
34713496
zend_ulong num_key;
34723497

@@ -3507,51 +3532,55 @@ PHP_FUNCTION(array_slice)
35073532
/* Initialize returned array */
35083533
array_init_size(return_value, (uint32_t)length);
35093534

3510-
/* Start at the beginning and go until we hit offset */
3511-
pos = 0;
3512-
if (HT_IS_PACKED(Z_ARRVAL_P(input)) &&
3513-
(!preserve_keys ||
3514-
(offset == 0 && HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(input))))) {
3515-
zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3516-
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3517-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
3518-
pos++;
3519-
if (pos <= offset) {
3520-
continue;
3521-
}
3522-
if (pos > offset + length) {
3523-
break;
3524-
}
3525-
if (UNEXPECTED(Z_ISREF_P(entry)) &&
3526-
UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
3527-
entry = Z_REFVAL_P(entry);
3528-
}
3529-
Z_TRY_ADDREF_P(entry);
3530-
ZEND_HASH_FILL_ADD(entry);
3531-
} ZEND_HASH_FOREACH_END();
3532-
} ZEND_HASH_FILL_END();
3533-
} else {
3534-
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
3535-
pos++;
3536-
if (pos <= offset) {
3537-
continue;
3538-
}
3539-
if (pos > offset + length) {
3540-
break;
3541-
}
3542-
3543-
if (string_key) {
3544-
entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
3545-
} else {
3546-
if (preserve_keys) {
3547-
entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
3548-
} else {
3549-
entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
3550-
}
3551-
}
3552-
zval_add_ref(entry);
3553-
} ZEND_HASH_FOREACH_END();
3554-
}
3535+
// Contains modified variants of ZEND_HASH_FOREACH_VAL
3536+
{
3537+
HashTable *ht = Z_ARRVAL_P(input);
3538+
Bucket *p = find_bucket_at_offset(ht, offset);
3539+
Bucket *end = ht->arData + ht->nNumUsed;
3540+
3541+
/* Start at the beginning and go until we hit offset */
3542+
if (HT_IS_PACKED(Z_ARRVAL_P(input)) &&
3543+
(!preserve_keys ||
3544+
(offset == 0 && HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(input))))) {
3545+
3546+
zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3547+
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3548+
for (; p != end; p++) {
3549+
if (__fill_idx >= length) {
3550+
break;
3551+
}
3552+
entry = &p->val;
3553+
if (UNEXPECTED(Z_ISREF_P(entry)) &&
3554+
UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
3555+
entry = Z_REFVAL_P(entry);
3556+
}
3557+
Z_TRY_ADDREF_P(entry);
3558+
ZEND_HASH_FILL_ADD(entry);
3559+
}
3560+
} ZEND_HASH_FILL_END();
3561+
} else {
3562+
zend_long n = 0; /* Current number of elements */
3563+
for (; p != end; p++, n++) {
3564+
if (n >= length) {
3565+
break;
3566+
}
3567+
num_key = p->h;
3568+
string_key = p->key;
3569+
entry = &p->val;
3570+
3571+
if (string_key) {
3572+
entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
3573+
} else {
3574+
if (preserve_keys) {
3575+
entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
3576+
} else {
3577+
entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
3578+
}
3579+
}
3580+
zval_add_ref(entry);
3581+
}
3582+
}
3583+
}
35553584
}
35563585
/* }}} */
35573586

0 commit comments

Comments
 (0)