Reduce memory usage of SplObjectStorage

Track an 8-byte pointer as the key instead of 16-byte zval.
(4 byte on 32-bit builds)

The memory used for a set of 1 million objects decreased from
114 MiB to 106 MiB.

Use the new `Z_PARAM_*` APIs to parse the `zend_object*` pointer directly

Closes GH-6566
This commit is contained in:
Tyson Andre 2018-07-18 18:03:19 -07:00
parent ce2e1076d4
commit e622e868c7
3 changed files with 82 additions and 72 deletions

View File

@ -628,11 +628,11 @@ PHP_FUNCTION(spl_autoload_functions)
/* {{{ Return hash id for given object */
PHP_FUNCTION(spl_object_hash)
{
zval *obj;
zend_object *obj;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_OBJ(obj)
ZEND_PARSE_PARAMETERS_END();
RETURN_NEW_STR(php_spl_object_hash(obj));
}
@ -651,7 +651,7 @@ PHP_FUNCTION(spl_object_id)
}
/* }}} */
PHPAPI zend_string *php_spl_object_hash(zval *obj) /* {{{*/
PHPAPI zend_string *php_spl_object_hash(zend_object *obj) /* {{{*/
{
intptr_t hash_handle, hash_handlers;
@ -661,7 +661,7 @@ PHPAPI zend_string *php_spl_object_hash(zval *obj) /* {{{*/
SPL_G(hash_mask_init) = 1;
}
hash_handle = SPL_G(hash_mask_handle)^(intptr_t)Z_OBJ_HANDLE_P(obj);
hash_handle = SPL_G(hash_mask_handle)^(intptr_t)obj->handle;
hash_handlers = SPL_G(hash_mask_handlers);
return strpprintf(32, "%016zx%016zx", hash_handle, hash_handlers);

View File

@ -62,6 +62,6 @@ ZEND_END_MODULE_GLOBALS(spl)
ZEND_EXTERN_MODULE_GLOBALS(spl)
#define SPL_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(spl, v)
PHPAPI zend_string *php_spl_object_hash(zval *obj);
PHPAPI zend_string *php_spl_object_hash(zend_object *obj);
#endif /* PHP_SPL_H */

View File

@ -55,7 +55,7 @@ typedef struct _spl_SplObjectStorage { /* {{{ */
/* {{{ storage is an assoc array of [zend_object*]=>[zval *obj, zval *inf] */
typedef struct _spl_SplObjectStorageElement {
zval obj;
zend_object *obj;
zval inf;
} spl_SplObjectStorageElement; /* }}} */
@ -75,11 +75,13 @@ void spl_SplObjectStorage_free_storage(zend_object *object) /* {{{ */
zend_hash_destroy(&intern->storage);
} /* }}} */
static int spl_object_storage_get_hash(zend_hash_key *key, spl_SplObjectStorage *intern, zval *obj) {
static int spl_object_storage_get_hash(zend_hash_key *key, spl_SplObjectStorage *intern, zend_object *obj) {
if (intern->fptr_get_hash) {
zval param;
zval rv;
ZVAL_OBJ(&param, obj);
zend_call_method_with_1_params(
&intern->std, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, obj);
&intern->std, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, &param);
if (!Z_ISUNDEF(rv)) {
if (Z_TYPE(rv) == IS_STRING) {
key->key = Z_STR(rv);
@ -95,7 +97,7 @@ static int spl_object_storage_get_hash(zend_hash_key *key, spl_SplObjectStorage
}
} else {
key->key = NULL;
key->h = Z_OBJ_HANDLE_P(obj);
key->h = obj->handle;
return SUCCESS;
}
}
@ -109,7 +111,7 @@ static void spl_object_storage_free_hash(spl_SplObjectStorage *intern, zend_hash
static void spl_object_storage_dtor(zval *element) /* {{{ */
{
spl_SplObjectStorageElement *el = Z_PTR_P(element);
zval_ptr_dtor(&el->obj);
zend_object_release(el->obj);
zval_ptr_dtor(&el->inf);
efree(el);
} /* }}} */
@ -123,7 +125,7 @@ static spl_SplObjectStorageElement* spl_object_storage_get(spl_SplObjectStorage
}
} /* }}} */
spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *intern, zval *obj, zval *inf) /* {{{ */
spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *intern, zend_object *obj, zval *inf) /* {{{ */
{
spl_SplObjectStorageElement *pelement, element;
zend_hash_key key;
@ -144,7 +146,8 @@ spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *int
return pelement;
}
ZVAL_COPY(&element.obj, obj);
element.obj = obj;
GC_ADDREF(obj);
if (inf) {
ZVAL_COPY(&element.inf, inf);
} else {
@ -159,7 +162,7 @@ spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *int
return pelement;
} /* }}} */
static int spl_object_storage_detach(spl_SplObjectStorage *intern, zval *obj) /* {{{ */
static int spl_object_storage_detach(spl_SplObjectStorage *intern, zend_object *obj) /* {{{ */
{
int ret = FAILURE;
zend_hash_key key;
@ -180,7 +183,7 @@ void spl_object_storage_addall(spl_SplObjectStorage *intern, spl_SplObjectStorag
spl_SplObjectStorageElement *element;
ZEND_HASH_FOREACH_PTR(&other->storage, element) {
spl_object_storage_attach(intern, &element->obj, &element->inf);
spl_object_storage_attach(intern, element->obj, &element->inf);
} ZEND_HASH_FOREACH_END();
intern->index = 0;
@ -256,12 +259,14 @@ static inline HashTable* spl_object_storage_debug_info(zend_object *obj) /* {{{
array_init(&storage);
ZEND_HASH_FOREACH_PTR(&intern->storage, element) {
md5str = php_spl_object_hash(&element->obj);
md5str = php_spl_object_hash(element->obj);
array_init(&tmp);
/* Incrementing the refcount of obj and inf would confuse the garbage collector.
* Prefer to null the destructor */
Z_ARRVAL_P(&tmp)->pDestructor = NULL;
add_assoc_zval_ex(&tmp, "obj", sizeof("obj") - 1, &element->obj);
zval obj;
ZVAL_OBJ(&obj, element->obj);
add_assoc_zval_ex(&tmp, "obj", sizeof("obj") - 1, &obj);
add_assoc_zval_ex(&tmp, "inf", sizeof("inf") - 1, &element->inf);
zend_hash_update(Z_ARRVAL(storage), md5str, &tmp);
zend_string_release_ex(md5str, 0);
@ -283,7 +288,7 @@ static HashTable *spl_object_storage_get_gc(zend_object *obj, zval **table, int
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
ZEND_HASH_FOREACH_PTR(&intern->storage, element) {
zend_get_gc_buffer_add_zval(gc_buffer, &element->obj);
zend_get_gc_buffer_add_obj(gc_buffer, element->obj);
zend_get_gc_buffer_add_zval(gc_buffer, &element->inf);
} ZEND_HASH_FOREACH_END();
@ -326,7 +331,7 @@ static zend_object *spl_SplObjectStorage_new(zend_class_entry *class_type)
}
/* }}} */
int spl_object_storage_contains(spl_SplObjectStorage *intern, zval *obj) /* {{{ */
int spl_object_storage_contains(spl_SplObjectStorage *intern, zend_object *obj) /* {{{ */
{
int found;
zend_hash_key key;
@ -346,25 +351,28 @@ int spl_object_storage_contains(spl_SplObjectStorage *intern, zval *obj) /* {{{
/* {{{ Attaches an object to the storage if not yet contained */
PHP_METHOD(SplObjectStorage, attach)
{
zval *obj, *inf = NULL;
zend_object *obj;
zval *inf = NULL;
spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|z!", &obj, &inf) == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_OBJ(obj)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL(inf)
ZEND_PARSE_PARAMETERS_END();
spl_object_storage_attach(intern, obj, inf);
} /* }}} */
/* {{{ Detaches an object from the storage */
PHP_METHOD(SplObjectStorage, detach)
{
zval *obj;
zend_object *obj;
spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_OBJ(obj)
ZEND_PARSE_PARAMETERS_END();
spl_object_storage_detach(intern, obj);
zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
@ -374,11 +382,11 @@ PHP_METHOD(SplObjectStorage, detach)
/* {{{ Returns the hash of an object */
PHP_METHOD(SplObjectStorage, getHash)
{
zval *obj;
zend_object *obj;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_OBJ(obj)
ZEND_PARSE_PARAMETERS_END();
RETURN_NEW_STR(php_spl_object_hash(obj));
@ -387,14 +395,14 @@ PHP_METHOD(SplObjectStorage, getHash)
/* {{{ Returns associated information for a stored object */
PHP_METHOD(SplObjectStorage, offsetGet)
{
zval *obj;
zend_object *obj;
spl_SplObjectStorageElement *element;
spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
zend_hash_key key;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_OBJ(obj)
ZEND_PARSE_PARAMETERS_END();
if (spl_object_storage_get_hash(&key, intern, obj) == FAILURE) {
return;
@ -446,7 +454,7 @@ PHP_METHOD(SplObjectStorage, removeAll)
zend_hash_internal_pointer_reset(&other->storage);
while ((element = zend_hash_get_current_data_ptr(&other->storage)) != NULL) {
if (spl_object_storage_detach(intern, &element->obj) == FAILURE) {
if (spl_object_storage_detach(intern, element->obj) == FAILURE) {
zend_hash_move_forward(&other->storage);
}
}
@ -472,8 +480,8 @@ PHP_METHOD(SplObjectStorage, removeAllExcept)
other = Z_SPLOBJSTORAGE_P(obj);
ZEND_HASH_FOREACH_PTR(&intern->storage, element) {
if (!spl_object_storage_contains(other, &element->obj)) {
spl_object_storage_detach(intern, &element->obj);
if (!spl_object_storage_contains(other, element->obj)) {
spl_object_storage_detach(intern, element->obj);
}
} ZEND_HASH_FOREACH_END();
@ -487,12 +495,12 @@ PHP_METHOD(SplObjectStorage, removeAllExcept)
/* {{{ Determine whether an object is contained in the storage */
PHP_METHOD(SplObjectStorage, contains)
{
zval *obj;
zend_object *obj;
spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_OBJ(obj)
ZEND_PARSE_PARAMETERS_END();
RETURN_BOOL(spl_object_storage_contains(intern, obj));
} /* }}} */
@ -563,7 +571,7 @@ PHP_METHOD(SplObjectStorage, current)
if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) {
return;
}
ZVAL_COPY(return_value, &element->obj);
ZVAL_OBJ_COPY(return_value, element->obj);
} /* }}} */
/* {{{ Returns associated information to current element */
@ -638,12 +646,14 @@ PHP_METHOD(SplObjectStorage, serialize)
zend_hash_internal_pointer_reset_ex(&intern->storage, &pos);
while (zend_hash_has_more_elements_ex(&intern->storage, &pos) == SUCCESS) {
zval obj;
if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &pos)) == NULL) {
smart_str_free(&buf);
PHP_VAR_SERIALIZE_DESTROY(var_hash);
RETURN_NULL();
}
php_var_serialize(&buf, &element->obj, &var_hash);
ZVAL_OBJ(&obj, element->obj);
php_var_serialize(&buf, &obj, &var_hash);
smart_str_appendc(&buf, ',');
php_var_serialize(&buf, &element->inf, &var_hash);
smart_str_appendc(&buf, ';');
@ -711,6 +721,7 @@ PHP_METHOD(SplObjectStorage, unserialize)
while (count-- > 0) {
spl_SplObjectStorageElement *pelement;
zend_hash_key key;
zval obj;
if (*p != ';') {
goto outexcept;
@ -738,7 +749,7 @@ PHP_METHOD(SplObjectStorage, unserialize)
goto outexcept;
}
if (spl_object_storage_get_hash(&key, intern, &entry) == FAILURE) {
if (spl_object_storage_get_hash(&key, intern, Z_OBJ(entry)) == FAILURE) {
zval_ptr_dtor(&entry);
zval_ptr_dtor(&inf);
goto outexcept;
@ -749,12 +760,12 @@ PHP_METHOD(SplObjectStorage, unserialize)
if (!Z_ISUNDEF(pelement->inf)) {
var_push_dtor(&var_hash, &pelement->inf);
}
if (!Z_ISUNDEF(pelement->obj)) {
var_push_dtor(&var_hash, &pelement->obj);
}
ZVAL_OBJ(&obj, pelement->obj);
var_push_dtor(&var_hash, &obj);
}
element = spl_object_storage_attach(intern, &entry, Z_ISUNDEF(inf)?NULL:&inf);
var_replace(&var_hash, &entry, &element->obj);
element = spl_object_storage_attach(intern, Z_OBJ(entry), Z_ISUNDEF(inf)?NULL:&inf);
ZVAL_OBJ(&obj, element->obj);
var_replace(&var_hash, &entry, &obj);
var_replace(&var_hash, &inf, &element->inf);
zval_ptr_dtor(&entry);
ZVAL_UNDEF(&entry);
@ -807,8 +818,9 @@ PHP_METHOD(SplObjectStorage, __serialize)
/* storage */
array_init_size(&tmp, 2 * zend_hash_num_elements(&intern->storage));
ZEND_HASH_FOREACH_PTR(&intern->storage, elem) {
Z_TRY_ADDREF(elem->obj);
zend_hash_next_index_insert(Z_ARRVAL(tmp), &elem->obj);
zval obj;
ZVAL_OBJ_COPY(&obj, elem->obj);
zend_hash_next_index_insert(Z_ARRVAL(tmp), &obj);
Z_TRY_ADDREF(elem->inf);
zend_hash_next_index_insert(Z_ARRVAL(tmp), &elem->inf);
} ZEND_HASH_FOREACH_END();
@ -853,7 +865,7 @@ PHP_METHOD(SplObjectStorage, __unserialize)
RETURN_THROWS();
}
spl_object_storage_attach(intern, key, val);
spl_object_storage_attach(intern, Z_OBJ_P(key), val);
key = NULL;
} else {
key = val;
@ -927,14 +939,14 @@ PHP_METHOD(MultipleIterator, setFlags)
PHP_METHOD(MultipleIterator, attachIterator)
{
spl_SplObjectStorage *intern;
zval *iterator = NULL;
zend_object *iterator = NULL;
zval zinfo;
zend_string *info_str;
zend_long info_long;
zend_bool info_is_null = 1;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_OBJECT_OF_CLASS(iterator, zend_ce_iterator)
Z_PARAM_OBJ_OF_CLASS(iterator, zend_ce_iterator)
Z_PARAM_OPTIONAL
Z_PARAM_STR_OR_LONG_OR_NULL(info_str, info_long, info_is_null)
ZEND_PARSE_PARAMETERS_END();
@ -975,7 +987,7 @@ PHP_METHOD(MultipleIterator, detachIterator)
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &iterator, zend_ce_iterator) == FAILURE) {
RETURN_THROWS();
}
spl_object_storage_detach(intern, iterator);
spl_object_storage_detach(intern, Z_OBJ_P(iterator));
zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
intern->index = 0;
@ -990,7 +1002,7 @@ PHP_METHOD(MultipleIterator, containsIterator)
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &iterator, zend_ce_iterator) == FAILURE) {
RETURN_THROWS();
}
RETURN_BOOL(spl_object_storage_contains(intern, iterator));
RETURN_BOOL(spl_object_storage_contains(intern, Z_OBJ_P(iterator)));
} /* }}} */
PHP_METHOD(MultipleIterator, countIterators)
@ -1009,7 +1021,6 @@ PHP_METHOD(MultipleIterator, rewind)
{
spl_SplObjectStorage *intern;
spl_SplObjectStorageElement *element;
zval *it;
intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
@ -1019,8 +1030,8 @@ PHP_METHOD(MultipleIterator, rewind)
zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
it = &element->obj;
zend_call_method_with_0_params(Z_OBJ_P(it), Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_rewind, "rewind", NULL);
zend_object *it = element->obj;
zend_call_method_with_0_params(it, it->ce, &it->ce->iterator_funcs_ptr->zf_rewind, "rewind", NULL);
zend_hash_move_forward_ex(&intern->storage, &intern->pos);
}
}
@ -1031,7 +1042,6 @@ PHP_METHOD(MultipleIterator, next)
{
spl_SplObjectStorage *intern;
spl_SplObjectStorageElement *element;
zval *it;
intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
@ -1041,8 +1051,8 @@ PHP_METHOD(MultipleIterator, next)
zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
it = &element->obj;
zend_call_method_with_0_params(Z_OBJ_P(it), Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_next, "next", NULL);
zend_object *it = element->obj;
zend_call_method_with_0_params(it, it->ce, &it->ce->iterator_funcs_ptr->zf_next, "next", NULL);
zend_hash_move_forward_ex(&intern->storage, &intern->pos);
}
}
@ -1053,7 +1063,7 @@ PHP_METHOD(MultipleIterator, valid)
{
spl_SplObjectStorage *intern;
spl_SplObjectStorageElement *element;
zval *it, retval;
zval retval;
zend_long expect, valid;
intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
@ -1070,8 +1080,8 @@ PHP_METHOD(MultipleIterator, valid)
zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
it = &element->obj;
zend_call_method_with_0_params(Z_OBJ_P(it), Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_valid, "valid", &retval);
zend_object *it = element->obj;
zend_call_method_with_0_params(it, it->ce, &it->ce->iterator_funcs_ptr->zf_valid, "valid", &retval);
if (!Z_ISUNDEF(retval)) {
valid = (Z_TYPE(retval) == IS_TRUE);
@ -1094,7 +1104,7 @@ PHP_METHOD(MultipleIterator, valid)
static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_type, zval *return_value) /* {{{ */
{
spl_SplObjectStorageElement *element;
zval *it, retval;
zval retval;
int valid = 1, num_elements;
num_elements = zend_hash_num_elements(&intern->storage);
@ -1106,8 +1116,8 @@ static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_
zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
it = &element->obj;
zend_call_method_with_0_params(Z_OBJ_P(it), Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_valid, "valid", &retval);
zend_object *it = element->obj;
zend_call_method_with_0_params(it, it->ce, &it->ce->iterator_funcs_ptr->zf_valid, "valid", &retval);
if (!Z_ISUNDEF(retval)) {
valid = Z_TYPE(retval) == IS_TRUE;
@ -1118,9 +1128,9 @@ static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_
if (valid) {
if (SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT == get_type) {
zend_call_method_with_0_params(Z_OBJ_P(it), Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_current, "current", &retval);
zend_call_method_with_0_params(it, it->ce, &it->ce->iterator_funcs_ptr->zf_current, "current", &retval);
} else {
zend_call_method_with_0_params(Z_OBJ_P(it), Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_key, "key", &retval);
zend_call_method_with_0_params(it, it->ce, &it->ce->iterator_funcs_ptr->zf_key, "key", &retval);
}
if (Z_ISUNDEF(retval)) {
zend_throw_exception(spl_ce_RuntimeException, "Failed to call sub iterator method", 0);