-
Notifications
You must be signed in to change notification settings - Fork 7.7k
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
Expose time spent collecting cycles in gc_status() #11523
Conversation
I cannot comment on the implementation, but this looks like what I asked for in #11327. Thank you for working on this! |
@@ -148,6 +148,12 @@ ZEND_FUNCTION(gc_status) | |||
add_assoc_long_ex(return_value, "threshold", sizeof("threshold")-1, (long)status.threshold); | |||
add_assoc_long_ex(return_value, "buffer_size", sizeof("buffer_size")-1, (long)status.buf_size); | |||
add_assoc_long_ex(return_value, "roots", sizeof("roots")-1, (long)status.num_roots); | |||
|
|||
/* Using double because zend_long may be too small on some platforms */ | |||
add_assoc_double_ex(return_value, "application_time", sizeof("application_time")-1, (double) status.application_time / ZEND_NANO_IN_SEC); |
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 init size above should be updated
also are/can these strings be interned for better perf?
what is the time measurement and gc_status()
itself overhead?
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've updated the initial array size, thank you.
Regarding the time measurement overhead:
The synthetic benchmarks here and here show no significant overhead over multiple runs. The number of 10k nodes was chosen because this is the default GC threshold. Applications will typically have more nodes to scan, and their measurement_overhead/actual_work
ratio will be lower.
I've seen no significant overhead on a long running application.
Regarding the overhead on gc_status()
itself, it would matter if the function was called a high number of times during a request. Do you think it will be the case?
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.
Regarding the overhead on gc_status() itself, it would matter if the function was called a high number of times during a request. Do you think it will be the case?
In PHPUnit 10, gc_status()
is called for each event that is emitted. This is "a lot", but IMO perfectly fine in a development context.
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 see no slowdown on the PHPUnit 10 test suite. However, gc_status()
was called only 128 times. Is there a configuration that would cause more events to be triggered?
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 was looking at the wrong file: gc_status()
is actually called more than 19k times during the test suite and the changes in this PR caused no slowdown.
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.
PHPUnit's test suite (main
branch) currently has 2845 tests. For each test, multiple events are emitted. For each event, gc_status()
is called. I am confused that you only see 128 calls.
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.
Aha! :)
Zend/zend_time.h
Outdated
|
||
#define ZEND_NANO_IN_SEC 1000000000 | ||
|
||
typedef uint64_t zend_time_t; |
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 would prefer this name to keep hr, since this clearly helps to identify the underlying abstraction used.
So zend_hrtime_t maybe, including names of the files etc
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.
Agreed that zend_time is too general. I've renamed to zend_hrtime. Thank you!
@@ -1,56 +0,0 @@ | |||
/* |
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.
Do we need to move this? It should be fine to include an ext/standard
header, as standard is a required extension.
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.
It's not absolutely necessary, but it felt right to not have Zend/ depend on ext/ (for no particular reason other than Zend/ being a lower layer than ext/)
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.
Moving code has a few disadvantages:
- It makes reviewing harder, because it's difficult to determine what code is new and what code was just moved. Sometimes small changes can sneak into moved code too, which is very hard to catch without using a diff tool.
- It creates merge conflicts when merging changes to the moved code. This is more of a problem for us than a "standard" code base because we have 3-4 active branches.
- It breaks git blame.
Of course, not refactoring also has downsides, e.g. leading to big, messy files, or making things hard to find.
I don't mind either way, you can decide what you see fit.
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 agree with your points. In this case I will retain the relocation of the code because, since I've also moved most of it from the .c to the .h for inlining purposes, keeping it in ext/ would not cancel all of these disadvantages.
@@ -439,6 +445,9 @@ static void gc_globals_ctor_ex(zend_gc_globals *gc_globals) | |||
|
|||
gc_globals->gc_runs = 0; | |||
gc_globals->collected = 0; | |||
gc_globals->collector_time = 0; |
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.
Is setting activated_at
not desired here?
Zend/zend_gc.h
Outdated
@@ -24,6 +24,8 @@ | |||
# define GC_BENCH 0 | |||
#endif | |||
|
|||
#include "zend_hrtime.h" |
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.
Nit: We usually put includes first.
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 see big problems.
The overhead of the patch is minimal and the collected information may be useful.
Zend/zend_hrtime.c
Outdated
|
||
#endif | ||
|
||
zend_result zend_startup_hrtime(void) |
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 return value is unused and isn't marked as ZEND_API
. Any reason it shouldn't be void zend_startup_hrtime(void)
?
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 was existing code I moved from ext/standard/hrtime.c, and I tried to limit the amount of changes. But you are right, the return value is unnecessary, so I've removed it.
Zend/zend_hrtime.h
Outdated
|
||
#endif | ||
|
||
#define ZEND_NANO_IN_SEC 1000000000 |
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 would avoid the need to cast this value:
#define ZEND_NANO_IN_SEC 1000000000 | |
#define ZEND_NANO_IN_SEC UINT64_C(1000000000) |
Zend/zend_hrtime.h
Outdated
return (zend_hrtime_t)mach_absolute_time() * zend_hrtime_timerlib_info.numer / zend_hrtime_timerlib_info.denom; | ||
#elif ZEND_HRTIME_PLATFORM_POSIX | ||
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; | ||
if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) { |
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 would expect branch predictors to handle this well, but I don't think this could hurt in this case:
if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) { | |
if (EXPECTED(0 == clock_gettime(CLOCK_MONOTONIC, &ts))) { |
* up/master: (571 commits) Expose time spent collecting cycles in gc_status() (php#11523) Warn when fpm socket was not registered on the expected path Implement DOMElement::id Fix ? Implement DOMParentNode::replaceChildren() Implement DOMElement::className RFC: Deprecate remains of string evaluated code assertions (php#11671) Prevent decimal int precision loss in number_format() Implement DOMNode::getRootNode() Implement DOMElement::getAttributeNames() Refactor dom_node_node_name_read() to avoid double allocation Handle fragments consisting out of multiple children without a single root correctly Avoid allocations in DOMElement::getAttribute() Avoid string allocation in dom_get_dom1_attribute() for as long as possible Fix use-of-uninitialized-value when calling php_posix_stream_get_fd (php#11694) Reorder list construction in the function php_intpow10 (php#11683) proc_open: Use posix_spawn(3) interface on systems where it is profitable zend_gdb disable gdb detection for FreeBSD < 11. Fix iface const visibility variance check Fix missing iface class const inheritance type check ...
See #11327
This adds the following new entries to the
gc_status()
result:Times have nanoseconds resolution and use a monotonic clock. They are exposed as a number of seconds in floats, because the int type may be too small for nanoseconds depending on the platform.
application_time
could be computed withhrtime(true) - $_SERVER['REQUEST_TIME_FLOAT']
, but it seems useful to still expose it because$_SERVER['REQUEST_TIME_FLOAT']
is not monotonic.