44# except in compliance with the License. See the license file in the root
55# directory of this source tree for more details.
66
7+ import collections
78import contextlib
89import struct
910
10- from typing import final , List
11+ from typing import final , Dict , List
1112
1213import mtk_converter
1314import mtk_neuron
1415import torch
16+ from executorch .exir ._serialize ._named_data_store import NamedDataStore
1517from executorch .exir .backend .backend_details import (
1618 BackendDetails ,
1719 ExportedProgram ,
2022from executorch .exir .backend .compile_spec_schema import CompileSpec
2123
2224SKIP_COMPILE_SPEC_KEYS = {"ImportForever" }
25+ EXTRACT_SHARED_BLOB_KEY = 'ExtractSharedBlobKey'
26+ HEADER_SIZE = 13
27+ HEADER_VERSION = 1
2328
2429
2530def assert_default_dim_order (edge_graph_module : torch .fx .GraphModule ) -> None :
@@ -39,6 +44,19 @@ def assert_default_dim_order(edge_graph_module: torch.fx.GraphModule) -> None:
3944 )
4045
4146
47+ def _pack_header (num_inputs , num_outputs , model_bytes_size ):
48+ header_bytes = struct .pack ("<BIII" , HEADER_VERSION , num_inputs , num_outputs , model_bytes_size )
49+ assert len (header_bytes ) == HEADER_SIZE
50+ return header_bytes
51+
52+
53+ def _unpack_header (header_bytes ):
54+ assert len (header_bytes ) == HEADER_SIZE
55+ version , num_inputs , num_outputs , buffer_size = struct .unpack ('<BIII' , header_bytes )
56+ assert version == HEADER_VERSION
57+ return num_inputs , num_outputs , buffer_size
58+
59+
4260@final
4361class NeuropilotBackend (BackendDetails ):
4462
@@ -67,8 +85,14 @@ def preprocess(
6785 # This default compile options are only for mt6989 SOC
6886 compile_options = ["--arch=mdla5.1,edpa1.0" , "--relax-fp32" , "--opt=3" ]
6987 for spec in module_compile_spec :
88+ # Special compile spec handling
7089 if spec .key in SKIP_COMPILE_SPEC_KEYS :
7190 continue
91+ if spec .key == EXTRACT_SHARED_BLOB_KEY :
92+ compile_options .append ('--dla-opt=0' )
93+ continue
94+
95+ # General compile spec handling
7296 if spec .value == b"" :
7397 compile_options .append (f"--{ spec .key } " )
7498 else :
@@ -89,5 +113,77 @@ def preprocess(
89113
90114 num_inputs = len (input_names )
91115 num_outputs = len (output_names )
92- header = struct .pack ("<BIII" , 1 , num_inputs , num_outputs , len (model_bytes ))
93- return PreprocessResult (processed_bytes = bytes (header + model_bytes ))
116+ header_bytes = _pack_header (num_inputs , num_outputs , len (model_bytes ))
117+ return PreprocessResult (processed_bytes = bytes (header_bytes + model_bytes ))
118+
119+ @classmethod
120+ def preprocess_multimethod (
121+ cls ,
122+ edge_programs : Dict [str , List [ExportedProgram ]],
123+ compile_specs : Dict [str , List [List [CompileSpec ]]],
124+ ) -> Dict [str , list [PreprocessResult ]]:
125+
126+ # Follow the default behavior of `preprocess_multimethod`
127+ preprocess_results = {}
128+ for method_name , programs in edge_programs .items ():
129+ assert (
130+ method_name in compile_specs
131+ ), f"Error: missing compile specs for { method_name } "
132+ compile_specs_for_method = compile_specs [method_name ]
133+ assert len (compile_specs_for_method ) == len (
134+ programs
135+ ), f"Error: method { method_name } has { len (programs )} partitions but only { len (compile_specs_for_method )} "
136+ results_for_method = []
137+ for program , compile_spec_for_program in zip (
138+ programs , compile_specs_for_method
139+ ):
140+ preprocess_result = cls .preprocess (program , compile_spec_for_program )
141+ results_for_method .append (preprocess_result )
142+
143+ preprocess_results [method_name ] = results_for_method
144+
145+ # Try extract shared data blob if necessary
146+ infos_dict = collections .defaultdict (list )
147+ models_dict = collections .defaultdict (list )
148+ result_dict = collections .defaultdict (list )
149+ preprocess_result_list = []
150+ for method_name , method_results in preprocess_results .items ():
151+ for idx , result in enumerate (method_results ):
152+ shared_blob_key = None
153+ for spec in compile_specs [method_name ][idx ]:
154+ if spec .key == EXTRACT_SHARED_BLOB_KEY :
155+ shared_blob_key = spec .value .decode ('utf-8' )
156+
157+ if shared_blob_key is None :
158+ continue
159+
160+ header_bytes = result .processed_bytes [:HEADER_SIZE ]
161+ model_bytes = result .processed_bytes [HEADER_SIZE :]
162+ num_inputs , num_outputs , model_bytes_size = _unpack_header (header_bytes )
163+ assert len (model_bytes ) == model_bytes_size
164+ infos_dict [shared_blob_key ].append ((num_inputs , num_outputs ))
165+ models_dict [shared_blob_key ].append (model_bytes )
166+ result_dict [shared_blob_key ].append (result )
167+
168+ data_store_output_dict = dict ()
169+ for key , models in models_dict .items ():
170+ ndm = NamedDataStore ()
171+ print ('------------------' )
172+ print (key )
173+ print ('Original DLA sizes: {}' .format ([len (model ) for model in models ]))
174+ blob , new_models = mtk_neuron .extract_shared_data (models , options = '-e union' )
175+ print ('Extracted data size: {}' .format (len (blob )))
176+ print ('New DLA sizes: {}' .format ([len (model ) for model in new_models ]))
177+ ndm .add_named_data (key , bytes (blob ))
178+ data_store_output_dict [key ] = ndm .get_named_data_store_output ()
179+ models .clear ()
180+ models .extend (new_models )
181+
182+ for key , data_store_output in data_store_output_dict .items ():
183+ for idx , (model_info , model_bytes ) in enumerate (zip (infos_dict [key ], models_dict [key ])):
184+ num_inputs , num_outputs = model_info
185+ header_bytes = _pack_header (num_inputs , num_outputs , len (model_bytes ))
186+ result_dict [key ][idx ].data_store_output = data_store_output
187+ result_dict [key ][idx ].processed_bytes = bytes (header_bytes + model_bytes )
188+
189+ return preprocess_results
0 commit comments