Caffe2 - C++ API
A deep learning, cross platform ML framework
net.cc
1 #include "caffe2/core/net.h"
2 
3 #include <set>
4 #include <unordered_map>
5 #include <unordered_set>
6 
7 #include "caffe2/core/operator.h"
8 #include "caffe2/core/static_tracepoint.h"
9 #include "caffe2/core/timer.h"
10 #include "caffe2/proto/caffe2.pb.h"
11 #include "caffe2/utils/proto_utils.h"
12 
13 namespace caffe2 {
14 
15 CAFFE_DEFINE_REGISTRY(NetRegistry, NetBase, const NetDef&, Workspace*);
16 
17 NetBase::NetBase(const NetDef& def, Workspace* /* unused */)
18  : external_input_(def.external_input().begin(), def.external_input().end()),
19  external_output_(
20  def.external_output().begin(),
21  def.external_output().end()),
22  name_(def.name()) {
23  // Go through the operators and make sure that blobs are correctly made.
24  std::set<string> known_blobs(
25  external_input_.begin(), external_input_.end());
26  std::set<string> remaining_output(
27  external_output_.begin(), external_output_.end());
28  for (const auto& blob : known_blobs) {
29  remaining_output.erase(blob);
30  }
31  for (const OperatorDef& op : def.op()) {
32  for (const string& in : op.input()) {
33  if (!known_blobs.count(in)) {
34  if (external_input_.size()) {
35  CAFFE_THROW(
36  "op ",
37  op.type(),
38  ": Source for input ",
39  in,
40  " is unknown for net ",
41  def.name(),
42  ", operator ",
43  ProtoDebugString(op));
44  } else {
45  // If we are not declaring input and output, we will simply VLOG it
46  // for debugging purposes.
47  VLOG(1) << "op " << op.type() << ": input " << in << " is unknown.";
48  }
49  }
50  }
51  for (const string& out : op.output()) {
52  known_blobs.insert(out);
53  remaining_output.erase(out);
54  }
55  }
56  // Finally, check if all declared outputs are being created.
57  CAFFE_ENFORCE(
58  remaining_output.size() == 0,
59  "Some of the blobs are declared as output but never produced by the "
60  "net ",
61  def.name(),
62  ", the first one is ",
63  *remaining_output.begin());
64 }
65 
66 unique_ptr<NetBase> CreateNet(const NetDef& net_def, Workspace* ws) {
67  // In default, we will return a simple network that just runs all operators
68  // sequentially.
69  if (!net_def.has_type()) {
70  return make_unique<SimpleNet>(net_def, ws);
71  }
72  return NetRegistry()->Create(net_def.type(), net_def, ws);
73 }
74 
75 SimpleNet::SimpleNet(const NetDef& net_def, Workspace* ws)
76  : NetBase(net_def, ws) {
77  VLOG(1) << "Constructing SimpleNet " << net_def.name();
78  bool net_def_has_device_option = net_def.has_device_option();
79  // Initialize the operators
80  for (const OperatorDef& operator_def : net_def.op()) {
81  VLOG(1) << "Creating operator " << operator_def.name()
82  << ":" << operator_def.type();
83  if (!operator_def.has_device_option() && net_def_has_device_option) {
84  // In the case that the operator def does not specify a device option but
85  // the net def has a default option, we copy the device option over to the
86  // operator def.
87  OperatorDef temp_def(operator_def);
88  temp_def.mutable_device_option()->CopyFrom(net_def.device_option());
89  operators_.emplace_back(CreateOperator(temp_def, ws));
90  } else {
91  operators_.emplace_back(CreateOperator(operator_def, ws));
92  }
93  }
94 }
95 
96 bool SimpleNet::Run() {
97  VLOG(1) << "Running net " << name_;
98  for (auto& op : operators_) {
99  VLOG(1) << "Running operator " << op->def().name()
100  << "(" << op->def().type() << ").";
101  if (!op->Run()) {
102  LOG(ERROR) << "Operator failed: "
103  << ProtoDebugString(op->def());
104  return false;
105  }
106  }
107  return true;
108 }
109 
110 bool SimpleNet::RunAsync() {
111  VLOG(1) << "Running net " << name_;
112  for (auto& op : operators_) {
113  VLOG(1) << "Running operator " << op->def().name()
114  << "(" << op->def().type() << ").";
115  if (!op->RunAsync()) {
116  LOG(ERROR) << "Operator failed: "
117  << ProtoDebugString(op->def());
118  return false;
119  }
120  }
121  return true;
122 }
123 
124 namespace {
125 template <typename A, typename B>
126 bool PairLargerThan(const std::pair<A, B>& x, const std::pair<A, B>& y) {
127  return x.second > y.second;
128 }
129 }
130 
132  const int warmup_runs,
133  const int main_runs,
134  const bool run_individual) {
135  LOG(INFO) << "Starting benchmark.";
136  LOG(INFO) << "Running warmup runs.";
137  CAFFE_ENFORCE(
138  warmup_runs >= 0,
139  "Number of warm up runs should be non negative, provided ",
140  warmup_runs,
141  ".");
142  for (int i = 0; i < warmup_runs; ++i) {
143  CAFFE_ENFORCE(Run(), "Warmup run ", i, " has failed.");
144  }
145 
146  LOG(INFO) << "Main runs.";
147  CAFFE_ENFORCE(
148  main_runs >= 0,
149  "Number of main runs should be non negative, provided ",
150  main_runs,
151  ".");
152  Timer timer;
153  for (int i = 0; i < main_runs; ++i) {
154  CAFFE_ENFORCE(Run(), "Main run ", i, " has failed.");
155  }
156  auto millis = timer.MilliSeconds();
157  LOG(INFO) << "Main run finished. Milliseconds per iter: "
158  << millis / main_runs
159  << ". Iters per second: " << 1000.0 * main_runs / millis;
160 
161  vector<float> time_per_op(operators_.size(), 0);
162  CaffeMap<string, float> time_per_op_type;
163  if (run_individual) {
164  for (int i = 0; i < main_runs; ++i) {
165  int idx = 0;
166  for (auto& op : operators_) {
167  const string& op_type = op->def().type();
168  timer.Start();
169  CAFFE_ENFORCE(
170  op->Run(),
171  "operator ",
172  op->def().name(),
173  "(",
174  op_type,
175  ") has failed.");
176  float spent = timer.MilliSeconds();
177  time_per_op[idx] += spent;
178  time_per_op_type[op_type] += spent;
179  ++idx;
180  }
181  }
182 
183  int idx = 0;
184  for (auto& op : operators_) {
185  const string& op_type = op->def().type();
186  const string& print_name =
187  (op->def().name().size()
188  ? op->def().name()
189  : (op->def().output_size() ? op->def().output(0) : "NO_OUTPUT"));
190  LOG(INFO) << "Operator #" << idx << " (" << print_name << ", " << op_type
191  << ") " << time_per_op[idx] / main_runs << " ms/iter";
192  ++idx;
193  }
194  LOG(INFO) << "Time per operator type:";
195  // sort by decreasing time spending.
196  std::vector<std::pair<string, float>> time_per_op_type_vec(
197  time_per_op_type.begin(), time_per_op_type.end());
198  std::sort(
199  time_per_op_type_vec.begin(),
200  time_per_op_type_vec.end(),
201  PairLargerThan<string, float>);
202  for (const auto& item : time_per_op_type_vec) {
203  LOG(INFO) << std::setw(15) << std::setfill(' ') << item.second / main_runs
204  << " " << item.first;
205  }
206  }
207  // We will reuse time_per_op to return the result of BenchmarkNet.
208  for (int i = 0; i < time_per_op.size(); ++i) {
209  time_per_op[i] /= main_runs;
210  }
211  time_per_op.insert(time_per_op.begin(), millis / main_runs);
212  return time_per_op;
213 }
214 
215 namespace {
216 
217 REGISTER_NET(simple, SimpleNet);
218 
219 }
220 
221 } // namespace caffe2
Workspace is a class that holds all the related objects created during runtime: (1) all blobs...
Definition: workspace.h:53
float MilliSeconds()
Returns the elapsed time in milliseconds.
Definition: timer.h:31
A simple timer object for measuring time.
Definition: timer.h:16
Simple registry implementation in Caffe2 that uses static variables to register object creators durin...
void Start()
Starts a timer.
Definition: timer.h:24
unique_ptr< NetBase > CreateNet(const NetDef &net_def, Workspace *ws)
Creates a network, accessing / creating blobs in the given workspace.
Definition: net.cc:66
vector< float > TEST_Benchmark(const int warmup_runs, const int main_runs, const bool run_individual) override
Benchmarks a network.
Definition: net.cc:131