1 /* Copyright 2019 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/core/profiler/utils/op_metrics_db_utils.h"
17 
18 #include <algorithm>
19 #include <string>
20 
21 #include "absl/container/flat_hash_map.h"
22 #include "absl/strings/string_view.h"
23 #include "tensorflow/core/platform/logging.h"
24 #include "tensorflow/core/platform/types.h"
25 #include "tensorflow/core/profiler/protobuf/op_metrics.pb.h"
26 #include "tensorflow/core/profiler/utils/math_utils.h"
27 #include "tensorflow/core/profiler/utils/tf_op_utils.h"
28 
29 namespace tensorflow {
30 namespace profiler {
31 
32 const absl::string_view kIdle = "IDLE";
33 
34 namespace {
35 
36 class DeviceTfOpMetricsDbBuilder : public OpMetricsDbBuilder {
37  public:
DeviceTfOpMetricsDbBuilder(OpMetricsDb * db)38   explicit DeviceTfOpMetricsDbBuilder(OpMetricsDb* db)
39       : OpMetricsDbBuilder(db) {}
40 
UpdateTfOpMetricsWithDeviceOpMetrics(absl::string_view tf_op_name,absl::string_view tf_op_type,const OpMetrics & device_op_metrics)41   void UpdateTfOpMetricsWithDeviceOpMetrics(
42       absl::string_view tf_op_name, absl::string_view tf_op_type,
43       const OpMetrics& device_op_metrics) {
44     OpMetrics* tf_op_metrics = OpMetricsDbBuilder::LookupOrInsertNewOpMetrics(
45         /*hlo_module_id=*/0, tf_op_name);
46     if (tf_op_metrics->category().empty()) {
47       tf_op_metrics->set_category(
48           tf_op_type == kUnknownOp ? "Unknown" : std::string(tf_op_type));
49     }
50     tf_op_metrics->set_is_eager(device_op_metrics.is_eager());
51     // The occurrences of a TF-op is the maximum among the occurrences of all
52     // device ops that it contains.
53     tf_op_metrics->set_occurrences(std::max(tf_op_metrics->occurrences(),
54                                             device_op_metrics.occurrences()));
55     tf_op_metrics->set_time_ps(tf_op_metrics->time_ps() +
56                                device_op_metrics.time_ps());
57     tf_op_metrics->set_self_time_ps(tf_op_metrics->self_time_ps() +
58                                     device_op_metrics.self_time_ps());
59     tf_op_metrics->set_flops(tf_op_metrics->flops() +
60                              device_op_metrics.flops());
61     tf_op_metrics->set_bytes_accessed(tf_op_metrics->bytes_accessed() +
62                                       device_op_metrics.bytes_accessed());
63   }
64 };
65 
66 }  // namespace
67 
OpMetricsDbBuilder(OpMetricsDb * db)68 OpMetricsDbBuilder::OpMetricsDbBuilder(OpMetricsDb* db) : db_(db) {
69   DCHECK_NE(db_, nullptr);
70   DCHECK_EQ(db_->metrics_db_size(), 0);
71 }
72 
LookupOrInsertNewOpMetrics(uint64 hlo_module_id,absl::string_view name)73 OpMetrics* OpMetricsDbBuilder::LookupOrInsertNewOpMetrics(
74     uint64 hlo_module_id, absl::string_view name) {
75   OpMetrics*& op_metrics = op_metrics_map_[hlo_module_id][name];
76   if (op_metrics == nullptr) {
77     op_metrics = db_->add_metrics_db();
78     op_metrics->set_hlo_module_id(hlo_module_id);
79     op_metrics->set_name(name.data(), name.size());
80   }
81   return op_metrics;
82 }
83 
IdleTimeRatio(const OpMetricsDb & metrics_db)84 double IdleTimeRatio(const OpMetricsDb& metrics_db) {
85   return 1.0 -
86          SafeDivide(metrics_db.total_op_time_ps(), metrics_db.total_time_ps());
87 }
88 
IdleTimePs(const OpMetricsDb & metrics_db)89 uint64 IdleTimePs(const OpMetricsDb& metrics_db) {
90   if (metrics_db.total_time_ps() <= metrics_db.total_op_time_ps()) return 0;
91   return metrics_db.total_time_ps() - metrics_db.total_op_time_ps();
92 }
93 
AddIdleOp(OpMetricsDb * db)94 void AddIdleOp(OpMetricsDb* db) {
95   uint64 idle_time_ps = IdleTimePs(*db);
96   OpMetrics* metrics = db->add_metrics_db();
97   metrics->set_name(std::string(kIdle));
98   metrics->set_category(std::string(kIdle));
99   metrics->set_occurrences(0);
100   metrics->set_time_ps(idle_time_ps);
101   metrics->set_self_time_ps(idle_time_ps);
102 }
103 
CreateTfMetricsDbFromDeviceOpMetricsDb(const OpMetricsDb & device_op_metrics_db,bool with_idle)104 OpMetricsDb CreateTfMetricsDbFromDeviceOpMetricsDb(
105     const OpMetricsDb& device_op_metrics_db, bool with_idle) {
106   OpMetricsDb tf_op_metrics_db;
107   DeviceTfOpMetricsDbBuilder builder(&tf_op_metrics_db);
108   for (const auto& device_op_metrics : device_op_metrics_db.metrics_db()) {
109     if (IsIdleOp(device_op_metrics)) {
110       if (with_idle) {
111         builder.UpdateTfOpMetricsWithDeviceOpMetrics(kIdle, kIdle,
112                                                      device_op_metrics);
113       }
114     } else if (device_op_metrics.provenance().empty()) {
115       builder.UpdateTfOpMetricsWithDeviceOpMetrics(
116           device_op_metrics.name(), kUnknownOp, device_op_metrics);
117     } else {
118       TfOp tf_op = ParseTfOpFullname(device_op_metrics.provenance());
119       builder.UpdateTfOpMetricsWithDeviceOpMetrics(tf_op.name, tf_op.type,
120                                                    device_op_metrics);
121     }
122   }
123   tf_op_metrics_db.set_total_op_time_ps(
124       device_op_metrics_db.total_op_time_ps());
125 
126   tf_op_metrics_db.set_total_time_ps(
127       with_idle ? device_op_metrics_db.total_time_ps()
128                 : device_op_metrics_db.total_op_time_ps());
129 
130   return tf_op_metrics_db;
131 }
132 }  // namespace profiler
133 }  // namespace tensorflow
134