github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/lib/Pass/PassTiming.cpp (about)

     1  //===- PassTiming.cpp -----------------------------------------------------===//
     2  //
     3  // Copyright 2019 The MLIR Authors.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //   http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  // =============================================================================
    17  
    18  #include "PassDetail.h"
    19  #include "mlir/Pass/PassManager.h"
    20  #include "llvm/ADT/MapVector.h"
    21  #include "llvm/ADT/SmallVector.h"
    22  #include "llvm/ADT/Statistic.h"
    23  #include "llvm/Support/Format.h"
    24  #include "llvm/Support/FormatVariadic.h"
    25  #include "llvm/Support/Threading.h"
    26  #include <chrono>
    27  
    28  using namespace mlir;
    29  using namespace mlir::detail;
    30  
    31  constexpr llvm::StringLiteral kPassTimingDescription =
    32      "... Pass execution timing report ...";
    33  
    34  namespace {
    35  /// Simple record class to record timing information.
    36  struct TimeRecord {
    37    TimeRecord(double wall = 0.0, double user = 0.0) : wall(wall), user(user) {}
    38  
    39    TimeRecord &operator+=(const TimeRecord &other) {
    40      wall += other.wall;
    41      user += other.user;
    42      return *this;
    43    }
    44  
    45    /// Print the current time record to 'os', with a breakdown showing
    46    /// contributions to the give 'total' time record.
    47    void print(raw_ostream &os, const TimeRecord &total) {
    48      if (total.user != total.wall)
    49        os << llvm::format("  %7.4f (%5.1f%%)  ", user,
    50                           100.0 * user / total.user);
    51      os << llvm::format("  %7.4f (%5.1f%%)  ", wall, 100.0 * wall / total.wall);
    52    }
    53  
    54    double wall, user;
    55  };
    56  
    57  struct Timer {
    58    explicit Timer(std::string &&name) : name(std::move(name)) {}
    59  
    60    /// Start the timer.
    61    void start() { startTime = std::chrono::system_clock::now(); }
    62  
    63    /// Stop the timer.
    64    void stop() {
    65      auto newTime = std::chrono::system_clock::now() - startTime;
    66      wallTime += newTime;
    67      userTime += newTime;
    68    }
    69  
    70    /// Get or create a child timer with the provided name and id.
    71    Timer *getChildTimer(const void *id,
    72                         std::function<std::string()> &&nameBuilder) {
    73      auto &child = children[id];
    74      if (!child)
    75        child.reset(new Timer(nameBuilder()));
    76      return child.get();
    77    }
    78  
    79    /// Returns the total time for this timer in seconds.
    80    TimeRecord getTotalTime() {
    81      // If we have a valid wall time, then we directly compute the seconds.
    82      if (wallTime.count()) {
    83        return TimeRecord(
    84            std::chrono::duration_cast<std::chrono::duration<double>>(wallTime)
    85                .count(),
    86            std::chrono::duration_cast<std::chrono::duration<double>>(userTime)
    87                .count());
    88      }
    89  
    90      // Otheriwse, accumulate the timing from each of the children.
    91      TimeRecord totalTime;
    92      for (auto &child : children)
    93        totalTime += child.second->getTotalTime();
    94      return totalTime;
    95    }
    96  
    97    /// A map of unique identifiers to child timers.
    98    using ChildrenMap = llvm::MapVector<const void *, std::unique_ptr<Timer>>;
    99  
   100    /// Merge the timing data from 'other' into this timer.
   101    void merge(Timer &&other) {
   102      if (wallTime < other.wallTime)
   103        wallTime = other.wallTime;
   104      userTime += other.userTime;
   105      mergeChildren(std::move(other.children), /*isStructural=*/false);
   106    }
   107  
   108    /// Merge the timer chilren in 'otherChildren' with the children of this
   109    /// timer. If 'isStructural' is true, the children are merged lexographically
   110    /// and 'otherChildren' must have the same number of elements as the children
   111    /// of this timer. Otherwise, the timer children are merged based upon the
   112    /// given timer key.
   113    void mergeChildren(ChildrenMap &&otherChildren, bool isStructural) {
   114      // Check for an empty children list.
   115      if (children.empty()) {
   116        children = std::move(otherChildren);
   117        return;
   118      }
   119  
   120      if (isStructural) {
   121        // If this is a structural merge, the number of children must be the same.
   122        assert(children.size() == otherChildren.size() &&
   123               "structural merge requires the same number of children");
   124        auto it = children.begin(), otherIt = otherChildren.begin();
   125        for (auto e = children.end(); it != e; ++it, ++otherIt)
   126          it->second->merge(std::move(*otherIt->second));
   127        return;
   128      }
   129  
   130      // Otherwise, we merge based upon the child timers key.
   131      for (auto &otherChild : otherChildren) {
   132        auto &child = children[otherChild.first];
   133        if (!child)
   134          child = std::move(otherChild.second);
   135        else
   136          child->merge(std::move(*otherChild.second));
   137      }
   138    }
   139  
   140    /// Raw timing information.
   141    std::chrono::time_point<std::chrono::system_clock> startTime;
   142    std::chrono::nanoseconds wallTime = std::chrono::nanoseconds(0);
   143    std::chrono::nanoseconds userTime = std::chrono::nanoseconds(0);
   144  
   145    /// A map of unique identifiers to child timers.
   146    ChildrenMap children;
   147  
   148    /// A descriptive name for this timer.
   149    std::string name;
   150  };
   151  
   152  struct PassTiming : public PassInstrumentation {
   153    PassTiming(PassTimingDisplayMode displayMode) : displayMode(displayMode) {}
   154    ~PassTiming() { print(); }
   155  
   156    /// Setup the instrumentation hooks.
   157    void runBeforePass(Pass *pass, Operation *) override { startPassTimer(pass); }
   158    void runAfterPass(Pass *pass, Operation *) override;
   159    void runAfterPassFailed(Pass *pass, Operation *op) override {
   160      runAfterPass(pass, op);
   161    }
   162    void runBeforeAnalysis(llvm::StringRef name, AnalysisID *id,
   163                           Operation *) override {
   164      startAnalysisTimer(name, id);
   165    }
   166    void runAfterAnalysis(llvm::StringRef, AnalysisID *, Operation *) override;
   167  
   168    /// Print and clear the timing results.
   169    void print();
   170  
   171    /// Start a new timer for the given pass.
   172    void startPassTimer(Pass *pass);
   173  
   174    /// Start a new timer for the given analysis.
   175    void startAnalysisTimer(llvm::StringRef name, AnalysisID *id);
   176  
   177    /// Stop a pass timer.
   178    void stopPassTimer(Pass *pass);
   179  
   180    /// Stop the last active timer.
   181    void stopTimer();
   182  
   183    /// Print the timing result in list mode.
   184    void printResultsAsList(raw_ostream &os, Timer *root, TimeRecord totalTime);
   185  
   186    /// Print the timing result in pipeline mode.
   187    void printResultsAsPipeline(raw_ostream &os, Timer *root,
   188                                TimeRecord totalTime);
   189  
   190    /// Returns a timer for the provided identifier and name.
   191    Timer *getTimer(const void *id, std::function<std::string()> &&nameBuilder) {
   192      auto tid = llvm::get_threadid();
   193  
   194      // If there is no active timer then add to the root timer.
   195      auto &activeTimers = activeThreadTimers[tid];
   196      if (activeTimers.empty()) {
   197        auto &rootTimer = rootTimers[tid];
   198        if (!rootTimer)
   199          rootTimer.reset(new Timer("root"));
   200        auto *timer = rootTimer->getChildTimer(id, std::move(nameBuilder));
   201        activeTimers.push_back(timer);
   202        return timer;
   203      }
   204  
   205      // Otherwise, add this to the active timer.
   206      auto timer = activeTimers.back()->getChildTimer(id, std::move(nameBuilder));
   207      activeTimers.push_back(timer);
   208      return timer;
   209    }
   210  
   211    /// The root top level timers for each thread.
   212    DenseMap<uint64_t, std::unique_ptr<Timer>> rootTimers;
   213  
   214    /// A stack of the currently active pass timers per thread.
   215    DenseMap<uint64_t, SmallVector<Timer *, 4>> activeThreadTimers;
   216  
   217    /// The display mode to use when printing the timing results.
   218    PassTimingDisplayMode displayMode;
   219  };
   220  } // end anonymous namespace
   221  
   222  /// Start a new timer for the given pass.
   223  void PassTiming::startPassTimer(Pass *pass) {
   224    Timer *timer = getTimer(pass, [pass] {
   225      if (isModuleToFunctionAdaptorPass(pass))
   226        return StringRef("Function Pipeline");
   227      return pass->getName();
   228    });
   229  
   230    // We don't actually want to time the adaptor passes, they gather their total
   231    // from their held passes.
   232    if (!isAdaptorPass(pass))
   233      timer->start();
   234  }
   235  
   236  /// Start a new timer for the given analysis.
   237  void PassTiming::startAnalysisTimer(llvm::StringRef name, AnalysisID *id) {
   238    Timer *timer = getTimer(id, [name] { return "(A) " + name.str(); });
   239    timer->start();
   240  }
   241  
   242  /// Stop a pass timer.
   243  void PassTiming::runAfterPass(Pass *pass, Operation *) {
   244    auto tid = llvm::get_threadid();
   245    auto &activeTimers = activeThreadTimers[tid];
   246    assert(!activeTimers.empty() && "expected active timer");
   247    Timer *timer = activeTimers.pop_back_val();
   248  
   249    // If this is an ModuleToFunctionPassAdaptorParallel, then we need to merge in
   250    // the timing data for the other threads.
   251    if (isa<ModuleToFunctionPassAdaptorParallel>(pass)) {
   252      // The asychronous pipeline timers should exist as children of root timers
   253      // for other threads.
   254      for (auto &rootTimer : llvm::make_early_inc_range(rootTimers)) {
   255        // Skip the current thread.
   256        if (rootTimer.first == tid)
   257          continue;
   258        // Check that this thread has no active timers.
   259        assert(activeThreadTimers[tid].empty() && "expected no active timers");
   260  
   261        // Structurally merge this timers children into the parallel
   262        // module-to-function pass timer.
   263        timer->mergeChildren(std::move(rootTimer.second->children),
   264                             /*isStructural=*/true);
   265        rootTimers.erase(rootTimer.first);
   266      }
   267      return;
   268    }
   269  
   270    // Adapator passes aren't timed directly, so we don't need to stop their
   271    // timers.
   272    if (!isAdaptorPass(pass))
   273      timer->stop();
   274  }
   275  
   276  /// Stop a timer.
   277  void PassTiming::runAfterAnalysis(llvm::StringRef, AnalysisID *, Operation *) {
   278    auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
   279    assert(!activeTimers.empty() && "expected active timer");
   280    Timer *timer = activeTimers.pop_back_val();
   281    timer->stop();
   282  }
   283  
   284  /// Utility to print the timer heading information.
   285  static void printTimerHeader(llvm::raw_ostream &os, TimeRecord total) {
   286    os << "===" << std::string(73, '-') << "===\n";
   287    // Figure out how many spaces to description name.
   288    unsigned Padding = (80 - kPassTimingDescription.size()) / 2;
   289    os.indent(Padding) << kPassTimingDescription << '\n';
   290    os << "===" << std::string(73, '-') << "===\n";
   291  
   292    // Print the total time followed by the section headers.
   293    os << llvm::format("  Total Execution Time: %5.4f seconds\n\n", total.wall);
   294    if (total.user != total.wall)
   295      os << "   ---User Time---";
   296    os << "   ---Wall Time---  --- Name ---\n";
   297  }
   298  
   299  /// Utility to print a single line entry in the timer output.
   300  static void printTimeEntry(raw_ostream &os, unsigned indent, StringRef name,
   301                             TimeRecord time, TimeRecord totalTime) {
   302    time.print(os, totalTime);
   303    os.indent(indent) << name << "\n";
   304  }
   305  
   306  /// Print out the current timing information.
   307  void PassTiming::print() {
   308    // Don't print anything if there is no timing data.
   309    if (rootTimers.empty())
   310      return;
   311  
   312    assert(rootTimers.size() == 1 && "expected one remaining root timer");
   313    auto &rootTimer = rootTimers.begin()->second;
   314    auto os = llvm::CreateInfoOutputFile();
   315  
   316    // Print the timer header.
   317    TimeRecord totalTime = rootTimer->getTotalTime();
   318    printTimerHeader(*os, totalTime);
   319  
   320    // Defer to a specialized printer for each display mode.
   321    switch (displayMode) {
   322    case PassTimingDisplayMode::List:
   323      printResultsAsList(*os, rootTimer.get(), totalTime);
   324      break;
   325    case PassTimingDisplayMode::Pipeline:
   326      printResultsAsPipeline(*os, rootTimer.get(), totalTime);
   327      break;
   328    }
   329    printTimeEntry(*os, 0, "Total", totalTime, totalTime);
   330    os->flush();
   331  
   332    // Reset root timers.
   333    rootTimers.clear();
   334    activeThreadTimers.clear();
   335  }
   336  
   337  /// Print the timing result in list mode.
   338  void PassTiming::printResultsAsList(raw_ostream &os, Timer *root,
   339                                      TimeRecord totalTime) {
   340    llvm::StringMap<TimeRecord> mergedTimings;
   341  
   342    std::function<void(Timer *)> addTimer = [&](Timer *timer) {
   343      // Check for timing information.
   344      if (timer->wallTime.count())
   345        mergedTimings[timer->name] += timer->getTotalTime();
   346      for (auto &children : timer->children)
   347        addTimer(children.second.get());
   348    };
   349  
   350    // Add each of the top level timers.
   351    for (auto &topLevelTimer : root->children)
   352      addTimer(topLevelTimer.second.get());
   353  
   354    // Sort the timing information by wall time.
   355    std::vector<std::pair<StringRef, TimeRecord>> timerNameAndTime;
   356    for (auto &it : mergedTimings)
   357      timerNameAndTime.emplace_back(it.first(), it.second);
   358    llvm::array_pod_sort(timerNameAndTime.begin(), timerNameAndTime.end(),
   359                         [](const std::pair<StringRef, TimeRecord> *lhs,
   360                            const std::pair<StringRef, TimeRecord> *rhs) {
   361                           return llvm::array_pod_sort_comparator<double>(
   362                               &rhs->second.wall, &lhs->second.wall);
   363                         });
   364  
   365    // Print the timing information sequentially.
   366    for (auto &timeData : timerNameAndTime)
   367      printTimeEntry(os, 0, timeData.first, timeData.second, totalTime);
   368  }
   369  
   370  /// Print the timing result in pipeline mode.
   371  void PassTiming::printResultsAsPipeline(raw_ostream &os, Timer *root,
   372                                          TimeRecord totalTime) {
   373    std::function<void(unsigned, Timer *)> printTimer = [&](unsigned indent,
   374                                                            Timer *timer) {
   375      printTimeEntry(os, indent, timer->name, timer->getTotalTime(), totalTime);
   376      for (auto &children : timer->children)
   377        printTimer(indent + 2, children.second.get());
   378    };
   379  
   380    // Print each of the top level timers.
   381    for (auto &topLevelTimer : root->children)
   382      printTimer(0, topLevelTimer.second.get());
   383  }
   384  
   385  //===----------------------------------------------------------------------===//
   386  // PassManager
   387  //===----------------------------------------------------------------------===//
   388  
   389  /// Add an instrumentation to time the execution of passes and the computation
   390  /// of analyses.
   391  void PassManager::enableTiming(PassTimingDisplayMode displayMode) {
   392    // Check if pass timing is already enabled.
   393    if (passTiming)
   394      return;
   395    addInstrumentation(new PassTiming(displayMode));
   396    passTiming = true;
   397  }