diff options
author | A. Unique TensorFlower <gardener@tensorflow.org> | 2018-01-09 17:05:15 -0800 |
---|---|---|
committer | TensorFlower Gardener <gardener@tensorflow.org> | 2018-01-09 17:13:03 -0800 |
commit | 118495de6165237a7027f5d8b77db833ac7210f2 (patch) | |
tree | 187b32bd553da19278096af2f210741d621a1b84 /tensorflow/cc/profiler | |
parent | cf3fb6bc1dfe5862bf03af2a38d0a52463edd77a (diff) |
profiler C++ API.
PiperOrigin-RevId: 181397308
Diffstat (limited to 'tensorflow/cc/profiler')
-rw-r--r-- | tensorflow/cc/profiler/BUILD | 36 | ||||
-rw-r--r-- | tensorflow/cc/profiler/profiler.cc | 57 | ||||
-rw-r--r-- | tensorflow/cc/profiler/profiler.h | 97 | ||||
-rw-r--r-- | tensorflow/cc/profiler/profiler_test.cc | 177 |
4 files changed, 367 insertions, 0 deletions
diff --git a/tensorflow/cc/profiler/BUILD b/tensorflow/cc/profiler/BUILD new file mode 100644 index 0000000000..00799526fc --- /dev/null +++ b/tensorflow/cc/profiler/BUILD @@ -0,0 +1,36 @@ +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) # Apache 2.0 + +load("//tensorflow:tensorflow.bzl", "tf_cuda_cc_test") + +tf_cuda_cc_test( + name = "profiler_test", + srcs = ["profiler_test.cc"], + deps = [ + ":profiler", + "//tensorflow/cc:cc_ops", + "//tensorflow/core:core_cpu", + "//tensorflow/core:framework", + "//tensorflow/core:lib", + "//tensorflow/core:protos_all_cc", + "//tensorflow/core:tensorflow", + "//tensorflow/core:test", + "//tensorflow/core:test_main", + ], +) + +cc_library( + name = "profiler", + srcs = ["profiler.cc"], + hdrs = ["profiler.h"], + deps = [ + "//tensorflow/core:lib", + "//tensorflow/core:protos_all_cc", + "//tensorflow/core/profiler:protos_all_cc", + "//tensorflow/core/profiler:tfprof_options", + "//tensorflow/core/profiler/internal:tfprof_stats", + ], +) diff --git a/tensorflow/cc/profiler/profiler.cc b/tensorflow/cc/profiler/profiler.cc new file mode 100644 index 0000000000..3e55bac73e --- /dev/null +++ b/tensorflow/cc/profiler/profiler.cc @@ -0,0 +1,57 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/cc/profiler/profiler.h" + +namespace tensorflow { +namespace tfprof { + +Profiler::Profiler(const GraphDef& graph) { + std::unique_ptr<GraphDef> graph_ptr(new GraphDef()); + *graph_ptr = graph; + stats_.reset(new TFStats(std::move(graph_ptr), nullptr, nullptr, nullptr)); +} + +void Profiler::AddStep(int64 step, const RunMetadata& run_meta) { + std::unique_ptr<RunMetadata> run_meta_ptr(new RunMetadata()); + *run_meta_ptr = run_meta; + stats_->AddRunMeta(step, std::move(run_meta_ptr)); +} + +GraphNodeProto Profiler::ProfileGraph(const Options& options) { + stats_->BuildView(kCmds[1]); + return stats_->ShowGraphNode(kCmds[1], options); +} + +GraphNodeProto Profiler::ProfileNameScope(const Options& options) { + stats_->BuildView(kCmds[0]); + return stats_->ShowGraphNode(kCmds[0], options); +} + +MultiGraphNodeProto Profiler::ProfileOperations(const Options& options) { + stats_->BuildView(kCmds[3]); + return stats_->ShowMultiGraphNode(kCmds[3], options); +} + +Status Profiler::SerializeToString(string* content) { + if (!content) { + return Status(error::Code::INVALID_ARGUMENT, + "Cannot use null string pointer for SerializeToString."); + } + stats_->SerializeToString(content); + return Status::OK(); +} + +} // namespace tfprof +} // namespace tensorflow diff --git a/tensorflow/cc/profiler/profiler.h b/tensorflow/cc/profiler/profiler.h new file mode 100644 index 0000000000..e1ce315d3c --- /dev/null +++ b/tensorflow/cc/profiler/profiler.h @@ -0,0 +1,97 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef THIRD_PARTY_TENSORFLOW_CC_PROFILER_PROFILER_H_ +#define THIRD_PARTY_TENSORFLOW_CC_PROFILER_PROFILER_H_ + +#include "tensorflow/core/framework/graph.pb.h" +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/profiler/internal/tfprof_stats.h" +#include "tensorflow/core/profiler/tfprof_options.h" +#include "tensorflow/core/profiler/tfprof_output.pb.h" + +namespace tensorflow { +namespace tfprof { + +/// @addtogroup core +/// @{ + +/// A `Profiler` object lets the caller profile the execution of a graph. +/// +/// Example: +/// // First build a graph and run tracing. +/// Scope root = Scope::NewRootScope(); +/// auto a = Placeholder(root, DT_INT32); +/// auto c = Add(root, a, {41}); +/// +/// ClientSession session(root); +/// std::vector<Tensor> outputs; +/// RunOptions run_options; +/// run_options.set_trace_level(RunOptions::FULL_TRACE); +/// RunMetadata run_meta; +/// Status s = session.Run(run_options, { {a, {1}} }, {c}, &outputs, +/// &run_meta); +/// if (!s.ok()) { ... } +/// +/// // Then create profiler to do profiling. +/// GraphDef graph; +/// root.ToGraphDef(&graph); +/// Profiler profiler(graph); +/// profiler.AddStep(0, run_meta); +/// Options opts = ... // TODO(xpan): Support option building API. +/// MultiGraphNodeProto r = profiler.ProfileOperations(opts); +/// +class Profiler { + public: + /// `graph` is the model's GraphDef. + Profiler(const GraphDef& graph); + + /// Adds tracing information `run_meta` to profiler. A `run_meta` is + /// generated by a TensorFlow session run call. `step` is the key + /// to the `run_meta`. When calling ProfileXXX methods, caller can specify + /// `step` in `options` to seletively profile the corresponding `run_meta`. + /// Multiple different `run_meta` can be keyed by the same `step` in order + /// to group them together. + void AddStep(int64 step, const RunMetadata& run_meta); + + /// Profiles the model by organizing nodes in graph structure. + /// Each node is an op and the nodes are contected by the op inputs/outputs. + GraphNodeProto ProfileGraph(const Options& options); + + /// Profiles the model by organizing nodes in name scope structure. + /// Each node is an op, and nodes are organized by the ops' name + /// scope, similar to a filesystem tree. + /// E.g. /foo is the root of operation /foo/matmul_1 and foo/conv_2. + GraphNodeProto ProfileNameScope(const Options& options); + + /// Profiles the model by organizing nodes by operation types. + /// Each node is an operation type (e.g. Conv2D or MatMul), containing all + /// ops belonging to that type in the model. + MultiGraphNodeProto ProfileOperations(const Options& options); + + /// Serialize the profile content (ProfileProto) into a binary string, + /// User can write the string to file for offline analysis by + /// tfprof command-line tools or graphical user interface. + Status SerializeToString(string* content); + + private: + std::unique_ptr<TFStats> stats_; +}; +/// @} + +} // namespace tfprof +} // namespace tensorflow + +#endif // THIRD_PARTY_TENSORFLOW_CC_PROFILER_PROFILER_H_ diff --git a/tensorflow/cc/profiler/profiler_test.cc b/tensorflow/cc/profiler/profiler_test.cc new file mode 100644 index 0000000000..280cd74827 --- /dev/null +++ b/tensorflow/cc/profiler/profiler_test.cc @@ -0,0 +1,177 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/core/platform/test.h" + +#include "tensorflow/cc/ops/standard_ops.h" +#include "tensorflow/cc/profiler/profiler.h" +#include "tensorflow/core/framework/graph.pb.h" +#include "tensorflow/core/framework/tensor.h" +#include "tensorflow/core/graph/default_device.h" +#include "tensorflow/core/lib/io/path.h" +#include "tensorflow/core/platform/env.h" +#include "tensorflow/core/public/session.h" + +namespace tensorflow { +namespace tfprof { + +class ProfilerTest : public ::testing::Test { + protected: + ProfilerTest() {} +}; + +GraphDef CreateGraphDef() { + Scope root = Scope::NewRootScope(); + + auto a = ops::Const<float>(root, {{3, 2}, {-1, 0}}); + + auto x = ops::Const(root.WithOpName("x"), {{1.f}, {1.f}}); + + auto y = ops::MatMul(root.WithOpName("y"), a, x); + + auto y2 = ops::Square(root, y); + + auto y2_sum = ops::Sum(root, y2, 0); + + auto y_norm = ops::Sqrt(root, y2_sum); + + auto y_div = ops::Div(root.WithOpName("y_normalized"), y, y_norm); + + GraphDef def; + TF_CHECK_OK(root.ToGraphDef(&def)); + + return def; +} + +Options Default() { + Options opts(1000, /* max_depth */ + 0, /* min_bytes */ + 0, /* min_peak_bytes */ + 0, /* min_residual_bytes */ + 0, /* min_output_bytes */ + 0, /* min_micros */ + 0, /* min_accelerator_micros */ + 0, /* min_cpu_micros */ + 0, /* min_params */ + 0, /* min_float_ops */ + 0, /* min_occurrence */ + 0, /* step */ + "name", /* order_by */ + {".*"}, /* account_type_regexes */ + {".*"}, /* start_name_regexes */ + {}, /* trim_name_regexes */ + {".*"}, {}, /* hide_name_regexes */ + false, /* account_displayed_op_only */ + {"micros"}, /* select */ + {"none"}, /* output_type */ + {}); + return opts; +} + +template <typename T> +const T* ExtractNode(const T& pb, const string& name) { + if (pb.name() == name) { + return &pb; + } + for (const T& c : pb.children()) { + const T* ret = ExtractNode(c, name); + if (ret) return ret; + } + return nullptr; +} + +TEST_F(ProfilerTest, Basics) { + SessionOptions options; + options.config.set_allow_soft_placement(true); + std::unique_ptr<Session> session(NewSession(options)); + GraphDef def = CreateGraphDef(); + if (options.target.empty()) { + graph::SetDefaultDevice("/gpu:0", &def); + } + + TF_CHECK_OK(session->Create(def)); + + Tensor x(DT_FLOAT, TensorShape({2, 1})); + auto x_flat = x.flat<float>(); + x_flat.setRandom(); + Eigen::Tensor<float, 0, Eigen::RowMajor> inv_norm = + x_flat.square().sum().sqrt().inverse(); + x_flat = x_flat * inv_norm(); + + std::vector<Tensor> outputs; + RunOptions run_options; + run_options.set_trace_level(RunOptions::FULL_TRACE); + RunMetadata run_metadata; + outputs.clear(); + + Profiler profiler(def); + for (int i = 0; i < 2; ++i) { + TF_CHECK_OK(session->Run(run_options, {{"x", x}}, {"y:0", "y_normalized:0"}, + {}, &outputs, &run_metadata)); + profiler.AddStep(i, run_metadata); + CHECK_EQ(size_t{2}, outputs.size()); + } + + std::vector<DeviceAttributes> resp; + TF_CHECK_OK(session->ListDevices(&resp)); + bool has_gpu = false; + for (const auto& dev : resp) { + if (dev.device_type() == "GPU") { + has_gpu = true; + } + } + + GraphNodeProto ret = profiler.ProfileNameScope(Default()); + const GraphNodeProto* matmul = ExtractNode(ret, "y"); + EXPECT_TRUE(matmul); + EXPECT_GT(matmul->exec_micros(), 0); + if (has_gpu) { + EXPECT_GT(matmul->accelerator_exec_micros(), 0); + } else { + EXPECT_EQ(matmul->accelerator_exec_micros(), 0); + } + const GraphNodeProto* square = ExtractNode(ret, "Square"); + EXPECT_TRUE(square); + EXPECT_GT(square->exec_micros(), 0); + if (has_gpu) { + EXPECT_GT(square->accelerator_exec_micros(), 0); + } else { + EXPECT_EQ(square->accelerator_exec_micros(), 0); + } + + Options opts2 = Default(); + opts2.output_type = "timeline"; + string timeline_file = io::JoinPath(testing::TmpDir(), "timeline"); + opts2.output_options["outfile"] = timeline_file; + GraphNodeProto ret2 = profiler.ProfileGraph(opts2); + string s; + TF_CHECK_OK(ReadFileToString(Env::Default(), timeline_file + "_0", &s)); + EXPECT_TRUE(s.find("Square") != s.npos); + + MultiGraphNodeProto ret3 = profiler.ProfileOperations(Default()); + const MultiGraphNodeProto* matmul2 = ExtractNode(ret3, "MatMul"); + EXPECT_TRUE(matmul2); + EXPECT_GT(matmul2->exec_micros(), 0); + if (has_gpu) { + EXPECT_GT(matmul2->accelerator_exec_micros(), 0); + } else { + EXPECT_EQ(matmul2->accelerator_exec_micros(), 0); + } + + TF_CHECK_OK(session->Close()); +} + +} // namespace tfprof +} // namespace tensorflow |