2022-12-21 14:04:41 +08:00
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
# include "precomp.hpp"
# include <opencv2/core/utils/logger.hpp>
# include "net_impl.hpp"
namespace cv { namespace dnn {
CV__DNN_INLINE_NS_BEGIN
# ifdef HAVE_CANN
static std : : shared_ptr < ge : : ModelBufferData > compileCannGraph ( std : : shared_ptr < ge : : Graph > graph ) ;
class NetImplCann CV_FINAL : public Net : : Impl
{
public :
typedef Net : : Impl Base ;
bool newWasSupported , netWasConverted ;
explicit NetImplCann ( const Ptr < Net : : Impl > & basePtr )
: Net : : Impl ( )
{
CV_LOG_INFO ( NULL , " Initializing NetImplCann " ) ;
basePtr_ = basePtr ;
newWasSupported = true ;
netWasConverted = false ;
init ( ) ;
CV_LOG_INFO ( NULL , " Finished initializing NetImplCann " ) ;
}
void init ( )
{
CV_TRACE_FUNCTION ( ) ;
CV_Assert ( basePtr_ ) ;
Net : : Impl & base = * basePtr_ ;
CV_Assert ( ! base . netWasAllocated ) ;
CV_Assert ( ! base . netWasQuantized ) ; // does not support quantized net for now
netInputLayer = base . netInputLayer ;
blobsToKeep = base . blobsToKeep ;
layers = base . layers ;
for ( MapIdToLayerData : : iterator it = layers . begin ( ) ; it ! = layers . end ( ) ; it + + )
{
LayerData & ld = it - > second ;
ld . resetAllocation ( ) ;
}
layerNameToId = base . layerNameToId ;
outputNameToId = base . outputNameToId ;
preferableBackend = DNN_BACKEND_CANN ;
preferableTarget = DNN_TARGET_NPU ; // force using NPU
hasDynamicShapes = base . hasDynamicShapes ;
CV_Assert ( base . backendWrappers . empty ( ) ) ; //backendWrappers = base.backendWrappers;
lastLayerId = base . lastLayerId ;
netWasAllocated = base . netWasAllocated ;
netWasQuantized = base . netWasQuantized ;
fusion = base . fusion ;
}
bool empty ( ) const override
{
return Base : : empty ( ) ;
}
void setPreferableBackend ( Net & net , int backendId ) override
{
if ( backendId = = preferableBackend )
return ; // no-op
else
CV_Error ( Error : : StsError , " DNN: Can't switch backend from CANN to other " ) ;
Ptr < Net : : Impl > & impl_ptr_ref = accessor : : DnnNetAccessor : : getImplPtrRef ( net ) ;
impl_ptr_ref = basePtr_ ;
basePtr_ - > setPreferableBackend ( net , backendId ) ;
}
void setPreferableTarget ( int targetId ) override
{
if ( targetId ! = preferableTarget )
{
CV_Error ( Error : : StsError , " DNN: Can't switch target from NPU to other " ) ;
}
}
Ptr < BackendWrapper > wrap ( Mat & host ) override
{
return Ptr < BackendWrapper > ( new CannBackendWrapper ( host ) ) ;
}
// void fuseLayers(const std::vector<LayerPin>& blobsToKeep_); // fusion is done in the CANN graph engine
void initBackend ( const std : : vector < LayerPin > & blobsToKeep_ ) override ;
void forwardLayer ( LayerData & ld ) override ;
} ;
void NetImplCann : : initBackend ( const std : : vector < LayerPin > & blobsToKeep_ )
{
CV_TRACE_FUNCTION ( ) ;
CV_CheckEQ ( preferableBackend , DNN_BACKEND_CANN , " " ) ;
// netWasAllocated turns to false if requested output is changed or input shape changes
if ( netWasConverted & & netWasAllocated )
return ;
if ( ! netWasConverted )
{
newWasSupported = true ;
for ( MapIdToLayerData : : iterator it = layers . begin ( ) ; it ! = layers . end ( ) ; + + it )
{
auto & ld = it - > second ;
auto layer = ld . layerInstance ;
if ( ld . id ! = 0 & & ! layer - > supportBackend ( preferableBackend ) )
{
newWasSupported = false ;
2023-04-20 15:18:35 +08:00
CV_LOG_ONCE_WARNING ( NULL , " DNN/CANN: layer (name= " < < ld . name < < " , type= " < < ld . type < < " ) is not supported by CANN backend. Going back to default backend on CPU target " ) ;
2022-12-21 14:04:41 +08:00
}
}
}
if ( ! newWasSupported )
return ;
2023-03-14 02:46:33 +08:00
// initialize each blob wrappers' names
for ( MapIdToLayerData : : const_iterator it = layers . begin ( ) ; it ! = layers . end ( ) ; + + it )
{
const LayerData & ld = it - > second ;
if ( ld . id = = 0 )
{
for ( int i = 0 ; i < ld . outputBlobsWrappers . size ( ) ; + + i )
{
auto cannWrapper = ld . outputBlobsWrappers [ i ] . dynamicCast < CannBackendWrapper > ( ) ;
// cannWrapper->name = netInputLayer->outNames.empty() ? cv::format("%s_%d", ld.name.c_str(), i) : netInputLayer->outNames[i];
cannWrapper - > name = std : : string ( " y " ) ;
}
}
else
{
for ( int i = 0 ; i < ld . outputBlobsWrappers . size ( ) ; + + i )
{
auto cannWrapper = ld . outputBlobsWrappers [ i ] . dynamicCast < CannBackendWrapper > ( ) ;
// cannWrapper->name = ld.outputBlobsWrappers.size() > 1 ? (ld.name + ":" + std::to_string(i)) : ld.name;
cannWrapper - > name = ld . outputBlobsWrappers . size ( ) > 1 ? ( std : : string ( " y " ) + std : : to_string ( i ) ) : std : : string ( " y " ) ;
}
}
}
2022-12-21 14:04:41 +08:00
// convert layers to CANN operators,
// collect graph input and output operators,
// collect and input and output wrappers
int firstOutputLayerId = - 1 ;
std : : vector < Ptr < BackendNode > > netInputNodes ;
std : : vector < ge : : Operator > graphInputOps , graphOutputOps ;
std : : vector < Ptr < BackendWrapper > > graphInputWrappers , graphOutputWrappers ;
CV_LOG_INFO ( NULL , " DNN/CANN: converting layers to CANN operators " ) ;
for ( MapIdToLayerData : : iterator it = layers . begin ( ) ; it ! = layers . end ( ) ; + + it )
{
LayerData & ld = it - > second ;
auto layer = ld . layerInstance ;
if ( ld . id = = 0 )
{
for ( int i = 0 ; i < ld . outputBlobsWrappers . size ( ) ; i + + )
{
// retrieve tensor description
auto wrapper = ld . outputBlobsWrappers [ i ] ;
graphInputWrappers . push_back ( wrapper ) ;
auto cannWrapper = wrapper . dynamicCast < CannBackendWrapper > ( ) ;
CV_Assert ( ! cannWrapper . empty ( ) ) ;
2023-03-14 02:46:33 +08:00
// create graph input op
std : : string inputOpName = netInputLayer - > outNames . empty ( ) ? cv : : format ( " %s_%d " , ld . name . c_str ( ) , i ) : netInputLayer - > outNames [ i ] ;
auto inputOp = std : : make_shared < ge : : op : : Data > ( inputOpName ) ;
2022-12-21 14:04:41 +08:00
inputOp - > update_input_desc_x ( * ( cannWrapper - > desc_ ) ) ;
inputOp - > update_output_desc_y ( * ( cannWrapper - > desc_ ) ) ;
graphInputOps . push_back ( * inputOp ) ;
netInputNodes . push_back ( Ptr < BackendNode > ( new CannBackendNode ( inputOp ) ) ) ;
}
}
else
{
ld . skip = true ; // skip all cann operators
std : : vector < Ptr < BackendNode > > layerInputNodes ;
for ( int i = 0 ; i < ld . inputBlobsId . size ( ) ; i + + )
{
int layerInputLid = ld . inputBlobsId [ i ] . lid ;
int layerInputOid = ld . inputBlobsId [ i ] . oid ;
if ( layerInputLid = = 0 )
{
layerInputNodes . push_back ( netInputNodes [ layerInputOid ] ) ;
}
2023-03-14 02:46:33 +08:00
else
2022-12-21 14:04:41 +08:00
{
layerInputNodes . push_back ( layers [ layerInputLid ] . backendNodes [ preferableBackend ] ) ;
}
}
CV_LOG_INFO ( NULL , " DNN/CANN: converting layer " < < ld . name < < " @ " < < ld . type < < " @ " < < ld . id < < " to CANN operator " ) ;
2023-04-20 15:18:35 +08:00
auto backendNode = layer - > initCann ( ld . inputBlobsWrappers , ld . outputBlobsWrappers , layerInputNodes ) ; // it's ok if ld.name is empty
2022-12-21 14:04:41 +08:00
// collect outputs
bool isOutputNode = ld . consumers . size ( ) = = 0 ? true : false ;
if ( isOutputNode )
{
if ( firstOutputLayerId < 0 )
firstOutputLayerId = ld . id ;
auto cannNode = backendNode . dynamicCast < CannBackendNode > ( ) ;
graphOutputOps . push_back ( * ( cannNode - > getOp ( ) ) ) ;
// assume cann graph outputs and dnn net outputs have the same order
for ( int i = 0 ; i < ld . outputBlobsWrappers . size ( ) ; + + i )
{
graphOutputWrappers . push_back ( ld . outputBlobsWrappers [ i ] ) ;
}
}
ld . backendNodes [ preferableBackend ] = backendNode ;
}
}
CV_LOG_INFO ( NULL , " DNN/CANN: done converting layers to CANN operators " ) ;
// build graph from collected graph inputs and outputs
CV_LOG_INFO ( NULL , " DNN/CANN: building ge::Graph " ) ;
2023-03-14 02:46:33 +08:00
std : : string graphName = cv : : format ( " graph_%d " , networkId ) ;
2022-12-21 14:04:41 +08:00
std : : shared_ptr < ge : : Graph > graph = std : : make_shared < ge : : Graph > ( graphName . c_str ( ) ) ;
( void ) graph - > SetInputs ( graphInputOps ) ;
( void ) graph - > SetOutputs ( graphOutputOps ) ;
CV_LOG_INFO ( NULL , " DNN/CANN: done building ge::Graph " ) ;
// convert ge::Graph to OM buffer
CV_LOG_INFO ( NULL , " DNN/CANN: converting ge::Graph to OM buffer " ) ;
std : : shared_ptr < ge : : ModelBufferData > modelBuffer = compileCannGraph ( graph ) ;
CV_LOG_INFO ( NULL , " DNN/CANN: OM buffer size = " < < modelBuffer - > length ) ;
CV_LOG_INFO ( NULL , " DNN/CANN: done building ge::Graph to OM buffer " ) ;
// keep net in the first output node and mark the node runnable
auto & ld = layers [ firstOutputLayerId ] ;
auto cannNode = ld . backendNodes [ preferableBackend ] . dynamicCast < CannBackendNode > ( ) ;
std : : shared_ptr < CannNet > net = std : : shared_ptr < CannNet > ( new CannNet ( ) ) ;
net - > loadModelBuffer ( modelBuffer ) ;
net - > bindInputWrappers ( graphInputWrappers ) ;
net - > bindOutputWrappers ( graphOutputWrappers ) ;
cannNode - > net = net ;
ld . skip = false ;
netWasConverted = true ;
}
void NetImplCann : : forwardLayer ( LayerData & ld )
{
CV_TRACE_FUNCTION ( ) ;
auto layer = ld . layerInstance ;
if ( ! ld . skip )
{
auto it = ld . backendNodes . find ( preferableBackend ) ;
if ( ld . id = = 0 | | it = = ld . backendNodes . end ( ) ) // input layer
{
return Base : : forwardLayer ( ld ) ;
}
CV_Assert ( it ! = ld . backendNodes . end ( ) ) ;
const Ptr < BackendNode > & node = it - > second ;
CV_Assert ( ! node . empty ( ) ) ;
auto cannNode = node . dynamicCast < CannBackendNode > ( ) ;
CV_Assert ( ! cannNode . empty ( ) ) ;
CV_Assert ( cannNode - > net ) ;
TickMeter tm ;
tm . start ( ) ;
cannNode - > net - > forward ( ) ;
tm . stop ( ) ;
int64_t t = tm . getTimeTicks ( ) ;
layersTimings [ ld . id ] = ( t > 0 ) ? t : 1 ;
}
else
{
layersTimings [ ld . id ] = 0 ;
}
ld . flag = 1 ;
}
std : : shared_ptr < ge : : ModelBufferData > compileCannGraph ( std : : shared_ptr < ge : : Graph > graph )
{
const size_t hdrsize = 32 ;
std : : shared_ptr < ge : : ModelBufferData > out_buffer = std : : make_shared < ge : : ModelBufferData > ( ) ;
size_t buf_size = ( 1 < < 27 ) , model_size ; // default buf_size 128 MB
for ( int iter = 0 ; iter < 2 ; + + iter )
{
size_t * shared_buf = ( size_t * ) mmap ( NULL , buf_size + hdrsize , PROT_READ | PROT_WRITE ,
MAP_SHARED | MAP_ANONYMOUS , - 1 , 0 ) ;
uint8_t * model_data = ( uint8_t * ) ( shared_buf + 1 ) ;
pid_t child ;
int childstate = 0 ;
bool ok ;
if ( ( child = fork ( ) ) = = 0 )
{
// initialize engine
std : : map < ge : : AscendString , ge : : AscendString > options = {
{ ge : : AscendString ( ge : : ir_option : : SOC_VERSION ) , ge : : AscendString ( " Ascend310 " ) } ,
} ;
ACL_CHECK_GRAPH_RET ( ge : : aclgrphBuildInitialize ( options ) ) ;
// build
std : : shared_ptr < ge : : ModelBufferData > om_model = std : : make_shared < ge : : ModelBufferData > ( ) ;
std : : map < ge : : AscendString , ge : : AscendString > build_options ;
ACL_CHECK_GRAPH_RET ( aclgrphBuildModel ( * graph , build_options , * om_model ) ) ;
#if 0
// (optional). Dump model
2023-03-14 02:46:33 +08:00
ge : : AscendString graph_name ;
graph - > GetName ( graph_name ) ;
aclgrphDumpGraph ( * graph , graph_name . GetString ( ) , 7 ) ;
2022-12-21 14:04:41 +08:00
// (optional). Save model
aclgrphSaveModel ( graph_name . GetString ( ) , * om_model ) ;
# endif
// finalize engine
ge : : aclgrphBuildFinalize ( ) ;
// send model from child to parent
size_t model_size = om_model - > length ;
* shared_buf = model_size ;
if ( model_size > buf_size )
{
exit ( 1 ) ;
}
else
{
memcpy ( model_data , om_model - > data . get ( ) , model_size ) ;
exit ( 0 ) ;
}
}
waitpid ( child , & childstate , 0 ) ;
model_size = * shared_buf ;
ok = WIFEXITED ( childstate ) & & WEXITSTATUS ( childstate ) = = 0 ;
if ( ok )
{
CV_LOG_INFO ( NULL , " Compile success, model size = " < < model_size ) ;
out_buffer - > data = std : : shared_ptr < uint8_t > ( new uint8_t [ model_size ] ) ;
memcpy ( out_buffer - > data . get ( ) , model_data , model_size ) ;
out_buffer - > length = model_size ;
}
munmap ( shared_buf , buf_size + hdrsize ) ;
if ( ok ) break ;
buf_size = model_size ;
}
return out_buffer ;
}
void switchToCannBackend ( Net & net )
{
CV_TRACE_FUNCTION ( ) ;
Ptr < Net : : Impl > & impl_ptr_ref = accessor : : DnnNetAccessor : : getImplPtrRef ( net ) ;
CV_Assert ( impl_ptr_ref ) ;
CV_LOG_INFO ( NULL , " DNN: switching to CANN backend... (networkID= " < < impl_ptr_ref - > networkId < < " ) " ) ;
Ptr < NetImplCann > impl_ptr_cann = makePtr < NetImplCann > ( impl_ptr_ref ) ;
impl_ptr_ref = impl_ptr_cann ;
}
# endif // HAVE_CANN
CV__DNN_INLINE_NS_END
} } // namespace cv::dnn