-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
perf: add annotations for non-concurrent cross-scoped duration measurements #13960
Conversation
Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@intel.com>
@@ -32,6 +32,23 @@ void PerfOperation::record(absl::string_view category, absl::string_view descrip | |||
// because the constructor is non-trivial due to the contained unordered_map. | |||
PerfAnnotationContext::PerfAnnotationContext() = default; | |||
|
|||
void PerfAnnotationContext::begin(absl::string_view category, absl::string_view description) { | |||
const CategoryDescription key = {std::string(category), std::string(description)}; | |||
RELEASE_ASSERT(!timestamps_map_.contains(key), |
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.
RELEASE_ASSERT use seems risky. Are these begin/end annotations meant to be used across event loop events or only within a single call stack?
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.
Within a single call stack only unfortunately. Would be nice to have uniquely identifiable events in the event loop though. Is it possible?
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 it's OK to use RELEASE_ASSERT as this code will only be activated when the binary is compiled with perf annotation enabled.
And it wouldn't do any good to use ASSERT because you generally would want opt-builds for measuring performance.
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.
There's a better way to do all of this: have an RAII object that is kept around for the duration of the perf operation and keeps the necessary state instead of keeping the state on a global as a string in a map.
It nicely solves the issue of concurrent performance measurements and works across call stacks.
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.
Yeah for cases where RAII is OK, that works today with PERF_OPERATION
which is just a wrapper that has all the code compile to nothing when perf annotations are not compiled in. I think that should be preferred when feasible to use.
I assume that PERF_BEGIN and PERF_END are where the plumbing needed to hold an object seems not worth it, and correlating by string seems worth it. It doesn't look to me like this adds any overhead when not used, so that's why I'm OK with this if it makes annotations easier to implement.
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 cleanest way though is just for begin() to return an object that you store in your filter undef ifdef
A-ha, now I see. In this case the owning object (e.g. filter) needs to be annotated too. And it has to be the same for a pair of begin() and end(). Right?
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.
Yeah though I think the logical next step of what I suggested is that rather than calling begin() you just instantiate an RAII object yourself, throwing it in an ifdef'd member variable in (say) the filter; maybe via unique_ptr, and destructing it works like end().
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.
Understood, thanks!
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.
Yes, exactly, ^ is what I'm proposing. I think we can make it so it compiles to nothing if annotations are not enabled.
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.
Updated. Turned out it's enough to add a few new macros to achieve the same result.
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.
nice. Just some trivial comments.
@@ -32,6 +32,23 @@ void PerfOperation::record(absl::string_view category, absl::string_view descrip | |||
// because the constructor is non-trivial due to the contained unordered_map. | |||
PerfAnnotationContext::PerfAnnotationContext() = default; | |||
|
|||
void PerfAnnotationContext::begin(absl::string_view category, absl::string_view description) { | |||
const CategoryDescription key = {std::string(category), std::string(description)}; | |||
RELEASE_ASSERT(!timestamps_map_.contains(key), |
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 it's OK to use RELEASE_ASSERT as this code will only be activated when the binary is compiled with perf annotation enabled.
And it wouldn't do any good to use ASSERT because you generally would want opt-builds for measuring performance.
const CategoryDescription key = {std::string(category), std::string(description)}; | ||
const MonotonicTime end_time = currentTime(); | ||
const auto result = timestamps_map_.extract(key); | ||
RELEASE_ASSERT(!result.empty(), "double perf measurement ending is disallowed!"); |
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.
might as well include the category & description in this message and the RELEASE_ASSERT above.
@@ -42,6 +42,31 @@ | |||
perf.record(category, description); \ | |||
} while (false) | |||
|
|||
/** | |||
* Initiates a duration measurement which can be finished in a different scope. | |||
* Please note that this kind of measurements makes sense only in a strictly |
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.
s/this/these/
s/makes/make/
* Initiates a duration measurement which can be finished in a different scope. | ||
* Please note that this kind of measurements makes sense only in a strictly | ||
* sequential flow, i.e. when a new stream isn't created until the current one | ||
* is destroyed. |
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 in reference only to a particular combination of category and description, right?
{ PERF_BEGIN("gamma", "4"); } | ||
{ PERF_BEGIN("sigma", "5"); } | ||
{ PERF_END("gamma", "4"); } | ||
{ PERF_END("sigma", "5"); } |
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.
Can you also add begin/end checking with same-category different-description and vice versa?
* shown as separate columns in the generated output table. Before ending the | ||
* measurement must be started first with a begin() call. The measurement can | ||
* be started and ended in different scopes, but the flow of these start and | ||
* stop points for the same category and description must be strictly sequential. |
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.
seems like you don't need to repeat all of this paragraph from above, but could just reference the doc in begin().
Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@intel.com>
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.
@envoyproxy/senior-maintainers
Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@intel.com>
Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@intel.com>
Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@intel.com>
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.
nice!
Can you check coverage? /wait |
/retest |
Retrying Azure Pipelines: |
@mattklein123 Looks it was a CI glitch. Everything is Ok now. |
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.
Awesome!
…ements (envoyproxy#13960) Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@intel.com> Signed-off-by: Qin Qin <qqin@google.com>
Commit Message: perf: add annotations for non-concurrent cross-scoped duration measurements
Additional Description:
Risk Level: Low
Testing: updated unit tests
Docs Changes: N/A
Release Notes: N/A
Platform Specific Features: N/A
Fixes #13860