Skip to content

Commit daebcde

Browse files
authored
dtype selective build from model API in OSS (#11760)
### Motivation Given a specific model, we want to produce a binary that only includes the minimal operators and dtypes needed to run the model. This requires parsing the model to determine what kernels it launch, the operators used in those kernels, and the dtypes of the tensors in the kernels. After parsing, a header file must be generated, and the portable_kernels lib can be rebuilt to only include the operators and dtypes specified in the generated header. ### Summary This changes completes this E2E process. A user can now specify the model they wish to optimize their binary for via the command line argument `-DEXECUTORCH_SELECT_OPS_FROM_MODEL="<file path to model pte>"`. When specified, the pte is parsed to produce a YAML file called `seleced_operators.yaml` which describes the model's operators and dtypes. From this YAML, a header file called `selected_op_variants.h` is generated that selects the described operators and dtypes. When command line argument `-DEXECUTORCH_DTYPE_SELECTIVE_BUILD=ON` is specified, the header file is linked to the `portable_kernels` lib when it's rebuilt. Only the model API is supported with dtype selective build, and using other methods such as `list` or `dict` will results in a build error. ### Results An example usage of this flow is included in `examples/selective_build/test_selective_build.sh:test_cmake_select_ops_in_model`. When run as `bash examples/selective_build/test_selective_build.sh cmake`, the `cmake-out/examples/selective_build/selective_build_test` binary is built. After stripping the binary, the following binary size results were seen with the following models: #### Working models | Model | Default Binary Size (KB) | Dtype Selected Binary Size (KB) | | ------- | :---:| :---:| |add | 359 | 275| |mul | 335 | 263 | | add_mul| 367 | 287 | | linear | 347 | 291 | | softmax | 251 | 251 | |resnet18| 643 | 515 | |resnet50 | 643 | 515 | |mobilebert| 707 | 415 | |lstm| 643 | 459| | dl3| 607 | 539| |edsr | 543 | 371| #### Models that crash when run | Model | Default Binary Size (KB) | Dtype Selected Binary Size (KB) | | ------- | :---:| :---:| |emformer_transcribe| 863| 555| |vit| 907| 687 | |mv2| 631 | 495 | |mv3| 843 | 535 | |llama | 1.1M | 827| |qwen2_5 | 1.1M | 827| ### Notes Although there is a noted reduction in the binary size, it seems that the pte file parsing functionality from `gen_oplist.py` is incomplete. Please see the discussion on [PR #11582](#11582) and on [issue #11762](#11762) for more details.
1 parent 5136175 commit daebcde

File tree

3 files changed

+111
-25
lines changed

3 files changed

+111
-25
lines changed

examples/selective_build/CMakeLists.txt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ if(NOT CMAKE_CXX_STANDARD)
3333
# Can't set to 11 due to executor_runner.cpp make_unique
3434
endif()
3535

36-
set(_common_compile_options -Wno-deprecated-declarations -fPIC)
36+
set(_common_compile_options -Wno-deprecated-declarations -fPIC -ffunction-sections -fdata-sections)
3737

3838
# Let files say "include <executorch/path/to/header.h>".
3939
set(_common_include_directories ${EXECUTORCH_ROOT}/..)
@@ -123,13 +123,25 @@ gen_selected_ops(
123123
)
124124

125125
generate_bindings_for_kernels(
126-
LIB_NAME "select_build_lib" FUNCTIONS_YAML
127-
${EXECUTORCH_ROOT}/kernels/portable/functions.yaml CUSTOM_OPS_YAML
126+
LIB_NAME
127+
"select_build_lib"
128+
FUNCTIONS_YAML
129+
${EXECUTORCH_ROOT}/kernels/portable/functions.yaml
130+
CUSTOM_OPS_YAML
128131
"${_custom_ops_yaml}"
132+
DTYPE_SELECTIVE_BUILD
133+
"${EXECUTORCH_DTYPE_SELECTIVE_BUILD}"
129134
)
130135

131136
gen_operators_lib(
132-
LIB_NAME "select_build_lib" KERNEL_LIBS ${_kernel_lib} DEPS executorch_core
137+
LIB_NAME
138+
"select_build_lib"
139+
KERNEL_LIBS
140+
${_kernel_lib}
141+
DEPS
142+
executorch_core
143+
DTYPE_SELECTIVE_BUILD
144+
"${EXECUTORCH_DTYPE_SELECTIVE_BUILD}"
133145
)
134146

135147
list(TRANSFORM _executor_runner__srcs PREPEND "${EXECUTORCH_ROOT}/")

examples/selective_build/test_selective_build.sh

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,17 @@ test_cmake_select_ops_in_yaml() {
162162
}
163163

164164
test_cmake_select_ops_in_model() {
165-
echo "Exporting MobilenetV2"
166-
${PYTHON_EXECUTABLE} -m examples.portable.scripts.export --model_name="mv2"
165+
local model_name="add_mul"
166+
local model_export_name="${model_name}.pte"
167+
echo "Exporting ${model_name}"
168+
${PYTHON_EXECUTABLE} -m examples.portable.scripts.export --model_name="${model_name}"
167169
local example_dir=examples/selective_build
168170
local build_dir=cmake-out/${example_dir}
169171
rm -rf ${build_dir}
170-
retry cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
171-
-DEXECUTORCH_SELECT_OPS_FROM_MODEL="./mv2.pte" \
172+
retry cmake -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" \
173+
-DEXECUTORCH_SELECT_OPS_FROM_MODEL="./${model_export_name}" \
174+
-DEXECUTORCH_DTYPE_SELECTIVE_BUILD=ON \
175+
-DEXECUTORCH_OPTIMIZE_SIZE=ON \
172176
-DCMAKE_INSTALL_PREFIX=cmake-out \
173177
-DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" \
174178
-B${build_dir} \
@@ -178,10 +182,10 @@ test_cmake_select_ops_in_model() {
178182
cmake --build ${build_dir} -j9 --config $CMAKE_BUILD_TYPE
179183

180184
echo 'Running selective build test'
181-
${build_dir}/selective_build_test --model_path="./mv2.pte"
185+
${build_dir}/selective_build_test --model_path="./${model_export_name}"
182186

183-
echo "Removing mv2.pte"
184-
rm "./mv2.pte"
187+
echo "Removing ${model_export_name}"
188+
rm "./${model_export_name}"
185189
}
186190

187191
if [[ -z $BUCK ]];

tools/cmake/Codegen.cmake

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,20 @@ function(gen_selected_ops)
2222
message(STATUS " INCLUDE_ALL_OPS: ${GEN_INCLUDE_ALL_OPS}")
2323
message(STATUS " OPS_FROM_MODEL: ${GEN_OPS_FROM_MODEL}")
2424
message(STATUS " DTYPE_SELECTIVE_BUILD: ${GEN_DTYPE_SELECTIVE_BUILD}")
25+
26+
set(_out_dir ${CMAKE_CURRENT_BINARY_DIR}/${GEN_LIB_NAME})
27+
2528
if(GEN_DTYPE_SELECTIVE_BUILD)
26-
message(STATUS " DTYPE_SELECTIVE_BUILD is still WIP and may not be fully functional")
29+
if(NOT GEN_OPS_FROM_MODEL)
30+
message(FATAL_ERROR " DTYPE_SELECTIVE_BUILD is only support with model API, please pass in a model")
31+
endif()
2732
endif()
2833

2934
set(_oplist_yaml
30-
${CMAKE_CURRENT_BINARY_DIR}/${GEN_LIB_NAME}/selected_operators.yaml
35+
${_out_dir}/selected_operators.yaml
3136
)
3237

33-
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${GEN_LIB_NAME})
38+
file(MAKE_DIRECTORY ${_out_dir})
3439

3540
file(GLOB_RECURSE _codegen_tools_srcs "${EXECUTORCH_ROOT}/codegen/tools/*.py")
3641

@@ -64,18 +69,18 @@ function(gen_selected_ops)
6469

6570
if(GEN_DTYPE_SELECTIVE_BUILD)
6671
set(_opvariant_h
67-
${CMAKE_CURRENT_BINARY_DIR}/${GEN_LIB_NAME}/selected_op_variants.h
72+
${_out_dir}/selected_op_variants.h
6873
)
6974
set(_gen_opvariant_command "${PYTHON_EXECUTABLE}" -m codegen.tools.gen_selected_op_variants
7075
--yaml-file=${_oplist_yaml}
71-
--output-dir=${CMAKE_CURRENT_BINARY_DIR}/${GEN_LIB_NAME}/
76+
--output-dir=${_out_dir}/
7277
)
7378
message("Command - ${_gen_opvariant_command}")
7479
add_custom_command(
75-
COMMENT "Generating selected_op_variants.h for ${GEN_LIB_NAME}"
80+
COMMENT "Generating ${_opvariant_h} for ${GEN_LIB_NAME}"
7681
OUTPUT ${_opvariant_h}
7782
COMMAND ${_gen_opvariant_command}
78-
DEPENDS ${_oplist_yaml} ${_codegen_tools_srcs}
83+
DEPENDS ${_oplist_yaml} ${GEN_OPS_SCHEMA_YAML} ${_codegen_tools_srcs}
7984
WORKING_DIRECTORY ${EXECUTORCH_ROOT}
8085
)
8186
endif()
@@ -88,14 +93,15 @@ endfunction()
8893
# functions_yaml CUSTOM_OPS_YAML custom_ops_yaml )
8994
function(generate_bindings_for_kernels)
9095
set(options ADD_EXCEPTION_BOUNDARY)
91-
set(arg_names LIB_NAME FUNCTIONS_YAML CUSTOM_OPS_YAML)
96+
set(arg_names LIB_NAME FUNCTIONS_YAML CUSTOM_OPS_YAML DTYPE_SELECTIVE_BUILD)
9297
cmake_parse_arguments(GEN "${options}" "${arg_names}" "" ${ARGN})
9398

9499
message(STATUS "Generating kernel bindings:")
95100
message(STATUS " LIB_NAME: ${GEN_LIB_NAME}")
96101
message(STATUS " FUNCTIONS_YAML: ${GEN_FUNCTIONS_YAML}")
97102
message(STATUS " CUSTOM_OPS_YAML: ${GEN_CUSTOM_OPS_YAML}")
98103
message(STATUS " ADD_EXCEPTION_BOUNDARY: ${GEN_ADD_EXCEPTION_BOUNDARY}")
104+
message(STATUS " DTYPE_SELECTIVE_BUILD: ${GEN_DTYPE_SELECTIVE_BUILD}")
99105

100106
# Command to generate selected_operators.yaml from custom_ops.yaml.
101107
file(GLOB_RECURSE _codegen_templates "${EXECUTORCH_ROOT}/codegen/templates/*")
@@ -104,6 +110,13 @@ function(generate_bindings_for_kernels)
104110
# By default selective build output is selected_operators.yaml
105111
set(_oplist_yaml ${_out_dir}/selected_operators.yaml)
106112

113+
# If dtype selective build is enable, force header file to be preserved
114+
if(GEN_DTYPE_SELECTIVE_BUILD)
115+
set(_opvariant_h ${_out_dir}/selected_op_variants.h)
116+
else()
117+
set(_opvariant_h "")
118+
endif()
119+
107120
# Command to codegen C++ wrappers to register custom ops to both PyTorch and
108121
# Executorch runtime.
109122
execute_process(
@@ -148,8 +161,9 @@ function(generate_bindings_for_kernels)
148161
COMMENT "Generating code for kernel registration"
149162
OUTPUT ${_gen_command_sources}
150163
COMMAND ${_gen_command}
151-
DEPENDS ${_oplist_yaml} ${GEN_CUSTOM_OPS_YAML} ${GEN_FUNCTIONS_YAML}
152-
${_codegen_templates} ${_torchgen_srcs}
164+
DEPENDS ${_oplist_yaml} ${_opvariant_h} ${GEN_CUSTOM_OPS_YAML}
165+
${GEN_FUNCTIONS_YAML} ${_codegen_templates}
166+
${_torchgen_srcs}
153167
WORKING_DIRECTORY ${EXECUTORCH_ROOT}
154168
)
155169
# Make generated file list available in parent scope
@@ -191,29 +205,85 @@ endfunction()
191205

192206
# Generate a runtime lib for registering operators in Executorch
193207
function(gen_operators_lib)
194-
set(multi_arg_names LIB_NAME KERNEL_LIBS DEPS)
208+
set(multi_arg_names LIB_NAME KERNEL_LIBS DEPS DTYPE_SELECTIVE_BUILD)
195209
cmake_parse_arguments(GEN "" "" "${multi_arg_names}" ${ARGN})
196210

197211
message(STATUS "Generating operator lib:")
198212
message(STATUS " LIB_NAME: ${GEN_LIB_NAME}")
199213
message(STATUS " KERNEL_LIBS: ${GEN_KERNEL_LIBS}")
200214
message(STATUS " DEPS: ${GEN_DEPS}")
215+
message(STATUS " DTYPE_SELECTIVE_BUILD: ${GEN_DTYPE_SELECTIVE_BUILD}")
201216

202217
set(_out_dir ${CMAKE_CURRENT_BINARY_DIR}/${GEN_LIB_NAME})
218+
if(GEN_DTYPE_SELECTIVE_BUILD)
219+
set(_opvariant_h
220+
${_out_dir}/selected_op_variants.h
221+
)
222+
endif()
203223

204224
add_library(${GEN_LIB_NAME})
225+
226+
set(_srcs_list
227+
${_out_dir}/RegisterCodegenUnboxedKernelsEverything.cpp
228+
${_out_dir}/Functions.h ${_out_dir}/NativeFunctions.h
229+
)
230+
if(GEN_DTYPE_SELECTIVE_BUILD)
231+
list(APPEND _srcs_list ${_opvariant_h})
232+
endif()
205233
target_sources(
206234
${GEN_LIB_NAME}
207-
PRIVATE ${_out_dir}/RegisterCodegenUnboxedKernelsEverything.cpp
208-
${_out_dir}/Functions.h ${_out_dir}/NativeFunctions.h
235+
PRIVATE ${_srcs_list}
209236
)
210237
target_link_libraries(${GEN_LIB_NAME} PRIVATE ${GEN_DEPS})
238+
set(portable_kernels_check "portable_kernels")
211239
if(GEN_KERNEL_LIBS)
212-
target_link_libraries(${GEN_LIB_NAME} PUBLIC ${GEN_KERNEL_LIBS})
240+
241+
set(_common_compile_options -Wno-deprecated-declarations -ffunction-sections -fdata-sections -Os)
242+
243+
if(GEN_DTYPE_SELECTIVE_BUILD)
244+
if("${portable_kernels_check}" IN_LIST GEN_KERNEL_LIBS)
245+
list(REMOVE_ITEM GEN_KERNEL_LIBS ${portable_kernels_check})
246+
247+
# Build kernels_util_all_deps, since later selected_portable_kernels depends on it
248+
list(TRANSFORM _kernels_util_all_deps__srcs PREPEND "${EXECUTORCH_ROOT}/")
249+
add_library(selected_kernels_util_all_deps ${_kernels_util_all_deps__srcs})
250+
target_link_libraries(selected_kernels_util_all_deps PRIVATE executorch_core)
251+
target_include_directories(selected_kernels_util_all_deps PUBLIC ${_common_include_directories})
252+
target_compile_definitions(selected_kernels_util_all_deps PUBLIC C10_USING_CUSTOM_GENERATED_MACROS)
253+
target_compile_options(selected_kernels_util_all_deps PUBLIC ${_common_compile_options})
254+
255+
# Build selected_portable_kernels
256+
list(TRANSFORM _portable_kernels__srcs PREPEND "${EXECUTORCH_ROOT}/")
257+
add_library(selected_portable_kernels ${_portable_kernels__srcs})
258+
target_link_libraries(selected_portable_kernels PRIVATE executorch_core selected_kernels_util_all_deps)
259+
target_compile_options(selected_portable_kernels PUBLIC ${_common_compile_options})
260+
target_include_directories(selected_portable_kernels PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/${GEN_LIB_NAME}/)
261+
262+
# Make sure the header is generated before compiling the library
263+
add_dependencies(selected_portable_kernels ${GEN_LIB_NAME})
264+
# Create a custom target for the header to ensure proper dependency tracking
265+
add_custom_target(selected_portable_kernels_header DEPENDS ${_opvariant_h})
266+
add_dependencies(selected_portable_kernels selected_portable_kernels_header)
267+
# Apply the compile definition for dtype selective build
268+
target_compile_definitions(selected_portable_kernels PRIVATE EXECUTORCH_SELECTIVE_BUILD_DTYPE=1)
269+
270+
target_link_libraries(${GEN_LIB_NAME} PUBLIC selected_portable_kernels)
271+
else()
272+
message(FATAL_ERROR "Currently dtype selective build is only supported for portable_kernels but {${GEN_KERNEL_LIBS}} were provided!")
273+
endif()
274+
endif()
275+
276+
# After removing portable_kernels, test if there are other kernel libs provided
277+
if(GEN_KERNEL_LIBS)
278+
target_link_libraries(${GEN_LIB_NAME} PUBLIC ${GEN_KERNEL_LIBS})
279+
endif()
213280
endif()
214281

215282
target_link_options_shared_lib(${GEN_LIB_NAME})
216283
set(_generated_headers ${_out_dir}/Functions.h ${_out_dir}/NativeFunctions.h)
284+
if(GEN_DTYPE_SELECTIVE_BUILD)
285+
list(APPEND _generated_headers ${_opvariant_h})
286+
endif()
217287
set_target_properties(
218288
${GEN_LIB_NAME} PROPERTIES PUBLIC_HEADER "${_generated_headers}"
219289
)

0 commit comments

Comments
 (0)