1 /* Copyright 2016 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/internal/tfprof_show_multi.h"
17
18 #include <memory>
19 #include <set>
20
21 #include "tensorflow/core/lib/strings/str_util.h"
22 #include "tensorflow/core/lib/strings/stringprintf.h"
23 #include "tensorflow/core/platform/env.h"
24 #include "tensorflow/core/platform/regexp.h"
25 #include "tensorflow/core/profiler/internal/tfprof_scope.h"
26
27 namespace tensorflow {
28 namespace tfprof {
29
Show(const string & prefix,const Options & opts)30 const MultiGraphNodeProto& TFMultiShow::Show(const string& prefix,
31 const Options& opts) {
32 if (opts.output_type == kOutput[0]) {
33 Timeline timeline(opts.step, opts.output_options.at(kTimelineOpts[0]));
34 return ShowInternal(opts, &timeline)->proto();
35 } else {
36 const ShowMultiNode* ret = ShowInternal(opts, nullptr);
37 if (opts.output_type == kOutput[1]) {
38 printf("%s", (prefix + ret->formatted_str).c_str());
39 fflush(stdout);
40 } else if (opts.output_type == kOutput[2]) {
41 Status s = WriteStringToFile(Env::Default(),
42 opts.output_options.at(kFileOpts[0]),
43 prefix + ret->formatted_str);
44 if (!s.ok()) {
45 fprintf(stderr, "%s\n", s.ToString().c_str());
46 }
47 } else if (opts.output_type == kOutput[3] ||
48 opts.output_type == kOutput[4]) {
49 } else {
50 fprintf(stderr, "Unknown output type: %s\n", opts.output_type.c_str());
51 }
52 return ret->proto();
53 }
54 }
55
ShouldShow(const ShowMultiNode * node,const Options & opts,int depth) const56 bool TFMultiShow::ShouldShow(const ShowMultiNode* node, const Options& opts,
57 int depth) const {
58 // Always show kTFProfRoot.
59 if (node->name() == kTFProfRoot) return true;
60
61 // TODO(xpan): Think more carefully about node filtering in code view.
62 // Unlike graph/scope view, which users want to see the exact leaf op.
63 // In code view, users want to see the middle code traces they wrote.
64 //
65 // This is a subtle difference from scope/graph view. Usually mostly
66 // want to see the middle code traces (i.e. their own codes.), instead
67 // of the TensorFlow internal codes traces.
68 if (node->proto().total_requested_bytes() < opts.min_bytes ||
69 node->proto().total_peak_bytes() < opts.min_peak_bytes ||
70 node->proto().total_residual_bytes() < opts.min_residual_bytes ||
71 node->proto().total_output_bytes() < opts.min_output_bytes ||
72 node->proto().total_exec_micros() < opts.min_micros ||
73 node->proto().total_accelerator_exec_micros() <
74 opts.min_accelerator_micros ||
75 node->proto().total_cpu_exec_micros() < opts.min_cpu_micros ||
76 node->proto().total_parameters() < opts.min_params ||
77 node->proto().total_float_ops() < opts.min_float_ops ||
78 depth > opts.max_depth || !ShouldShowIfExtra(node, opts, depth)) {
79 return false;
80 }
81
82 bool show = false;
83 if (opts.show_name_regexes.size() == 1 && opts.show_name_regexes[0] == ".*") {
84 show = true;
85 } else {
86 for (const string& regex : opts.show_name_regexes) {
87 if (RE2::FullMatch(node->name(), regex)) {
88 show = true;
89 break;
90 }
91 }
92 }
93 // Don't show if show_name_regexes don't cover it.
94 if (!show) return false;
95 // Don't show if hide_name_regexes cover it.
96 for (const string& regex : opts.hide_name_regexes) {
97 if (RE2::FullMatch(node->name(), regex)) return false;
98 }
99 return true;
100 }
101
ShouldTrim(const ShowMultiNode * node,const std::vector<string> & regexes) const102 bool TFMultiShow::ShouldTrim(const ShowMultiNode* node,
103 const std::vector<string>& regexes) const {
104 for (const string& regex : regexes) {
105 if (RE2::FullMatch(node->name(), regex)) {
106 return true;
107 }
108 }
109 return false;
110 }
111
ReAccount(ShowMultiNode * node,const Options & opts)112 bool TFMultiShow::ReAccount(ShowMultiNode* node, const Options& opts) {
113 return node->ReInit(opts.step, opts.account_type_regexes);
114 }
115
FormatLegend(const Options & opts) const116 string TFMultiShow::FormatLegend(const Options& opts) const {
117 std::vector<string> legends;
118 if (opts.select.find(kShown[0]) != opts.select.end()) {
119 legends.push_back("requested bytes");
120 }
121 if (opts.select.find(kShown[11]) != opts.select.end()) {
122 legends.push_back("peak bytes");
123 }
124 if (opts.select.find(kShown[12]) != opts.select.end()) {
125 legends.push_back("residual bytes");
126 }
127 if (opts.select.find(kShown[13]) != opts.select.end()) {
128 legends.push_back("output bytes");
129 }
130 if (opts.select.find(kShown[1]) != opts.select.end()) {
131 legends.push_back("total execution time");
132 legends.push_back("accelerator execution time");
133 legends.push_back("cpu execution time");
134 }
135 if (opts.select.find(kShown[9]) != opts.select.end() &&
136 opts.select.find(kShown[1]) == opts.select.end()) {
137 legends.push_back("accelerator execution time");
138 }
139 if (opts.select.find(kShown[10]) != opts.select.end() &&
140 opts.select.find(kShown[1]) == opts.select.end()) {
141 legends.push_back("cpu execution time");
142 }
143 if (opts.select.find(kShown[2]) != opts.select.end()) {
144 legends.push_back("# parameters");
145 }
146 if (opts.select.find(kShown[3]) != opts.select.end()) {
147 legends.push_back("# float_ops");
148 }
149 if (opts.select.find(kShown[5]) != opts.select.end()) {
150 legends.push_back("assigned devices");
151 }
152 if (opts.select.find(kShown[6]) != opts.select.end()) {
153 legends.push_back("op types");
154 }
155 if (opts.select.find(kShown[7]) != opts.select.end()) {
156 legends.push_back("op occurrence (run|defined)");
157 }
158 if (opts.select.find(kShown[8]) != opts.select.end()) {
159 legends.push_back("input shapes");
160 }
161 return strings::Printf("node name | %s\n",
162 str_util::Join(legends, " | ").c_str());
163 }
164
FormatInputShapes(const MultiGraphNodeProto & proto) const165 string TFMultiShow::FormatInputShapes(const MultiGraphNodeProto& proto) const {
166 // input_shape string -> (static defined count, run count, run_micros)
167 std::map<string, std::tuple<int64, int64, int64>> input_shapes_attr;
168 for (int i = 0; i < proto.graph_nodes_size(); ++i) {
169 const GraphNodeProto& gnode = proto.graph_nodes(i);
170 // Convert and sort by input_idx.
171 std::map<int, std::vector<int64>> input_shapes;
172 for (const auto& inp : gnode.input_shapes()) {
173 input_shapes[inp.first] = ShapeProtoToVec(inp.second);
174 }
175
176 std::vector<string> input_vec;
177 for (const auto& s : input_shapes) {
178 if (s.second.empty()) {
179 input_vec.push_back(strings::Printf("%d:unknown", s.first));
180 } else {
181 input_vec.push_back(strings::Printf(
182 "%d:%s", s.first, str_util::Join(s.second, "x").c_str()));
183 }
184 }
185 string shape_type_str = strings::Printf(
186 "input_type: %s", str_util::Join(input_vec, ",\t").c_str());
187 auto t = input_shapes_attr.find(shape_type_str);
188 if (t == input_shapes_attr.end()) {
189 input_shapes_attr.insert(
190 std::make_pair(shape_type_str, std::make_tuple(0, 0, 0)));
191 t = input_shapes_attr.find(shape_type_str);
192 }
193 input_shapes_attr[shape_type_str] = std::make_tuple(
194 std::get<0>(t->second) + 1, std::get<1>(t->second) + gnode.run_count(),
195 std::get<2>(t->second) + gnode.exec_micros());
196 }
197 if (input_shapes_attr.empty()) {
198 return "";
199 }
200
201 std::vector<std::pair<string, std::tuple<int64, int64, int64>>>
202 shape_count_vec(input_shapes_attr.begin(), input_shapes_attr.end());
203 std::sort(
204 shape_count_vec.begin(), shape_count_vec.end(),
205 [](const std::pair<const string, std::tuple<int64, int64, int64>>& a,
206 const std::pair<const string, std::tuple<int64, int64, int64>>& b) {
207 return std::get<1>(a.second) > std::get<1>(b.second);
208 });
209
210 std::vector<string> input_types;
211 input_types.reserve(shape_count_vec.size());
212 for (const auto& s : shape_count_vec) {
213 std::tuple<int64, int64, int64> t = s.second;
214 input_types.push_back(strings::Printf(
215 "%s\t(run*%lld|defined*%lld)\texec_time: %s", s.first.c_str(),
216 std::get<1>(t), std::get<0>(t), FormatTime(std::get<2>(t)).c_str()));
217 }
218 return str_util::Join(input_types, "\n");
219 }
220
FormatTimes(const ShowMultiNode * node,const Options & opts) const221 std::vector<string> TFMultiShow::FormatTimes(const ShowMultiNode* node,
222 const Options& opts) const {
223 std::vector<string> attrs;
224 if (opts.select.find(kShown[1]) != opts.select.end()) {
225 attrs.push_back(FormatTotalExecTime(node, opts));
226 attrs.push_back(FormatAcceleratorExecTime(node, opts));
227 attrs.push_back(FormatCPUExecTime(node, opts));
228 }
229 if (opts.select.find(kShown[9]) != opts.select.end() &&
230 opts.select.find(kShown[1]) == opts.select.end()) {
231 attrs.push_back(FormatAcceleratorExecTime(node, opts));
232 }
233 if (opts.select.find(kShown[10]) != opts.select.end() &&
234 opts.select.find(kShown[1]) == opts.select.end()) {
235 attrs.push_back(FormatCPUExecTime(node, opts));
236 }
237 return attrs;
238 }
239
240 } // namespace tfprof
241 } // namespace tensorflow
242