You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tvm.apache.org by GitBox <gi...@apache.org> on 2021/04/08 08:42:11 UTC

[GitHub] [tvm] leandron commented on a change in pull request #7797: [PROFILER] Add CSV output to profiler

leandron commented on a change in pull request #7797:
URL: https://github.com/apache/tvm/pull/7797#discussion_r608446283



##########
File path: src/runtime/profiling.cc
##########
@@ -148,52 +148,209 @@ String ShapeString(const std::vector<NDArray>& shapes) {
   return String(sizes.str());
 }
 
-std::string FormatTable(const std::vector<std::unordered_map<std::string, ObjectRef>>& rows,
-                        std::unordered_set<std::string> hidden_cols = {"Argument Shapes",
-                                                                       "Device"}) {
+String ReportNode::AsCSV() const {
+  // get unique headers
   std::unordered_set<std::string> unique_headers;
 
-  for (auto row : rows) {
+  for (auto row : calls) {
     for (auto p : row) {
       unique_headers.insert(p.first);
     }
   }
 
-  std::vector<std::string> headers = {"Name", "Duration (us)", "Percent"};
+  std::vector<std::string> headers;
+  for (auto x : unique_headers) {
+    headers.push_back(x);
+  }
+
+  std::stringstream s;
+
+  for (size_t i = 0; i < headers.size(); i++) {
+    std::string header = headers[i];
+    s << header;
+    if (i < headers.size() - 1) {
+      s << ",";
+    }
+  }
+  s << std::endl;
+  for (auto row : calls) {
+    for (size_t i = 0; i < headers.size(); i++) {
+      std::string header = headers[i];
+      auto it = row.find(header);
+      if (it != row.end()) {
+        std::string val;
+        if ((*it).second.as<CountNode>()) {
+          s << (*it).second.as<CountNode>()->value;
+        } else if ((*it).second.as<DurationNode>()) {
+          s << (*it).second.as<DurationNode>()->microseconds;
+        } else if ((*it).second.as<PercentNode>()) {
+          s << (*it).second.as<PercentNode>()->percent;
+        } else if ((*it).second.as<StringObj>()) {
+          s << "\"" << Downcast<String>((*it).second) << "\"";
+        }
+      }
+      if (i < headers.size() - 1) {
+        s << ",";
+      }
+    }
+    s << std::endl;
+  }
+  return s.str();
+}
+
+String ReportNode::AsTable(bool sort, bool aggregate) const {
+  // aggregate calls by op hash (or op name if hash is not set) + argument shapes
+  std::vector<Map<String, ObjectRef>> aggregated_calls;
+  if (aggregate) {
+    std::unordered_map<std::string, std::vector<size_t>> aggregates;
+    for (size_t i = 0; i < calls.size(); i++) {
+      auto& frame = calls[i];
+      auto it = frame.find("Hash");
+      std::string name = Downcast<String>(frame["Name"]);
+      if (it != frame.end()) {
+        name = Downcast<String>((*it).second);
+      }
+      if (frame.find("Argument Shapes") != frame.end()) {
+        name += Downcast<String>(frame["Argument Shapes"]);
+      }
+
+      if (aggregates.find(name) == aggregates.end()) {
+        aggregates[name] = {i};
+      } else {
+        aggregates[name].push_back(i);
+      }
+    }
+    for (const auto& p : aggregates) {
+      std::unordered_map<String, ObjectRef> aggregated;
+      for (auto i : p.second) {
+        for (auto& metric : calls[i]) {
+          auto it = aggregated.find(metric.first);
+          if (it == aggregated.end()) {
+            aggregated[metric.first] = metric.second;
+          } else {
+            if (metric.second.as<DurationNode>()) {
+              aggregated[metric.first] = ObjectRef(
+                  make_object<DurationNode>(it->second.as<DurationNode>()->microseconds +
+                                            metric.second.as<DurationNode>()->microseconds));
+            } else if (metric.second.as<CountNode>()) {
+              aggregated[metric.first] = ObjectRef(make_object<CountNode>(
+                  it->second.as<CountNode>()->value + metric.second.as<CountNode>()->value));
+            } else if (metric.second.as<PercentNode>()) {
+              aggregated[metric.first] =
+                  ObjectRef(make_object<PercentNode>(it->second.as<PercentNode>()->percent +
+                                                     metric.second.as<PercentNode>()->percent));
+            } else if (metric.second.as<StringObj>()) {
+              // Don't do anything. Assume the two strings are the same.
+            } else {
+              LOG(FATAL) << "Can only aggregate metrics with types DurationNode, CountNode, "
+                            "PercentNode, and StringObj, but got "
+                         << metric.second->GetTypeKey();
+            }
+          }
+        }
+      }
+      aggregated_calls.push_back(aggregated);
+    }
+  } else {
+    for (auto call : calls) {
+      aggregated_calls.push_back(call);
+    }
+  }
+
+  // sort rows by duration
+  if (sort) {
+    std::sort(aggregated_calls.begin(), aggregated_calls.end(),
+              [&](const Map<String, ObjectRef>& a, const Map<String, ObjectRef>& b) {
+                return a.at("Duration (us)").as<DurationNode>()->microseconds >
+                       b.at("Duration (us)").as<DurationNode>()->microseconds;
+              });
+  }
+
+  // compute columnwise sums
+  std::unordered_map<String, ObjectRef> col_sums;
+  for (auto call : aggregated_calls) {
+    for (auto p : call) {
+      if (p.second.as<CountNode>()) {
+        int64_t val = p.second.as<CountNode>()->value;
+        auto it = col_sums.find(p.first);
+        if (it != col_sums.end()) {
+          val += it->second.as<CountNode>()->value;
+        }
+        col_sums[p.first] = ObjectRef(make_object<CountNode>(val));
+      } else if (p.second.as<DurationNode>()) {
+        double val = p.second.as<DurationNode>()->microseconds;
+        auto it = col_sums.find(p.first);
+        if (it != col_sums.end()) {
+          val += it->second.as<DurationNode>()->microseconds;
+        }
+        col_sums[p.first] = ObjectRef(make_object<DurationNode>(val));
+      } else if (p.second.as<PercentNode>()) {
+        double val = p.second.as<PercentNode>()->percent;
+        auto it = col_sums.find(p.first);
+        if (it != col_sums.end()) {
+          val += it->second.as<PercentNode>()->percent;
+        }
+        col_sums[p.first] = ObjectRef(make_object<PercentNode>(val));
+      }
+    }
+  }
+  col_sums["Name"] = String("Sum");
+  aggregated_calls.push_back({{String("Name"), String("----------")}});  // seperator

Review comment:
       ```suggestion
     aggregated_calls.push_back({{String("Name"), String("----------")}});  // separator
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org