Caffe2 - C++ API
A deep learning, cross platform ML framework
caffe2_benchmark.cc
1 #include <fstream>
2 #include <iterator>
3 #include <string>
4 
5 #include "caffe2/core/blob_serialization.h"
6 #include "caffe2/core/init.h"
7 #include "caffe2/core/logging.h"
8 #include "caffe2/core/operator.h"
9 #include "caffe2/proto/caffe2.pb.h"
10 #include "caffe2/utils/proto_utils.h"
11 #include "caffe2/utils/string_utils.h"
12 
13 #include "observers/observer_config.h"
14 
15 CAFFE2_DEFINE_string(
16  backend,
17  "builtin",
18  "The backend to use when running the model. The allowed "
19  "backend choices are: builtin, default, nnpack, eigen, mkl");
20 CAFFE2_DEFINE_string(
21  init_net,
22  "",
23  "The given net to initialize any parameters.");
24 CAFFE2_DEFINE_string(
25  input,
26  "",
27  "Input that is needed for running the network. If "
28  "multiple input needed, use comma separated string.");
29 CAFFE2_DEFINE_string(
30  input_dims,
31  "",
32  "Alternate to input_files, if all inputs are simple "
33  "float TensorCPUs, specify the dimension using comma "
34  "separated numbers. If multiple input needed, use "
35  "semicolon to separate the dimension of different "
36  "tensors.");
37 CAFFE2_DEFINE_string(
38  input_file,
39  "",
40  "Input file that contain the serialized protobuf for "
41  "the input blobs. If multiple input needed, use comma "
42  "separated string. Must have the same number of items "
43  "as input does.");
44 CAFFE2_DEFINE_string(
45  input_type,
46  "float",
47  "Input type when specifying the input dimension."
48  "The supported types are float, uint8_t.");
49 CAFFE2_DEFINE_int(iter, 10, "The number of iterations to run.");
50 CAFFE2_DEFINE_string(net, "", "The given net to benchmark.");
51 CAFFE2_DEFINE_string(
52  output,
53  "",
54  "Output that should be dumped after the execution "
55  "finishes. If multiple outputs are needed, use comma "
56  "separated string. If you want to dump everything, pass "
57  "'*' as the output value.");
58 CAFFE2_DEFINE_string(
59  output_folder,
60  "",
61  "The folder that the output should be written to. This "
62  "folder must already exist in the file system.");
63 CAFFE2_DEFINE_bool(
64  run_individual,
65  false,
66  "Whether to benchmark individual operators.");
67 CAFFE2_DEFINE_bool(
68  text_output,
69  false,
70  "Whether to write out output in text format for regression purpose.");
71 CAFFE2_DEFINE_int(warmup, 0, "The number of iterations to warm up.");
72 
73 using std::string;
74 using std::unique_ptr;
75 using std::vector;
76 
77 static void writeTextOutput(
78  caffe2::TensorCPU* tensor,
79  const string& output_prefix,
80  const string& name) {
81  string output_name = output_prefix + "/" + name + ".txt";
83  caffe2::BlobProto blob_proto;
84  ser.Serialize(
85  *tensor, output_name, blob_proto.mutable_tensor(), 0, tensor->size());
86  blob_proto.set_name(output_name);
87  blob_proto.set_type("Tensor");
88  CAFFE_ENFORCE(blob_proto.has_tensor());
89  caffe2::TensorProto tensor_proto = blob_proto.tensor();
90  vector<float> data;
91  switch (tensor_proto.data_type()) {
92  case caffe2::TensorProto::FLOAT: {
93  std::copy(
94  tensor_proto.float_data().begin(),
95  tensor_proto.float_data().end(),
96  std::back_inserter(data));
97  break;
98  }
99  case caffe2::TensorProto::INT32: {
100  std::copy(
101  tensor_proto.int32_data().begin(),
102  tensor_proto.int32_data().end(),
103  std::back_inserter(data));
104  break;
105  }
106  default:
107  CAFFE_THROW("Unimplemented Blob type.");
108  }
109  std::ofstream output_file(output_name);
110  std::ostream_iterator<float> output_iterator(output_file, "\n");
111  std::copy(data.begin(), data.end(), output_iterator);
112 }
113 
114 int main(int argc, char** argv) {
115  caffe2::GlobalInit(&argc, &argv);
117  unique_ptr<caffe2::Workspace> workspace(new caffe2::Workspace());
118 
119  // Run initialization network.
120  caffe2::NetDef init_net_def;
121  CAFFE_ENFORCE(ReadProtoFromFile(caffe2::FLAGS_init_net, &init_net_def));
122  CAFFE_ENFORCE(workspace->RunNetOnce(init_net_def));
123 
124  // Load input.
125  if (caffe2::FLAGS_input.size()) {
126  vector<string> input_names = caffe2::split(',', caffe2::FLAGS_input);
127  if (caffe2::FLAGS_input_file.size()) {
128  vector<string> input_files = caffe2::split(',', caffe2::FLAGS_input_file);
129  CAFFE_ENFORCE_EQ(
130  input_names.size(),
131  input_files.size(),
132  "Input name and file should have the same number.");
133  for (int i = 0; i < input_names.size(); ++i) {
134  caffe2::BlobProto blob_proto;
135  CAFFE_ENFORCE(caffe2::ReadProtoFromFile(input_files[i], &blob_proto));
136  workspace->CreateBlob(input_names[i])->Deserialize(blob_proto);
137  }
138  } else if (caffe2::FLAGS_input_dims.size()) {
139  vector<string> input_dims_list =
140  caffe2::split(';', caffe2::FLAGS_input_dims);
141  CAFFE_ENFORCE_EQ(
142  input_names.size(),
143  input_dims_list.size(),
144  "Input name and dims should have the same number of items.");
145  for (int i = 0; i < input_names.size(); ++i) {
146  vector<string> input_dims_str = caffe2::split(',', input_dims_list[i]);
147  vector<int> input_dims;
148  for (const string& s : input_dims_str) {
149  input_dims.push_back(caffe2::stoi(s));
150  }
151  if (!workspace->HasBlob(input_names[i])) {
152  workspace->CreateBlob(input_names[i]);
153  }
154  caffe2::TensorCPU* tensor =
155  workspace->GetBlob(input_names[i])->GetMutable<caffe2::TensorCPU>();
156  tensor->Resize(input_dims);
157  if (caffe2::FLAGS_input_type == "float") {
158  tensor->mutable_data<float>();
159  } else {
160  CAFFE_ENFORCE(
161  caffe2::FLAGS_input_type == "uint8_t",
162  "Only supported input types are: float, uint8_t");
163  tensor->mutable_data<uint8_t>();
164  }
165  }
166  } else {
167  CAFFE_THROW(
168  "You requested input tensors, but neither input_file nor "
169  "input_dims is set.");
170  }
171  }
172 
173  // Run main network.
174  caffe2::NetDef net_def;
175  CAFFE_ENFORCE(ReadProtoFromFile(caffe2::FLAGS_net, &net_def));
176  if (caffe2::FLAGS_backend != "builtin") {
177  std::string engine = caffe2::FLAGS_backend == "nnpack"
178  ? "NNPACK"
179  : caffe2::FLAGS_backend == "eigen" ? "EIGEN"
180  : caffe2::FLAGS_backend == "mkl"
181  ? "MKLDNN"
182  : caffe2::FLAGS_backend == "default" ? "" : "NONE";
183  CAFFE_ENFORCE(engine != "NONE", "Backend is not supported");
184  for (int i = 0; i < net_def.op_size(); i++) {
185  caffe2::OperatorDef* op_def = net_def.mutable_op(i);
186  op_def->set_engine(engine);
187  }
188  }
189 
190  caffe2::NetBase* net = workspace->CreateNet(net_def);
191  CHECK_NOTNULL(net);
192 
193  LOG(INFO) << "Starting benchmark.";
194  caffe2::ObserverConfig::initSampleRate(
195  1, 1, 1, caffe2::FLAGS_run_individual, caffe2::FLAGS_warmup);
196  LOG(INFO) << "Running warmup runs.";
197  for (int i = 0; i < caffe2::FLAGS_warmup; ++i) {
198  CAFFE_ENFORCE(net->Run(), "Warmup run ", i, " has failed.");
199  }
200 
201  LOG(INFO) << "Main runs.";
202  CAFFE_ENFORCE(
203  caffe2::FLAGS_iter >= 0,
204  "Number of main runs should be non negative, provided ",
205  caffe2::FLAGS_iter,
206  ".");
207  for (int i = 0; i < caffe2::FLAGS_iter; ++i) {
208  caffe2::ObserverConfig::initSampleRate(1, 1, 1, 0, caffe2::FLAGS_warmup);
209  CAFFE_ENFORCE(net->Run(), "Main run ", i, " has failed.");
210  if (caffe2::FLAGS_run_individual) {
211  caffe2::ObserverConfig::initSampleRate(1, 1, 1, 1, caffe2::FLAGS_warmup);
212  CAFFE_ENFORCE(net->Run(), "Main run ", i, " with operator has failed.");
213  }
214  }
215 
216  string output_prefix = caffe2::FLAGS_output_folder.size()
217  ? caffe2::FLAGS_output_folder + "/"
218  : "";
219  if (caffe2::FLAGS_output.size()) {
220  vector<string> output_names = caffe2::split(',', caffe2::FLAGS_output);
221  if (caffe2::FLAGS_output == "*") {
222  output_names = workspace->Blobs();
223  }
224  for (const string& name : output_names) {
225  CAFFE_ENFORCE(
226  workspace->HasBlob(name),
227  "You requested a non-existing blob: ",
228  name);
229  if (caffe2::FLAGS_text_output) {
230  auto blob = workspace->GetBlob(name)->GetMutable<caffe2::TensorCPU>();
231  writeTextOutput(blob, output_prefix, name);
232  } else {
233  string serialized = workspace->GetBlob(name)->Serialize(name);
234  string output_filename = output_prefix + name;
235  caffe2::WriteStringToFile(serialized, output_filename.c_str());
236  }
237  }
238  }
239 
240  return 0;
241 }
bool GlobalInit(int *pargc, char ***pargv)
Initialize the global environment of caffe2.
Definition: init.cc:18
TensorSerializer is the serializer for Tensors.
TIndex size() const
Returns the size (i.e.
Definition: tensor.h:593
T * mutable_data()
Returns a typed pointer of the underlying storage.
Definition: tensor.h:578
Workspace is a class that holds all the related objects created during runtime: (1) all blobs...
Definition: workspace.h:47
void Serialize(const Blob &blob, const string &name, SerializationAcceptor acceptor) override
Serializes a Blob.
void Resize(Ts...dim_source)
Resizes a tensor.
Definition: tensor.h:288
void ShowLogInfoToStderr()
A utility to allow one to show log info to stderr after the program starts.
Definition: logging.cc:196