Skip to content
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

[RFC] Extract PropertyAccessor readProperty and writeProperty methods to allow faster ObjectNormalizer #28926

Closed
fbourigault opened this issue Oct 19, 2018 · 4 comments
Labels
PropertyAccess RFC RFC = Request For Comments (proposals about features that you want to be discussed)

Comments

@fbourigault
Copy link
Contributor

fbourigault commented Oct 19, 2018

Background

While working on improving the ObjectNormalizer, I spotted that most of the execution time is spent in PropertyAccessor::getValue (https://blackfire.io/profiles/e52b7b58-060d-448a-bcc4-015df290812a/graph).

After looking at the PropertyAccessor::getValue, I first had the feeling that calling PropertyAccessor::readProperiesUntil to get an attribute value was overkill.
After adding a PropertyAccessor::hack to expose PropertyAccessor::readProperty, I got a significant improvement of ObjectNormalizer perfs (https://blackfire.io/profiles/compare/6e4aa9d9-c5dd-4243-89c0-c19f431b6bc9/graph).

Proposal

The idea is to extract PropertyAccessor::readProperty to allow it's usage by the ObjectNormalizer and bypass all the array logic executed when calling PropertyAccessor::getValue.

To achieve this, I would first replace the $zval and $result arrays by an internal Zval class. This will allow type safety.

/**
 * @internal
 */
class Zval
{
    public $value;
    public $ref;
    public $isRefChained;
}

Then I would add two internal classes (ObjectPropertyReader and ObjectPropertyWriter) that will use Zval objects as input and output.

/**
 * @internal
 */
class ObjectPropertyReader
{
    /**
     * Reads the a property from an object.
     *
     * @param Zval   $zval     The array containing the object to read from
     * @param string $property The property to read
     *
     * @return Zval The array containing the value of the property
     *
     * @throws NoSuchPropertyException if the property does not exist or is not public
     */
    public function readProperty(Zval $zval, string $property): Zval
    {
        // current PropertyAccessor::readProperty code modified to use Zval instead of array.
    }
}
/**
 * @internal
 */
class ObjectPropertyWriter
{
    /**
     * Sets the value of a property in the given object.
     *
     * @param Zval   $zval     The array containing the object to write to
     * @param string $property The property to write
     * @param mixed  $value    The value to write
     *
     * @throws NoSuchPropertyException if the property does not exist or is not public
     */
    public function writeProperty(Zval $zval, string $property, $value): void
    {
        // current PropertyAccessor::writeProperty code modified to use Zval instead of array.
    }
}

Finally I would add an ObjectPropertyAccessor class that will expose ObjectPropertyReader::readProperty and ObjectPropertyWriter::writeProperty to users and hide Zval stuff.

class ObjectPropertyAccessor implements ObjectPropertyAccessorInterface
{
    private $reader;
    private $writer;

    public function __construct(ObjectPropertyReader $reader, ObjectPropertyWriter $writer)
    {
        $this->reader = $reader;
        $this->writer = $writer;
    }

    public function readProperty($object, string $property)
    {
        $zval = new Zval();  
        $zval->value = $object;  
  
        return $this->reader->readProperty($zval, $property)->value;
    }

    public function writeProperty($object, string $property, $value)
    {
        $zval = new Zval();  
        $zval->value = $object;  
        $zval->ref = &$object;
        $this->writer->writeProperty($zval, $property, $value);
    }
}

The ObjectNormalizer will use this new ObjectPropertyAccessor instead of the PropertyAccessor to get the perf improvement.

Going further

Deprecating PropertyNormalizer and GetSetMethodNormalizer

Having an interface to access object properties will help us to deprecate PropertyNormalizer and GetSetMethodNormalizer by extracting code used to access properties into implementations of ObjectPropertyAccessorInterface.

Extracting getReadAccessInfo and getWriteAccessInfo from ObjectPropertyAccessor

To go further in the performance improvement, we could extract the getReadAccessInfo and getWriteAccessInfo logic to an other class. Then we will be able to ship a decorator which leverage the PHP7 static array cache.

Having an interface for getReadAccessInfo and getWriteAccessInfo open the path to solve issues like #9336 by introducing metadata which could also be cached to keep the performance!

WDYT ?

cc @dunglas @nicolas-grekas, @Tobion and @bendavies.

@fbourigault fbourigault changed the title [PropertyAccessor] Extract readProperty and writeProperty to allow faster ObjectNormalizer [RFC] Extract PropertyAccessor readProperty and writeProperty methods to allow faster ObjectNormalizer Oct 19, 2018
@dunglas
Copy link
Member

dunglas commented Oct 23, 2018

I 100% agree with this agenda.

@xabbuh xabbuh added PropertyAccess RFC RFC = Request For Comments (proposals about features that you want to be discussed) labels Oct 24, 2018
@fbourigault
Copy link
Contributor Author

@nicolas-grekas any thoughts on this?

@bendavies
Copy link
Contributor

probably related:
#22051

@fbourigault
Copy link
Contributor Author

Closing as #29999 is a better solution.

fabpot added a commit that referenced this issue Jan 30, 2019
…(xabbuh)

This PR was merged into the 4.3-dev branch.

Discussion
----------

[PropertyAccess] speed up accessing object properties

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #28926, #29405
| License       | MIT
| Doc PR        |

I propose to improve the performance of the `ObjectNormalizer` by not adding a new interface to the PropertyAccess component, but by adding some shortcut for cases where we know that we do not need to perform all checks. The added benefit is that this will not only speed up the `ObjectNormalizer` class, but will be available for every consumer of the `PropertyAccessor` without having to adapt to a new API.

TODO:

- [ ] confirm that these changes indeed introduce the same benefit as #29405 doing an actual benchmark

Commits
-------

ef7876e speed up accessing object properties
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PropertyAccess RFC RFC = Request For Comments (proposals about features that you want to be discussed)
Projects
None yet
Development

No branches or pull requests

4 participants