-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Caffe2 Code Review
*** This is a draft! ***
Page 9 shows SimpleNet::Run and SimpleNet::RunAsync.:q
Both calls ops in the topologically sorted order; whereas SimpleNet::Run calls OperatorBase::Run(stream_id=0), and SimpleNet::RunAsync calls OperatorBased::RunAsync(stream_id=0).
OperatorBase::Run/RunAsync are both virtual methods and are overloaded by Operator, which derives from OperatorBase. Also, Operator::Run/RunAsync calls Operator::RunOnDevice, which is what users are supposed to overload when they define their own operators. The difference is that Operator::Run calls
- Context::SwitchToDeivce
- Operator::Run
- Context::FinishDevice
whereas Operator::RunAsync calls only the first two:
- Context::SwitchToDeivce
- Operator::Run
So SimpleNet::Run actually calls
workspace.CreateNet calls C.create_net:
def CreateNet(net, overwrite=False, input_blobs=None):
if input_blobs is None:
input_blobs = []
for input_blob in input_blobs:
C.create_blob(input_blob)
return CallWithExceptionIntercept(
C.create_net, C.last_failed_op_uuid, StringifyProto(net), overwrite)C.create_net calls gWorkspace->CreateNet:
m.def(
"create_net",
[](py::bytes net_def, bool overwrite) {
caffe2::NetDef proto;
CAFFE_ENFORCE(
ParseProtobufFromLargeString(net_def, &proto),
"Can't parse net proto: ",
std::string(net_def));
CAFFE_ENFORCE(
gWorkspace->CreateNet(proto, overwrite),
"Error creating net with proto: ",
std::string(net_def));
return true;
},
py::arg("net_def"),
py::arg("overwrite") = kPyBindFalse);where gWorkspace is of type WorkSpace.
Workspace::CreateNet calls caffe2::CreateNet:
net_map_[net_def.name()] =
unique_ptr<NetBase>(caffe2::CreateNet(net_def, this));caffe2::CreateNet calls SimpleNet's constructor via make_unique:
unique_ptr<NetBase> CreateNet(const NetDef& net_def, Workspace* ws) {
// In default, we will return a simple network that just runs all operators
// sequentially.
if (!net_def.has_type()) {
return make_unique<SimpleNet>(net_def, ws);
}
return NetRegistry()->Create(net_def.type(), net_def, ws);
}SimpleNet::SimpleNet call SimpleNet::CreateOperator:
SimpleNet::SimpleNet(const NetDef& net_def, Workspace* ws)
: NetBase(net_def, ws) {
VLOG(1) << "Constructing SimpleNet " << net_def.name();
bool net_def_has_device_option = net_def.has_device_option();
// Initialize the operators
for (const OperatorDef& operator_def : net_def.op()) {
VLOG(1) << "Creating operator " << operator_def.name()
<< ":" << operator_def.type();
if (!operator_def.has_device_option() && net_def_has_device_option) {
// In the case that the operator def does not specify a device option but
// the net def has a default option, we copy the device option over to the
// operator def.
OperatorDef temp_def(operator_def);
temp_def.mutable_device_option()->CopyFrom(net_def.device_option());
operators_.emplace_back(CreateOperator(temp_def, ws));
} else {
operators_.emplace_back(CreateOperator(operator_def, ws));
}
}
}where CreateOperator is a global function
unique_ptr<OperatorBase> CreateOperator(
const OperatorDef& operator_def, Workspace* ws);whose implementation class OpertorBase's constructor, which creates blobs according to OperatorDef:
OperatorBase::OperatorBase(const OperatorDef& operator_def, Workspace* ws)
: operator_ws_(ws),
operator_def_(operator_def),
arg_helper_(operator_def_) {
...
for (const string& output_str : operator_def_.output()) {
outputs_.push_back(CHECK_NOTNULL(ws->CreateBlob(output_str)));
}
}Please note that all input/output blobs are created by calling Workspace::CreateBlob, so the workspace would know all blobs.
Please be aware that the blob creation doesn't allocate tensor memory. It is Operator::Input/Output who interpret blob's