Skip to content

Conversation

@ivanaivanovska
Copy link
Contributor

Added an instant event type and issuing such events each time a template instantiation is deferred.

@hokein hokein requested review from ilya-biryukov and usx95 August 13, 2024 10:35
@github-actions
Copy link

github-actions bot commented Aug 13, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@usx95
Copy link
Contributor

usx95 commented Aug 28, 2024

What is the reason for changing from llvm::TimeTraceProfilerEntry* to std::shared_ptr<DurableEvent>? We are also adding significant devirtualization cost with the added different event types.
Can we do something like this:

// Instant event.
struct TimeProfilerInstantEntry {
  const TimePointType Instant;
  const std::string Name;
  TimeTraceMetadata Metadata;
};
// Duration event.
struct TimeProfilerDurationEntry {
  const TimePointType Start;
  TimePointType End;
  
  const std::string Name;
  TimeTraceMetadata Metadata;
  
  std::vector<std::unique_ptr<TimeTraceProfilerInstantEntry>> InstantEvents; // Instant event associated to the duration event.
}

This will remove the need for different implementations for each event type and reduce the complexity.

We can also move the TimeTraceProfilerEntry decl to TimeProfiler.h. I do not see a point of having just a forward decl there.

@ivanaivanovska ivanaivanovska marked this pull request as ready for review August 29, 2024 13:14
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" llvm:support labels Aug 29, 2024
@llvmbot
Copy link
Member

llvmbot commented Aug 29, 2024

@llvm/pr-subscribers-llvm-support

@llvm/pr-subscribers-clang

Author: None (ivanaivanovska)

Changes

Added an instant event type and issuing such events each time a template instantiation is deferred.


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

5 Files Affected:

  • (modified) clang/lib/Sema/SemaExpr.cpp (+15)
  • (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+13)
  • (modified) clang/unittests/Support/TimeProfilerTest.cpp (+49-2)
  • (modified) llvm/include/llvm/Support/TimeProfiler.h (+7)
  • (modified) llvm/lib/Support/TimeProfiler.cpp (+91-31)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 95f53dfefbcc52..a7dd01c936bac8 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18,6 +18,7 @@
 #include "clang/AST/ASTLambda.h"
 #include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/EvaluatedExprVisitor.h"
@@ -64,6 +65,7 @@
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/ConvertUTF.h"
 #include "llvm/Support/SaveAndRestore.h"
+#include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/TypeSize.h"
 #include <optional>
 
@@ -18046,6 +18048,19 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
                 std::make_pair(Func, PointOfInstantiation));
             // Notify the consumer that a function was implicitly instantiated.
             Consumer.HandleCXXImplicitFunctionInstantiation(Func);
+
+            llvm::timeTraceProfilerInsert("DeferInstantiation", [&]() {
+              llvm::TimeTraceMetadata M;
+              llvm::raw_string_ostream OS(M.Detail);
+              Func->getNameForDiagnostic(OS, getPrintingPolicy(),
+                                         /*Qualified=*/true);
+              if (llvm::isTimeTraceVerbose()) {
+                auto Loc = SourceMgr.getExpansionLoc(Func->getLocation());
+                M.File = SourceMgr.getFilename(Loc);
+                M.Line = SourceMgr.getExpansionLineNumber(Loc);
+              }
+              return M;
+            });
           }
         }
       } else {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 0e064be2391838..550add5495c833 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4983,6 +4983,19 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
       Function->setInstantiationIsPending(true);
       PendingInstantiations.push_back(
         std::make_pair(Function, PointOfInstantiation));
+
+      llvm::timeTraceProfilerInsert("DeferInstantiation", [&]() {
+        llvm::TimeTraceMetadata M;
+        llvm::raw_string_ostream OS(M.Detail);
+        Function->getNameForDiagnostic(OS, getPrintingPolicy(),
+                                       /*Qualified=*/true);
+        if (llvm::isTimeTraceVerbose()) {
+          auto Loc = SourceMgr.getExpansionLoc(Function->getLocation());
+          M.File = SourceMgr.getFilename(Loc);
+          M.Line = SourceMgr.getExpansionLineNumber(Loc);
+        }
+        return M;
+      });
     } else if (TSK == TSK_ImplicitInstantiation) {
       if (AtEndOfTU && !getDiagnostics().hasErrorOccurred() &&
           !getSourceManager().isInSystemHeader(PatternDecl->getBeginLoc())) {
diff --git a/clang/unittests/Support/TimeProfilerTest.cpp b/clang/unittests/Support/TimeProfilerTest.cpp
index f53fe71d630bf5..ccda12e943dd73 100644
--- a/clang/unittests/Support/TimeProfilerTest.cpp
+++ b/clang/unittests/Support/TimeProfilerTest.cpp
@@ -238,13 +238,55 @@ Frontend (test.cc)
             buildTraceGraph(Json));
 }
 
+TEST(TimeProfilerTest, ClassTemplateInstantiations) {
+  std::string Code = R"(
+    template<class T>
+    struct S
+    {
+      void foo() {}
+      void bar();
+    };
+
+    template struct S<double>; // explicit instantiation of S<double>
+
+    void user() {
+      S<int> a; // implicit instantiation of S<int>
+      S<float>* b;
+      b->foo(); // implicit instatiation of S<float> and S<float>::foo()
+    }
+  )";
+
+  setupProfiler();
+  ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc"));
+  std::string Json = teardownProfiler();
+  ASSERT_EQ(R"(
+Frontend (test.cc)
+| ParseClass (S)
+| InstantiateClass (S<double>, test.cc:9)
+| InstantiateFunction (S<double>::foo, test.cc:5)
+| ParseDeclarationOrFunctionDefinition (test.cc:11:5)
+| | ParseFunctionDefinition (user)
+| | | InstantiateClass (S<int>, test.cc:3)
+| | | InstantiateClass (S<float>, test.cc:3)
+| | | DeferInstantiation (S<float>::foo, test.cc:5)
+| PerformPendingInstantiations
+| | InstantiateFunction (S<float>::foo, test.cc:5)
+)",
+            buildTraceGraph(Json));
+}
+
 TEST(TimeProfilerTest, TemplateInstantiations) {
   std::string B_H = R"(
     template <typename T>
-    T fooB(T t) {
+    T fooC(T t) {
       return T();
     }
 
+    template <typename T>
+    constexpr T fooB(T t) {
+      return fooC(t);
+    }
+
     #define MacroTemp(x) template <typename T> void foo##x(T) { T(); }
   )";
 
@@ -267,14 +309,19 @@ TEST(TimeProfilerTest, TemplateInstantiations) {
   std::string Json = teardownProfiler();
   ASSERT_EQ(R"(
 Frontend (test.cc)
+| ParseFunctionDefinition (fooC)
 | ParseFunctionDefinition (fooB)
 | ParseFunctionDefinition (fooMTA)
 | ParseFunctionDefinition (fooA)
 | ParseDeclarationOrFunctionDefinition (test.cc:3:5)
 | | ParseFunctionDefinition (user)
+| | | DeferInstantiation (fooA<int>, a.h:7)
 | PerformPendingInstantiations
 | | InstantiateFunction (fooA<int>, a.h:7)
-| | | InstantiateFunction (fooB<int>, b.h:3)
+| | | InstantiateFunction (fooB<int>, b.h:8)
+| | | | DeferInstantiation (fooC<int>, b.h:3)
+| | | DeferInstantiation (fooMTA<int>, a.h:4)
+| | | InstantiateFunction (fooC<int>, b.h:3)
 | | | InstantiateFunction (fooMTA<int>, a.h:4)
 )",
             buildTraceGraph(Json));
diff --git a/llvm/include/llvm/Support/TimeProfiler.h b/llvm/include/llvm/Support/TimeProfiler.h
index 9e2ba31991f542..9a6bcdefee6c20 100644
--- a/llvm/include/llvm/Support/TimeProfiler.h
+++ b/llvm/include/llvm/Support/TimeProfiler.h
@@ -83,6 +83,8 @@ namespace llvm {
 
 class raw_pwrite_stream;
 
+enum class TimeTraceEventType { CompleteEvent, InstantEvent, AsyncEvent };
+
 struct TimeTraceMetadata {
   std::string Detail;
   // Source file and line number information for the event.
@@ -152,6 +154,11 @@ timeTraceProfilerBegin(StringRef Name,
 TimeTraceProfilerEntry *timeTraceAsyncProfilerBegin(StringRef Name,
                                                     StringRef Detail);
 
+// Mark an instant event.
+void timeTraceProfilerInsert(StringRef Name,
+                             llvm::function_ref<TimeTraceMetadata()> Metadata);
+void timeTraceProfilerInsert(StringRef Name, StringRef Detail);
+
 /// Manually end the last time section.
 void timeTraceProfilerEnd();
 void timeTraceProfilerEnd(TimeTraceProfilerEntry *E);
diff --git a/llvm/lib/Support/TimeProfiler.cpp b/llvm/lib/Support/TimeProfiler.cpp
index c2014028ddadca..31faa36c29f14d 100644
--- a/llvm/lib/Support/TimeProfiler.cpp
+++ b/llvm/lib/Support/TimeProfiler.cpp
@@ -13,6 +13,7 @@
 #include "llvm/Support/TimeProfiler.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/Path.h"
@@ -75,18 +76,18 @@ struct llvm::TimeTraceProfilerEntry {
   const std::string Name;
   TimeTraceMetadata Metadata;
 
-  const bool AsyncEvent = false;
+  TimeTraceEventType EventType;
   TimeTraceProfilerEntry(TimePointType &&S, TimePointType &&E, std::string &&N,
-                         std::string &&Dt, bool Ae)
+                         std::string &&Dt, TimeTraceEventType Et)
       : Start(std::move(S)), End(std::move(E)), Name(std::move(N)), Metadata(),
-        AsyncEvent(Ae) {
+        EventType(Et) {
     Metadata.Detail = std::move(Dt);
   }
 
   TimeTraceProfilerEntry(TimePointType &&S, TimePointType &&E, std::string &&N,
-                         TimeTraceMetadata &&Mt, bool Ae)
+                         TimeTraceMetadata &&Mt, TimeTraceEventType Et)
       : Start(std::move(S)), End(std::move(E)), Name(std::move(N)),
-        Metadata(std::move(Mt)), AsyncEvent(Ae) {}
+        Metadata(std::move(Mt)), EventType(Et) {}
 
   // Calculate timings for FlameGraph. Cast time points to microsecond precision
   // rather than casting duration. This avoids truncation issues causing inner
@@ -104,6 +105,23 @@ struct llvm::TimeTraceProfilerEntry {
   }
 };
 
+struct InProgressEntry {
+  std::unique_ptr<TimeTraceProfilerEntry> Event;
+  std::vector<TimeTraceProfilerEntry> InstantEvents;
+
+  InProgressEntry(TimePointType &&S, TimePointType &&E, std::string &&N,
+                  std::string &&Dt, TimeTraceEventType Et)
+      : Event(std::make_unique<TimeTraceProfilerEntry>(
+            std::move(S), std::move(E), std::move(N), std::move(Dt), Et)),
+        InstantEvents() {}
+
+  InProgressEntry(TimePointType &&S, TimePointType &&E, std::string &&N,
+                  TimeTraceMetadata &&Mt, TimeTraceEventType Et)
+      : Event(std::make_unique<TimeTraceProfilerEntry>(
+            std::move(S), std::move(E), std::move(N), std::move(Mt), Et)),
+        InstantEvents() {}
+};
+
 struct llvm::TimeTraceProfiler {
   TimeTraceProfiler(unsigned TimeTraceGranularity = 0, StringRef ProcName = "",
                     bool TimeTraceVerbose = false)
@@ -114,27 +132,46 @@ struct llvm::TimeTraceProfiler {
     llvm::get_thread_name(ThreadName);
   }
 
-  TimeTraceProfilerEntry *begin(std::string Name,
-                                llvm::function_ref<std::string()> Detail,
-                                bool AsyncEvent = false) {
-    Stack.emplace_back(std::make_unique<TimeTraceProfilerEntry>(
+  TimeTraceProfilerEntry *
+  begin(std::string Name, llvm::function_ref<std::string()> Detail,
+        TimeTraceEventType EventType = TimeTraceEventType::CompleteEvent) {
+    Stack.emplace_back(std::make_unique<InProgressEntry>(
         ClockType::now(), TimePointType(), std::move(Name), Detail(),
-        AsyncEvent));
-    return Stack.back().get();
+        EventType));
+    return Stack.back()->Event.get();
   }
 
   TimeTraceProfilerEntry *
   begin(std::string Name, llvm::function_ref<TimeTraceMetadata()> Metadata,
-        bool AsyncEvent = false) {
-    Stack.emplace_back(std::make_unique<TimeTraceProfilerEntry>(
+        TimeTraceEventType EventType = TimeTraceEventType::CompleteEvent) {
+    Stack.emplace_back(std::make_unique<InProgressEntry>(
+        ClockType::now(), TimePointType(), std::move(Name), Metadata(),
+        EventType));
+    return Stack.back()->Event.get();
+  }
+
+  void insert(std::string Name, llvm::function_ref<std::string()> Detail) {
+    if (Stack.empty())
+      return;
+
+    Stack.back().get()->InstantEvents.emplace_back(TimeTraceProfilerEntry(
+        ClockType::now(), TimePointType(), std::move(Name), Detail(),
+        TimeTraceEventType::InstantEvent));
+  }
+
+  void insert(std::string Name,
+              llvm::function_ref<TimeTraceMetadata()> Metadata) {
+    if (Stack.empty())
+      return;
+
+    Stack.back().get()->InstantEvents.emplace_back(TimeTraceProfilerEntry(
         ClockType::now(), TimePointType(), std::move(Name), Metadata(),
-        AsyncEvent));
-    return Stack.back().get();
+        TimeTraceEventType::InstantEvent));
   }
 
   void end() {
     assert(!Stack.empty() && "Must call begin() first");
-    end(*Stack.back());
+    end(*Stack.back()->Event);
   }
 
   void end(TimeTraceProfilerEntry &E) {
@@ -145,8 +182,14 @@ struct llvm::TimeTraceProfiler {
     DurationType Duration = E.End - E.Start;
 
     // Only include sections longer or equal to TimeTraceGranularity msec.
-    if (duration_cast<microseconds>(Duration).count() >= TimeTraceGranularity)
+    if (duration_cast<microseconds>(Duration).count() >= TimeTraceGranularity) {
       Entries.emplace_back(E);
+      if (Stack.back()->Event.get() == &E) {
+        for (auto &IE : Stack.back()->InstantEvents) {
+          Entries.emplace_back(IE);
+        }
+      }
+    }
 
     // Track total time taken by each "name", but only the topmost levels of
     // them; e.g. if there's a template instantiation that instantiates other
@@ -154,18 +197,17 @@ struct llvm::TimeTraceProfiler {
     // happens to be the ones that don't have any currently open entries above
     // itself.
     if (llvm::none_of(llvm::drop_begin(llvm::reverse(Stack)),
-                      [&](const std::unique_ptr<TimeTraceProfilerEntry> &Val) {
-                        return Val->Name == E.Name;
+                      [&](const std::unique_ptr<InProgressEntry> &Val) {
+                        return Val->Event->Name == E.Name;
                       })) {
       auto &CountAndTotal = CountAndTotalPerName[E.Name];
       CountAndTotal.first++;
       CountAndTotal.second += Duration;
     };
 
-    llvm::erase_if(Stack,
-                   [&](const std::unique_ptr<TimeTraceProfilerEntry> &Val) {
-                     return Val.get() == &E;
-                   });
+    llvm::erase_if(Stack, [&](const std::unique_ptr<InProgressEntry> &Val) {
+      return Val->Event.get() == &E;
+    });
   }
 
   // Write events from this TimeTraceProfilerInstance and
@@ -194,13 +236,15 @@ struct llvm::TimeTraceProfiler {
         J.attribute("pid", Pid);
         J.attribute("tid", int64_t(Tid));
         J.attribute("ts", StartUs);
-        if (E.AsyncEvent) {
+        if (TimeTraceEventType::AsyncEvent == E.EventType) {
           J.attribute("cat", E.Name);
           J.attribute("ph", "b");
           J.attribute("id", 0);
-        } else {
+        } else if (E.EventType == TimeTraceEventType::CompleteEvent) {
           J.attribute("ph", "X");
           J.attribute("dur", DurUs);
+        } else { // instant event
+          J.attribute("ph", "i");
         }
         J.attribute("name", E.Name);
         if (!E.Metadata.isEmpty()) {
@@ -215,7 +259,7 @@ struct llvm::TimeTraceProfiler {
         }
       });
 
-      if (E.AsyncEvent) {
+      if (TimeTraceEventType::AsyncEvent == E.EventType) {
         J.object([&] {
           J.attribute("pid", Pid);
           J.attribute("tid", int64_t(Tid));
@@ -319,7 +363,7 @@ struct llvm::TimeTraceProfiler {
     J.objectEnd();
   }
 
-  SmallVector<std::unique_ptr<TimeTraceProfilerEntry>, 16> Stack;
+  SmallVector<std::unique_ptr<InProgressEntry>, 16> Stack;
   SmallVector<TimeTraceProfilerEntry, 128> Entries;
   StringMap<CountAndDurationType> CountAndTotalPerName;
   // System clock time when the session was begun.
@@ -406,7 +450,8 @@ TimeTraceProfilerEntry *llvm::timeTraceProfilerBegin(StringRef Name,
                                                      StringRef Detail) {
   if (TimeTraceProfilerInstance != nullptr)
     return TimeTraceProfilerInstance->begin(
-        std::string(Name), [&]() { return std::string(Detail); }, false);
+        std::string(Name), [&]() { return std::string(Detail); },
+        TimeTraceEventType::CompleteEvent);
   return nullptr;
 }
 
@@ -414,7 +459,8 @@ TimeTraceProfilerEntry *
 llvm::timeTraceProfilerBegin(StringRef Name,
                              llvm::function_ref<std::string()> Detail) {
   if (TimeTraceProfilerInstance != nullptr)
-    return TimeTraceProfilerInstance->begin(std::string(Name), Detail, false);
+    return TimeTraceProfilerInstance->begin(std::string(Name), Detail,
+                                            TimeTraceEventType::CompleteEvent);
   return nullptr;
 }
 
@@ -422,7 +468,8 @@ TimeTraceProfilerEntry *
 llvm::timeTraceProfilerBegin(StringRef Name,
                              llvm::function_ref<TimeTraceMetadata()> Metadata) {
   if (TimeTraceProfilerInstance != nullptr)
-    return TimeTraceProfilerInstance->begin(std::string(Name), Metadata, false);
+    return TimeTraceProfilerInstance->begin(std::string(Name), Metadata,
+                                            TimeTraceEventType::CompleteEvent);
   return nullptr;
 }
 
@@ -430,10 +477,23 @@ TimeTraceProfilerEntry *llvm::timeTraceAsyncProfilerBegin(StringRef Name,
                                                           StringRef Detail) {
   if (TimeTraceProfilerInstance != nullptr)
     return TimeTraceProfilerInstance->begin(
-        std::string(Name), [&]() { return std::string(Detail); }, true);
+        std::string(Name), [&]() { return std::string(Detail); },
+        TimeTraceEventType::AsyncEvent);
   return nullptr;
 }
 
+void llvm::timeTraceProfilerInsert(StringRef Name, StringRef Detail) {
+  if (TimeTraceProfilerInstance != nullptr)
+    TimeTraceProfilerInstance->insert(std::string(Name),
+                                      [&]() { return std::string(Detail); });
+}
+
+void llvm::timeTraceProfilerInsert(
+    StringRef Name, llvm::function_ref<TimeTraceMetadata()> Metadata) {
+  if (TimeTraceProfilerInstance != nullptr)
+    TimeTraceProfilerInstance->insert(std::string(Name), Metadata);
+}
+
 void llvm::timeTraceProfilerEnd() {
   if (TimeTraceProfilerInstance != nullptr)
     TimeTraceProfilerInstance->end();

Copy link
Contributor

@ilya-biryukov ilya-biryukov left a comment

Choose a reason for hiding this comment

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

I have left quite a few comments, but they are mostly about the code style.
I think this looks really good overall and almost ready to land.

Could you split the change to Sema into a separate PR so that we have time profiler changes separately?

Copy link
Contributor

@usx95 usx95 left a comment

Choose a reason for hiding this comment

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

Thanks. Looks great now.

Should be ready to land. Left some nit comments.

Copy link
Contributor

@usx95 usx95 left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks.

Copy link
Contributor

@usx95 usx95 left a comment

Choose a reason for hiding this comment

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

Thanks for splitting the change.

Looks good to me. Left a nit to remove double negation.

Please fix the change description and title. I will land the change once these are done.

@ivanaivanovska ivanaivanovska changed the title Added instant events and marking defered templated instantiation. Added instant events to llvm TimeProfiler. Oct 8, 2024
We will use them in a follow-up to mark defered templated instantiation.
Copy link
Contributor

@ilya-biryukov ilya-biryukov left a comment

Choose a reason for hiding this comment

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

The change LG, thanks!

One small NIT: let's make sure the [TimeProfiler] tag gets added to the commit title when we land it. It's currently only in the commit, but not in the GitHub PR title, so we might loose it when GitHub pushes the commit to main.

I believe @usx95 was about to push it, I'll leave it up to him.

@ivanaivanovska ivanaivanovska changed the title Added instant events to llvm TimeProfiler. [TimeProfiler] Added instant events to llvm TimeProfiler. Oct 8, 2024
@usx95 usx95 merged commit 871f69f into llvm:main Oct 8, 2024
6 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category llvm:support

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants