Skip to content
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

[time-trace] Add a new time trace scope variable named "ParseDeclarationOrFunctionDefinition". #65268

Closed
wants to merge 2 commits into from

Conversation

MaggieYingYi
Copy link
Contributor

When profiling code using -ftime-trace with the default time-trace-granularity value (500 microseconds), in some code there is a large empty timeline in the flame chart profiling view (using Chrome Tracing view). If you pass -ftime-trace-granularity=0 in order to show all time traces, the empty timeline consists of a large number of small time scopes named EvaluateAsConstExpr. This change adds an enclosing time trace scope for the function Parser::ParseDeclarationOrFunctionDefinition to record the time spent parsing the function's declaration or definition.

@MaggieYingYi MaggieYingYi self-assigned this Sep 4, 2023
@MaggieYingYi MaggieYingYi requested a review from a team as a code owner September 4, 2023 15:07
@MaggieYingYi
Copy link
Contributor Author

Gentle ping ...

Copy link
Collaborator

@rnk rnk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems fine to me, but it's hard to understand if this is redundant with some other "ParseFunction" scope from the tests. WDYT @MaskRay ?

@MaskRay
Copy link
Member

MaskRay commented Sep 20, 2023

This seems fine to me, but it's hard to understand if this is redundant with some other "ParseFunction" scope from the tests. WDYT @MaskRay ?

I share similar concern but I am not familiar with the parser...
@AaronBallman

@MaggieYingYi
Copy link
Contributor Author

Gentle ping ...

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies on the delayed review! Thank you for working on this; we really could use better time trace output in Clang to start helping us get a better handle on improving compile time performance.

clang/lib/Parse/Parser.cpp Outdated Show resolved Hide resolved
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Oct 12, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 12, 2023

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-driver

Author: None (MaggieYingYi)

Changes

When profiling code using -ftime-trace with the default time-trace-granularity value (500 microseconds), in some code there is a large empty timeline in the flame chart profiling view (using Chrome Tracing view). If you pass -ftime-trace-granularity=0 in order to show all time traces, the empty timeline consists of a large number of small time scopes named EvaluateAsConstExpr. This change adds an enclosing time trace scope for the function Parser::ParseDeclarationOrFunctionDefinition to record the time spent parsing the function's declaration or definition.


Full diff: https://github.com/llvm/llvm-project/pull/65268.diff

3 Files Affected:

  • (modified) clang/lib/Parse/Parser.cpp (+9-1)
  • (added) clang/test/Driver/check-time-trace-ParseDeclarationOrFunctionDefinition.cpp (+14)
  • (modified) clang/unittests/Support/TimeProfilerTest.cpp (+26-18)
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 0f930248e77174b..533a9af173178b0 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -13,8 +13,8 @@
 #include "clang/Parse/Parser.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/ASTLambda.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Parse/ParseDiagnostic.h"
 #include "clang/Parse/RAIIObjectsForParser.h"
@@ -22,6 +22,7 @@
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/Scope.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/TimeProfiler.h"
 using namespace clang;
 
 
@@ -1229,6 +1230,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal(
 Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition(
     ParsedAttributes &Attrs, ParsedAttributes &DeclSpecAttrs,
     ParsingDeclSpec *DS, AccessSpecifier AS) {
+  // Add an enclosing time trace scope for a bunch of small scopes with
+  // "EvaluateAsConstExpr".
+  llvm::TimeTraceScope TimeScope("ParseDeclarationOrFunctionDefinition");
   if (DS) {
     return ParseDeclOrFunctionDefInternal(Attrs, DeclSpecAttrs, *DS, AS);
   } else {
@@ -1259,6 +1263,10 @@ Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition(
 Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
                                       const ParsedTemplateInfo &TemplateInfo,
                                       LateParsedAttrList *LateParsedAttrs) {
+  llvm::TimeTraceScope TimeScope(
+      "ParseFunctionDefinition",
+      Actions.GetNameForDeclarator(D).getName().getAsString());
+
   // Poison SEH identifiers so they are flagged as illegal in function bodies.
   PoisonSEHIdentifiersRAIIObject PoisonSEHIdentifiers(*this, true);
   const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo();
diff --git a/clang/test/Driver/check-time-trace-ParseDeclarationOrFunctionDefinition.cpp b/clang/test/Driver/check-time-trace-ParseDeclarationOrFunctionDefinition.cpp
new file mode 100644
index 000000000000000..96bb37d57823e8b
--- /dev/null
+++ b/clang/test/Driver/check-time-trace-ParseDeclarationOrFunctionDefinition.cpp
@@ -0,0 +1,14 @@
+// RUN: %clangxx -S -ftime-trace -ftime-trace-granularity=0 -o %T/check-time-trace-ParseDeclarationOrFunctionDefinition %s
+// RUN: cat %T/check-time-trace-ParseDeclarationOrFunctionDefinition.json \
+// RUN:   | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \
+// RUN:   | FileCheck %s
+
+// CHECK-DAG: "name": "ParseDeclarationOrFunctionDefinition"
+// CHECK-DAG: "name": "ParseFunctionDefinition"
+// CHECK-DAG: "detail": "foo"
+// CHECK-DAG: "name": "ParseFunctionDefinition"
+// CHECK-DAG: "detail": "bar"
+
+template <typename T>
+void foo(T) {}
+void bar() { foo(0); }
diff --git a/clang/unittests/Support/TimeProfilerTest.cpp b/clang/unittests/Support/TimeProfilerTest.cpp
index a7ca2bf91e474ef..f69a54692b0072a 100644
--- a/clang/unittests/Support/TimeProfilerTest.cpp
+++ b/clang/unittests/Support/TimeProfilerTest.cpp
@@ -177,22 +177,29 @@ constexpr int slow_init_list[] = {1, 1, 2, 3, 5, 8, 13, 21}; // 25th line
   std::string TraceGraph = buildTraceGraph(Json);
   ASSERT_TRUE(TraceGraph == R"(
 Frontend
-| EvaluateAsRValue (<test.cc:8:21>)
-| EvaluateForOverflow (<test.cc:8:21, col:25>)
-| EvaluateForOverflow (<test.cc:8:30, col:32>)
-| EvaluateAsRValue (<test.cc:9:14>)
-| EvaluateForOverflow (<test.cc:9:9, col:14>)
-| isPotentialConstantExpr (slow_namespace::slow_func)
-| EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
-| | EvaluateAsRValue (<test.cc:8:21, col:25>)
-| EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
-| | EvaluateAsRValue (<test.cc:8:21, col:25>)
-| EvaluateAsInitializer (slow_value)
-| EvaluateAsConstantExpr (<test.cc:17:33, col:59>)
-| EvaluateAsConstantExpr (<test.cc:18:11, col:37>)
-| EvaluateAsConstantExpr (<test.cc:23:31, col:57>)
-| EvaluateAsRValue (<test.cc:22:14, line:23:58>)
-| EvaluateAsInitializer (slow_init_list)
+| ParseDeclarationOrFunctionDefinition
+| ParseDeclarationOrFunctionDefinition
+| | ParseFunctionDefinition (slow_func)
+| | | EvaluateAsRValue (<test.cc:8:21>)
+| | | EvaluateForOverflow (<test.cc:8:21, col:25>)
+| | | EvaluateForOverflow (<test.cc:8:30, col:32>)
+| | | EvaluateAsRValue (<test.cc:9:14>)
+| | | EvaluateForOverflow (<test.cc:9:9, col:14>)
+| | | isPotentialConstantExpr (slow_namespace::slow_func)
+| | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
+| | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
+| | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
+| | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
+| ParseDeclarationOrFunctionDefinition
+| | ParseFunctionDefinition (slow_test)
+| | | EvaluateAsInitializer (slow_value)
+| | | EvaluateAsConstantExpr (<test.cc:17:33, col:59>)
+| | | EvaluateAsConstantExpr (<test.cc:18:11, col:37>)
+| ParseDeclarationOrFunctionDefinition
+| | EvaluateAsConstantExpr (<test.cc:23:31, col:57>)
+| | EvaluateAsRValue (<test.cc:22:14, line:23:58>)
+| ParseDeclarationOrFunctionDefinition
+| | EvaluateAsInitializer (slow_init_list)
 | PerformPendingInstantiations
 )");
 
@@ -213,8 +220,9 @@ struct {
   std::string TraceGraph = buildTraceGraph(Json);
   ASSERT_TRUE(TraceGraph == R"(
 Frontend
-| isIntegerConstantExpr (<test.c:3:18>)
-| EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
+| ParseDeclarationOrFunctionDefinition
+| | isIntegerConstantExpr (<test.c:3:18>)
+| | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
 | PerformPendingInstantiations
 )");
 

@MaggieYingYi
Copy link
Contributor Author

Gentle ping ...

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you for this! Please add a release note letting users know about the improvement when you land the changes.

@MaggieYingYi
Copy link
Contributor Author

Hi @AaronBallman,

Thanks for reviewing and approving the patch.

I have added a release note in the commit 014ad43. Since I haven't ever added a release note before, could you please let me know is it the correct file and the correct location to add a release note? If not, could you please give me some suggestions/links on how to add a release note?

Many thanks,
Maggie

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your release note looks great, just a few formatting related nits with it.

clang/docs/ReleaseNotes.rst Outdated Show resolved Hide resolved
A time trace scope variable of `ParseDeclarationOrFunctionDefinition`
with the function's source location is added to record the time spent
parsing the function's declaration or definition. Another time trace
scope variable of `ParseFunctionDefinition` is also added to record the
name of the defined function. A release note is added as well.

Reviewed by: Aaron Ballman
Pull request: llvm#65268
MaggieYingYi added a commit that referenced this pull request Oct 31, 2023
A time trace scope variable of `ParseDeclarationOrFunctionDefinition`
with the function's source location is added to record the time spent
parsing the function's declaration or definition. Another time trace
scope variable of `ParseFunctionDefinition` is also added to record the
name of the defined function. A release note is added as well.

Reviewed by: Aaron Ballman
Pull request: #65268
@MaggieYingYi
Copy link
Contributor Author

I tried to use squash and merge but the commit message using the wrong email address. Therefore, I have committed the changes using the git command.

@nikic
Copy link
Contributor

nikic commented Oct 31, 2023

I've reverted this change because it causes a large compile-time regression: http://llvm-compile-time-tracker.com/compare.php?from=61b9176cf70444c54f3ac6eebd82fc9ffd69944d&to=33b85867e30e1adc2ff2173039c199b81c10f52b&stat=instructions:u

From a quick glance at your implementation, I suspect the issues is that the used strings may be expensive to compute. TimeTraceScope has an overload accepting a closure that calculates the detail string for such cases.

@MaggieYingYi
Copy link
Contributor Author

Hi @nikic, thanks for spotting the issue and reverted the commit.

Hi @AaronBallman,

As @nikic mentioned that the issue is that the used strings may be expensive to compute. If we remove the change to record the function name and function location, I think the issue will be fixed.

This means:

  1. Remove the time trace scope variable of ParseFunctionDefinition.
llvm::TimeTraceScope TimeScope(
      "ParseFunctionDefinition",
      Actions.GetNameForDeclarator(D).getName().getAsString());
  1. Remove the source location for the time trace scope variable of "ParseDeclarationOrFunctionDefinition".
    Change:
  llvm::TimeTraceScope TimeScope(
      "ParseDeclarationOrFunctionDefinition",
      Tok.getLocation().printToString(
          Actions.getASTContext().getSourceManager()));

To:

  llvm::TimeTraceScope TimeScope(
      "ParseDeclarationOrFunctionDefinition");

Could you please let me know your thoughts? or any suggestion?

Thanks,
Maggie

@MaggieYingYi MaggieYingYi reopened this Oct 31, 2023
@AaronBallman
Copy link
Collaborator

Hi @nikic, thanks for spotting the issue and reverted the commit.

Hi @AaronBallman,

As @nikic mentioned that the issue is that the used strings may be expensive to compute. If we remove the change to record the function name and function location, I think the issue will be fixed.

This means:

1. Remove the time trace scope variable of `ParseFunctionDefinition`.
llvm::TimeTraceScope TimeScope(
      "ParseFunctionDefinition",
      Actions.GetNameForDeclarator(D).getName().getAsString());
2. Remove the source location for the time trace scope variable of "ParseDeclarationOrFunctionDefinition".
   Change:
  llvm::TimeTraceScope TimeScope(
      "ParseDeclarationOrFunctionDefinition",
      Tok.getLocation().printToString(
          Actions.getASTContext().getSourceManager()));

To:

  llvm::TimeTraceScope TimeScope(
      "ParseDeclarationOrFunctionDefinition");

Could you please let me know your thoughts? or any suggestion?

Thanks, Maggie

I think the suggestion from @nikic is to use the TimeTraceScope constructor accepting a function reference instead of the one accepting a string. e.g., change to using something like:

 llvm::TimeTraceScope TimeScope(
      "ParseDeclarationOrFunctionDefinition", [&] {    
        return Tok.getLocation().printToString(
            Actions.getASTContext().getSourceManager())
         });

…tead of the one accepting a string to avoid the compile-time regression.
@MaggieYingYi
Copy link
Contributor Author

Hi @AaronBallman,

Thanks so much for the explanation using an example. I now understand. I have updated the patch in the commit f66e7d2.

Kind regards,
Maggie

@MaggieYingYi
Copy link
Contributor Author

Hi @AaronBallman and @nikic, are you happy for me to recommit the change? Thanks

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you!

MaggieYingYi added a commit that referenced this pull request Nov 1, 2023
A time trace scope variable of `ParseDeclarationOrFunctionDefinition`
with the function's source location is added to record the time spent
parsing the function's declaration or definition. Another time trace
scope variable of `ParseFunctionDefinition` is also added to record the
name of the defined function. A release note is added as well.

Reviewed by: Aaron Ballman
Pull request: #65268
@MaggieYingYi
Copy link
Contributor Author

Thanks @AaronBallman, the patch is recommitted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants