Skip to content

Commit a25886d

Browse files
committed
Optimize SplFixedArray when magic methods aren't overridden
This decreases the memory usage of SplFixedArrays by 32 bytes per object on 64-bit systems (use 1 null pointer instead of 5 null pointers) If allocating a lot of arrays of size 1, memory usage was 19.44MiB before this change, and 16.24MiB after the change. Existing tests continue to pass. Subclassing SplFixedArray is already inefficient and rarely done. It checks for the existence of 5 methods every time a subclass is instantiated. (and has to switch back from C to the php vm to call those methods) Closes GH-6552
1 parent 7eed6c0 commit a25886d

File tree

1 file changed

+45
-29
lines changed

1 file changed

+45
-29
lines changed

ext/spl/spl_fixedarray.c

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,22 @@ ZEND_GET_MODULE(spl_fixedarray)
4141

4242
typedef struct _spl_fixedarray {
4343
zend_long size;
44+
/* It is possible to resize this, so this can't be combined with the object */
4445
zval *elements;
4546
} spl_fixedarray;
4647

47-
typedef struct _spl_fixedarray_object {
48-
spl_fixedarray array;
48+
typedef struct _spl_fixedarray_methods {
4949
zend_function *fptr_offset_get;
5050
zend_function *fptr_offset_set;
5151
zend_function *fptr_offset_has;
5252
zend_function *fptr_offset_del;
5353
zend_function *fptr_count;
54-
zend_object std;
54+
} spl_fixedarray_methods;
55+
56+
typedef struct _spl_fixedarray_object {
57+
spl_fixedarray array;
58+
spl_fixedarray_methods *methods;
59+
zend_object std;
5560
} spl_fixedarray_object;
5661

5762
typedef struct _spl_fixedarray_it {
@@ -222,6 +227,9 @@ static void spl_fixedarray_object_free_storage(zend_object *object)
222227
spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
223228
spl_fixedarray_dtor(&intern->array);
224229
zend_object_std_dtor(&intern->std);
230+
if (intern->methods) {
231+
efree(intern->methods);
232+
}
225233
}
226234

227235
static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, zend_object *orig, bool clone_orig)
@@ -252,26 +260,34 @@ static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, z
252260

253261
ZEND_ASSERT(parent);
254262

255-
if (inherited) {
256-
intern->fptr_offset_get = zend_hash_str_find_ptr(&class_type->function_table, "offsetget", sizeof("offsetget") - 1);
257-
if (intern->fptr_offset_get->common.scope == parent) {
258-
intern->fptr_offset_get = NULL;
263+
if (UNEXPECTED(inherited)) {
264+
spl_fixedarray_methods methods;
265+
methods.fptr_offset_get = zend_hash_str_find_ptr(&class_type->function_table, "offsetget", sizeof("offsetget") - 1);
266+
if (methods.fptr_offset_get->common.scope == parent) {
267+
methods.fptr_offset_get = NULL;
268+
}
269+
methods.fptr_offset_set = zend_hash_str_find_ptr(&class_type->function_table, "offsetset", sizeof("offsetset") - 1);
270+
if (methods.fptr_offset_set->common.scope == parent) {
271+
methods.fptr_offset_set = NULL;
259272
}
260-
intern->fptr_offset_set = zend_hash_str_find_ptr(&class_type->function_table, "offsetset", sizeof("offsetset") - 1);
261-
if (intern->fptr_offset_set->common.scope == parent) {
262-
intern->fptr_offset_set = NULL;
273+
methods.fptr_offset_has = zend_hash_str_find_ptr(&class_type->function_table, "offsetexists", sizeof("offsetexists") - 1);
274+
if (methods.fptr_offset_has->common.scope == parent) {
275+
methods.fptr_offset_has = NULL;
263276
}
264-
intern->fptr_offset_has = zend_hash_str_find_ptr(&class_type->function_table, "offsetexists", sizeof("offsetexists") - 1);
265-
if (intern->fptr_offset_has->common.scope == parent) {
266-
intern->fptr_offset_has = NULL;
277+
methods.fptr_offset_del = zend_hash_str_find_ptr(&class_type->function_table, "offsetunset", sizeof("offsetunset") - 1);
278+
if (methods.fptr_offset_del->common.scope == parent) {
279+
methods.fptr_offset_del = NULL;
267280
}
268-
intern->fptr_offset_del = zend_hash_str_find_ptr(&class_type->function_table, "offsetunset", sizeof("offsetunset") - 1);
269-
if (intern->fptr_offset_del->common.scope == parent) {
270-
intern->fptr_offset_del = NULL;
281+
methods.fptr_count = zend_hash_str_find_ptr(&class_type->function_table, "count", sizeof("count") - 1);
282+
if (methods.fptr_count->common.scope == parent) {
283+
methods.fptr_count = NULL;
271284
}
272-
intern->fptr_count = zend_hash_str_find_ptr(&class_type->function_table, "count", sizeof("count") - 1);
273-
if (intern->fptr_count->common.scope == parent) {
274-
intern->fptr_count = NULL;
285+
/* Assume that most of the time in performance-sensitive code, SplFixedArray won't be subclassed with overrides for these ArrayAccess methods. */
286+
/* Save 32 bytes per object on 64-bit systems by combining the 5 null pointers into 1 null pointer */
287+
/* (This is already looking up 5 functions when any subclass of SplFixedArray is instantiated, which is inefficient) */
288+
if (methods.fptr_offset_get || methods.fptr_offset_set || methods.fptr_offset_del || methods.fptr_offset_has || methods.fptr_count) {
289+
intern->methods = emalloc(sizeof(spl_fixedarray_methods));
290+
*intern->methods = methods;
275291
}
276292
}
277293

@@ -329,15 +345,15 @@ static zval *spl_fixedarray_object_read_dimension(zend_object *object, zval *off
329345
return &EG(uninitialized_zval);
330346
}
331347

332-
if (intern->fptr_offset_get) {
348+
if (UNEXPECTED(intern->methods && intern->methods->fptr_offset_get)) {
333349
zval tmp;
334350
if (!offset) {
335351
ZVAL_NULL(&tmp);
336352
offset = &tmp;
337353
} else {
338354
SEPARATE_ARG_IF_REF(offset);
339355
}
340-
zend_call_method_with_1_params(object, intern->std.ce, &intern->fptr_offset_get, "offsetGet", rv, offset);
356+
zend_call_method_with_1_params(object, intern->std.ce, &intern->methods->fptr_offset_get, "offsetGet", rv, offset);
341357
zval_ptr_dtor(offset);
342358
if (!Z_ISUNDEF_P(rv)) {
343359
return rv;
@@ -380,15 +396,15 @@ static void spl_fixedarray_object_write_dimension(zend_object *object, zval *off
380396

381397
intern = spl_fixed_array_from_obj(object);
382398

383-
if (intern->fptr_offset_set) {
399+
if (UNEXPECTED(intern->methods && intern->methods->fptr_offset_set)) {
384400
if (!offset) {
385401
ZVAL_NULL(&tmp);
386402
offset = &tmp;
387403
} else {
388404
SEPARATE_ARG_IF_REF(offset);
389405
}
390406
SEPARATE_ARG_IF_REF(value);
391-
zend_call_method_with_2_params(object, intern->std.ce, &intern->fptr_offset_set, "offsetSet", NULL, offset, value);
407+
zend_call_method_with_2_params(object, intern->std.ce, &intern->methods->fptr_offset_set, "offsetSet", NULL, offset, value);
392408
zval_ptr_dtor(value);
393409
zval_ptr_dtor(offset);
394410
return;
@@ -422,9 +438,9 @@ static void spl_fixedarray_object_unset_dimension(zend_object *object, zval *off
422438

423439
intern = spl_fixed_array_from_obj(object);
424440

425-
if (intern->fptr_offset_del) {
441+
if (UNEXPECTED(intern->methods && intern->methods->fptr_offset_del)) {
426442
SEPARATE_ARG_IF_REF(offset);
427-
zend_call_method_with_1_params(object, intern->std.ce, &intern->fptr_offset_del, "offsetUnset", NULL, offset);
443+
zend_call_method_with_1_params(object, intern->std.ce, &intern->methods->fptr_offset_del, "offsetUnset", NULL, offset);
428444
zval_ptr_dtor(offset);
429445
return;
430446
}
@@ -462,12 +478,12 @@ static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset
462478

463479
intern = spl_fixed_array_from_obj(object);
464480

465-
if (intern->fptr_offset_has) {
481+
if (UNEXPECTED(intern->methods && intern->methods->fptr_offset_has)) {
466482
zval rv;
467483
zend_bool result;
468484

469485
SEPARATE_ARG_IF_REF(offset);
470-
zend_call_method_with_1_params(object, intern->std.ce, &intern->fptr_offset_has, "offsetExists", &rv, offset);
486+
zend_call_method_with_1_params(object, intern->std.ce, &intern->methods->fptr_offset_has, "offsetExists", &rv, offset);
471487
zval_ptr_dtor(offset);
472488
result = zend_is_true(&rv);
473489
zval_ptr_dtor(&rv);
@@ -482,9 +498,9 @@ static int spl_fixedarray_object_count_elements(zend_object *object, zend_long *
482498
spl_fixedarray_object *intern;
483499

484500
intern = spl_fixed_array_from_obj(object);
485-
if (intern->fptr_count) {
501+
if (UNEXPECTED(intern->methods && intern->methods->fptr_count)) {
486502
zval rv;
487-
zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv);
503+
zend_call_method_with_0_params(object, intern->std.ce, &intern->methods->fptr_count, "count", &rv);
488504
if (!Z_ISUNDEF(rv)) {
489505
*count = zval_get_long(&rv);
490506
zval_ptr_dtor(&rv);

0 commit comments

Comments
 (0)