Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[VarDumper] Keep and reuse array stubs in memory #23683

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

Merged
merged 1 commit into from
Jul 28, 2017

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Jul 26, 2017

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

(to be reviewed ignoring whitespaces)

As highlighted by @vtsykun in #23620, the patch in #23644 improves performance but increases the memory usage.

The issue is that even a small array($k => $v) consumes more memory than a new Stub().
That's a shame, but since our small arrays have a low variety, we can keep them in a cache and leverage php's COW mechanism to reduce memory. Effectively, this creates a memory leak. But the leak is upper bounded by the data you had already in memory, since that's what is cloned. Looking at the numbers, it looks worth it:

3.3.5 +#23644 +this PR
Wall Time 39.4s 26.1s 18.6s 17.3s
Memory 391MB 539MB 217MB 216MB

https://blackfire.io/profiles/compare/846b58bc-7863-4502-9ca2-f82eebd4173f/graph

@nicolas-grekas nicolas-grekas added this to the 3.3 milestone Jul 26, 2017
@nicolas-grekas nicolas-grekas changed the title [DI] Keep and reuse array stubs in memory [VarDumper] Keep and reuse array stubs in memory Jul 26, 2017
@nicolas-grekas
Copy link
Member Author

On a smaller profile with GC disabled to be sure we don't compare just GC's time:
https://blackfire.io/profiles/compare/53798f9a-e2ab-42b4-94b4-a564f15814d5/graph
still -32% in memory, and -5% in wall time.

Another https://blackfire.io achievement! :)

@Koc
Copy link
Contributor

Koc commented Jul 26, 2017

18.6s is too much anyway

@nicolas-grekas
Copy link
Member Author

@Koc see #23659 for the core reason why this test page is slow.

vtsykun referenced this pull request in oroinc/platform Jul 26, 2017
…1598)

- added local cache for a logger collector
- overwrite symfony`s data_collector.logger class
- added tests
$a = null; // Array cast for nested structures
$stub = null; // Stub capturing the main properties of an original item value
// or null if the original value is used directly

if (!self::$hashMask) {
self::$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason why this doesn't just use random_bytes? (unrelated to the actual change)

Copy link
Member Author

Choose a reason for hiding this comment

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

Just history, since there is a version of this code that works on PHP 5.3 and the provided uniqueness is good enough

Copy link
Contributor

Choose a reason for hiding this comment

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

PHP 5.3 is not a problem, but if the provided uniqueness is good enough, that's fine, too. Didn't look what it's used for, it's just the uniqid that auto-triggered me writing a comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

If it's just a unique string, why not just use a global counter with $counter = "a"; $counter++? This ensures uniqueness in the process and avoids two function calls there.

Copy link
Member Author

Choose a reason for hiding this comment

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

it's a unique string that needs to be collision free with the $GLOBALS arrays, where users can put any keys. global uniqueness is important

Copy link
Contributor

Choose a reason for hiding this comment

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

If you want to, you can just read the current value with (function () { var_dump(self::$gid); })->bindTo(null, VarCloner::class)();, but I think I gave enough input, don't care to discuss it further.

Copy link
Member Author

Choose a reason for hiding this comment

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

you can just read the current value with [...]

true, fixed now :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Hum, not quite, I forgot about ReflectionMethod::getStaticVariables() :)

Copy link
Member Author

@nicolas-grekas nicolas-grekas Jul 27, 2017

Choose a reason for hiding this comment

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

now replaced by a local variable, using spl_object_hash instead of uniqid

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, there's no real way to prevent that. It's not public API, if people break it, they deserve what they get.

@@ -268,7 +273,6 @@ protected function doClone($var)
}

$queue[$i] = $vals;
unset($indexedArrays[$i]);
Copy link
Member Author

Choose a reason for hiding this comment

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

another µ-optim: unsetting scalars from arrays does not free memory, but takes CPU, better not doing it

Copy link
Contributor

Choose a reason for hiding this comment

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

Curious: Why does it not free memory?

Copy link
Member

Choose a reason for hiding this comment

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

because they are still referenced elsewhere (in the variable being cloned)

Copy link
Member Author

Choose a reason for hiding this comment

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

well, not exactly for this reason in this case: it's because PHP allocates arrays as they grow, and never frees the allocated memory when they shrink. I think this article might explain it: http://jpauli.github.io/2016/04/08/hashtables.html

Copy link
Member Author

Choose a reason for hiding this comment

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

So, when the table is full and when we empty it, the memory usage doesn't move at all (modulo noise). When we finished unset()ing every value, we end having a hashtable which arData is 32768 slots all full of UNDEF zvals.

@nicolas-grekas nicolas-grekas force-pushed the var-perf branch 2 times, most recently from c1dcf06 to d63887b Compare July 27, 2017 12:20
@@ -36,7 +37,7 @@ protected function doClone($var)
$maxItems = $this->maxItems;
$maxString = $this->maxString;
$cookie = (object) array(); // Unique object used to detect hard references
$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
$gid = \spl_object_hash($cookie); // Unique string used to detect the special $GLOBALS variable
Copy link
Member

Choose a reason for hiding this comment

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

Not sure if that's relevant in this context, but spl_object_hash does not guarantee that it returns a unique value. The value can be reused when objects are reclaimed. And we did hit that limitation in the past (and Doctrine as well), so that's not just theoretical.

Copy link
Member Author

Choose a reason for hiding this comment

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

you're right, back to my first version, it was fine :)

@fabpot
Copy link
Member

fabpot commented Jul 28, 2017

Thank you @nicolas-grekas.

@fabpot fabpot merged commit 92fa55d into symfony:3.3 Jul 28, 2017
fabpot added a commit that referenced this pull request Jul 28, 2017
…grekas)

This PR was merged into the 3.3 branch.

Discussion
----------

[VarDumper] Keep and reuse array stubs in memory

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

(to be reviewed [ignoring whitespaces](https://github.com/symfony/symfony/pull/23683/files?w=1))

As highlighted by @vtsykun in #23620, the patch in #23644 improves performance but increases the memory usage.

The issue is that even a small `array($k => $v)` consumes more memory than a `new Stub()`.
That's a shame, but since our small arrays have a low variety, we can keep them in a cache and leverage php's COW mechanism to reduce memory. Effectively, this creates a memory leak. But the leak is upper bounded by the data you had already in memory, since that's what is cloned. Looking at the numbers, it looks worth it:

|   | 3.3.5 | +#23644 | +this PR
| --- | --- | --- | ---
| Wall Time    | 39.4s | 26.1s | ~~18.6s~~ 17.3s
| Memory       | 391MB | 539MB | ~~217MB~~ 216MB

https://blackfire.io/profiles/compare/846b58bc-7863-4502-9ca2-f82eebd4173f/graph

Commits
-------

92fa55d [VarDumper] Keep and reuse array stubs in memory
@nicolas-grekas nicolas-grekas deleted the var-perf branch July 28, 2017 16:42
@fabpot fabpot mentioned this pull request Aug 1, 2017
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.

6 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.