Skip to content

Commit ccfb4f2

Browse files
committed
bug symfony#23683 [VarDumper] Keep and reuse array stubs in memory (nicolas-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 symfony#23620, the patch in symfony#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 | +symfony#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
2 parents b5c7319 + 92fa55d commit ccfb4f2

File tree

1 file changed

+40
-39
lines changed

1 file changed

+40
-39
lines changed

src/Symfony/Component/VarDumper/Cloner/VarCloner.php

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
*/
1717
class VarCloner extends AbstractCloner
1818
{
19+
private static $gid;
1920
private static $hashMask = 0;
2021
private static $hashOffset = 0;
22+
private static $arrayCache = array();
2123

2224
/**
2325
* {@inheritdoc}
@@ -36,14 +38,15 @@ protected function doClone($var)
3638
$maxItems = $this->maxItems;
3739
$maxString = $this->maxString;
3840
$cookie = (object) array(); // Unique object used to detect hard references
39-
$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
4041
$a = null; // Array cast for nested structures
4142
$stub = null; // Stub capturing the main properties of an original item value
4243
// or null if the original value is used directly
4344

4445
if (!self::$hashMask) {
46+
self::$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
4547
self::initHashMask();
4648
}
49+
$gid = self::$gid;
4750
$hashMask = self::$hashMask;
4851
$hashOffset = self::$hashOffset;
4952
$arrayStub = new Stub();
@@ -58,9 +61,9 @@ protected function doClone($var)
5861
if (\is_int($k)) {
5962
continue;
6063
}
61-
foreach (array($k => true) as $j => $v) {
64+
foreach (array($k => true) as $gk => $gv) {
6265
}
63-
if ($k !== $j) {
66+
if ($gk !== $k) {
6467
$fromObjCast = true;
6568
$refs = $vals = \array_values($queue[$i]);
6669
break;
@@ -95,7 +98,7 @@ protected function doClone($var)
9598
case true === $v:
9699
case \is_int($v):
97100
case \is_float($v):
98-
break;
101+
continue 2;
99102

100103
case \is_string($v):
101104
if (!\preg_match('//u', $v)) {
@@ -114,7 +117,10 @@ protected function doClone($var)
114117
$stub->class = Stub::STRING_UTF8;
115118
$stub->cut = $cut;
116119
$stub->value = \mb_substr($v, 0, $maxString, 'UTF-8');
120+
} else {
121+
continue 2;
117122
}
123+
$a = null;
118124
break;
119125

120126
case \is_array($v):
@@ -146,11 +152,9 @@ protected function doClone($var)
146152
} else {
147153
$a = $v;
148154
}
149-
} else {
155+
} elseif (\PHP_VERSION_ID < 70200) {
150156
$indexedArrays[$len] = true;
151157
}
152-
153-
$stub->value = \count($a);
154158
break;
155159

156160
case \is_object($v):
@@ -210,42 +214,40 @@ protected function doClone($var)
210214
break;
211215
}
212216

213-
if (isset($stub)) {
214-
if ($a) {
215-
if (!$i || 0 > $maxItems) {
216-
$queue[$len] = $a;
217-
$stub->position = $len++;
218-
} elseif ($pos < $maxItems) {
219-
if ($maxItems < $pos += \count($a)) {
220-
$a = \array_slice($a, 0, $maxItems - $pos);
221-
if ($stub->cut >= 0) {
222-
$stub->cut += $pos - $maxItems;
223-
}
217+
if ($a) {
218+
if (!$i || 0 > $maxItems) {
219+
$queue[$len] = $a;
220+
$stub->position = $len++;
221+
} elseif ($pos < $maxItems) {
222+
if ($maxItems < $pos += \count($a)) {
223+
$a = \array_slice($a, 0, $maxItems - $pos);
224+
if ($stub->cut >= 0) {
225+
$stub->cut += $pos - $maxItems;
224226
}
225-
$queue[$len] = $a;
226-
$stub->position = $len++;
227-
} elseif ($stub->cut >= 0) {
228-
$stub->cut += \count($a);
229-
$stub->position = 0;
230-
}
231-
}
232-
233-
if ($arrayStub === $stub) {
234-
if ($arrayStub->cut) {
235-
$stub = array($arrayStub->cut, $arrayStub->class => $arrayStub->position);
236-
$arrayStub->cut = 0;
237-
} else {
238-
$stub = array($arrayStub->class => $arrayStub->position);
239227
}
228+
$queue[$len] = $a;
229+
$stub->position = $len++;
230+
} elseif ($stub->cut >= 0) {
231+
$stub->cut += \count($a);
232+
$stub->position = 0;
240233
}
234+
}
241235

242-
if ($zvalIsRef) {
243-
$refs[$k]->value = $stub;
236+
if ($arrayStub === $stub) {
237+
if ($arrayStub->cut) {
238+
$stub = array($arrayStub->cut, $arrayStub->class => $arrayStub->position);
239+
$arrayStub->cut = 0;
240+
} elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) {
241+
$stub = self::$arrayCache[$arrayStub->class][$arrayStub->position];
244242
} else {
245-
$vals[$k] = $stub;
243+
self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = array($arrayStub->class => $arrayStub->position);
246244
}
245+
}
247246

248-
$stub = $a = null;
247+
if ($zvalIsRef) {
248+
$refs[$k]->value = $stub;
249+
} else {
250+
$vals[$k] = $stub;
249251
}
250252
}
251253

@@ -255,9 +257,9 @@ protected function doClone($var)
255257
$vals = array();
256258
$j = -1;
257259
foreach ($queue[$i] as $k => $v) {
258-
foreach (array($k => true) as $a => $v) {
260+
foreach (array($k => true) as $gk => $gv) {
259261
}
260-
if ($a !== $k) {
262+
if ($gk !== $k) {
261263
$vals = (object) $vals;
262264
$vals->{$k} = $refs[++$j];
263265
$vals = (array) $vals;
@@ -268,7 +270,6 @@ protected function doClone($var)
268270
}
269271

270272
$queue[$i] = $vals;
271-
unset($indexedArrays[$i]);
272273
}
273274

274275
foreach ($values as $h => $v) {

0 commit comments

Comments
 (0)