• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/compiler/xla/metric_table_report.h"
17 
18 #include <unordered_map>
19 
20 #include "absl/strings/ascii.h"
21 #include "absl/strings/str_cat.h"
22 #include "absl/strings/str_format.h"
23 #include "tensorflow/core/platform/logging.h"
24 #include "tensorflow/core/platform/types.h"
25 
26 namespace xla {
27 
AddEntry(Entry entry)28 void MetricTableReport::AddEntry(Entry entry) {
29   entries_.push_back(std::move(entry));
30 }
31 
SetMetricName(string metric_name)32 void MetricTableReport::SetMetricName(string metric_name) {
33   metric_name_ = std::move(metric_name);
34 }
35 
SetEntryName(string entry_name)36 void MetricTableReport::SetEntryName(string entry_name) {
37   entry_name_ = std::move(entry_name);
38 }
39 
SetShowAllEntries()40 void MetricTableReport::SetShowAllEntries() {
41   max_entries_to_show_ = std::numeric_limits<int64>::max();
42   max_entries_per_category_to_show_ = std::numeric_limits<int64>::max();
43   max_metric_proportion_to_show_ = 1.1;  // more than 100%
44 }
45 
SetShowCategoryTable()46 void MetricTableReport::SetShowCategoryTable() { show_category_table_ = true; }
47 
SetShowEntryTable()48 void MetricTableReport::SetShowEntryTable() { show_entry_table_ = true; }
49 
MakeReport(double expected_metric_sum)50 string MetricTableReport::MakeReport(double expected_metric_sum) {
51   expected_metric_sum_ = expected_metric_sum;
52   report_.clear();
53 
54   // Sort the entries.
55   const auto metric_greater = [](const Entry& a, const Entry& b) {
56     return a.metric > b.metric;
57   };
58   absl::c_sort(entries_, metric_greater);
59 
60   // Create the report
61   AppendLine();
62   AppendHeader();
63 
64   if (show_category_table_) {
65     AppendLine();
66     AppendCategoryTable();
67   }
68   if (show_entry_table_) {
69     AppendLine();
70     AppendEntryTable();
71   }
72   AppendLine();
73 
74   return std::move(report_);
75 }
76 
WriteReportToInfoLog(double expected_metric_sum)77 void MetricTableReport::WriteReportToInfoLog(double expected_metric_sum) {
78   // Write something to the log normally to get the date-time and file prefix.
79   LOG(INFO) << "Writing report to log.";
80 
81   int64 pos = 0;
82   const string report = MakeReport(expected_metric_sum);
83   while (pos < report.size()) {
84     int64 end_of_line = report.find('\n', pos);
85     if (end_of_line == string::npos) {
86       end_of_line = report.size();
87     }
88     absl::string_view line(report.data() + pos, end_of_line - pos);
89 
90     // TODO(b/34779244): Figure out how to do this without the verbose log-line
91     // prefix. The usual way didn't compile on open source.
92     LOG(INFO) << line;
93 
94     pos = end_of_line + 1;
95   }
96 }
97 
MakeCategories(const std::vector<Entry> * entries)98 std::vector<MetricTableReport::Category> MetricTableReport::MakeCategories(
99     const std::vector<Entry>* entries) {
100   // Create the categories using a category_text -> category map.
101   std::unordered_map<string, Category> category_map;
102   for (const Entry& entry : *entries) {
103     Category& category = category_map[entry.category_text];
104     category.metric_sum += entry.metric;
105     category.entries.push_back(&entry);
106   }
107 
108   // Move the categories to a vector.
109   std::vector<Category> categories;
110   categories.reserve(category_map.size());
111   for (auto& key_value_pair : category_map) {
112     categories.push_back(std::move(key_value_pair.second));
113     categories.back().category_text = key_value_pair.first;
114   }
115 
116   // Sort the categories.
117   auto metric_sum_greater = [](const Category& a, const Category& b) {
118     return a.metric_sum > b.metric_sum;
119   };
120   absl::c_sort(categories, metric_sum_greater);
121 
122   return categories;
123 }
124 
AppendHeader()125 void MetricTableReport::AppendHeader() {
126   AppendLine("********** ", metric_name_, " report **********");
127   AppendLine("There are ", MetricString(expected_metric_sum_), " ",
128              metric_name_, " in total.");
129   AppendLine("There are ", MetricString(UnaccountedMetric()), " ", metric_name_,
130              " (", MetricPercent(UnaccountedMetric()),
131              ") not accounted for by the data.");
132   AppendLine("There are ", entries_.size(), " ", entry_name_, ".");
133 }
134 
AppendCategoryTable()135 void MetricTableReport::AppendCategoryTable() {
136   const std::vector<Category> categories = MakeCategories(&entries_);
137 
138   AppendLine("********** categories table for ", metric_name_, " **********");
139   AppendLine();
140 
141   double metric_sum = UnaccountedMetric();
142   int64 categories_shown = 0;
143   for (const auto& category : categories) {
144     if (categories_shown >= max_entries_to_show_ ||
145         metric_sum / expected_metric_sum_ > max_metric_proportion_to_show_) {
146       break;
147     }
148     ++categories_shown;
149     metric_sum += category.metric_sum;
150 
151     // Show the category.
152     string text = category.category_text;
153     if (text.empty()) {
154       text = "[no category]";
155     }
156     absl::StrAppend(&text, " (", category.entries.size(), " ", entry_name_,
157                     ")");
158     AppendTableRow(text, category.metric_sum, metric_sum);
159 
160     // Show the top entries in the category.
161     const char* const kIndentPrefix = "                              * ";
162     int64 entries_to_show = std::min<int64>(max_entries_per_category_to_show_,
163                                             category.entries.size());
164     if (category.entries.size() == entries_to_show + 1) {
165       // May as well show the last entry on the line that would otherwise say
166       // that there is a single entry not shown.
167       ++entries_to_show;
168     }
169     for (int64 i = 0; i < entries_to_show; ++i) {
170       AppendLine(kIndentPrefix, MetricPercent(category.entries[i]->metric), " ",
171                  category.entries[i]->short_text);
172     }
173     const int64 remaining_entries = category.entries.size() - entries_to_show;
174     if (remaining_entries > 0) {
175       AppendLine(kIndentPrefix, "... (", remaining_entries, " more ",
176                  entry_name_, ")");
177     }
178   }
179   const int64 remaining_categories = categories.size() - categories_shown;
180   if (remaining_categories > 0) {
181     AppendTableRow(
182         absl::StrCat("... (", remaining_categories, " more categories)"),
183         expected_metric_sum_ - metric_sum, expected_metric_sum_);
184   }
185 }
186 
AppendEntryTable()187 void MetricTableReport::AppendEntryTable() {
188   AppendLine("********** ", entry_name_, " table for ", metric_name_,
189              " **********");
190   AppendLine();
191 
192   double metric_sum = UnaccountedMetric();
193   int64 entries_shown = 0;
194   for (const auto& entry : entries_) {
195     if (entries_shown >= max_entries_to_show_ ||
196         metric_sum / expected_metric_sum_ > max_metric_proportion_to_show_) {
197       break;
198     }
199     ++entries_shown;
200     metric_sum += entry.metric;
201 
202     string text = entry.text;
203     if (text.empty()) {
204       text = "[no entry text]";
205     }
206     AppendTableRow(text, entry.metric, metric_sum);
207   }
208   const int64 remaining_entries = entries_.size() - entries_shown;
209   if (remaining_entries > 0) {
210     AppendTableRow(
211         absl::StrCat("... (", remaining_entries, " more ", entry_name_, ")"),
212         expected_metric_sum_ - metric_sum, expected_metric_sum_);
213   }
214 }
215 
AppendTableRow(const string & text,const double metric,const double running_metric_sum)216 void MetricTableReport::AppendTableRow(const string& text, const double metric,
217                                        const double running_metric_sum) {
218   // This is the widest metric number possible, assuming non-negative metrics,
219   // so align to that width.
220   const int64 max_metric_string_size =
221       MetricString(expected_metric_sum_).size();
222   string metric_string = MetricString(metric);
223 
224   // Don't try to make a gigantic string and crash if expected_metric_sum_ is
225   // wrong somehow.
226   int64 padding_len = 1;
227   if (max_metric_string_size >= metric_string.size()) {
228     padding_len += max_metric_string_size - metric_string.size();
229   }
230   string padding(padding_len, ' ');
231   AppendLine(padding, metric_string, " (", MetricPercent(metric), " Σ",
232              MetricPercent(running_metric_sum), ")   ", text);
233 }
234 
UnaccountedMetric()235 double MetricTableReport::UnaccountedMetric() {
236   double metric_sum = 0.0;
237   for (const auto& entry : entries_) {
238     metric_sum += entry.metric;
239   }
240   return expected_metric_sum_ - metric_sum;
241 }
242 
MetricString(double metric)243 string MetricTableReport::MetricString(double metric) {
244   // Round to integer and stringify.
245   string s1 = absl::StrCat(std::llround(metric));
246 
247   // Code below commafies the string, e.g. "1234" becomes "1,234".
248   absl::string_view sp1(s1);
249   string output;
250   // Copy leading non-digit characters unconditionally.
251   // This picks up the leading sign.
252   while (!sp1.empty() && !absl::ascii_isdigit(sp1[0])) {
253     output.push_back(sp1[0]);
254     sp1.remove_prefix(1);
255   }
256   // Copy rest of input characters.
257   for (int64 i = 0; i < sp1.size(); ++i) {
258     if (i > 0 && (sp1.size() - i) % 3 == 0) {
259       output.push_back(',');
260     }
261     output.push_back(sp1[i]);
262   }
263   return output;
264 }
265 
MetricPercent(double metric)266 string MetricTableReport::MetricPercent(double metric) {
267   return absl::StrFormat("%5.2f%%", metric / expected_metric_sum_ * 100.0);
268 }
269 
270 }  // namespace xla
271