Skip to content

Commit 5350547

Browse files
Support preprocess_multimethod with extracted_share_data in Neuropilot backend
1 parent 5e8295e commit 5350547

File tree

1 file changed

+99
-3
lines changed

1 file changed

+99
-3
lines changed

backends/mediatek/preprocess.py

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
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
78
import contextlib
89
import struct
910

10-
from typing import final, List
11+
from typing import final, Dict, List
1112

1213
import mtk_converter
1314
import mtk_neuron
1415
import torch
16+
from executorch.exir._serialize._named_data_store import NamedDataStore
1517
from executorch.exir.backend.backend_details import (
1618
BackendDetails,
1719
ExportedProgram,
@@ -20,6 +22,9 @@
2022
from executorch.exir.backend.compile_spec_schema import CompileSpec
2123

2224
SKIP_COMPILE_SPEC_KEYS = {"ImportForever"}
25+
EXTRACT_SHARED_BLOB_KEY = 'ExtractSharedBlobKey'
26+
HEADER_SIZE = 13
27+
HEADER_VERSION = 1
2328

2429

2530
def 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
4361
class 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

Comments
 (0)