Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Cache] Optimize ArrayAdapter by using a generator #17435

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

Closed
wants to merge 1 commit into from
Closed

[Cache] Optimize ArrayAdapter by using a generator #17435

wants to merge 1 commit into from

Conversation

aurimasniekis
Copy link
Contributor

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes/no Depends on
License MIT

Added to generators to where possible to optimize code and save memory especially on ArrayAdapter.

Code works and test pass with fixed cache/integration-tests (Currently does not support \Traversable)

@jvasseur
Copy link
Contributor

Not sure if this is worth it, the values are already loader into memory by the doFetch method so this doesn't save any memory.

I'm not an expert but this could reduce performances by replacing arrays with generators that are a more complex structure.

@aurimasniekis
Copy link
Contributor Author

For ArrayAdaptor is definitely must thing. Because if I have like million keys array, and I am asking for half of it, I will be then having 1,5 the same array in memory. Afterwards, if I just need two values from it will be not efficient.

@jvasseur
Copy link
Contributor

The values won't be duplicated because of copy on write.

@aurimasniekis
Copy link
Contributor Author

https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php#L67 adds value from $this->values to $items so it's basically duplicates. I could do benchmark if you don't believe :)

@stof
Copy link
Member

stof commented Jan 19, 2016

This is wrong: it is subject to race condition if you change the cache inside a loop on the retrieved items (as your generator does not actually read the cache when asking to read it but when iterating)

@aurimasniekis
Copy link
Contributor Author

AbstractAdapter yes would need changes to make fetching on iteration, but on ArrayAdapter it's visible improvement in performance

With generators:

Before Memory: 116.02MB
Time: 0.35666489601135
After Memory: 116.02MB

With arrays

Before Memory: 116.02MB
Time: 0.43092918395996
After Memory: 124.27MB

Benchmark code

@stof
Copy link
Member

stof commented Jan 19, 2016

@GCDS fetching on iteration is precisely what is broken.

@nicolas-grekas
Copy link
Member

I started a similar patch locally :) My own bench show also that generator are faster for bigger array, and a bit slower for short array, but the added flexibility of generator is really worth it.

@aurimasniekis
Copy link
Contributor Author

@nicolas-grekas so bad that STANDARD does not allow it :(

@nicolas-grekas
Copy link
Member

Does not allow what? PSR-6 allows returning a Traversable, so yes, generators are allowed to me.

@aurimasniekis
Copy link
Contributor Author

@stof
Copy link
Member

stof commented Jan 19, 2016

@GCDS the race condition issue is related to the implementation. Changing the standard would not change the fact that delaying the cache fetching means that the cache can change in the meantime. This is not related to the standard at all, but to common sense.

@aurimasniekis
Copy link
Contributor Author

@stof I am talking only about ArrayAdaptor now as others need modification for race condition issue

@stof
Copy link
Member

stof commented Jan 19, 2016

@GCDS the issue in your code is that you delay the validation of keys. AFAIK, this is not a hard requirement for a generator-based implementation. It is just an issue in your naive implementation of generator-based cache. And delaying the validation later than the getItems methods simply means it is impossible to catch it. So a bad standard would be a standard allowing it IMO.

@aurimasniekis
Copy link
Contributor Author

@stof Yes, I understand the problem here... I trying different aproaches to keep validation happening before generator somehow but I am not able to think a way of doing it...

@aurimasniekis
Copy link
Contributor Author

Removed unnecessary code and managed to get verification of keys before return generator. Updating cache/integration-tests pull request also

@@ -177,4 +172,16 @@ private function validateKey($key)

return $key;
}

private function createGenerator(array $keys)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure splitting this to a private provides any benefit, do you?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By doing this, I am getting key validation before returning a generator. Otherwise, you will not get invalid key exception until you call the first iteration

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolas-grekas this is about making some logic running before the creation of the generator. If you inline the method, all the code is part of the generator and so runs only when you start iterating. Here, the code before the private method is not part of the generator logic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, of course!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible to do inline function but I thought from code style approach it's not a good way

/**
     * {@inheritdoc}
     */
    public function getItems(array $keys = array())
    {
        foreach ($keys as $key) {
            $this->validateKey($key);
        }

        return (function (array $keys) {
            $f = $this->createCacheItem;
            $now = time();

            foreach ($keys as $key) {
                $isHit = isset($this->expiries[$key]) && ($this->expiries[$key] >= $now || !$this->deleteItem($key));

                yield $key => $f($key, $isHit ? $this->values[$key] : null, $isHit);
            }
        })($keys);
    }

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the private method :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is still not inlining the function. It still uses a separate function, even though it is anonymous rather than named.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for incorrect naming

@nicolas-grekas
Copy link
Member

See #17438 for a complementary PR on AbstractAdapter

private function createGenerator(array $keys)
{
$f = $this->createCacheItem;
$now = time();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to call time() in the loop now since you don't know how many time will be spent between iterations

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@nicolas-grekas nicolas-grekas changed the title [Cache] Added generators to optimize code [Cache] Optimize ArrayAdapter by using a generator Jan 19, 2016
@aurimasniekis
Copy link
Contributor Author

For some reason travis cached did not installed new version of cache/integration-tests :(

@nicolas-grekas
Copy link
Member

@aurimasniekis
Copy link
Contributor Author

Fixed the composer problems so code is now passing tests. Good to merge

@derrabus
Copy link
Member

Status: Reviewed

@aurimasniekis
Copy link
Contributor Author

Any changes on this?

@derrabus
Copy link
Member

@GCDS: @nicolas-grekas wrote in #17438 that he's off for a couple of days. I guess, we'll have to wait until he returns. 😃

@xabbuh
Copy link
Member

xabbuh commented Jan 25, 2016

I think we can close here because #17438 has been merged, right? Or do I miss anything?

@fabpot fabpot closed this Jan 25, 2016
@aurimasniekis
Copy link
Contributor Author

wow, why closed? ArrayAdapter was not fixed in #17438

@derrabus
Copy link
Member

@xabbuh No, because #17438 introduced Generators to all classes but ArrayAdapter. @nicolas-grekas designed #17438 as a complementary PR to this one, see his comment. Please reopen this PR.

@nicolas-grekas
Copy link
Member

I'm taking care of this now

fabpot added a commit that referenced this pull request Jan 25, 2016
This PR was merged into the 3.1-dev branch.

Discussion
----------

[Cache] Use generator in ArrayAdapter

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #17435
| License       | MIT
| Doc PR        | -

Commits
-------

367e784 [Cache] Use generator in ArrayAdapter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants