Reduce memory used by UnionType representation. - #729
Conversation
50a97dc to
cab026c
Compare
|
Could spl_object_hash() not be used here instead of runkit_object_id()? |
This would increase overall memory usage, spl_object_hash would be a 32 byte string and would need reference tracking and garbage collection. An
|
| * @param string|null $type_name | ||
| * @return UnionType|null | ||
| */ | ||
| $getForGlobalContext = function($type_name) { |
There was a problem hiding this comment.
nit: we should use underscores for variable names rather than camel casing
|
|
||
| /** | ||
| * @param Type[]|\Iterator|null $type_list | ||
| * @param bool $is_an_array_set - Whether or not this is already a set. Only set to true within UnionSet code. |
There was a problem hiding this comment.
This is pretty confusing. Why are implementation details leaking out into the public interface?
There was a problem hiding this comment.
I didn't think many things would use new UnionType() directly, it's usually helpers from Phan such as UnionType::fromStringInContext, $type->asUnionType(), etc.
|
|
||
| return new UnionType( | ||
| array_map(function (string $type_name) { | ||
| static $memoizeMap = []; |
There was a problem hiding this comment.
we should use underscores for variable names.
| return new UnionType( | ||
| array_map(function (string $type_name) { | ||
| static $memoizeMap = []; | ||
| $types_set = $memoizeMap[$fully_qualified_string] ?? null; |
There was a problem hiding this comment.
Do we need to do any normalization on the FQSEN string?
There was a problem hiding this comment.
No, this is optional to speed up parsing UnionType a bit (not yet tested)
| public static function map(array $object_set, \Closure $cb) : array { | ||
| $result = []; | ||
| foreach ($object_set as $object) { | ||
| $newObject = $cb($object); |
There was a problem hiding this comment.
we should use underscores for variable names.
|
|
||
| use ast\Node; | ||
|
|
||
| if (!function_exists('runkit_object_id')) { |
There was a problem hiding this comment.
does Runkit need to marked as a composer dependency? Is it possible to define optional composer dependencies?
There was a problem hiding this comment.
https://getcomposer.org/doc/04-schema.md#suggest
runkit_object_id isn't on PECL, so it may be hard to find
There was a problem hiding this comment.
Also, this is either of two extensions https://github.com/runkit7/runkit7 or https://github.com/runkit7/runkit_object_id
The suggested option ( https://github.com/runkit7/runkit_object_id/blob/master/runkit_object_id.c ) would be an easier extension to understand.
|
|
||
| // Override two magic methods to ensure that Type isn't being cloned accidentally. | ||
| public function __wakeup() { | ||
| debug_print_backtrace(); |
There was a problem hiding this comment.
We can probably leave this to be printed by whoever is catching the exception.
|
You can always turn the 32-byte string into an integer. You only need the object handle part which is in the first 16 bytes, so: hexdec(substr(spl_object_hash($class),0,16)); and you effectively have the object id as an int |
|
Surprised the runkit version is so much faster than that wrapper. Sounds like we should add spl_object_id() to PHP then because having a dependency on runkit is terrible, even if it is optional. |
3c345fc to
962a7fa
Compare
EDIT: Found an identical proposal at https://marc.info/?t=143835274500003&r=1&w=2
https://bugs.php.net/bug.php?id=52657 It's been requested for a while, but they hope to be able to move away from keeping object ids if they need to (The exact object id is currently also available inside a larger string in var_dump.
Also, spl_object_hash returns it xored() with a random number, to make php slightly harder to exploit in some cases I'm not familiar with (runkit_object_id extension could add that if needed)) It's not really a surprise pure PHP would be slow, since the wrapper would be called frequently in the proposed use case, and allocates a string, converts numbers to a string, allocates a substring of that string (and calls a function to do that), then converts the string back into numbers. |
9bd7684 to
6b0364a
Compare
…presentation Add a common base test class, to make common configuration easier in future PRs.
…presentation And add test of ArrayAccess implementation, to avoid regression. Add a common base test class, to make common configuration easier in future PRs.
Add related code changes from #729, without changing UnionType representation
6b0364a to
83c1098
Compare
83c1098 to
c5e43f8
Compare
c5e43f8 to
a992e25
Compare
|
Fixed merge conflicts. Converted some relative function calls to fully qualified function calls. |
Performance improvement: - 3.68s before this change - 3.60s after this change, with php implementation of runkit_object_id() - 3.36s after this change, with native C implementation of runkit_object_id() Memory usage decreased by 10%, with or without the native C implementation. The UnionType implementation details of the Set are rarely important outside of a few Phan classes. Having SplObjectStorage is inefficient for memory usage - An empty SplObjectStorage requires more memory than an empty array, and if it's cloned, a clone of the empty SplObjectStorage must be made In contrast, creating a clone of an array is easy due to copy on write semantics in php (`$x = $y`) This uses an array, with the object id as an array key, and the object itself as a value. - The only pecl I'm aware of that has the desired functionality (an integer id for any object, unique for the lifetime of that object) is runkit (undocumented, but there for a long time). This has a fork in php7 as well. This change should work with/without a stripped down module providing only the function `runkit_object_id(object $x) : int` See https://github.com/runkit7/runkit_object_id Fix bug in suppressing PhanRedefineFunctionInternal - We check if PhanRedefineFunction was suppressed, but should have checked PhanRedefineFunctionInternal Add BaseTest, so that static properties can be preserved without being touched between test runs. Without the special use properties, PHPUnit would modify the private static properties. Fix loading the fallback implementation of runkit_object_id, fix test The first place the fallback is used is in Type::make(), so require_once the fallback in Type.php (Need to load fallback when running and in unit tests) Also, the Type::make() object caching logic changed, causing object to be confused with \\object, etc. - Use a different key space for NativeType by adding a unique prefix. (Only shows up in the factory method) Address review comments. Use underscores for variable names. Update name in phpdoc Use fully qualified function names to speed up calls to those functions.
a992e25 to
8e12624
Compare
The |
Performance improvement for phan self-testing (single threaded):
Memory usage reported by PHP decreased by 10%, with or without the native C
implementation.
The UnionType implementation details of the Set are rarely important outside
of a few Phan classes.
Having SplObjectStorage is inefficient for memory usage - An empty
SplObjectStorage requires more memory than an empty array,
and if it's cloned, a clone of the empty SplObjectStorage must be made
In contrast, creating a clone of an array is easy due to copy on write
semantics in php (
$x = $y)This uses an array, with the object id(int) as an array key, and the object itself
as a value.
(an integer id for any object, unique for the lifetime of that object)
is runkit (undocumented, but there for a long time).
This has a fork in php7 as well.
This change should work with/without a stripped down module providing
only the function
runkit_object_id(object $x) : intSee https://github.com/runkit7/runkit_object_id , which is intended as an optional dependency
Fix bug in suppressing PhanRedefineFunctionInternal
checked PhanRedefineFunctionInternal
Add BaseTest, so that static properties can be preserved without being
touched between test runs. Without the special use properties,
PHPUnit would modify the private static properties.
The type of getTypeSet() changed to an array
This contains debugging code, which will be cleaned up later (More interested in the feedback on the change in general)
Also, there are some commented out type assertions (which would pass)