• 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   const int report_size = report.size();
84   while (pos < report_size) {
85     int64 end_of_line = report.find('\n', pos);
86     const int64 _npos = string::npos;
87     if (end_of_line == _npos) {
88       end_of_line = report.size();
89     }
90     absl::string_view line(report.data() + pos, end_of_line - pos);
91 
92     // TODO(b/34779244): Figure out how to do this without the verbose log-line
93     // prefix. The usual way didn't compile on open source.
94     LOG(INFO) << line;
95 
96     pos = end_of_line + 1;
97   }
98 }
99 
MakeCategories(const std::vector<Entry> * entries)100 std::vector<MetricTableReport::Category> MetricTableReport::MakeCategories(
101     const std::vector<Entry>* entries) {
102   // Create the categories using a category_text -> category map.
103   std::unordered_map<string, Category> category_map;
104   for (const Entry& entry : *entries) {
105     Category& category = category_map[entry.category_text];
106     category.metric_sum += entry.metric;
107     category.entries.push_back(&entry);
108   }
109 
110   // Move the categories to a vector.
111   std::vector<Category> categories;
112   categories.reserve(category_map.size());
113   for (auto& key_value_pair : category_map) {
114     categories.push_back(std::move(key_value_pair.second));
115     categories.back().category_text = key_value_pair.first;
116   }
117 
118   // Sort the categories.
119   auto metric_sum_greater = [](const Category& a, const Category& b) {
120     return a.metric_sum > b.metric_sum;
121   };
122   absl::c_sort(categories, metric_sum_greater);
123 
124   return categories;
125 }
126 
AppendHeader()127 void MetricTableReport::AppendHeader() {
128   AppendLine("********** ", metric_name_, " report **********");
129   AppendLine("There are ", MetricString(expected_metric_sum_), " ",
130              metric_name_, " in total.");
131   AppendLine("There are ", MetricString(UnaccountedMetric()), " ", metric_name_,
132              " (", MetricPercent(UnaccountedMetric()),
133              ") not accounted for by the data.");
134   AppendLine("There are ", entries_.size(), " ", entry_name_, ".");
135 }
136 
AppendCategoryTable()137 void MetricTableReport::AppendCategoryTable() {
138   const std::vector<Category> categories = MakeCategories(&entries_);
139 
140   AppendLine("********** categories table for ", metric_name_, " **********");
141   AppendLine();
142 
143   double metric_sum = UnaccountedMetric();
144   int64 categories_shown = 0;
145   for (const auto& category : categories) {
146     if (categories_shown >= max_entries_to_show_ ||
147         metric_sum / expected_metric_sum_ > max_metric_proportion_to_show_) {
148       break;
149     }
150     ++categories_shown;
151     metric_sum += category.metric_sum;
152 
153     // Show the category.
154     string text = category.category_text;
155     if (text.empty()) {
156       text = "[no category]";
157     }
158     absl::StrAppend(&text, " (", category.entries.size(), " ", entry_name_,
159                     ")");
160     AppendTableRow(text, category.metric_sum, metric_sum);
161 
162     // Show the top entries in the category.
163     const char* const kIndentPrefix = "                              * ";
164     int64 entries_to_show = std::min<int64>(max_entries_per_category_to_show_,
165                                             category.entries.size());
166     const int64 category_entries_size = category.entries.size();
167     if (category_entries_size == entries_to_show + 1) {
168       // May as well show the last entry on the line that would otherwise say
169       // that there is a single entry not shown.
170       ++entries_to_show;
171     }
172     for (int64 i = 0; i < entries_to_show; ++i) {
173       AppendLine(kIndentPrefix, MetricPercent(category.entries[i]->metric), " ",
174                  category.entries[i]->short_text);
175     }
176     const int64 remaining_entries = category.entries.size() - entries_to_show;
177     if (remaining_entries > 0) {
178       AppendLine(kIndentPrefix, "... (", remaining_entries, " more ",
179                  entry_name_, ")");
180     }
181   }
182   const int64 remaining_categories = categories.size() - categories_shown;
183   if (remaining_categories > 0) {
184     AppendTableRow(
185         absl::StrCat("... (", remaining_categories, " more categories)"),
186         expected_metric_sum_ - metric_sum, expected_metric_sum_);
187   }
188 }
189 
AppendEntryTable()190 void MetricTableReport::AppendEntryTable() {
191   AppendLine("********** ", entry_name_, " table for ", metric_name_,
192              " **********");
193   AppendLine();
194 
195   double metric_sum = UnaccountedMetric();
196   int64 entries_shown = 0;
197   for (const auto& entry : entries_) {
198     if (entries_shown >= max_entries_to_show_ ||
199         metric_sum / expected_metric_sum_ > max_metric_proportion_to_show_) {
200       break;
201     }
202     ++entries_shown;
203     metric_sum += entry.metric;
204 
205     string text = entry.text;
206     if (text.empty()) {
207       text = "[no entry text]";
208     }
209     AppendTableRow(text, entry.metric, metric_sum);
210   }
211   const int64 remaining_entries = entries_.size() - entries_shown;
212   if (remaining_entries > 0) {
213     AppendTableRow(
214         absl::StrCat("... (", remaining_entries, " more ", entry_name_, ")"),
215         expected_metric_sum_ - metric_sum, expected_metric_sum_);
216   }
217 }
218 
AppendTableRow(const string & text,const double metric,const double running_metric_sum)219 void MetricTableReport::AppendTableRow(const string& text, const double metric,
220                                        const double running_metric_sum) {
221   // This is the widest metric number possible, assuming non-negative metrics,
222   // so align to that width.
223   const int64 max_metric_string_size =
224       MetricString(expected_metric_sum_).size();
225   string metric_string = MetricString(metric);
226 
227   // Don't try to make a gigantic string and crash if expected_metric_sum_ is
228   // wrong somehow.
229   int64 padding_len = 1;
230   const int64 metric_string_size = metric_string.size();
231   if (max_metric_string_size >= metric_string_size) {
232     padding_len += max_metric_string_size - metric_string.size();
233   }
234   string padding(padding_len, ' ');
235   AppendLine(padding, metric_string, " (", MetricPercent(metric), " Σ",
236              MetricPercent(running_metric_sum), ")   ", text);
237 }
238 
UnaccountedMetric()239 double MetricTableReport::UnaccountedMetric() {
240   double metric_sum = 0.0;
241   for (const auto& entry : entries_) {
242     metric_sum += entry.metric;
243   }
244   return expected_metric_sum_ - metric_sum;
245 }
246 
MetricString(double metric)247 string MetricTableReport::MetricString(double metric) {
248   // Round to integer and stringify.
249   string s1 = absl::StrCat(std::llround(metric));
250 
251   // Code below commafies the string, e.g. "1234" becomes "1,234".
252   absl::string_view sp1(s1);
253   string output;
254   // Copy leading non-digit characters unconditionally.
255   // This picks up the leading sign.
256   while (!sp1.empty() && !absl::ascii_isdigit(sp1[0])) {
257     output.push_back(sp1[0]);
258     sp1.remove_prefix(1);
259   }
260   // Copy rest of input characters.
261   for (int64 i = 0, end = sp1.size(); i < end; ++i) {
262     if (i > 0 && (sp1.size() - i) % 3 == 0) {
263       output.push_back(',');
264     }
265     output.push_back(sp1[i]);
266   }
267   return output;
268 }
269 
MetricPercent(double metric)270 string MetricTableReport::MetricPercent(double metric) {
271   return absl::StrFormat("%5.2f%%", metric / expected_metric_sum_ * 100.0);
272 }
273 
274 }  // namespace xla
275