diff --git a/CMakeLists.txt b/CMakeLists.txt index a6efb3d4623..492dcba194f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. +# Copyright 2024-2025 Arm Limited and/or its affiliates. # All rights reserved. # # This source code is licensed under the BSD-style license found in the @@ -820,6 +821,14 @@ if(EXECUTORCH_BUILD_EXECUTOR_RUNNER) list(APPEND _executor_runner_libs quantized_ops_lib) endif() + if(EXECUTORCH_ENABLE_EVENT_TRACER) + if(EXECUTORCH_BUILD_DEVTOOLS) + list(APPEND _executor_runner_libs etdump flatccrt) + else() + message(SEND_ERROR "Use of 'EXECUTORCH_ENABLE_EVENT_TRACER' requires 'EXECUTORCH_BUILD_DEVTOOLS' to be enabled.") + endif() + endif() + add_executable(executor_runner ${_executor_runner__srcs}) if(CMAKE_BUILD_TYPE STREQUAL "Release") if(APPLE) diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index 3c66796594b..bc0f51a236b 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -1,4 +1,5 @@ # Copyright (c) Qualcomm Innovation Center, Inc. +# Copyright 2025 Arm Limited and/or its affiliates. # All rights reserved # # This source code is licensed under the BSD-style license found in the @@ -199,11 +200,6 @@ target_link_libraries( # target_link_options_shared_lib(qnn_executorch_backend) -# -# add compile option -# -target_compile_options(executorch PUBLIC -DET_EVENT_TRACER_ENABLED) - # # add sources # diff --git a/backends/xnnpack/CMakeLists.txt b/backends/xnnpack/CMakeLists.txt index ed8cf8d8e14..a21ef4f6686 100644 --- a/backends/xnnpack/CMakeLists.txt +++ b/backends/xnnpack/CMakeLists.txt @@ -1,4 +1,5 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. +# Copyright 2024-2025 Arm Limited and/or its affiliates. # All rights reserved. # # This source code is licensed under the BSD-style license found in the @@ -128,8 +129,17 @@ if(NOT CMAKE_TOOLCHAIN_FILE MATCHES ".*(iOS|ios\.toolchain)\.cmake$") # list(TRANSFORM _xnn_executor_runner__srcs PREPEND "${EXECUTORCH_ROOT}/") add_executable(xnn_executor_runner ${_xnn_executor_runner__srcs}) + + if(EXECUTORCH_ENABLE_EVENT_TRACER) + if(EXECUTORCH_BUILD_DEVTOOLS) + list(APPEND xnn_executor_runner_libs etdump) + else() + message(SEND_ERROR "Use of 'EXECUTORCH_ENABLE_EVENT_TRACER' requires 'EXECUTORCH_BUILD_DEVTOOLS' to be enabled.") + endif() + endif() + target_link_libraries( - xnn_executor_runner xnnpack_backend gflags portable_ops_lib + xnn_executor_runner gflags portable_ops_lib ${xnn_executor_runner_libs} ) target_compile_options(xnn_executor_runner PUBLIC ${_common_compile_options}) endif() diff --git a/docs/source/native-delegates-executorch-xnnpack-delegate.md b/docs/source/native-delegates-executorch-xnnpack-delegate.md index de54de7706f..6bfbfa6be36 100644 --- a/docs/source/native-delegates-executorch-xnnpack-delegate.md +++ b/docs/source/native-delegates-executorch-xnnpack-delegate.md @@ -70,7 +70,7 @@ Since weight packing creates an extra copy of the weights inside XNNPACK, We fre When executing the XNNPACK subgraphs, we prepare the tensor inputs and outputs and feed them to the XNNPACK runtime graph. After executing the runtime graph, the output pointers are filled with the computed tensors. #### **Profiling** -We have enabled basic profiling for XNNPACK delegate that can be enabled with the following compiler flag `-DENABLE_XNNPACK_PROFILING`. With ExecuTorch's Developer Tools integration, you can also now use the Developer Tools to profile the model. You can follow the steps in [Using the ExecuTorch Developer Tools to Profile a Model](./tutorials/devtools-integration-tutorial) on how to profile ExecuTorch models and use Developer Tools' Inspector API to view XNNPACK's internal profiling information. +We have enabled basic profiling for the XNNPACK delegate that can be enabled with the compiler flag `-DEXECUTORCH_ENABLE_EVENT_TRACER` (add `-DENABLE_XNNPACK_PROFILING` for additional details). With ExecuTorch's Developer Tools integration, you can also now use the Developer Tools to profile the model. You can follow the steps in [Using the ExecuTorch Developer Tools to Profile a Model](./tutorials/devtools-integration-tutorial) on how to profile ExecuTorch models and use Developer Tools' Inspector API to view XNNPACK's internal profiling information. An example implementation is available in the `xnn_executor_runner` (see [tutorial here](tutorial-xnnpack-delegate-lowering.md#profiling)). [comment]: <> (TODO: Refactor quantizer to a more official quantization doc) diff --git a/docs/source/tutorial-xnnpack-delegate-lowering.md b/docs/source/tutorial-xnnpack-delegate-lowering.md index 1c71a6ba800..d1148511c5f 100644 --- a/docs/source/tutorial-xnnpack-delegate-lowering.md +++ b/docs/source/tutorial-xnnpack-delegate-lowering.md @@ -177,3 +177,6 @@ Now you should be able to find the executable built at `./cmake-out/backends/xnn ## Building and Linking with the XNNPACK Backend You can build the XNNPACK backend [CMake target](https://github.com/pytorch/executorch/blob/main/backends/xnnpack/CMakeLists.txt#L83), and link it with your application binary such as an Android or iOS application. For more information on this you may take a look at this [resource](demo-apps-android.md) next. + +## Profiling +To enable profiling in the `xnn_executor_runner` pass the flags `-DEXECUTORCH_ENABLE_EVENT_TRACER=ON` and `-DEXECUTORCH_BUILD_DEVTOOLS=ON` to the build command (add `-DENABLE_XNNPACK_PROFILING=ON` for additional details). This will enable ETDump generation when running the inference and enables command line flags for profiling (see `xnn_executor_runner --help` for details). diff --git a/examples/portable/executor_runner/executor_runner.cpp b/examples/portable/executor_runner/executor_runner.cpp index 65ba7627431..f7702fae3de 100644 --- a/examples/portable/executor_runner/executor_runner.cpp +++ b/examples/portable/executor_runner/executor_runner.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) Meta Platforms, Inc. and affiliates. + * Copyright 2024-2025 Arm Limited and/or its affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the @@ -25,10 +26,14 @@ #include #include #include +#include #include #include #include #include +#ifdef ET_EVENT_TRACER_ENABLED +#include +#endif // ET_EVENT_TRACER_ENABLED static uint8_t method_allocator_pool[4 * 1024U * 1024U]; // 4 MB @@ -38,10 +43,15 @@ DEFINE_string( model_path, "model.pte", "Model serialized in flatbuffer format."); +DEFINE_uint32(num_executions, 1, "Number of times to run the model."); +#ifdef ET_EVENT_TRACER_ENABLED +DEFINE_string(etdump_path, "model.etdump", "Write ETDump data to this path."); +#endif // ET_EVENT_TRACER_ENABLED using executorch::extension::FileDataLoader; using executorch::runtime::Error; using executorch::runtime::EValue; +using executorch::runtime::EventTracer; using executorch::runtime::HierarchicalAllocator; using executorch::runtime::MemoryAllocator; using executorch::runtime::MemoryManager; @@ -51,6 +61,56 @@ using executorch::runtime::Program; using executorch::runtime::Result; using executorch::runtime::Span; +/// Helper to manage resources for ETDump generation +class EventTraceManager { + public: + EventTraceManager() : event_tracer_ptr_(nullptr) { +#ifdef ET_EVENT_TRACER_ENABLED + event_tracer_ptr_ = std::make_shared(); +#endif // ET_EVENT_TRACER_ENABLED + } + + EventTracer* get_event_tracer() const { + return event_tracer_ptr_.get(); + }; + + Error write_etdump_to_file() const { + EventTracer* const event_tracer_ptr = get_event_tracer(); + if (!event_tracer_ptr) { + return Error::NotSupported; + } + +#ifdef ET_EVENT_TRACER_ENABLED + executorch::etdump::ETDumpGen* const etdump_ptr = + static_cast(event_tracer_ptr); + + const char* filename = FLAGS_etdump_path.c_str(); + + std::unique_ptr etdump_file( + fopen(filename, "w+"), fclose); + if (!etdump_file) { + ET_LOG(Error, "Failed to open ETDump file at %s.", filename); + return Error::AccessFailed; + } + + executorch::etdump::ETDumpResult result = etdump_ptr->get_etdump_data(); + if (result.buf != nullptr && result.size > 0) { + fwrite((uint8_t*)result.buf, 1, result.size, etdump_file.get()); + free(result.buf); + ET_LOG(Info, "ETDump written to file '%s'.", filename); + } else { + ET_LOG(Error, "No ETDump data available!"); + return Error::NotFound; + } +#endif // ET_EVENT_TRACER_ENABLED + + return Error::Ok; + } + + private: + std::shared_ptr event_tracer_ptr_; +}; + int main(int argc, char** argv) { executorch::runtime::runtime_init(); @@ -158,8 +218,9 @@ int main(int argc, char** argv) { // the method can mutate the memory-planned buffers, so the method should only // be used by a single thread at at time, but it can be reused. // - - Result method = program->load_method(method_name, &memory_manager); + EventTraceManager tracer; + Result method = program->load_method( + method_name, &memory_manager, tracer.get_event_tracer()); ET_CHECK_MSG( method.ok(), "Loading of method %s failed with status 0x%" PRIx32, @@ -178,18 +239,23 @@ int main(int argc, char** argv) { ET_LOG(Info, "Inputs prepared."); // Run the model. - Error status = method->execute(); - ET_CHECK_MSG( - status == Error::Ok, - "Execution of method %s failed with status 0x%" PRIx32, - method_name, - (uint32_t)status); - ET_LOG(Info, "Model executed successfully."); + for (uint32_t i = 0; i < FLAGS_num_executions; i++) { + Error status = method->execute(); + ET_CHECK_MSG( + status == Error::Ok, + "Execution of method %s failed with status 0x%" PRIx32, + method_name, + (uint32_t)status); + } + ET_LOG( + Info, + "Model executed successfully %" PRIu32 " time(s).", + FLAGS_num_executions); // Print the outputs. std::vector outputs(method->outputs_size()); ET_LOG(Info, "%zu outputs: ", outputs.size()); - status = method->get_outputs(outputs.data(), outputs.size()); + Error status = method->get_outputs(outputs.data(), outputs.size()); ET_CHECK(status == Error::Ok); // Print the first and last 100 elements of long lists of scalars. std::cout << executorch::extension::evalue_edge_items(100); @@ -197,5 +263,12 @@ int main(int argc, char** argv) { std::cout << "Output " << i << ": " << outputs[i] << std::endl; } + if (tracer.get_event_tracer()) { + // Dump ETDump data containing profiling/debugging data to file specified in + // command line flag. + Error status = tracer.write_etdump_to_file(); + ET_CHECK_MSG(status == Error::Ok, "Failed to save ETDump file."); + } + return 0; }