多线程inference问题
对于caffe前向算法的使用,通常会遇到一种情况:一次初始化,多次进行predict方法,按照caffe的设计,shared_ptr是线程不安全的,如果同时起多个线程同时进行predict,会导致数据参数共享,进而出错。为此需要修改caffe教程中的实现,在每次predict的时候在内存中复制一份网络。
// old init
shared_ptr<Net<float> > net_;
net_.reset(new Net<float>(deploy_file, TEST));
net_->CopyTrainedLayersFrom(trained_file);
// new init
shared_ptr<Net<float> > net_;
NetParameter param_;
ReadNetParamsFromTextFileOrDie(deploy_file, ¶m_);
param_.mutable_state()->set_phase(TEST);
net_.reset(new Net<float>(param_));
net_->CopyTrainedLayersFrom(trained_file);
// old predict
net_->Forward();
// new predict
shared_ptr<Net<float> > net_tmp;
net_tmp.reset(new Net<float>(param_));
net_tmp->ShareTrainedLayersWith(net_.get());
net_->Forward();
网络复制优化
上面这段代码基本可以解决问题,由于ShareTrainedLayersWith
是从内存中将网络参数复制过来,效率很高。但new Net<float>(param_)
这个方法由于要基于param_重新构建一个网络并初始化参数,这个性能是很低的,而且对于predict来说也是没有意义的,因为后面还会用ShareTrainedLayersWith方法再次重新赋值。通常weight_filler
还使用xavier
或者gauss
初始化,更加重了初始化的计算量,即使忽略这个参数,也还是会用constant
进行初始化。为此,我们只能更改caffe的源码来屏蔽weight和bias初始化的动作。
vim src/caffe/layers/inner_product_layer.cpp
##################
//weight_filler->Fill(this->blobs_[0].get());
//bias_filler->Fill(this->blobs_[1].get());
##################
vim src/caffe/layers/base_conv_layer.cpp
##################
//weight_filler->Fill(this->blobs_[0].get());
//bias_filler->Fill(this->blobs_[1].get());
而且predict的过程,由于没有backward方法,也不需要使用net中的diff参数。所以在初始化的过程中也可以屏蔽。
vim src/caffe/blob.cpp
##################
//diff_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
vtune使用
上述这种优化都是通过vtune工具分析代码的运行时间分析了程序初始化过程中的耗时的点,从而进行的优化。vtune大法好,vtune是intel的代码性能优化工具。
vtune_amplifier_xe/bin64/amplxe-gui