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
Inheritance cache #6627
Inheritance cache #6627
Conversation
This is a PoC yet (some variance related tests fail). Inheritance cache should eliminate overhead of copying class data from SHM to process memory, run-time class linking, and then data deallocation. This also may help JIT generating better code for property access.
@nikic could you please review this and share your opinion and ideas for improvement. |
d75c5a6
to
442fe8f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this generally makes sense, I will need to look into the details a bit more.
it would be great to eliminate limitation for ZEND_ACC_CONSTANTS_UPDATED and ZEND_ACC_PROPERTY_TYPES_RESOLVED to make all cached classes ZEND_ACC_IMMUTABLE
Any ideas on how to do that? More map_ptr usage?
@@ -1059,7 +1059,15 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * | |||
if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) || | |||
((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) && | |||
(ce->ce_flags & ZEND_ACC_NEARLY_LINKED))) { | |||
ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES; | |||
if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we need the hashtable for immutable classes, I think it would make sense to always use it rather than having two different mechanisms. It's a rare case, so being optimal is not so important.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Immutable class may be lazily loaded later, so the same hash key won't work.
} | ||
|
||
if (ZCG(enabled) && accel_startup_ok) { | ||
/* Override inheritance cache callbaks */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/* Override inheritance cache callbaks */ | |
/* Override inheritance cache callbacks */ |
Zend/zend_inheritance.c
Outdated
@@ -373,6 +376,11 @@ static inheritance_status zend_perform_covariant_class_type_check( | |||
zend_class_entry *proto_scope, zend_type proto_type, | |||
bool register_unresolved) { | |||
bool have_unresolved = 0; | |||
|
|||
if (CG(current_linking_class)) { | |||
CG(current_linking_class)->ce_flags |= ZEND_ACC_NEEDS_VARIANCE_CHECKS; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also sets the flag if the
php-src/Zend/zend_inheritance.c
Line 410 in 442fe8f
if (zend_string_equals_ci(fe_class_name, proto_class_name)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ZEND_ACC_NEEDS_VARIANCE_CHECKS is completely removed.
Did you already run any tests on how much difference this makes? |
Yes, I already solved the typed property limitation and work on constants now. |
0.5% improvement on Wordpress (according to callgrind). I expect better improvement on heavy OOP apps. I'll test after fixing limitations (typed properties, AST constants, variance checks). |
a81856a
to
895976c
Compare
1d698ff
to
36c869d
Compare
@nikic I think, the PR is ready to merge. Please, review this and schedule more tests. |
I checked the performance improvement on https://github.com/phpbenchmarks/symfony (without JIT). 1000 requests takes 8% less time (one worker: For the same 1000 requests Callgrind shows 5% less executed instructions.
|
===DONE=== | ||
--EXPECTF-- | ||
Warning: Can't preload class Test with unresolved property types in %s on line %d | ||
--EXPECT-- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change in expectation seems weird: is the --FILE--
section mostly ignored and only here for representative purposes? Does the Warning still get emitted in some cases, after this patch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now opcache may preload classes with unresolved properties without problems.
?> | ||
--FILE-- | ||
Unreachable | ||
<?php | ||
class Foo { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to ext/opcache/tests/preload_unresolved_prop_type.phpt
- possibly --FILE--
section unused and only needed for representative purposes?
?> | ||
--FILE-- | ||
Unreachable | ||
<?php |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a reader's perspective, this test is not really understandable: previously, it was clear that --FILE--
was not being used, but now it is, which makes the change in expected results quite suspicious
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously, opcache couldn't preload classes with unresolved constants. Now this is not a problem. Constants that are not resolved during preloading, may be resolved later at run-time.
@@ -11,5 +11,16 @@ require_once('skipif.inc'); | |||
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); | |||
?> | |||
--FILE-- | |||
--EXPECTF-- | |||
Warning: Can't preload class Test with unresolved initializer for constant C in %s on line %d | |||
<?php |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same feedback as in ext/opcache/tests/preload_loadable_classes_2.phpt
: would be better to keep --FILE--
as it was, then change only --EXPECTF--
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would have test with empty --FILE-- and empty --EXPECT-- . The new code shows, that now Test is preloaded, but resolution of constant "Test::C" failed, because "Foo::BAR" is not defined.
I triggered some additional tests, with the following new failures:
|
} zend_type; | ||
|
||
typedef struct { | ||
uint32_t num_types; | ||
zend_type types[1]; | ||
} zend_type_list; | ||
|
||
#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 24 | ||
#define _ZEND_TYPE_MASK ((1u << 24) - 1) | ||
#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intention here was that new flags are added at the low end, i.e. 1<<19. Types only go to 15, so this should be fine.
ext/opcache/ZendAccelerator.c
Outdated
break; | ||
} | ||
} | ||
if (entry->dependencies) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will try to check dependencies even if the above traits_and_interfaces check failed.
It would be best to extract the body of the while(entry)
loop into a separate function so that early return can be used.
SEPARATE_ARRAY(zv); | ||
} else if (Z_TYPE_P(zv) == IS_OBJECT || Z_TYPE_P(zv) == IS_RESOURCE) { | ||
/* Can't cache */ | ||
return NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this miss something like array of objects?
entry->dependencies_count = zend_hash_num_elements(dependencies); | ||
entry->dependencies = (zend_class_dependency*)ZCG(mem); | ||
ZEND_HASH_FOREACH_STR_KEY_PTR(dependencies, dep_name, dep_ce) { | ||
entry->dependencies[i].name = dep_name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we guaranteed that dep_name is a permanent string here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Class prototype, parent, and all interfaces and traits are IMMUTABLE. So they may refer only to strings in shared memory. Anyway, I'll add assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nikic all known test failures should be fixed now. Could you please reschedule tests.
I initiated full test run myself.
c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); | ||
memcpy(c, parent_const, sizeof(zend_class_constant)); | ||
parent_const = c; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really get why this copy is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't update constant value in shared memory, so we copy it into process memory.
If you comment this some tests are going to fail. For example tests/classes/constants_basic_006.phpt
MacOS build currently fails with:
|
…erform any class compatibility checks during inheritance. It should be possible to reduce (or completely eliminate) this retriction.
…TY_TYPES_RESOLVED flag) to be immutable and to be used for inheritance caching.
2bd659f
to
83a6238
Compare
@nikic all known test failures should be fixed now. Could you please reschedule tests. |
Codecov Report
@@ Coverage Diff @@
## master #6627 +/- ##
==========================================
+ Coverage 71.92% 71.96% +0.04%
==========================================
Files 826 838 +12
Lines 316227 316737 +510
==========================================
+ Hits 227439 227940 +501
- Misses 88788 88797 +9
Continue to review full report at Codecov.
|
The following table compares Inheritance Cache and Preloading.
As you can see, Inheritance Cache gives a part of Preloading speedup for free (Inheritance Cache - 12%, Preloading - 18%) |
This reverts commit 2062e04.
…ed object, rsource or reference in static variable.
I'm only moderately familiar with parts of opcache, so I'm not able to understand this as well as other reviewers: What does this opcache code do for cyclic dependencies that would cause a runtime fatal error in php (E.g. trait A uses B, and trait B uses A)? (Same for cyclic dependencies in classes/interfaces, etc) |
Inheritance Cache just can't store classes before they are linked by PHP, so PHP is going to fail as usually. |
Merged as 4b79dba |
No description provided.