Home | History | Annotate | Download | only in xla
      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 <cctype>
     19 #include <unordered_map>
     20 
     21 #include "tensorflow/core/lib/strings/stringprintf.h"
     22 #include "tensorflow/core/platform/logging.h"
     23 #include "tensorflow/core/platform/types.h"
     24 
     25 namespace xla {
     26 
     27 void MetricTableReport::AddEntry(Entry entry) {
     28   entries_.push_back(std::move(entry));
     29 }
     30 
     31 void MetricTableReport::SetMetricName(string metric_name) {
     32   metric_name_ = std::move(metric_name);
     33 }
     34 
     35 void MetricTableReport::SetEntryName(string entry_name) {
     36   entry_name_ = std::move(entry_name);
     37 }
     38 
     39 void MetricTableReport::SetShowAllEntries() {
     40   max_entries_to_show_ = std::numeric_limits<int64>::max();
     41   max_entries_per_category_to_show_ = std::numeric_limits<int64>::max();
     42   max_metric_proportion_to_show_ = 1.1;  // more than 100%
     43 }
     44 
     45 void MetricTableReport::SetShowCategoryTable() { show_category_table_ = true; }
     46 
     47 void MetricTableReport::SetShowEntryTable() { show_entry_table_ = true; }
     48 
     49 string MetricTableReport::MakeReport(double expected_metric_sum) {
     50   expected_metric_sum_ = expected_metric_sum;
     51   report_.clear();
     52 
     53   // Sort the entries.
     54   const auto metric_greater = [](const Entry& a, const Entry& b) {
     55     return a.metric > b.metric;
     56   };
     57   std::sort(entries_.begin(), entries_.end(), metric_greater);
     58 
     59   // Create the report
     60   AppendLine();
     61   AppendHeader();
     62 
     63   if (show_category_table_) {
     64     AppendLine();
     65     AppendCategoryTable();
     66   }
     67   if (show_entry_table_) {
     68     AppendLine();
     69     AppendEntryTable();
     70   }
     71   AppendLine();
     72 
     73   return std::move(report_);
     74 }
     75 
     76 void MetricTableReport::WriteReportToInfoLog(double expected_metric_sum) {
     77   // Write something to the log normally to get the date-time and file prefix.
     78   LOG(INFO) << "Writing report to log.";
     79 
     80   int64 pos = 0;
     81   const string report = MakeReport(expected_metric_sum);
     82   while (pos < report.size()) {
     83     int64 end_of_line = report.find('\n', pos);
     84     if (end_of_line == string::npos) {
     85       end_of_line = report.size();
     86     }
     87     tensorflow::StringPiece line(report.data() + pos, end_of_line - pos);
     88 
     89     // TODO(b/34779244): Figure out how to do this without the verbose log-line
     90     // prefix. The usual way didn't compile on open source.
     91     LOG(INFO) << line;
     92 
     93     pos = end_of_line + 1;
     94   }
     95 }
     96 
     97 std::vector<MetricTableReport::Category> MetricTableReport::MakeCategories(
     98     const std::vector<Entry>* entries) {
     99   // Create the categories using a category_text -> category map.
    100   std::unordered_map<string, Category> category_map;
    101   for (const Entry& entry : *entries) {
    102     Category& category = category_map[entry.category_text];
    103     category.metric_sum += entry.metric;
    104     category.entries.push_back(&entry);
    105   }
    106 
    107   // Move the categories to a vector.
    108   std::vector<Category> categories;
    109   categories.reserve(category_map.size());
    110   for (auto& key_value_pair : category_map) {
    111     categories.push_back(std::move(key_value_pair.second));
    112     categories.back().category_text = key_value_pair.first;
    113   }
    114 
    115   // Sort the categories.
    116   auto metric_sum_greater = [](const Category& a, const Category& b) {
    117     return a.metric_sum > b.metric_sum;
    118   };
    119   std::sort(categories.begin(), categories.end(), metric_sum_greater);
    120 
    121   return categories;
    122 }
    123 
    124 void MetricTableReport::AppendHeader() {
    125   AppendLine("********** ", metric_name_, " report **********");
    126   AppendLine("There are ", MetricString(expected_metric_sum_), " ",
    127              metric_name_, " in total.");
    128   AppendLine("There are ", MetricString(UnaccountedMetric()), " ", metric_name_,
    129              " (", MetricPercent(UnaccountedMetric()),
    130              ") not accounted for by the data.");
    131   AppendLine("There are ", entries_.size(), " ", entry_name_, ".");
    132 }
    133 
    134 void MetricTableReport::AppendCategoryTable() {
    135   const std::vector<Category> categories = MakeCategories(&entries_);
    136 
    137   AppendLine("********** categories table **********");
    138   AppendLine("The left hand side numbers are ", 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     tensorflow::strings::StrAppend(&text, " (", category.entries.size(), " ",
    157                                    entry_name_, ")");
    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(tensorflow::strings::StrCat("... (", remaining_categories,
    182                                                " more categories)"),
    183                    expected_metric_sum_ - metric_sum, expected_metric_sum_);
    184   }
    185 }
    186 
    187 void MetricTableReport::AppendEntryTable() {
    188   AppendLine("********** ", entry_name_, " table **********");
    189   AppendLine("The left hand side numbers are ", metric_name_, ".");
    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(tensorflow::strings::StrCat("... (", remaining_entries,
    211                                                " more ", entry_name_, ")"),
    212                    expected_metric_sum_ - metric_sum, expected_metric_sum_);
    213   }
    214 }
    215 
    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 
    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 
    243 string MetricTableReport::MetricString(double metric) {
    244   // Round to integer and stringify.
    245   string s1 = tensorflow::strings::StrCat(std::llround(metric));
    246 
    247   // Code below commafies the string, e.g. "1234" becomes "1,234".
    248   tensorflow::StringPiece sp1(s1);
    249   string output;
    250   // Copy leading non-digit characters unconditionally.
    251   // This picks up the leading sign.
    252   while (!sp1.empty() && !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 
    266 string MetricTableReport::MetricPercent(double metric) {
    267   return tensorflow::strings::Printf("%5.2f%%",
    268                                      metric / expected_metric_sum_ * 100.0);
    269 }
    270 
    271 }  // namespace xla
    272