-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Optimize SplObjectStorage native read/write/isset dimension handlers #7695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
784e1a5
to
37383a6
Compare
The build failure is spurious; the review comment on the internal representation of flags was addressed |
37383a6
to
7e59d4a
Compare
Hello , |
They'd have the same computed size. You use zend_object_alloc when you don't know the size of the internal (C) data in the struct before the zend_object embedded in the internal data - not knowing makes zend_object_alloc a bit less efficient. This is properly accounting for the amount of memory for the number of PHP properties in the subclasses |
The count() change looks good, feel free to land separately. |
... though you probably want to update the implementation in array.c as well. |
This makes reading/writing with `$splObjectStorage[$offset]` shorthand twice as fast as it was previously, and a bit faster than offsetGet/offsetSet instead of (previously) much slower. Call destructor after overriding old SplObjectStorage entry. Previously, it was called before, which was possibly unsafe if the destructor had side effects. Add tests. Related to phpGH-7690
85ee3f6
to
13f46bf
Compare
spl_object_storage_free_hash(intern, &key); | ||
ZEND_ASSERT(key.key); | ||
bool found = zend_hash_exists(&intern->storage, key.key); | ||
zend_string_release_ex(key.key, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just in case any other reviewers wonder: the spl_object_storage_free_hash
goes away because we know that we have key.key
, so we can release the string directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any serious problems
ext/spl/spl_observer.c
Outdated
ZEND_ASSERT(Z_TYPE(element->inf) != IS_REFERENCE); | ||
ZVAL_COPY(rv, &element->inf); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why element of the storage can't be a reference. May be this correct. May be it's safer to use ZVAL_COPY_DEREF()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe it can be a reference - it's because you can't create a reference with offsetSet, offsetGet, and offsetExists in the zend_std_read_dimension code.
In many places it was using ZVAL_COPY.
Attempting to do so resulted in Notice: Indirect modification of overloaded element of SplObjectStorage has no effect in php shell code on line 1
before/after this change
Separately, I think there might be a bug in the __unserialize
implementation when a value is a reference
The handlers I've added are based on zend_std_read_dimension and zend_std_write_dimension. #7695 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
php > echo PHP_VERSION;
7.4.19-dev
php > $s = new SplObjectStorage();
php > $y = 1;
php > $x = [new stdClass(), &$y];
php > var_dump($s->__unserialize([$x, []]));
NULL
php > var_dump($s);
object(SplObjectStorage)#1 (1) {
["storage":"SplObjectStorage":private]=>
array(1) {
["000000001f5767c4000000002919f006"]=>
array(2) {
["obj"]=>
object(stdClass)#2 (0) {
}
["inf"]=>
&int(1)
}
}
}
The entry for inf
is a reference
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Figuring out what to do for the legacy Serializable::unserialize
interface is a different change - if something was serialized that referred to that index, subsequent objects being unserialized could refer back to it.
The only way I can think of to add a reference to a value of an entry of https://www.php.net/splobjectstorage is through manually constructed serializable data, so it's probably fine to convert references to non-references in __unserialize
in this case
SplObjectStorage->unserialize may be a different cause of references for malformed inputs, so continue checking In internally used methods, convert references to non-references if they're found.
620d63c
to
9f69bf3
Compare
This makes reading/writing with
$splObjectStorage[$offset]
shorthand twice asfast as it was previously, and a bit faster than offsetGet/offsetSet instead of
(previously) much slower.
There have been no attempts at adding these read/write handlers in the commit history of this file.
The shorthand syntax is also more concise than calling offsetGet/offsetSet directly.
Related to GH-7690
A synthetic benchmark that sequentially writes then sequentially reads data in a SplObjectStorage with 100000 entries:
Before work on WeakMap
With this PR, bench_spl_object_storage_short is much faster than before:
With GH-7690 and related work on WeakMap, create+read for WeakMap is faster than before
It's safer to call
zval_ptr_dtor
on a copy of the zval (ZVAL_COPY_VALUE) AFTER updating the SplObjectStorage entry in case the entry is moved or updated by the destructor (e.g.__destruct
deletes the entry while it's being updated, causing that pointer to be invalidated) - this is a bug unrelated to this change that is unlikely in typical code.