From f78e7c076c5e60d72ca2686c31048f15b5db4494 Mon Sep 17 00:00:00 2001 From: pytorchbot Date: Thu, 25 Apr 2024 11:35:40 +0000 Subject: [PATCH] 2024-04-25 nightly release (30128f3f8e367062663fb1800afb52465b71aad2) --- .ci/scripts/test.sh | 5 +- .ci/scripts/test_quantized_aot_lib.sh | 3 +- .ci/scripts/utils.sh | 2 +- CMakeLists.txt | 11 +- backends/apple/mps/mps_preprocess.py | 12 + backends/apple/mps/operators/indexing_ops.py | 77 ++++++- backends/apple/mps/operators/unary_ops.py | 3 + .../apple/mps/partition/mps_partitioner.py | 48 +++- backends/apple/mps/runtime/MPSDevice.h | 23 ++ backends/apple/mps/runtime/MPSDevice.mm | 65 ++++++ backends/apple/mps/runtime/MPSGraphBuilder.h | 7 + backends/apple/mps/runtime/MPSGraphBuilder.mm | 53 ++++- .../mps/runtime/operations/IndexingOps.mm | 96 ++++++++ .../mps/runtime/operations/OperationUtils.mm | 8 + .../apple/mps/runtime/operations/ShapeOps.mm | 8 +- .../apple/mps/runtime/operations/UnaryOps.mm | 1 + .../mps/serialization/mps_graph_schema.py | 26 +++ backends/apple/mps/serialization/schema.fbs | 26 +++ backends/apple/mps/setup.md | 101 ++++++--- backends/apple/mps/targets.bzl | 1 + backends/arm/arm_quantizer_utils.py | 11 +- backends/arm/arm_vela.py | 9 +- backends/arm/operators/op_addmm.py | 23 +- backends/arm/operators/op_common.py | 4 +- backends/arm/operators/op_conv2d.py | 2 +- backends/arm/operators/op_hardtanh.py | 31 ++- backends/arm/operators/op_placeholder.py | 12 +- backends/arm/test/ops/test_conv_combos.py | 67 ++++-- backends/arm/tosa_quant_utils.py | 29 ++- backends/qualcomm/setup.md | 1 - .../vulkan/partitioner/vulkan_partitioner.py | 4 + backends/vulkan/runtime/api/Tensor.h | 4 + backends/vulkan/runtime/api/Utils.h | 37 +++ backends/vulkan/runtime/graph/Logging.h | 8 + .../vulkan/runtime/graph/ops/glsl/clone.glsl | 30 +++ .../vulkan/runtime/graph/ops/glsl/clone.yaml | 10 + .../vulkan/runtime/graph/ops/glsl/conv1d.glsl | 148 +++++++----- .../runtime/graph/ops/glsl/copy_offset.glsl | 54 +++++ .../runtime/graph/ops/glsl/copy_offset.yaml | 10 + .../graph/ops/glsl/repeat_channel.glsl | 58 +++++ .../graph/ops/glsl/repeat_channel.yaml | 10 + .../vulkan/runtime/graph/ops/impl/Clone.cpp | 55 +++++ .../runtime/graph/ops/impl/Convolution.cpp | 78 +++---- .../vulkan/runtime/graph/ops/impl/Copy.cpp | 70 ++++++ backends/vulkan/runtime/graph/ops/impl/Copy.h | 25 ++ .../vulkan/runtime/graph/ops/impl/Permute.cpp | 53 +++-- .../vulkan/runtime/graph/ops/impl/Permute.h | 25 ++ .../vulkan/runtime/graph/ops/impl/Pool.cpp | 2 +- .../vulkan/runtime/graph/ops/impl/Repeat.cpp | 213 ++++++++++++++++++ .../runtime/graph/ops/impl/Unsqueeze.cpp | 52 +++++ .../graph/ops/impl/utils/KernelUtils.cpp | 2 +- .../graph/ops/impl/utils/KernelUtils.h | 21 +- backends/vulkan/test/op_tests/cases.py | 119 ++++++++++ .../vulkan/test/op_tests/utils/codegen.py | 28 ++- .../test/op_tests/utils/codegen_base.py | 6 +- backends/vulkan/test/test_vulkan_delegate.py | 79 ++++++- backends/xnnpack/README.md | 160 ++++++++----- backends/xnnpack/test/tester/tester.py | 2 +- .../source/_static/img/print_data_tabular.png | Bin 68673 -> 280808 bytes docs/source/build-run-coreml.md | 8 +- docs/source/build-run-xtensa.md | 3 +- docs/source/compiler-memory-planning.md | 12 +- docs/source/debug-backend-delegate.md | 65 ++++++ docs/source/getting-started-setup.md | 33 +++ docs/source/index.rst | 1 + .../kernel-library-custom-aten-kernel.md | 104 ++++++++- docs/source/llm/getting-started.md | 169 +++++++++----- ...e-delegates-executorch-xnnpack-delegate.md | 10 +- .../runtime-build-and-cross-compilation.md | 2 +- docs/source/sdk-inspector.rst | 1 + .../tutorial-xnnpack-delegate-lowering.md | 2 +- .../sdk-integration-tutorial.py | 27 +-- examples/README.md | 7 +- .../apple/coreml/scripts/inspector_cli.py | 9 +- .../mps/scripts/build_mps_executor_runner.sh | 64 ++++++ .../arm/ethos-u-setup/arm-none-eabi-gcc.cmake | 8 +- examples/arm/executor_runner/CMakeLists.txt | 16 ++ examples/arm/setup.sh | 2 +- .../apple_ios/ExecuTorchDemo/README.md | 58 ++--- examples/demo-apps/apple_ios/LLaMA/README.md | 29 ++- examples/llm_manual/CMakeLists.txt | 33 +++ examples/llm_manual/README.md | 3 + examples/llm_manual/basic_sampler.h | 20 ++ examples/llm_manual/basic_tokenizer.h | 192 ++++++++++++++++ examples/llm_manual/export_nanogpt.py | 45 ++++ examples/llm_manual/main.cpp | 122 ++++++++++ examples/llm_manual/managed_tensor.h | 74 ++++++ examples/models/llama2/README.md | 23 +- examples/models/llama2/custom_ops/TARGETS | 1 + .../custom_ops/test_sdpa_with_kv_cache.py | 2 + examples/models/llama2/lib/partitioner_lib.py | 2 +- .../scripts/test_demo_backend_delegation.sh | 3 +- examples/sdk/CMakeLists.txt | 21 +- examples/sdk/README.md | 2 +- examples/sdk/test_sdk_example_runner.sh | 3 +- kernels/portable/cpu/op_isinf.cpp | 5 +- kernels/portable/cpu/op_isnan.cpp | 5 +- runtime/core/memory_allocator.h | 2 +- runtime/core/portable_type/targets.bzl | 1 + runtime/executor/method.cpp | 9 +- runtime/kernel/kernel_runtime_context.h | 46 +++- runtime/kernel/targets.bzl | 1 + .../test/kernel_runtime_context_test.cpp | 53 +++++ sdk/inspector/_inspector.py | 48 +++- sdk/inspector/tests/TARGETS | 1 + sdk/inspector/tests/inspector_test.py | 49 ++++ setup.py | 1 + 107 files changed, 3037 insertions(+), 488 deletions(-) create mode 100644 backends/vulkan/runtime/graph/ops/glsl/clone.glsl create mode 100644 backends/vulkan/runtime/graph/ops/glsl/clone.yaml create mode 100644 backends/vulkan/runtime/graph/ops/glsl/copy_offset.glsl create mode 100644 backends/vulkan/runtime/graph/ops/glsl/copy_offset.yaml create mode 100644 backends/vulkan/runtime/graph/ops/glsl/repeat_channel.glsl create mode 100644 backends/vulkan/runtime/graph/ops/glsl/repeat_channel.yaml create mode 100644 backends/vulkan/runtime/graph/ops/impl/Clone.cpp create mode 100644 backends/vulkan/runtime/graph/ops/impl/Copy.cpp create mode 100644 backends/vulkan/runtime/graph/ops/impl/Copy.h create mode 100644 backends/vulkan/runtime/graph/ops/impl/Permute.h create mode 100644 backends/vulkan/runtime/graph/ops/impl/Repeat.cpp create mode 100644 backends/vulkan/runtime/graph/ops/impl/Unsqueeze.cpp create mode 100644 docs/source/debug-backend-delegate.md create mode 100755 examples/apple/mps/scripts/build_mps_executor_runner.sh create mode 100644 examples/llm_manual/CMakeLists.txt create mode 100644 examples/llm_manual/README.md create mode 100644 examples/llm_manual/basic_sampler.h create mode 100644 examples/llm_manual/basic_tokenizer.h create mode 100644 examples/llm_manual/export_nanogpt.py create mode 100644 examples/llm_manual/main.cpp create mode 100644 examples/llm_manual/managed_tensor.h diff --git a/.ci/scripts/test.sh b/.ci/scripts/test.sh index 2d91550615..c29c09dc63 100755 --- a/.ci/scripts/test.sh +++ b/.ci/scripts/test.sh @@ -37,7 +37,7 @@ build_cmake_executor_runner() { (rm -rf ${CMAKE_OUTPUT_DIR} \ && mkdir ${CMAKE_OUTPUT_DIR} \ && cd ${CMAKE_OUTPUT_DIR} \ - && retry cmake -DBUCK2=buck2 -DCMAKE_BUILD_TYPE=Release \ + && retry cmake -DCMAKE_BUILD_TYPE=Release \ -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" ..) cmake --build ${CMAKE_OUTPUT_DIR} -j4 @@ -84,8 +84,7 @@ build_cmake_xnn_executor_runner() { (rm -rf ${CMAKE_OUTPUT_DIR} \ && mkdir ${CMAKE_OUTPUT_DIR} \ && cd ${CMAKE_OUTPUT_DIR} \ - && retry cmake -DBUCK2=buck2 \ - -DCMAKE_BUILD_TYPE=Release \ + && retry cmake -DCMAKE_BUILD_TYPE=Release \ -DEXECUTORCH_BUILD_XNNPACK=ON \ -DCMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH" \ -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" ..) diff --git a/.ci/scripts/test_quantized_aot_lib.sh b/.ci/scripts/test_quantized_aot_lib.sh index 0ab9ceb81a..610144f80d 100755 --- a/.ci/scripts/test_quantized_aot_lib.sh +++ b/.ci/scripts/test_quantized_aot_lib.sh @@ -21,8 +21,7 @@ build_cmake_quantized_aot_lib() { (rm -rf ${CMAKE_OUTPUT_DIR} \ && mkdir ${CMAKE_OUTPUT_DIR} \ && cd ${CMAKE_OUTPUT_DIR} \ - && retry cmake -DBUCK2=buck2 \ - -DCMAKE_BUILD_TYPE=Release \ + && retry cmake -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH" \ -DEXECUTORCH_BUILD_QUANTIZED_OPS_AOT=ON \ -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" ..) diff --git a/.ci/scripts/utils.sh b/.ci/scripts/utils.sh index c7c00be257..f0675f56cc 100644 --- a/.ci/scripts/utils.sh +++ b/.ci/scripts/utils.sh @@ -99,7 +99,7 @@ build_executorch_runner_cmake() { pushd "${CMAKE_OUTPUT_DIR}" || return # This command uses buck2 to gather source files and buck2 could crash flakily # on MacOS - retry cmake -DBUCK2=buck2 -DPYTHON_EXECUTABLE="${PYTHON_EXECUTABLE}" -DCMAKE_BUILD_TYPE=Release .. + retry cmake -DPYTHON_EXECUTABLE="${PYTHON_EXECUTABLE}" -DCMAKE_BUILD_TYPE=Release .. popd || return if [ "$(uname)" == "Darwin" ]; then diff --git a/CMakeLists.txt b/CMakeLists.txt index fd39521d1d..0610462aed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -558,10 +558,6 @@ if(EXECUTORCH_BUILD_PYBIND) list(APPEND _dep_libs xnnpack_backend XNNPACK) endif() - if(EXECUTORCH_BUILD_CUSTOM) - list(APPEND _dep_libs custom_ops) - endif() - if(EXECUTORCH_BUILD_QUANTIZED) target_link_options_shared_lib(quantized_ops_lib) list(APPEND _dep_libs quantized_kernels quantized_ops_lib) @@ -571,6 +567,13 @@ if(EXECUTORCH_BUILD_PYBIND) if(EXECUTORCH_BUILD_CUSTOM_OPS_AOT AND NOT APPLE) list(APPEND _dep_libs custom_ops_aot_lib) endif() + # TODO(laryliu): Fix linux duplicate registation problem. In GH CI worker + # libcustom_ops.a doesn't dedup with the one indirectly linked from + # libcustom_ops_aot_lib.a + if(EXECUTORCH_BUILD_CUSTOM AND APPLE) + target_link_options_shared_lib(custom_ops) + list(APPEND _dep_libs custom_ops) + endif() # compile options for pybind set(_pybind_compile_options -Wno-deprecated-declarations -fPIC -frtti diff --git a/backends/apple/mps/mps_preprocess.py b/backends/apple/mps/mps_preprocess.py index 0e543d7e07..bb828ed0f9 100644 --- a/backends/apple/mps/mps_preprocess.py +++ b/backends/apple/mps/mps_preprocess.py @@ -18,6 +18,7 @@ from executorch.backends.apple.mps.serialization.mps_graph_schema import ( MPSGraph, MPSTensor, + OpType, ) from executorch.backends.apple.mps.serialization.mps_graph_serialize import ( @@ -65,6 +66,7 @@ def preprocess( input_ids=[], output_ids=[], constant_ids=[], + graph_type=OpType.mps_graph, ) convert_model_to_fp16 = True @@ -111,6 +113,16 @@ def handle_call_function( mps_graph: MPSGraph, ) -> None: logging.info(f"Visiting: {node}, {node.target.__name__}") + + if ( + "delegation_tag" in node.meta + and "metal_kernel" in node.meta["delegation_tag"] + ): + logging.info( + f"Node '{node.target.__name__}' was marked as a Metal kernel by the MPSPartitioner!" + ) + mps_graph.graph_type = OpType.metal_kernel + if node.target.__name__ in node_visitors: node_visitors[node.target.__name__].define_node(node, mps_graph) else: diff --git a/backends/apple/mps/operators/indexing_ops.py b/backends/apple/mps/operators/indexing_ops.py index f2c9dc6aea..690549973a 100644 --- a/backends/apple/mps/operators/indexing_ops.py +++ b/backends/apple/mps/operators/indexing_ops.py @@ -3,7 +3,7 @@ # Provided subject to the LICENSE file in the top level directory. # -from typing import cast +from typing import cast, List import torch from executorch.backends.apple.mps.operators.node_visitor import ( @@ -13,9 +13,12 @@ from executorch.backends.apple.mps.serialization.mps_graph_schema import ( MPSEmbedding, MPSGraph, + MPSIndexPut, MPSIndexSelect, + MPSIndexTensor, ) from executorch.backends.apple.mps.utils.mps_utils import get_input_node +from executorch.backends.transforms import get_shape from executorch.exir.sym_util import eval_expr @@ -40,6 +43,78 @@ def define_node( mps_graph.mps_nodes.append(mps_node) +@register_node_visitor +class IndexTensorVisitor(NodeVisitor): + target = "aten.index.Tensor" + + def __init__(self, *args) -> None: + super().__init__(*args) + + def define_node( + self, + node: torch.fx.Node, + mps_graph: MPSGraph, + ) -> None: + mps_node = self.create_unary_node(node, mps_graph, MPSIndexTensor) + tensors = cast(List[torch.fx.Node], node.args[1]) + for tensor in tensors: + mps_node.mpsnode_union.indices_id.append( + self.define_tensor(tensor, mps_graph) + ) + + mps_graph.mps_nodes.append(mps_node) + + +# [MPS TODO]: Works on a single iteration of llama2, but subsequent tokens +# are wrong when using Index put. Disabling it for now. +@register_node_visitor +class IndexPutVisitor(NodeVisitor): + # target = "aten.index_put.default" + target = "disabled" + + def __init__(self, *args) -> None: + super().__init__(*args) + + def infer_sizes(self, a: List[int], b: List[int]): + dimsA = len(a) + dimsB = len(b) + ndim = dimsA if dimsA > dimsB else dimsB + expandedSizes = [0] * ndim + for i in range(ndim - 1, -1, -1): + offset = ndim - 1 - i + dimA = dimsA - 1 - offset + dimB = dimsB - 1 - offset + sizeA = a[dimA] if dimA >= 0 else -1 + sizeB = b[dimB] if dimB >= 0 else -1 + expandedSizes[i] = sizeA if sizeB == -1 else sizeB + + return expandedSizes + + def define_node( + self, + node: torch.fx.Node, + mps_graph: MPSGraph, + ) -> None: + mps_node = self.create_unary_node(node, mps_graph, MPSIndexPut) + updates_shape = get_shape(node.args[2]) + input_shape = get_shape(node.args[0]) + new_shape = [] + if len(updates_shape) != 1 and len(updates_shape) != len(input_shape): + new_shape = self.infer_sizes(input_shape, updates_shape) + mps_node.mpsnode_union.values_shape = new_shape + + tensors = cast(List[torch.fx.Node], node.args[1]) + for tensor in tensors: + mps_node.mpsnode_union.indices_id.append( + self.define_tensor(tensor, mps_graph) + ) + + mps_node.mpsnode_union.values_id = self.define_tensor( + get_input_node(node, 2), mps_graph + ) + mps_graph.mps_nodes.append(mps_node) + + @register_node_visitor class EmbeddingVisitor(NodeVisitor): target = "aten.embedding.default" diff --git a/backends/apple/mps/operators/unary_ops.py b/backends/apple/mps/operators/unary_ops.py index 411924d040..8b67d7dfba 100644 --- a/backends/apple/mps/operators/unary_ops.py +++ b/backends/apple/mps/operators/unary_ops.py @@ -30,6 +30,7 @@ MPSLog, MPSLog10, MPSLog2, + MPSLogicalNot, MPSNeg, MPSReciprocal, MPSRound, @@ -79,6 +80,7 @@ class UnaryOpVisitor(NodeVisitor): "aten.isnan.default", "aten.isinf.default", "aten.round.default", + "aten.logical_not.default", ] def __init__(self, *args) -> None: @@ -115,6 +117,7 @@ def __init__(self, *args) -> None: exir_ops.edge.aten.isnan.default: MPSIsnan, exir_ops.edge.aten.isinf.default: MPSIsinf, exir_ops.edge.aten.round.default: MPSRound, + exir_ops.edge.aten.logical_not.default: MPSLogicalNot, } def define_node( diff --git a/backends/apple/mps/partition/mps_partitioner.py b/backends/apple/mps/partition/mps_partitioner.py index a06677a59a..e5497389d1 100644 --- a/backends/apple/mps/partition/mps_partitioner.py +++ b/backends/apple/mps/partition/mps_partitioner.py @@ -4,12 +4,13 @@ # import logging -from typing import Any, Dict, List, Union +from typing import Any, cast, Dict, List, Union import torch from executorch.backends.apple.mps.mps_preprocess import MPSBackend from executorch.backends.apple.mps.operators.node_visitor import get_node_visitors from executorch.backends.apple.mps.utils.mps_utils import is_parameter +from executorch.backends.transforms import get_shape from executorch.exir.backend.backend_details import CompileSpec from executorch.exir.backend.canonical_partitioners.pattern_op_partitioner import ( generate_partitions_from_list_of_nodes, @@ -20,6 +21,7 @@ PartitionResult, ) from executorch.exir.backend.utils import tag_constant_data +from executorch.exir.dialects._ops import ops as exir_ops from torch.export.exported_program import ExportedProgram from torch.fx.passes.infra.partitioner import Partition from torch.fx.passes.operator_support import OperatorSupportBase @@ -28,6 +30,13 @@ logging.basicConfig(level=logging.DEBUG, format=FORMAT) +# ops implemented as Metal kernels. +METAL_KERNELS = [ + exir_ops.edge.aten.index.Tensor, + exir_ops.edge.aten.index_put.default, +] + + class MPSOperatorSupport(OperatorSupportBase): def __init__(self, edge_program: torch.export.ExportedProgram, compiler_specs): self.node_visitors = get_node_visitors(edge_program) @@ -65,10 +74,47 @@ def generate_partitions(self, edge_program: ExportedProgram) -> List[Any]: op_support=self.supported_ops, ) + def mps_graph_advanced_indexing_support(self, node: torch.fx.Node): + num_indices = 0 + tensors = cast(List[torch.fx.Node], node.args[1]) + input = cast(torch.fx.Node, node.args[0]) + for t in tensors: + if t is not None: + num_indices += 1 + # Can dispatch to MPSGraph if the length of the slices is equal + # to the number of dimensions of the sliced tensors, or only one + # slice is present. All other cases will fallback to a Metal kernel. + if num_indices == len(get_shape(input)) or num_indices == 1: + return True + + return False + + def use_metal_kernel(self, node: torch.fx.Node): + if node.target in METAL_KERNELS: + if ( + node.target == exir_ops.edge.aten.index.Tensor + or node.target == exir_ops.edge.aten.index_put.default + ): + if not self.mps_graph_advanced_indexing_support(node): + return True + return False + def tag_nodes(self, partitions: List[Partition]) -> None: for partition in partitions: + crt_partition_counter = 0 for node in partition.nodes: delegation_tag = f"mps_{partition.id}" + if self.use_metal_kernel(node): + logging.warning(f"[WARNING] Using Metal kernel for op {node.name}!") + # Partition the Metal kernel into a separate partition + crt_partition_counter += 1 + delegation_tag = ( + f"{delegation_tag}_metal_kernel_{crt_partition_counter}" + ) + crt_partition_counter += 1 + else: + delegation_tag = f"{delegation_tag}_{crt_partition_counter}" + node.meta["delegation_tag"] = delegation_tag self.partition_tags[delegation_tag] = self.delegation_spec diff --git a/backends/apple/mps/runtime/MPSDevice.h b/backends/apple/mps/runtime/MPSDevice.h index d9ab403e80..a8b5dbe2b8 100644 --- a/backends/apple/mps/runtime/MPSDevice.h +++ b/backends/apple/mps/runtime/MPSDevice.h @@ -5,10 +5,19 @@ #pragma once +// Obj-C headers #include #include + +// Runtime headers +#include + +// MPS headers #include +#include +#include + #define MB(x) (x * 1048576UL) namespace torch { @@ -25,6 +34,11 @@ enum class MacOSVersion : uint32_t { MACOS_VER_14_0_PLUS, }; +enum class LibraryType : uint32_t { + INDEXING_KERNELS = 0, + MAX = INDEXING_KERNELS, +}; + class MPSDevice { public: /** @@ -53,9 +67,18 @@ class MPSDevice { ~MPSDevice(); + /** + * Compile a PSO for a given library type. + * Once compiled, the library and PSOs are cached. + */ + Error compilePSO(LibraryType libraryType, const char* kernelName); + Error compileLibrary(LibraryType); + private: static MPSDevice* _device; id _mtl_device; + std::unordered_map> _m_library_cache; + std::unordered_map> _m_pso_cache; MPSDevice(); }; diff --git a/backends/apple/mps/runtime/MPSDevice.mm b/backends/apple/mps/runtime/MPSDevice.mm index 86518fd002..f51851c379 100644 --- a/backends/apple/mps/runtime/MPSDevice.mm +++ b/backends/apple/mps/runtime/MPSDevice.mm @@ -16,6 +16,20 @@ static std::unique_ptr mps_device; static std::once_flag mpsdev_init; +static inline MTLLanguageVersion getMetalLanguageVersion(const id& device, bool macOS13Plus) { + // MPS Advanced Indexing needs at least Metal 2.0 (support for Argument Buffers and function constants) + // host_name attribute needs at least Metal 2.2 and ulong needs Metal 2.3 (supported on MacOS 11+) + MTLLanguageVersion languageVersion = MTLLanguageVersion2_3; +#if defined(__MAC_13_0) + if (macOS13Plus) { + languageVersion = MTLLanguageVersion3_0; + } +#endif + + ET_CHECK_MSG([device supportsFamily:MTLGPUFamilyMac2], "Missing Metal support for MTLGPUFamilyMac2"); + return languageVersion; +} + MPSDevice::~MPSDevice() { [_mtl_device release]; _mtl_device = nil; @@ -79,6 +93,57 @@ } } +const char* getLibraryCString(LibraryType libraryType) { + switch (libraryType) { + case LibraryType::INDEXING_KERNELS: + return "TODO"; + default: + ET_CHECK_MSG(false, "Unhandled library type!"); + } +} + +Error +MPSDevice::compileLibrary(LibraryType libraryType) { + Error err = Error::Ok; + NSError* error = nil; + MTLCompileOptions* options = [MTLCompileOptions new]; + [options setLanguageVersion:getMetalLanguageVersion(_mtl_device, isMacOS13Plus(MacOSVersion::MACOS_VER_13_0_PLUS))]; + [options setFastMathEnabled:YES]; + id lib = + [_mtl_device newLibraryWithSource:[NSString stringWithCString:getLibraryCString(libraryType) + encoding:NSASCIIStringEncoding] + options:options + error:&error]; + + ET_CHECK_OR_RETURN_ERROR( + lib != nil, + Internal, + "Failed to create indexing library, error: %s", [[error description] UTF8String] + ); + + _m_library_cache[libraryType] = lib; + return err; +} + +Error +MPSDevice::compilePSO(LibraryType libraryType, const char* kernelName) { + Error err = Error::Ok; + if (_m_library_cache.find(libraryType) == _m_library_cache.end()) { + ET_LOG(Debug, "Compiling library type: %d", libraryType); + err = compileLibrary(libraryType); + ET_CHECK_OR_RETURN_ERROR( + err == Error::Ok, + Internal, + "An error occured occured while compiling library %d", libraryType + ); + } + if (_m_pso_cache.find(kernelName) == _m_pso_cache.end()) { + ET_LOG(Debug, "Compiling kernel: %s", kernelName); + // err = compilePSO(libraryType, kernelName); + } + return err; +} + bool isMacOS13OrNewer(MacOSVersion version) { return MPSDevice::getInstance()->isMacOS13Plus(version); } diff --git a/backends/apple/mps/runtime/MPSGraphBuilder.h b/backends/apple/mps/runtime/MPSGraphBuilder.h index 0a7bf835a7..e4e89d6869 100644 --- a/backends/apple/mps/runtime/MPSGraphBuilder.h +++ b/backends/apple/mps/runtime/MPSGraphBuilder.h @@ -109,6 +109,7 @@ class MPSGraphBuilder { _DEFINE_MPS_OP(Isnan); _DEFINE_MPS_OP(Isinf); _DEFINE_MPS_OP(Round); + _DEFINE_MPS_OP(LogicalNot); _DEFINE_MPS_OP(NormCdf); // Clamp ops _DEFINE_MPS_OP(Clamp); @@ -120,6 +121,8 @@ class MPSGraphBuilder { // Indexing ops _DEFINE_MPS_OP(IndexSelect); _DEFINE_MPS_OP(Embedding); + _DEFINE_MPS_OP(IndexTensor); + _DEFINE_MPS_OP(IndexPut); // Linear algebra ops _DEFINE_MPS_OP(MatMul); _DEFINE_MPS_OP(Addmm); @@ -153,6 +156,7 @@ class MPSGraphBuilder { // Helper functions Error addNodeToMPSGraph(NodePtr nodePtr); + Error compileMetalKernel(NodePtr nodePtr); MPSShape *getMPSShape(int32_t id); MPSShape *getMPSShape(const flatbuffers::Vector *shape); int64_t numel(const flatbuffers::Vector *shape); @@ -161,6 +165,8 @@ class MPSGraphBuilder { MPSGraphTensor *getMPSGraphTensor(int32_t id); NSData *getConstantData(int32_t id); std::pair getMinMaxValues(NodePtr nodePtr); + Error compileMPSGraph(); + Error compileMetalKernel(); // Each MPSGraph op result in at least MPSGraphTensor being // produced, which will be stored in this structure. Other ops @@ -172,6 +178,7 @@ class MPSGraphBuilder { // FlatBuffer raw bytes of the serialized MPS model. const void *_buffer_pointer; + bool _metal_kernel; MPSGraph *_mpsGraph; MPSGraphExecutable *_mpsGraphExecutable; NSMutableDictionary *_feeds; diff --git a/backends/apple/mps/runtime/MPSGraphBuilder.mm b/backends/apple/mps/runtime/MPSGraphBuilder.mm index d82b677066..8b571001d4 100644 --- a/backends/apple/mps/runtime/MPSGraphBuilder.mm +++ b/backends/apple/mps/runtime/MPSGraphBuilder.mm @@ -17,6 +17,7 @@ _targetTensors = [NSMutableArray new]; _mpsGraphExecutable = nil; + _metal_kernel = false; } Error @@ -32,8 +33,34 @@ mpsgraph::MPSGraphIdentifier()); _flatBufferGraph = mpsgraph::GetMPSGraph(_buffer_pointer); - _idToMPSGraphTensor.resize(_flatBufferGraph->mps_values()->size(), nullptr); + switch (_flatBufferGraph->graph_type()) { + case mpsgraph::OpType::metal_kernel: + { + _metal_kernel = true; + err = compileMetalKernel(); + break; + } + case mpsgraph::OpType::mps_graph: + { + err = compileMPSGraph(); + break; + } + default: + ET_CHECK_OR_RETURN_ERROR( + false, + DelegateInvalidCompatibility, + "Received an invalid operation type: expected MPSGraph or metal kernel, but got: %s", + EnumNameOpType(_flatBufferGraph->graph_type())); + } + + return err; +} +Error +MPSGraphBuilder::compileMPSGraph() { + Error err = Error::Ok; + + _idToMPSGraphTensor.resize(_flatBufferGraph->mps_values()->size(), nullptr); // Add the placeholder nodes to the graph. for (auto in_id : *_flatBufferGraph->input_ids()) { err = mpsGraphRankedPlaceholder(in_id); @@ -71,6 +98,30 @@ return err; } +Error +MPSGraphBuilder::compileMetalKernel() { + Error err = Error::Ok; + + ET_CHECK_OR_RETURN_ERROR( + _flatBufferGraph->mps_nodes()->size() == 1, + DelegateInvalidCompatibility, + "Currently supporting dispatching a single Metal kernel."); + ET_CHECK_OR_RETURN_ERROR( + _flatBufferGraph->constant_ids()->size() == 0, + DelegateInvalidCompatibility, + "Currently not supporting dispatching Metal kernels with constants."); + + // Compile the corresponding Metal kernel + for (auto node : *_flatBufferGraph->mps_nodes()) { + err = compileMetalKernel(node); + if (err != Error::Ok) { + return err; + } + } + + return err; +} + Error MPSGraphBuilder::mpsGraphRankedPlaceholder(int32_t id) { ET_LOG(Debug, "%s: %d", __FUNCTION__, id); diff --git a/backends/apple/mps/runtime/operations/IndexingOps.mm b/backends/apple/mps/runtime/operations/IndexingOps.mm index 1c02cbea5c..b4dcf192b4 100644 --- a/backends/apple/mps/runtime/operations/IndexingOps.mm +++ b/backends/apple/mps/runtime/operations/IndexingOps.mm @@ -108,6 +108,102 @@ return Error::Ok; } +Error +MPSGraphBuilder::mpsIndexTensorOp(NodePtr nodePtr) { + Error err = Error::Ok; + auto graphNode = nodePtr->mpsnode_union_as_MPSIndexTensor(); + ET_LOG( + Debug, "%s: %d -> %d", + __FUNCTION__, graphNode->input1_id(), graphNode->output_id() + ); + + if (_metal_kernel) { + err = MPSDevice::getInstance()->compilePSO(LibraryType::INDEXING_KERNELS, "index_select"); + ET_CHECK_MSG(false, "Metal kernel path not yet implemented\n"); + } else { + int validIndices = 0; + int numIndices = graphNode->indices_id()->size(); + int axis = -1; + int indexId = -1; + for (int i = 0; i < numIndices; i++) { + int32_t index_id = graphNode->indices_id()->Get(i); + if (index_id == -1) { + continue; + } + validIndices++; + axis = i; + indexId = index_id; + } + ET_LOG(Debug, "index.Tensor with %d indices (axis = %d)", validIndices, axis); + ET_CHECK(validIndices > 0); + + if (validIndices == 1) { + MPSGraphTensor* updatesTensor = getMPSGraphTensor(graphNode->input1_id()); + MPSGraphTensor* indexTensor = getMPSGraphTensor(indexId); + _idToMPSGraphTensor[graphNode->output_id()] = + [_mpsGraph gatherWithUpdatesTensor:updatesTensor indicesTensor:indexTensor axis:axis batchDimensions:0 name:nil]; + } else { + ET_CHECK_MSG(false, "Not yet implemented"); + } + } + + return err; +} + +Error +MPSGraphBuilder::mpsIndexPutOp(NodePtr nodePtr) { + Error err = Error::Ok; + auto graphNode = nodePtr->mpsnode_union_as_MPSIndexPut(); + ET_LOG( + Debug, "%s: %d -> %d", + __FUNCTION__, graphNode->input1_id(), graphNode->output_id() + ); + + if (_metal_kernel) { + err = MPSDevice::getInstance()->compilePSO(LibraryType::INDEXING_KERNELS, "index_put"); + ET_CHECK_MSG(false, "Metal kernel path not yet implemented\n"); + } else { + int validIndices = 0; + int numIndices = graphNode->indices_id()->size(); + int axis = -1; + int indexId = -1; + for (int i = 0; i < numIndices; i++) { + int32_t index_id = graphNode->indices_id()->Get(i); + if (index_id == -1) { + continue; + } + validIndices++; + axis = i; + indexId = index_id; + } + ET_LOG(Debug, "index_put with %d indices (axis = %d)", validIndices, axis); + ET_CHECK(validIndices > 0); + + if (validIndices == 1) { + MPSGraphTensor* dataTensor = getMPSGraphTensor(graphNode->input1_id()); + MPSGraphTensor* updatesTensor = getMPSGraphTensor(graphNode->values_id()); + MPSGraphTensor* indicesTensor = getMPSGraphTensor(indexId); + if (graphNode->values_shape()->size() != 0) { + updatesTensor = [_mpsGraph broadcastTensor:updatesTensor + toShape:getMPSShape(graphNode->values_shape()) + name:nil]; + } + + _idToMPSGraphTensor[graphNode->output_id()] = + [_mpsGraph scatterWithDataTensor:dataTensor + updatesTensor:updatesTensor + indicesTensor:indicesTensor + axis:axis + mode:MPSGraphScatterModeSet + name:nil]; + } else { + ET_CHECK_MSG(false, "Not yet implemented"); + } + } + + return err; +} + } // namespace delegate } // namespace mps } // namespace executor diff --git a/backends/apple/mps/runtime/operations/OperationUtils.mm b/backends/apple/mps/runtime/operations/OperationUtils.mm index 71c36c967e..648421ee2c 100644 --- a/backends/apple/mps/runtime/operations/OperationUtils.mm +++ b/backends/apple/mps/runtime/operations/OperationUtils.mm @@ -166,6 +166,7 @@ _DEFINE_MPS_NODE(Isnan); _DEFINE_MPS_NODE(Isinf); _DEFINE_MPS_NODE(Round); + _DEFINE_MPS_NODE(LogicalNot); // Clamp ops _DEFINE_MPS_NODE(Clamp); _DEFINE_MPS_NODE(Where); @@ -178,6 +179,8 @@ //Indexing ops _DEFINE_MPS_NODE(IndexSelect); _DEFINE_MPS_NODE(Embedding); + _DEFINE_MPS_NODE(IndexTensor); + _DEFINE_MPS_NODE(IndexPut); // Reduce ops _DEFINE_MPS_NODE(Mean); // Shape ops @@ -223,6 +226,11 @@ } } +Error +MPSGraphBuilder::compileMetalKernel(NodePtr nodePtr) { + return addNodeToMPSGraph(nodePtr); +} + #undef _DEFINE_MPS_NODE MPSGraphTensor* diff --git a/backends/apple/mps/runtime/operations/ShapeOps.mm b/backends/apple/mps/runtime/operations/ShapeOps.mm index 720161b955..75de566e4a 100644 --- a/backends/apple/mps/runtime/operations/ShapeOps.mm +++ b/backends/apple/mps/runtime/operations/ShapeOps.mm @@ -42,13 +42,9 @@ __FUNCTION__, graphNode->input1_id(), graphNode->output_id() ); - NSMutableArray* shape = [NSMutableArray array]; - for (int32_t i = 0; i < graphNode->num_dims(); i++) { - [shape addObject:[NSNumber numberWithInteger:graphNode->shape()->Get(i)]]; - } _idToMPSGraphTensor[graphNode->output_id()] = [_mpsGraph reshapeTensor:getMPSGraphTensor(graphNode->input1_id()) - withShape:shape + withShape:getMPSShape(graphNode->shape()) name:@"view_copy"]; return Error::Ok; @@ -91,7 +87,7 @@ __FUNCTION__, graphNode->output_id() ); - NSMutableArray* inputTensors = [NSMutableArray array]; + NSMutableArray* inputTensors = [NSMutableArray arrayWithCapacity:graphNode->input_ids()->size()];; for (auto id : *graphNode->input_ids()) { MPSGraphTensor* catTensor = getMPSGraphTensor(id); if (catTensor != nil) diff --git a/backends/apple/mps/runtime/operations/UnaryOps.mm b/backends/apple/mps/runtime/operations/UnaryOps.mm index 31246bd44f..ed06584b27 100644 --- a/backends/apple/mps/runtime/operations/UnaryOps.mm +++ b/backends/apple/mps/runtime/operations/UnaryOps.mm @@ -92,6 +92,7 @@ REGISTER_UNARY_OP(Isnan, isNaN) REGISTER_UNARY_OP(Isinf, isInfinite) REGISTER_UNARY_OP(Round, round) +REGISTER_UNARY_OP(LogicalNot, not) Error diff --git a/backends/apple/mps/serialization/mps_graph_schema.py b/backends/apple/mps/serialization/mps_graph_schema.py index 66697b04b7..8134091a01 100644 --- a/backends/apple/mps/serialization/mps_graph_schema.py +++ b/backends/apple/mps/serialization/mps_graph_schema.py @@ -27,6 +27,11 @@ class MPSDataType(IntEnum): mps_data_type_complex_float32 = 11 +class OpType(IntEnum): + mps_graph = 0 + metal_kernel = 1 + + @dataclass class MPSNode1x1: input1_id: int @@ -359,6 +364,11 @@ class MPSRound(MPSNode1x1): pass +@dataclass +class MPSLogicalNot(MPSNode1x1): + pass + + @dataclass class MPSBitwise(MPSNode1x1): pass @@ -434,6 +444,18 @@ class MPSEmbedding(MPSNode2x1): sparse: bool = False +@dataclass +class MPSIndexTensor(MPSNode1x1): + indices_id: List[int] = field(default_factory=list) + + +@dataclass +class MPSIndexPut(MPSNode1x1): + indices_id: List[int] = field(default_factory=list) + values_shape: List[int] = field(default_factory=list) + values_id: int = -1 + + ## ## Shape ops ## @@ -664,6 +686,7 @@ class MPSArange: MPSIsnan, MPSIsinf, MPSRound, + MPSLogicalNot, # Linear algebra ops MPSMatMul, MPSAddmm, @@ -678,6 +701,8 @@ class MPSArange: # Indexing ops MPSIndexSelect, MPSEmbedding, + MPSIndexTensor, + MPSIndexPut, # Shape ops MPSPermute, MPSView, @@ -741,3 +766,4 @@ class MPSGraph: input_ids: List[int] output_ids: List[int] constant_ids: List[int] + graph_type: OpType diff --git a/backends/apple/mps/serialization/schema.fbs b/backends/apple/mps/serialization/schema.fbs index c3e3eaa4fa..6ba2c937f3 100644 --- a/backends/apple/mps/serialization/schema.fbs +++ b/backends/apple/mps/serialization/schema.fbs @@ -24,6 +24,13 @@ enum MPSDataType : short { mps_data_type_complex_float32 = 11, } +// ops like index.Tensor and index.put are currentely implemented as +// Metal kernels for unsupported MPSGraph cases. +enum OpType : short { + mps_graph, + metal_kernel +} + // Helper classes to define the number of input and output tensors for a node. // Not meant to be used directly. @@ -145,6 +152,20 @@ table MPSEmbedding { sparse:bool; } +table MPSIndexTensor { + input1_id:int; + indices_id:[int]; + output_id:int; +} + +table MPSIndexPut { + input1_id:int; + indices_id:[int]; + values_shape:[int]; + values_id:int; + output_id:int; +} + // Shape ops. table MPSPermute { input1_id:int; @@ -350,6 +371,7 @@ union MPSNodeUnion { MPSIsnan: _MPSNode1x1, MPSIsinf: _MPSNode1x1, MPSRound: _MPSNode1x1, + MPSLogicalNot: _MPSNode1x1, // Linear algebra ops MPSMatMul: _MPSNode2x1, @@ -366,6 +388,8 @@ union MPSNodeUnion { // Indexing ops MPSIndexSelect, MPSEmbedding, + MPSIndexTensor, + MPSIndexPut, // Reduce ops MPSMean, @@ -438,6 +462,8 @@ table MPSGraph { input_ids:[int]; output_ids:[int]; constant_ids:[int]; + + graph_type:OpType; } root_type MPSGraph; diff --git a/backends/apple/mps/setup.md b/backends/apple/mps/setup.md index 9c2222bb6d..c8fdfeb98e 100644 --- a/backends/apple/mps/setup.md +++ b/backends/apple/mps/setup.md @@ -15,15 +15,28 @@ The MPS backend device maps machine learning computational graphs and primitives * [Introduction to ExecuTorch](intro-how-it-works.md) * [Setting up ExecuTorch](getting-started-setup.md) * [Building ExecuTorch with CMake](runtime-build-and-cross-compilation.md) +* [ExecuTorch iOS Demo App](demo-apps-ios.md) +* [ExecuTorch iOS LLaMA Demo App](llm/llama-demo-ios.md) ::: :::: ## Prerequisites (Hardware and Software) -In order to be able to successfully build and run a model using the MPS backend for ExecuTorch, you'll need the following hardware and software components. - - macOS 12 / iOS 15 or later (for MPS runtime) - - Xcode command-line tools: xcode-select --install +In order to be able to successfully build and run a model using the MPS backend for ExecuTorch, you'll need the following hardware and software components: + +### Hardware: + - A [mac](https://www.apple.com/mac/) for tracing the model + +### Software: + + - **Ahead of time** tracing: + - [macOS](https://www.apple.com/macos/) 12 + + - **Runtime**: + - [macOS](https://www.apple.com/macos/) >= 12.4 + - [iOS](https://www.apple.com/ios) >= 15.4 + - [Xcode](https://developer.apple.com/xcode/) >= 14.1 ## Setting up Developer Environment @@ -40,47 +53,34 @@ In order to be able to successfully build and run a model using the MPS backend ### AOT (Ahead-of-time) Components **Compiling model for MPS delegate**: -- In this step, you will generate a simple ExecuTorch program that lowers MobileNetV3 model to the MPS delegate. You'll then pass this Program(the `.pte` file) during the runtime to run it using the MPS backend. +- In this step, you will generate a simple ExecuTorch program that lowers MobileNetV3 model to the MPS delegate. You'll then pass this Program (the `.pte` file) during the runtime to run it using the MPS backend. ```bash cd executorch -python3 -m examples.apple.mps.scripts.mps_example --model_name="mv3" --bundled +# Note: `mps_example` script uses by default the MPSPartitioner for ops that are not yet supported by the MPS delegate. To turn it off, pass `--no-use_partitioner`. +python3 -m examples.apple.mps.scripts.mps_example --model_name="mv3" --bundled --use_fp16 + +# To see all options, run following command: +python3 -m examples.apple.mps.scripts.mps_example --help ``` ### Runtime -**Building the MPS executor runner** -- In this step, you'll be building the `mps_executor_runner` that is able to run MPS lowered modules. - +**Building the MPS executor runner:** ```bash -# Build the mps_executor_runner +# In this step, you'll be building the `mps_executor_runner` that is able to run MPS lowered modules: +cd executorch +./examples/apple/mps/scripts/build_mps_executor_runner.sh +``` + +## Run the mv3 generated model using the mps_executor_runner + ```bash -# Build and install executorch -cmake -DBUCK2="$BUCK" \ - -DCMAKE_INSTALL_PREFIX=cmake-out \ - -DCMAKE_BUILD_TYPE=Release \ - -DEXECUTORCH_BUILD_SDK=ON \ - -DEXECUTORCH_ENABLE_EVENT_TRACER=ON \ - -DEXECUTORCH_BUILD_MPS=ON \ - -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" \ - -Bcmake-out . -cmake --build cmake-out -j9 --target install --config Release -CMAKE_PREFIX_PATH="${PWD}/cmake-out/lib/cmake/ExecuTorch;${PWD}/cmake-out/third-party/gflags" -# build mps_executor_runner -rm -rf cmake-out/examples/apple/mps -cmake \ - -DCMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH" \ - -DCMAKE_BUILD_TYPE=Release \ - -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" \ - -Bcmake-out/examples/apple/mps \ - examples/apple/mps - -cmake --build cmake-out/examples/apple/mps -j9 --config Release - -# Run the mv2 generated model using the mps_executor_runner ./cmake-out/examples/apple/mps/mps_executor_runner --model_path mv3_mps_bundled_fp16.pte --bundled_program +``` -# You should see the following results. Note that no output file will be generated in this example: +- You should see the following results. Note that no output file will be generated in this example: +``` I 00:00:00.003290 executorch:mps_executor_runner.mm:286] Model file mv3_mps_bundled_fp16.pte is loaded. I 00:00:00.003306 executorch:mps_executor_runner.mm:292] Program methods: 1 I 00:00:00.003308 executorch:mps_executor_runner.mm:294] Running method forward @@ -94,12 +94,43 @@ I 00:00:00.118731 executorch:mps_executor_runner.mm:438] Model executed successf I 00:00:00.122615 executorch:mps_executor_runner.mm:501] Model verified successfully. ``` +### [Optional] Run the generated model directly using pybind +1. Make sure `pybind` MPS support was installed: +```bash +./install_requirements.sh --pybind mps +``` +2. Run the `mps_example` script to trace the model and run it directly from python: +```bash +cd executorch +# Check correctness between PyTorch eager forward pass and ExecuTorch MPS delegate forward pass +python3 -m examples.apple.mps.scripts.mps_example --model_name="mv3" --no-use_fp16 --check_correctness +# You should see following output: `Results between ExecuTorch forward pass with MPS backend and PyTorch forward pass for mv3_mps are matching!` + +# Check performance between PyTorch MPS forward pass and ExecuTorch MPS forward pass +python3 -m examples.apple.mps.scripts.mps_example --model_name="mv3" --no-use_fp16 --bench_pytorch +``` + +### Profiling: +1. [Optional] Generate an [ETRecord](./sdk-etrecord.rst) while you're exporting your model. +```bash +cd executorch +python3 -m examples.apple.mps.scripts.mps_example --model_name="mv3" --generate_etrecord -b +``` +2. Run your Program on the ExecuTorch runtime and generate an [ETDump](./sdk-etdump.md). +``` +./cmake-out/examples/apple/mps/mps_executor_runner --model_path mv3_mps_bundled_fp16.pte --bundled_program --dump-outputs +``` +3. Create an instance of the Inspector API by passing in the ETDump you have sourced from the runtime along with the optionally generated ETRecord from step 1. +```bash +python3 -m sdk.inspector.inspector_cli --etdump_path etdump.etdp --etrecord_path etrecord.bin +``` + ## Deploying and Running on Device ***Step 1***. Create the ExecuTorch core and MPS delegate frameworks to link on iOS ```bash cd executorch -./build/build_apple_frameworks.sh --Release --mps +./build/build_apple_frameworks.sh --mps ``` `mps_delegate.xcframework` will be in `cmake-out` folder, along with `executorch.xcframework` and `portable_delegate.xcframework`: @@ -123,4 +154,4 @@ In this tutorial, you have learned how to lower a model to the MPS delegate, bui ## Frequently encountered errors and resolution. -If you encountered any bugs or issues following this tutorial please file a bug/issue on the ExecuTorch repository, with hashtag **#mps**. +If you encountered any bugs or issues following this tutorial please file a bug/issue on the [ExecuTorch repository](https://github.com/pytorch/executorch/issues), with hashtag **#mps**. diff --git a/backends/apple/mps/targets.bzl b/backends/apple/mps/targets.bzl index 94f030310d..4d2862eb72 100644 --- a/backends/apple/mps/targets.bzl +++ b/backends/apple/mps/targets.bzl @@ -22,6 +22,7 @@ def define_common_targets(is_xplat = False, platforms = []): "-Wno-unused-const-variable", "-Wno-unused-variable", "-fno-objc-arc", + "-std=c++17", ], "deps": [ "//executorch/runtime/core:core", diff --git a/backends/arm/arm_quantizer_utils.py b/backends/arm/arm_quantizer_utils.py index 63c98ee42d..7945569576 100644 --- a/backends/arm/arm_quantizer_utils.py +++ b/backends/arm/arm_quantizer_utils.py @@ -780,11 +780,11 @@ def _annotate_add( if _is_annotated([add_node]): continue + input_act0 = add_node.args[0] input_act_qspec = get_input_act_qspec(quantization_config) - output_act_qspec = get_output_act_qspec(quantization_config) + shared_with_input0_qspec = SharedQuantizationSpec((input_act0, add_node)) input_qspec_map = {} - input_act0 = add_node.args[0] if isinstance(input_act0, Node): if _is_input_large_scalar(input_act0, gm): continue @@ -798,11 +798,14 @@ def _annotate_add( continue if _is_input_non_float_tensor(input_act1): continue - input_qspec_map[input_act1] = input_act_qspec + if input_act0 is not input_act1: + input_qspec_map[input_act1] = shared_with_input0_qspec + else: + input_qspec_map[input_act1] = input_act_qspec add_node.meta["quantization_annotation"] = QuantizationAnnotation( input_qspec_map=input_qspec_map, - output_qspec=output_act_qspec, + output_qspec=shared_with_input0_qspec, _annotated=True, ) return annotated_partitions diff --git a/backends/arm/arm_vela.py b/backends/arm/arm_vela.py index 0d0f0eb037..f387672b7b 100644 --- a/backends/arm/arm_vela.py +++ b/backends/arm/arm_vela.py @@ -43,7 +43,14 @@ def vela_compile(tosa_graph, args: List[str]): # invoke vela vela_command = f"cd {tmpdir}; vela {' '.join(args)} {tosaname}" - subprocess.run([vela_command], shell=True, check=True) + try: + subprocess.run([vela_command], shell=True, check=True, capture_output=True) + except subprocess.CalledProcessError as process_error: + raise RuntimeError( + f"Vela compiler ('{vela_command}') failed with error:\n \ + {process_error.stderr.decode()}\n \ + Stdout:\n{process_error.stdout.decode()}" + ) np_path = os.path.join(tmpdir, "output", "out_sg0_vela.npz") blocks = b"" diff --git a/backends/arm/operators/op_addmm.py b/backends/arm/operators/op_addmm.py index cc49e5c382..444799d353 100644 --- a/backends/arm/operators/op_addmm.py +++ b/backends/arm/operators/op_addmm.py @@ -73,7 +73,7 @@ def define_node( quant_node = input_node.all_input_nodes[0] else: quant_node = input_node - input_zp = get_quant_node_args(quant_node)[1] + input_zp = get_quant_node_args(quant_node).zp attr.ConvAttribute( pad=pad_attr, stride=stride_attr, @@ -111,24 +111,21 @@ def define_node( # rank > 2 linear layer if input_node.target == exir_ops.edge.aten.view_copy.default: quant_node = input_node.all_input_nodes[0] - input_scale, _ = get_quant_node_args(quant_node) + input_scale = get_quant_node_args(quant_node).scale consumer_node = list(node.users)[0] consumer_consumer_node = list(consumer_node.users)[0] - ( - consumer_node_scale, - consumer_node_node_zp, - ) = get_quant_node_args(consumer_consumer_node) - + quant_args = get_quant_node_args(consumer_consumer_node) + consumer_node_scale = quant_args.scale + consumer_node_node_zp = quant_args.zp else: - input_scale, _ = get_quant_node_args(input_node) + input_scale = get_quant_node_args(input_node).scale consumer_node = list(node.users)[0] - ( - consumer_node_scale, - consumer_node_node_zp, - ) = get_quant_node_args(consumer_node) + quant_args = get_quant_node_args(consumer_node) + consumer_node_scale = quant_args.scale + consumer_node_node_zp = quant_args.zp weight_node_q_node = weight_node.all_input_nodes[0] - weight_scale, _ = get_quant_node_args(weight_node_q_node) + weight_scale = get_quant_node_args(weight_node_q_node).scale output_rescale_scale = (input_scale * weight_scale) / consumer_node_scale ( diff --git a/backends/arm/operators/op_common.py b/backends/arm/operators/op_common.py index 4701343e8e..eadf00c294 100644 --- a/backends/arm/operators/op_common.py +++ b/backends/arm/operators/op_common.py @@ -31,8 +31,8 @@ def build_avg_pool_2d_common( output_zp = 0 if is_quant_node: - _, input_zp = get_quant_node_args(node.args[0]) - _, output_zp = get_quant_node_args(list(node.users)[0]) + input_zp = get_quant_node_args(node.args[0]).zp + output_zp = get_quant_node_args(list(node.users)[0]).zp attr = ts.TosaSerializerAttribute() attr.PoolAttribute( diff --git a/backends/arm/operators/op_conv2d.py b/backends/arm/operators/op_conv2d.py index 45a9a6671d..cc1c1f3c26 100644 --- a/backends/arm/operators/op_conv2d.py +++ b/backends/arm/operators/op_conv2d.py @@ -80,7 +80,7 @@ def define_node( ) input_zp = ( - get_quant_node_args(node.all_input_nodes[0])[1] if is_quant_node else 0 + get_quant_node_args(node.all_input_nodes[0]).zp if is_quant_node else 0 ) attr.ConvAttribute( diff --git a/backends/arm/operators/op_hardtanh.py b/backends/arm/operators/op_hardtanh.py index eb9b0a18fb..3d58f6d628 100644 --- a/backends/arm/operators/op_hardtanh.py +++ b/backends/arm/operators/op_hardtanh.py @@ -1,4 +1,4 @@ -# Copyright 2023 Arm Limited and/or its affiliates. +# Copyright 2023-2024 Arm Limited and/or its affiliates. # # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. @@ -11,6 +11,8 @@ register_node_visitor, ) from executorch.backends.arm.tosa_mapping import TosaArg + +from executorch.backends.arm.tosa_quant_utils import get_quant_node_args from serializer.tosa_serializer import TosaOp @@ -30,12 +32,31 @@ def define_node( is_quant_node: bool, ) -> None: attr = ts.TosaSerializerAttribute() + + if is_quant_node: + # Get quant parameters + scale, zp, qmin, qmax = get_quant_node_args(node.all_input_nodes[0]) + # Convert to quantized representation + clamp_min_qs = round((inputs[1].number / scale) + zp) + clamp_min_qs = max(clamp_min_qs, qmin) + clamp_max_qs = round((inputs[2].number / scale) + zp) + clamp_max_qs = min(clamp_max_qs, qmax) + # Set fp values to 0.0 since they are not used + clamp_min_fp = 0.0 + clamp_max_fp = 0.0 + else: + clamp_min_fp = inputs[1].number + clamp_max_fp = inputs[2].number + # Set qs values to 0 since they are not used + clamp_min_qs = 0 + clamp_max_qs = 0 + attr.ClampAttribute( tosa_graph.builder, - int(inputs[1].number), - int(inputs[2].number), - inputs[1].number, - inputs[2].number, + clamp_min_qs, + clamp_max_qs, + clamp_min_fp, + clamp_max_fp, ) tosa_graph.addOperator(TosaOp.Op().CLAMP, [inputs[0].name], [output.name], attr) diff --git a/backends/arm/operators/op_placeholder.py b/backends/arm/operators/op_placeholder.py index 6a57d895ff..05e02468d6 100644 --- a/backends/arm/operators/op_placeholder.py +++ b/backends/arm/operators/op_placeholder.py @@ -50,11 +50,13 @@ def process_placeholder( weight_node = weight_node_permuted.all_input_nodes[0] if input_node.target == exir_ops.edge.aten.view_copy.default: - input_node_scale, _ = get_quant_node_args(input_node.all_input_nodes[0]) + input_node_scale = get_quant_node_args( + input_node.all_input_nodes[0] + ).scale else: - input_node_scale, _ = get_quant_node_args(input_node) + input_node_scale = get_quant_node_args(input_node).scale - weight_node_scale, _ = get_quant_node_args(weight_node) + weight_node_scale = get_quant_node_args(weight_node).scale bias_values_quantized = ( (parameter_values / (input_node_scale * weight_node_scale)) @@ -81,8 +83,8 @@ def process_placeholder( bias_node, ) = consumer_node.all_input_nodes - input_node_scale, _ = get_quant_node_args(input_node) - weight_node_scale, _ = get_quant_node_args(weight_node) + input_node_scale = get_quant_node_args(input_node).scale + weight_node_scale = get_quant_node_args(weight_node).scale bias_scales = input_node_scale * weight_node_scale parameter_values_quantized = ( diff --git a/backends/arm/test/ops/test_conv_combos.py b/backends/arm/test/ops/test_conv_combos.py index 1fd6849379..2bde068848 100644 --- a/backends/arm/test/ops/test_conv_combos.py +++ b/backends/arm/test/ops/test_conv_combos.py @@ -12,6 +12,7 @@ import torch from executorch.backends.arm.test import common from executorch.backends.arm.test.tester.arm_tester import ArmTester +from parameterized import parameterized logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -126,6 +127,32 @@ def forward(self, x): return x +class ComboConvRelu6(torch.nn.Module): + edge_op_list = [ + "executorch_exir_dialects_edge__ops_aten_convolution_default", + "executorch_exir_dialects_edge__ops_aten_hardtanh_default", + ] + + test_data = [ + (20 * torch.randn(1, 3, 256, 256),), + (5 * torch.randn(1, 3, 256, 256),), + (torch.randn(1, 3, 256, 256),), + (-5 * torch.randn(1, 3, 256, 256),), + ] + + def __init__(self): + super().__init__() + self.conv2d = torch.nn.Conv2d( + in_channels=3, out_channels=3, kernel_size=3, stride=1, groups=1 + ) + self.relu6 = torch.nn.ReLU6() + + def forward(self, x): + x = self.conv2d(x) + x = self.relu6(x) + return x + + class TestConvCombos(unittest.TestCase): def _test_conv_combo_tosa_MI_pipeline( self, module: torch.nn.Module, test_data: Tuple[torch.Tensor] @@ -222,15 +249,9 @@ def test_conv_batchnorm_relu_tosa_MI(self): model = ComboConvBatchnormRelu() self._test_conv_combo_tosa_MI_pipeline(model, model.get_inputs()) - # TODO(MLETORCH-85): Investigate numerical issue. This diff is present in legacy - # testcase as well (and also not tested). For now, just increase the - # tolerance, such that we don't skip the test entirely (i.e. we maintain - # functionality). def test_conv_batchnorm_relu_tosa_BI(self): model = ComboConvBatchnormRelu() - self._test_conv_combo_tosa_BI_pipeline( - model, model.get_inputs(), atol=1.0, rtol=1.0 - ) + self._test_conv_combo_tosa_BI_pipeline(model, model.get_inputs()) @unittest.skipIf( not common.VELA_INSTALLED, @@ -240,6 +261,31 @@ def test_conv_batchnorm_relu_u55_BI(self): model = ComboConvBatchnormRelu() self._test_conv_combo_u55_BI_pipeline(model, model.get_inputs()) + ################## + ## Conv + ReLU6 ## + ################## + @parameterized.expand(ComboConvRelu6.test_data) + def test_conv_relu6_tosa_MI(self, test_data: torch.Tensor): + model = ComboConvRelu6() + test_data = (test_data,) + self._test_conv_combo_tosa_MI_pipeline(model, test_data) + + @parameterized.expand(ComboConvRelu6.test_data) + def test_conv_relu6_tosa_BI(self, test_data: torch.Tensor): + model = ComboConvRelu6() + test_data = (test_data,) + self._test_conv_combo_tosa_BI_pipeline(model, test_data) + + @parameterized.expand(ComboConvRelu6.test_data) + @unittest.skipIf( + not common.VELA_INSTALLED, + "There is no point in running U55 tests if the Vela tool is not installed", + ) + def test_conv_relu6_u55_BI(self, test_data: torch.Tensor): + model = ComboConvRelu6() + test_data = (test_data,) + self._test_conv_combo_u55_BI_pipeline(model, test_data) + ############################### ## Block bottleneck residual ## ############################### @@ -247,14 +293,9 @@ def test_block_bottleneck_residual_tosa_MI(self): model = ComboBlockBottleneckResidual() self._test_conv_combo_tosa_MI_pipeline(model, model.get_inputs()) - # TODO(MLETORCH-85): Investigate numerical issue. This diff was present in legacy - # testcase as well. For now, just increase the tolerance, such that - # we don't skip the test entirely (i.e. we maintain functionality). def test_block_bottleneck_residual_tosa_BI(self): model = ComboBlockBottleneckResidual() - self._test_conv_combo_tosa_BI_pipeline( - model, model.get_inputs(), atol=1.0, rtol=1.0 - ) + self._test_conv_combo_tosa_BI_pipeline(model, model.get_inputs()) @unittest.skipIf( not common.VELA_INSTALLED, diff --git a/backends/arm/tosa_quant_utils.py b/backends/arm/tosa_quant_utils.py index 9e04ba68ee..25fba25039 100644 --- a/backends/arm/tosa_quant_utils.py +++ b/backends/arm/tosa_quant_utils.py @@ -6,8 +6,10 @@ # Utiliy functions for TOSA quantized lowerings import math +from typing import NamedTuple import serializer.tosa_serializer as ts +import torch.fx from executorch.backends.arm.tosa_mapping import TosaArg from executorch.exir.dialects._ops import ops as exir_ops from serializer.tosa_serializer import TosaOp, TosaSerializerTensor @@ -17,7 +19,14 @@ dq_q_ops = [q_op, dq_op] -def is_quant_node(node): +class QuantArgs(NamedTuple): + scale: float + zp: int + qmin: int + qmax: int + + +def is_quant_node(node: torch.fx.Node): consumer_node = list(node.users)[0] input = node.all_input_nodes[0] @@ -41,10 +50,22 @@ def is_quant_arg(arg): return consumer_node.target == q_op -def get_quant_node_args(node): +def get_quant_node_args(node: torch.fx.Node): + """ + Get the quantization parameters from a quant node. + + Args: + node: The quant node. + Returns: + QuantArgs: scale, zp, qmin, qmax + """ quant_args = [TosaArg(arg) for arg in node.args] - # Return the scale and zp - return quant_args[1].number, quant_args[2].number + return QuantArgs( + quant_args[1].number, + quant_args[2].number, + quant_args[3].number, + quant_args[4].number, + ) # Check if scale32 mode is used for given output element type diff --git a/backends/qualcomm/setup.md b/backends/qualcomm/setup.md index 18ebf412fc..b78b481e86 100644 --- a/backends/qualcomm/setup.md +++ b/backends/qualcomm/setup.md @@ -93,7 +93,6 @@ mkdir build_android cd build_android # build executorch & qnn_executorch_backend cmake .. \ - -DBUCK2=buck2 \ -DCMAKE_INSTALL_PREFIX=$PWD \ -DEXECUTORCH_BUILD_QNN=ON \ -DQNN_SDK_ROOT=$QNN_SDK_ROOT \ diff --git a/backends/vulkan/partitioner/vulkan_partitioner.py b/backends/vulkan/partitioner/vulkan_partitioner.py index 976f15e3e5..e9ec9f2d84 100644 --- a/backends/vulkan/partitioner/vulkan_partitioner.py +++ b/backends/vulkan/partitioner/vulkan_partitioner.py @@ -52,6 +52,10 @@ def is_node_supported(self, submodules, node: torch.fx.Node) -> bool: exir_ops.edge.aten.convolution.default, # Normalization exir_ops.edge.aten.native_layer_norm.default, + # Shape-related operators + exir_ops.edge.aten.select_copy.int, + exir_ops.edge.aten.unsqueeze_copy.default, + exir_ops.edge.aten.view_copy.default, # Other operator.getitem, exir_ops.edge.aten.full.default, diff --git a/backends/vulkan/runtime/api/Tensor.h b/backends/vulkan/runtime/api/Tensor.h index 53dbfecffe..787e811120 100644 --- a/backends/vulkan/runtime/api/Tensor.h +++ b/backends/vulkan/runtime/api/Tensor.h @@ -220,6 +220,10 @@ class vTensor final { */ const api::BufferBindInfo texture_limits_ubo(); + inline const api::utils::ivec3 texture_limits() const { + return texture_limits_.limits; + } + inline size_t numel() const { return api::utils::multiply_integers(sizes()); } diff --git a/backends/vulkan/runtime/api/Utils.h b/backends/vulkan/runtime/api/Utils.h index 3b0139b8ef..d12844bbf1 100644 --- a/backends/vulkan/runtime/api/Utils.h +++ b/backends/vulkan/runtime/api/Utils.h @@ -262,12 +262,23 @@ inline std::ostream& operator<<(std::ostream& os, const uvec3& v) { return os; } +inline std::ostream& operator<<(std::ostream& os, const ivec3& v) { + os << "(" << v.data[0u] << ", " << v.data[1u] << ", " << v.data[2u] << ")"; + return os; +} + inline std::ostream& operator<<(std::ostream& os, const uvec4& v) { os << "(" << v.data[0u] << ", " << v.data[1u] << ", " << v.data[2u] << ", " << v.data[3u] << ")"; return os; } +inline std::ostream& operator<<(std::ostream& os, const ivec4& v) { + os << "(" << v.data[0u] << ", " << v.data[1u] << ", " << v.data[2u] << ", " + << v.data[3u] << ")"; + return os; +} + // // std::vector Handling // @@ -298,6 +309,25 @@ inline ivec2 make_ivec2( } } +inline ivec3 make_ivec3( + const std::vector& ints, + bool reverse = false) { + VK_CHECK_COND(ints.size() == 3); + if (reverse) { + return { + safe_downcast(ints[2]), + safe_downcast(ints[1]), + safe_downcast(ints[0]), + }; + } else { + return { + safe_downcast(ints[0]), + safe_downcast(ints[1]), + safe_downcast(ints[2]), + }; + } +} + inline ivec4 make_ivec4( const std::vector& ints, bool reverse = false) { @@ -338,6 +368,13 @@ inline ivec3 make_ivec3(uvec3 ints) { safe_downcast(ints.data[2u])}; } +inline uvec3 make_uvec3(ivec3 ints) { + return { + safe_downcast(ints.data[0u]), + safe_downcast(ints.data[1u]), + safe_downcast(ints.data[2u])}; +} + /* * Given an vector of up to 4 uint64_t representing the sizes of a tensor, * constructs a uvec4 containing those elements in reverse order. diff --git a/backends/vulkan/runtime/graph/Logging.h b/backends/vulkan/runtime/graph/Logging.h index 5ee068100f..447d52d16b 100644 --- a/backends/vulkan/runtime/graph/Logging.h +++ b/backends/vulkan/runtime/graph/Logging.h @@ -34,6 +34,14 @@ inline std::ostream& operator<<(std::ostream& os, const api::utils::uvec4& v) { return api::utils::operator<<(os, v); } +inline std::ostream& operator<<(std::ostream& os, const api::utils::ivec3& v) { + return api::utils::operator<<(os, v); +} + +inline std::ostream& operator<<(std::ostream& os, const api::utils::ivec4& v) { + return api::utils::operator<<(os, v); +} + template inline std::ostream& operator<<(std::ostream& os, const std::optional& opt) { os << "["; diff --git a/backends/vulkan/runtime/graph/ops/glsl/clone.glsl b/backends/vulkan/runtime/graph/ops/glsl/clone.glsl new file mode 100644 index 0000000000..64def8d700 --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/glsl/clone.glsl @@ -0,0 +1,30 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#version 450 core + +#define PRECISION ${PRECISION} + +layout(std430) buffer; + +layout(set = 0, binding = 0, ${IMAGE_FORMAT[DTYPE]}) uniform PRECISION restrict writeonly ${IMAGE_T[NDIM][DTYPE]} image_out; +layout(set = 0, binding = 1) uniform PRECISION sampler3D image_in; + +layout(set = 0, binding = 2) uniform PRECISION restrict OutLimits { + ivec3 out_limits; +}; + +layout(local_size_x_id = 0, local_size_y_id = 1, local_size_z_id = 2) in; + +void main() { + ivec3 pos = ivec3(gl_GlobalInvocationID); + if (any(greaterThanEqual(pos, out_limits))) { + return; + } + imageStore(image_out, pos, texelFetch(image_in, pos, 0)); +} diff --git a/backends/vulkan/runtime/graph/ops/glsl/clone.yaml b/backends/vulkan/runtime/graph/ops/glsl/clone.yaml new file mode 100644 index 0000000000..5dbce0e9d8 --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/glsl/clone.yaml @@ -0,0 +1,10 @@ +clone: + parameter_names_with_default_values: + DTYPE: float + NDIM: 3 + generate_variant_forall: + DTYPE: + - VALUE: half + - VALUE: float + shader_variants: + - NAME: clone diff --git a/backends/vulkan/runtime/graph/ops/glsl/conv1d.glsl b/backends/vulkan/runtime/graph/ops/glsl/conv1d.glsl index b7ce7996c4..b77f171dcc 100644 --- a/backends/vulkan/runtime/graph/ops/glsl/conv1d.glsl +++ b/backends/vulkan/runtime/graph/ops/glsl/conv1d.glsl @@ -21,78 +21,104 @@ layout(set = 0, binding = 1) uniform PRECISION sampler3D image_in; layout(set = 0, binding = 2) uniform PRECISION sampler3D kernel_in; layout(set = 0, binding = 3) uniform PRECISION sampler3D bias_in; -layout(set = 0, binding = 4) uniform PRECISION restrict Out_channels { - int data; -} -out_channels; - -layout(set = 0, binding = 5) uniform PRECISION restrict In_length { - int data; -} -in_length; - -layout(set = 0, binding = 6) uniform PRECISION restrict Kernel_size { - int data; -} -kernel_size; +layout(set = 0, binding = 4) uniform PRECISION restrict OutLimits { + ivec3 out_limits; +}; + +layout(set = 0, binding = 5) uniform PRECISION restrict InSizes { + ivec4 in_sizes; +}; + +layout(set = 0, binding = 6) uniform PRECISION restrict Params { + int kernel_size; + int stride; + int padding; + int dilation; + int in_group_size; + int out_group_size; +}; layout(local_size_x_id = 0, local_size_y_id = 1, local_size_z_id = 2) in; -/* - * This implementation optimize for simplicity (and partially performance) for a - * (1, C, L) where C == groups. Hence we only focus on calculating the rolling - * kernel of the L dimension. - */ +// Let us define +// +// input = (N, in_C, in_L), +// output = (N, out_C, out_L), +// groups = G, +// kernel = K, +// +// which results in shapes +// +// weight = (out_C, in_C / G, K), +// bias = (out_C,). +// +// This implementation performs out_C shader invocations, where each invocation +// calculates the rolling kernel of the length dimension for each batch, i.e., +// computes out_L * N results. +// +// Note that we can rewrite this implementation as out_L * out_C * ceil(N / 4) +// shader invocations, where each invocation computes 1 result. But that +// performs worse. void main() { const ivec3 pos = ivec3(gl_GlobalInvocationID); - // The global workgroup should have taken care of it. We only perform one - // work item for each 1d tensor on lengths - if (pos.x >= 1) { + if (any(greaterThanEqual(pos, out_limits))) { return; } - int c = pos.y; - if (c >= out_channels.data) { - return; - } - - // Assume n = 1, do not handle n > 1 case for now. - int n = pos.z; - if (n >= 1) { - return; - } - - vec4 bias = texelFetch(bias_in, ivec3(c, 0, 0), 0); - - for (int i = 0; i < in_length.data - kernel_size.data + 1; ++i) { - vec4 v = vec4(0); - for (int k = 0; k < kernel_size.data; ++k) { - const ivec3 in_pos = ivec3(i+k, c, 0); - const vec4 input_value = texelFetch(image_in, in_pos, 0); - - // Note that we are reading weight in the inner loop, this could be - // improved by moving it before the outer loop. Since the weight vector is - // contant for the entire call. - - // weight in input-space: (c, 0, k); - // notice that c is 4-packed. We need to mod 4 to get the actual weight. - const ivec3 w_pos = ivec3(k, 0, c / 4); - const vec4 weight = texelFetch(kernel_in, w_pos, 0); - - float w = weight.x; - if (c % 4 == 1) { - w = weight.y; - } else if (c % 4 == 2) { - w = weight.z; - } else if (c % 4 == 3) { - w = weight.w; + int in_length = in_sizes.x; + int batch_size = in_sizes.z; + + // "out_c" is the output's channel index where we write our result. + // Across shader invocations, this is the only value that varies. + int out_c = pos.y; + vec4 bias = texelFetch(bias_in, ivec3(out_c, 0, 0), 0); + + // "in_c" tracks the input's channel start index. + // We iterate over the input group that corresponds to the output group. + int c_start = (out_c / out_group_size) * in_group_size; + int c_end = c_start + in_group_size; + + // "in_l" tracks the input's length start index for our input-kernel overlay + // region. + int l_start = -padding; + int l_end = in_length + padding - dilation * (kernel_size - 1); + + // Since the input/output tensors are channel-packed, which is along the + // batch dimension, we can batch-read/write four elements at a time. + for (int n = 0; n < batch_size; n += 4) { + // "out_l" tracks the output's length index where we write our result. + int out_l = 0; + + for (int in_l = l_start; in_l < l_end; in_l += stride, ++out_l) { + vec4 sum = vec4(0); + + for (int in_c = c_start; in_c < c_end; ++in_c) { + // "k" tracks the kernel's index for our input-kernel computation. + // It reads out-of-bound zeros, but trying to avoid them complicates + // for-loop conditions, which results in worse performance. + for (int k = 0; k < kernel_size; k += 4) { + // Since the weight tensor is width-packed, which is along the length + // dimension, we can batch-read four elements at a time. + const ivec3 w_pos = ivec3(k / 4, in_c % in_group_size, out_c); + const vec4 weight = texelFetch(kernel_in, w_pos, 0); + + const ivec3 in_pos_0 = ivec3(in_l + k * dilation, in_c, n / 4); + sum = fma(weight.xxxx, texelFetch(image_in, in_pos_0, 0), sum); + + const ivec3 in_pos_1 = ivec3(in_l + (k+1) * dilation, in_c, n / 4); + sum = fma(weight.yyyy, texelFetch(image_in, in_pos_1, 0), sum); + + const ivec3 in_pos_2 = ivec3(in_l + (k+2) * dilation, in_c, n / 4); + sum = fma(weight.zzzz, texelFetch(image_in, in_pos_2, 0), sum); + + const ivec3 in_pos_3 = ivec3(in_l + (k+3) * dilation, in_c, n / 4); + sum = fma(weight.wwww, texelFetch(image_in, in_pos_3, 0), sum); + } } - v += w * input_value.x; + ivec3 out_pos = ivec3(out_l, out_c, n / 4); + imageStore(image_out, out_pos, sum + bias.x); } - - ivec3 out_pos = ivec3(i, c, 0); - imageStore(image_out, out_pos, vec4(v.x + bias.x, 0, 0, 0)); } } diff --git a/backends/vulkan/runtime/graph/ops/glsl/copy_offset.glsl b/backends/vulkan/runtime/graph/ops/glsl/copy_offset.glsl new file mode 100644 index 0000000000..17b3e06e61 --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/glsl/copy_offset.glsl @@ -0,0 +1,54 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#version 450 core + +#define PRECISION ${PRECISION} + +#define VEC4_T ${texel_type(DTYPE)} + +layout(std430) buffer; + +#include "indexing_utils.h" + +layout(set = 0, binding = 0, ${IMAGE_FORMAT[DTYPE]}) uniform PRECISION restrict writeonly ${IMAGE_T[NDIM][DTYPE]} image_out; +layout(set = 0, binding = 1) uniform PRECISION sampler3D image_in; + +layout(set = 0, binding = 2) uniform PRECISION restrict OutLimits { + ivec3 out_limits; +}; + +layout(set = 0, binding = 3) uniform PRECISION restrict InLimits { + ivec3 in_limits; +}; + + + +layout(set = 0, binding = 4) uniform PRECISION restrict CopyArgs { + ivec3 range; + int unused0; + ivec3 src_offset; + int unused1; + ivec3 dst_offset; + int unused2; +}; + +layout(local_size_x_id = 0, local_size_y_id = 1, local_size_z_id = 2) in; + +void main() { + const ivec3 pos = ivec3(gl_GlobalInvocationID); + + const ivec3 out_pos = pos + dst_offset; + const ivec3 in_pos = pos + src_offset; + + if (any(greaterThanEqual(pos, range))) { + return; + } + + imageStore(image_out, out_pos, texelFetch(image_in, in_pos, 0)); +} diff --git a/backends/vulkan/runtime/graph/ops/glsl/copy_offset.yaml b/backends/vulkan/runtime/graph/ops/glsl/copy_offset.yaml new file mode 100644 index 0000000000..4a31ba6bbc --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/glsl/copy_offset.yaml @@ -0,0 +1,10 @@ +copy_offset: + parameter_names_with_default_values: + DTYPE: float + NDIM: 3 + generate_variant_forall: + DTYPE: + - VALUE: half + - VALUE: float + shader_variants: + - NAME: copy_offset diff --git a/backends/vulkan/runtime/graph/ops/glsl/repeat_channel.glsl b/backends/vulkan/runtime/graph/ops/glsl/repeat_channel.glsl new file mode 100644 index 0000000000..42c7f86aea --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/glsl/repeat_channel.glsl @@ -0,0 +1,58 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#version 450 core + +#define PRECISION ${PRECISION} + +#define VEC4_T ${texel_type(DTYPE)} + +layout(std430) buffer; + +#include "indexing_utils.h" + +layout(set = 0, binding = 0, ${IMAGE_FORMAT[DTYPE]}) uniform PRECISION restrict writeonly ${IMAGE_T[NDIM][DTYPE]} image_out; +layout(set = 0, binding = 1) uniform PRECISION sampler3D image_in; + +layout(set = 0, binding = 2) uniform PRECISION restrict RepeatArgs { + // With input_size (n, c_i, h, w) and repeat r + // out_size == (n, c_i * r, h, w) + ivec4 out_sizes; + ivec4 in_sizes; +}; + +layout(local_size_x_id = 0, local_size_y_id = 1, local_size_z_id = 2) in; + +layout(constant_id = 3) const int packed_dim = C_DIM; + + +void main() { + const ivec3 out_pos = ivec3(gl_GlobalInvocationID); + + const ivec4 out_whcn = to_tensor_idx(out_pos, out_sizes, packed_dim); + + if (any(greaterThanEqual(out_whcn, out_sizes))) { + return; + } + + VEC4_T v; + // Loop over the 4 elements in texel, calculate the corresponding elem, and + // fetch. Not most efficient algorithm because likely we fetch same texel + // multiple times in this loop. + + for (int i=0; i<4;i++) { + ivec4 in_whcn = out_whcn; + in_whcn.z = (out_whcn.z + i) % in_sizes.z; + + ivec4 in_elem_pos = to_texture_elem_pos(in_whcn, in_sizes, packed_dim); + + v[i] = VEC4_T(texelFetch(image_in, in_elem_pos.xyz, 0))[in_elem_pos.w]; + } + + imageStore(image_out, out_pos, v); +} diff --git a/backends/vulkan/runtime/graph/ops/glsl/repeat_channel.yaml b/backends/vulkan/runtime/graph/ops/glsl/repeat_channel.yaml new file mode 100644 index 0000000000..4147e82965 --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/glsl/repeat_channel.yaml @@ -0,0 +1,10 @@ +repeat_channel: + parameter_names_with_default_values: + DTYPE: float + NDIM: 3 + generate_variant_forall: + DTYPE: + - VALUE: half + - VALUE: float + shader_variants: + - NAME: repeat_channel diff --git a/backends/vulkan/runtime/graph/ops/impl/Clone.cpp b/backends/vulkan/runtime/graph/ops/impl/Clone.cpp new file mode 100644 index 0000000000..e95e7bdc00 --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/impl/Clone.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include + +#include +#include +#include + +namespace vkcompute { + +void add_clone_node( + ComputeGraph& graph, + const ValueRef in, + const ValueRef out) { + vTensorPtr t_out = graph.get_tensor(out); + + std::string kernel_name = "clone"; + add_dtype_suffix(kernel_name, *t_out); + + api::utils::uvec3 global_size = t_out->extents(); + api::utils::uvec3 local_size = adaptive_work_group_size(global_size); + + graph.execute_nodes().emplace_back(new ExecuteNode( + graph, + VK_KERNEL_FROM_STR(kernel_name), + global_size, + local_size, + {{out, api::MemoryAccessType::WRITE}, {in, api::MemoryAccessType::READ}}, + {t_out->texture_limits_ubo()})); +} + +void clone(ComputeGraph& graph, const std::vector& args) { + // The vulkan delegate does not support changing memory format. + return add_clone_node(graph, args[0], args[2]); +} + +// Clone node is not the most efficient implementation for the aten.clone +// operation. A more efficient implementation can be achieved during vulkan +// export with the use of shared object. This clone node is introduced to enable +// a "copy" mechanism if there is no alternative (e.g. during direct +// ComputeGraph manipulation, we need to make a copy of a Tensor). + +REGISTER_OPERATORS { + VK_REGISTER_OP(aten.clone.default, clone); +} + +} // namespace vkcompute diff --git a/backends/vulkan/runtime/graph/ops/impl/Convolution.cpp b/backends/vulkan/runtime/graph/ops/impl/Convolution.cpp index 20d7c9256b..d40352d224 100644 --- a/backends/vulkan/runtime/graph/ops/impl/Convolution.cpp +++ b/backends/vulkan/runtime/graph/ops/impl/Convolution.cpp @@ -61,6 +61,11 @@ void resize_conv1d_node( vTensorPtr out = graph->get_tensor(args[0].refs[0]); vTensorPtr self = graph->get_tensor(args[1].refs[0]); TensorRefPtr weight_ref = graph->get_tref(extra_args[0]); + + int64_t stride_size = graph->get_int_list(extra_args[1])->at(0); + int64_t padding_size = graph->get_int_list(extra_args[2])->at(0); + int64_t dilation_size = graph->get_int_list(extra_args[3])->at(0); + const std::vector& weight_sizes = weight_ref->sizes; const std::vector& in_sizes = self->sizes(); @@ -71,8 +76,9 @@ void resize_conv1d_node( int64_t in_length = in_sizes.at(2); new_out_sizes.at(0) = in_sizes.at(0); - new_out_sizes.at(1) = in_sizes.at(1); - new_out_sizes.at(2) = in_length - kernel_size + 1; + new_out_sizes.at(1) = weight_sizes.at(0); + new_out_sizes.at(2) = calc_out_size( + in_length, kernel_size, stride_size, padding_size, dilation_size, false); out->virtual_resize(new_out_sizes); } @@ -244,10 +250,6 @@ ValueRef prepack_weights( } void check_conv_args(const vTensor& in, const vTensor& out) { - if (in.sizes().at(0) > 1) { - VK_THROW( - "aten.convolution.default: input batch size > 1 is not supported yet!"); - } VK_CHECK_COND(check_memory_layout_is(in, api::kChannelsPacked)); VK_CHECK_COND(check_memory_layout_is(out, api::kChannelsPacked)); } @@ -260,7 +262,7 @@ struct Conv2dParams final { Conv2dParams create_conv2d_params( ComputeGraph& graph, const ValueRef weight, - const KernelParams& p, + const Kernel2dParams& p, const bool transposed) { const auto& overlay_region = api::utils::make_ivec2({ p.kernel_size.data[0] + @@ -275,7 +277,7 @@ Conv2dParams create_conv2d_params( return {overlay_region, in_group_size}; } -void check_conv2d_params(const KernelParams& p, const bool transposed) { +void check_conv2d_params(const Kernel2dParams& p, const bool transposed) { if (transposed) { if (p.dilation.data[0] > 1 || p.dilation.data[1] > 1) { VK_THROW( @@ -342,12 +344,15 @@ void add_conv2d_node( vTensorPtr t_in = graph.get_tensor(arg_in); vTensorPtr t_out = graph.get_tensor(out); + if (t_in->sizes().at(0) > 1) { + VK_THROW("conv2d: input batch size > 1 is not supported yet!"); + } check_conv_args(*t_in, *t_out); api::utils::uvec3 global_size = t_out->extents(); api::utils::uvec3 local_size = adaptive_work_group_size(global_size); - KernelParams kernel_params = create_kernel_params( + Kernel2dParams kernel_params = create_kernel2d_params( graph, weight, /*kernel_size_only = */ false, @@ -395,8 +400,7 @@ void add_conv1d_node( const ValueRef groups, const ValueRef out) { ValueRef arg_in = prepack_if_tensor_ref(graph, in); - ValueRef arg_weight = - prepack_if_tensor_ref(graph, weight, graph.memory_layout_of(arg_in)); + ValueRef arg_weight = prepack_if_tensor_ref(graph, weight, api::kWidthPacked); ValueRef arg_bias = prepack_biases( graph, bias, @@ -414,37 +418,29 @@ void add_conv1d_node( std::vector in_sizes = t_in->sizes(); std::vector weight_sizes = t_weight->sizes(); std::vector out_sizes = t_out->sizes(); - IntListPtr stride_sizes = graph.get_int_list(stride); - IntListPtr padding_sizes = graph.get_int_list(padding); - IntListPtr dilation_sizes = graph.get_int_list(dilation); - int64_t weight_out_channels = weight_sizes.at(0); - int64_t kernel_size = weight_sizes.at(2); - int64_t in_length = in_sizes.at(2); - - VK_CHECK_COND(in_sizes.size() == 3, "input must be a 3-dim tensor"); - VK_CHECK_COND(weight_sizes.size() == 3, "weight must be a 3-dim tensor"); - VK_CHECK_COND( - stride_sizes->size() == 1 && stride_sizes->at(0) == 1, - "stride must be 1"); - VK_CHECK_COND( - padding_sizes->size() == 1 && padding_sizes->at(0) == 0, - "padding must be 0"); - VK_CHECK_COND( - dilation_sizes->size() == 1 && dilation_sizes->at(0) == 1, - "dilation must be 1"); - VK_CHECK_COND( - groups_val == in_sizes.at(1), "groups must be equal to in_channels"); - VK_CHECK_COND( - groups_val == weight_sizes.at(0), - "groups must be equal to weight_sizes.at(0)"); - VK_CHECK_COND(weight_sizes.at(1) == 1, "weight_sizes.at(1) must be 1"); check_conv_args(*t_in, *t_out); - api::utils::uvec3 global_size = { - 1, static_cast(weight_out_channels), 1}; + int32_t in_channels = in_sizes.at(1); + int32_t out_channels = weight_sizes.at(0); + int32_t kernel_size = weight_sizes.at(2); + int32_t stride_size = graph.get_int_list(stride)->at(0); + int32_t padding_size = graph.get_int_list(padding)->at(0); + int32_t dilation_size = graph.get_int_list(dilation)->at(0); + int32_t in_group_size = static_cast(in_channels / groups_val); + int32_t out_group_size = static_cast(out_channels / groups_val); + + api::utils::uvec3 global_size = {1, static_cast(out_channels), 1}; api::utils::uvec3 local_size = {1, 1, 1}; + Kernel1dParams kernel_params = { + kernel_size, + stride_size, + padding_size, + dilation_size, + in_group_size, + out_group_size}; + std::string kernel_name("conv1d"); kernel_name.reserve(kShaderNameReserve); @@ -460,15 +456,15 @@ void add_conv1d_node( {{arg_in, arg_weight, arg_bias}, api::MemoryAccessType::READ}}, // Shader params buffers { - graph.create_params_buffer(weight_out_channels), - graph.create_params_buffer(in_length), - graph.create_params_buffer(kernel_size), + t_out->texture_limits_ubo(), + t_in->sizes_ubo(), + graph.create_params_buffer(kernel_params), }, // Specialization Constants {}, // Resizing Logic resize_conv1d_node, - {weight})); + {weight, stride, padding, dilation})); } void conv(ComputeGraph& graph, const std::vector& args) { diff --git a/backends/vulkan/runtime/graph/ops/impl/Copy.cpp b/backends/vulkan/runtime/graph/ops/impl/Copy.cpp new file mode 100644 index 0000000000..0a5e20e4f7 --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/impl/Copy.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include +#include + +namespace vkcompute { + +void add_copy_offset_node( + ComputeGraph& graph, + const ValueRef in, + const api::utils::ivec3& range, + const api::utils::ivec3& src_offset, + const api::utils::ivec3& dst_offset, + const ValueRef out) { + vTensorPtr t_in = graph.get_tensor(in); + vTensorPtr t_out = graph.get_tensor(out); + + VK_CHECK_COND(check_memory_layout_is(*t_in, api::kChannelsPacked)); + VK_CHECK_COND(check_memory_layout_is(*t_out, api::kChannelsPacked)); + + std::string kernel_name = "copy_offset"; + kernel_name.reserve(kShaderNameReserve); + add_dtype_suffix(kernel_name, *t_out); + + api::utils::uvec3 global_size = api::utils::make_uvec3(range); + api::utils::uvec3 local_size = adaptive_work_group_size(global_size); + + const struct Block final { + api::utils::ivec3 range; + int32_t unused0; + api::utils::ivec3 src_offset; + int32_t unused1; + api::utils::ivec3 dst_offset; + int32_t unused2; + } offset_params{ + range, + 0, + src_offset, + 0, + dst_offset, + 0, + }; + + auto shader = VK_KERNEL_FROM_STR(kernel_name); + + graph.execute_nodes().emplace_back(new ExecuteNode( + graph, + VK_KERNEL_FROM_STR(kernel_name), + global_size, + local_size, + // Inputs and Outputs + {{out, api::MemoryAccessType::WRITE}, {in, api::MemoryAccessType::READ}}, + // Parameter buffers + {t_out->texture_limits_ubo(), + t_in->texture_limits_ubo(), + graph.create_params_buffer(offset_params)}, + // Specialization Constants + {})); +} + +} // namespace vkcompute diff --git a/backends/vulkan/runtime/graph/ops/impl/Copy.h b/backends/vulkan/runtime/graph/ops/impl/Copy.h new file mode 100644 index 0000000000..6e0deb6b74 --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/impl/Copy.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include + +namespace vkcompute { + +void add_copy_offset_node( + ComputeGraph& graph, + const ValueRef in, + const api::utils::ivec3& range, + const api::utils::ivec3& src_offset, + const api::utils::ivec3& dst_offset, + const ValueRef out); + +} // namespace vkcompute diff --git a/backends/vulkan/runtime/graph/ops/impl/Permute.cpp b/backends/vulkan/runtime/graph/ops/impl/Permute.cpp index 3bc6ca52c6..14b77e3b45 100644 --- a/backends/vulkan/runtime/graph/ops/impl/Permute.cpp +++ b/backends/vulkan/runtime/graph/ops/impl/Permute.cpp @@ -6,8 +6,11 @@ * LICENSE file in the root directory of this source tree. */ +#include + #include +#include #include #include #include @@ -18,55 +21,57 @@ using api::utils::ivec3; using api::utils::uvec2; using api::utils::uvec4; +namespace { + void check_args( const vTensor& in, - const IntListPtr& permute_dims, + const std::vector& permute_dims, const vTensor& out) { VK_CHECK_COND(check_memory_layout_is(in, api::kChannelsPacked)); VK_CHECK_COND(check_memory_layout_is(out, api::kChannelsPacked)); - int64_t in_dim = in.dim(); + // This implementation doesn't not requires the input tensor to have the same + // dim size as the argument. The code will work as long as the input tensor's + // dim size is shorter than the permute dim array. In this case, the code + // assume size of 1 at the higher dimensions. + + int64_t out_dim = out.dim(); VK_CHECK_COND( - in_dim == permute_dims->size(), - "Input tensor dim size must match argument"); + out_dim == permute_dims.size(), + "Output tensor dim size must match argument"); } +} // namespace + void add_permute_node( ComputeGraph& graph, ValueRef in, - ValueRef permute_dims_ref, + const std::vector& permute_dims, ValueRef out) { vTensorPtr t_in = graph.get_tensor(in); vTensorPtr t_out = graph.get_tensor(out); - IntListPtr permute_dims = graph.get_int_list(permute_dims_ref); - check_args(*t_in, permute_dims, *t_out); - uvec4 in_size{1u, 1u, 1u, 1u}, out_size{1u, 1u, 1u, 1u}; uvec4 out_dims{0u, 1u, 2u, 3u}; - int64_t in_dim = t_in->dim(); - - std::vector seen(in_dim); - for (int i = 0; i < in_dim; i++) { - int64_t permute_dim = (*permute_dims)[i]; + int64_t out_dim = t_out->dim(); + std::vector seen(out_dim); + for (int i = 0; i < t_out->dim(); i++) { + int64_t permute_dim = permute_dims[i]; VK_CHECK_COND( !seen[permute_dim], "Argument dim ", permute_dim, " is repeated"); seen[permute_dim] = true; - // Map to 4D tensor dims. - in_size.data[(4u - in_dim) + i] = t_in->size(i); - out_size.data[(4u - in_dim) + i] = t_in->size(permute_dim); - out_dims.data[(4u - in_dim) + i] = permute_dim + (4u - in_dim); + out_dims.data[(4u - out_dim) + i] = permute_dim + (4u - out_dim); } std::string kernel_name = "permute"; kernel_name.reserve(kShaderNameReserve); add_dtype_suffix(kernel_name, *t_out); - uint32_t out_channels = out_size.data[1u]; - uint32_t in_channels = in_size.data[1u]; + uint32_t out_channels = dim_at(t_out->sizes()); + uint32_t in_channels = dim_at(t_in->sizes()); uint32_t out_c_aligned = api::utils::align_up(out_channels, 4u); uint32_t in_c_aligned = api::utils::align_up(in_channels, 4u); @@ -98,6 +103,16 @@ void add_permute_node( {})); } +void add_permute_node( + ComputeGraph& graph, + ValueRef in, + ValueRef permute_dims_ref, + ValueRef out) { + IntListPtr permute_dims = graph.get_int_list(permute_dims_ref); + + add_permute_node(graph, in, *permute_dims, out); +} + void permute(ComputeGraph& graph, const std::vector& args) { return add_permute_node(graph, args[0], args[1], args[2]); } diff --git a/backends/vulkan/runtime/graph/ops/impl/Permute.h b/backends/vulkan/runtime/graph/ops/impl/Permute.h new file mode 100644 index 0000000000..941a8896fe --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/impl/Permute.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include + +#include + +namespace vkcompute { + +void add_permute_node( + ComputeGraph& graph, + ValueRef in, + const std::vector& permute_dims, + ValueRef out); + +} // namespace vkcompute diff --git a/backends/vulkan/runtime/graph/ops/impl/Pool.cpp b/backends/vulkan/runtime/graph/ops/impl/Pool.cpp index 1a8a258627..87aed6e273 100644 --- a/backends/vulkan/runtime/graph/ops/impl/Pool.cpp +++ b/backends/vulkan/runtime/graph/ops/impl/Pool.cpp @@ -76,7 +76,7 @@ void add_max_pool2d_node( std::string kernel_name("max_pool2d"); add_dtype_suffix(kernel_name, *t_out); - KernelParams kernel_params = create_kernel_params( + Kernel2dParams kernel_params = create_kernel2d_params( graph, kernel_size, /*kernel_size_only = */ true, diff --git a/backends/vulkan/runtime/graph/ops/impl/Repeat.cpp b/backends/vulkan/runtime/graph/ops/impl/Repeat.cpp new file mode 100644 index 0000000000..dedc7978ad --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/impl/Repeat.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include +#include +#include + +#include + +namespace vkcompute { + +namespace { + +void check_args( + const vTensor& in, + const std::vector& repeats, + const vTensor& out) { + VK_CHECK_COND(check_memory_layout_is(in, api::kChannelsPacked)); + VK_CHECK_COND(check_memory_layout_is(out, api::kChannelsPacked)); + + int64_t in_dim = in.dim(); + VK_CHECK_COND( + in_dim <= repeats.size(), + "Input tensor dim size must be not greater than the repeat argument's size"); + + VK_CHECK_COND( + dim_at(in.sizes()) * dim_at(repeats) == + dim_at(out.sizes()), + "Output's width doesn't match input's width * repeat count"); + + VK_CHECK_COND( + dim_at(in.sizes()) * dim_at(repeats) == + dim_at(out.sizes()), + "Output's height doesn't match input's height * repeat count"); + + VK_CHECK_COND( + dim_at(in.sizes()) * dim_at(repeats) == + dim_at(out.sizes()), + "Output's channel doesn't match input's channel * repeat count"); + + VK_CHECK_COND( + dim_at(in.sizes()) * dim_at(repeats) == + dim_at(out.sizes()), + "Output's batch doesn't match input's batch * repeat count"); +} + +} // namespace + +void add_repeat_channel_node( + ComputeGraph& graph, + ValueRef in, + int64_t repeat_channel, + ValueRef out, + api::utils::ivec3& running_range) { + vTensorPtr t_in = graph.get_tensor(in); + vTensorPtr t_out = graph.get_tensor(out); + + std::string kernel_name = "repeat_channel"; + kernel_name.reserve(kShaderNameReserve); + add_dtype_suffix(kernel_name, *t_out); + + const std::vector& in_sizes = t_in->sizes(); + + int32_t in_width = + api::utils::safe_downcast(dim_at(in_sizes)); + int32_t in_height = + api::utils::safe_downcast(dim_at(in_sizes)); + int32_t in_channel = + api::utils::safe_downcast(dim_at(in_sizes)); + int32_t in_batch = + api::utils::safe_downcast(dim_at(in_sizes)); + + int32_t out_channel = repeat_channel * in_channel; + + api::utils::ivec4 out_whcn_sizes{in_width, in_height, out_channel, in_batch}; + + api::utils::ivec4 in_whcn_sizes{in_width, in_height, in_channel, in_batch}; + + // Channel packed global work ids + running_range.data[2] = + out_whcn_sizes.data[3] * api::utils::div_up(out_whcn_sizes.data[2], 4); + api::utils::uvec3 global_size = api::utils::make_uvec3(running_range); + api::utils::uvec3 local_size = adaptive_work_group_size(global_size); + + const struct Block final { + api::utils::ivec4 out_sizes; + api::utils::ivec4 in_size; + } repeat_channel_args{ + out_whcn_sizes, + in_whcn_sizes, + }; + + auto shader = VK_KERNEL_FROM_STR(kernel_name); + + graph.execute_nodes().emplace_back(new ExecuteNode( + graph, + VK_KERNEL_FROM_STR(kernel_name), + global_size, + local_size, + // Inputs and Outputs + {{out, api::MemoryAccessType::WRITE}, {in, api::MemoryAccessType::READ}}, + // Parameter buffers + {graph.create_params_buffer(repeat_channel_args)}, + // Specialization Constants + {SV(t_out->gpu_memory_layout_int())})); +} + +void add_repeat_node( + ComputeGraph& graph, + ValueRef in, + ValueRef repeats_ref, + ValueRef out) { + std::vector repeats = *(graph.get_int_list(repeats_ref)); + + vTensorPtr t_in = graph.get_tensor(in); + vTensorPtr t_out = graph.get_tensor(out); + check_args(*t_in, repeats, *t_out); + + // In this function, we expand the dimensions in the following order: + // 1. Channel + // 2. Width + // 3. Height + // 4. Batch + // After expanding a dimension, we will update the "running_range" since we + // will need to copy the "expanded" area. + + api::utils::ivec3 running_range = t_in->texture_limits(); + + const std::vector& in_sizes = t_in->sizes(); + + // Since we use channel packing, repeating the channel dimension is the most + // complicated and time-consuming, as we need to reason over misaligned + // channels. Hence we expand it first to minimize cost. Also, in this first + // dimension, we copy over the input texure to the output. In subsequent + // dimensions, we read and write from the same tensor. + + if (int64_t channel_repeat = dim_at(repeats); + channel_repeat == 1) { + // If no repeat, short-cut to a direct copy + api::utils::ivec3 src_offset = api::utils::make_ivec3({0, 0, 0}, false); + api::utils::ivec3 dst_offset = api::utils::make_ivec3({0, 0, 0}, false); + + add_copy_offset_node(graph, in, running_range, src_offset, dst_offset, out); + + } else { + add_repeat_channel_node(graph, in, channel_repeat, out, running_range); + } + + // TODO: refactor width, height, and batch into a common helper function. + // Width + if (int64_t width_repeat = dim_at(repeats); width_repeat > 1) { + api::utils::ivec3 src_offset = api::utils::make_ivec3({0, 0, 0}, false); + + for (int i = 1; i < width_repeat; ++i) { + api::utils::ivec3 dst_offset = api::utils::make_ivec3( + {i * dim_at(in_sizes), 0, 0}, false); + + add_copy_offset_node( + graph, out, running_range, src_offset, dst_offset, out); + } + + running_range.data[0] = running_range.data[0] * width_repeat; + } + + // Height + if (int64_t height_repeat = dim_at(repeats); + height_repeat > 1) { + api::utils::ivec3 src_offset = api::utils::make_ivec3({0, 0, 0}, false); + + for (int i = 1; i < height_repeat; ++i) { + api::utils::ivec3 dst_offset = api::utils::make_ivec3( + {0, i * dim_at(in_sizes), 0}, false); + + add_copy_offset_node( + graph, out, running_range, src_offset, dst_offset, out); + } + + running_range.data[1] = running_range.data[1] * height_repeat; + } + + // Batch + if (int64_t batch_repeat = dim_at(repeats); batch_repeat > 1) { + api::utils::ivec3 src_offset = api::utils::make_ivec3({0, 0, 0}, false); + + for (int i = 1; i < batch_repeat; ++i) { + api::utils::ivec3 dst_offset = + api::utils::make_ivec3({0, 0, i * running_range.data[2]}, false); + + add_copy_offset_node( + graph, out, running_range, src_offset, dst_offset, out); + } + + running_range.data[2] = running_range.data[2] * batch_repeat; + } +} + +void repeat(ComputeGraph& graph, const std::vector& args) { + add_repeat_node(graph, args[0], args[1], args[2]); +} + +REGISTER_OPERATORS { + VK_REGISTER_OP(aten.repeat.default, repeat); +} + +} // namespace vkcompute diff --git a/backends/vulkan/runtime/graph/ops/impl/Unsqueeze.cpp b/backends/vulkan/runtime/graph/ops/impl/Unsqueeze.cpp new file mode 100644 index 0000000000..c8ada796e8 --- /dev/null +++ b/backends/vulkan/runtime/graph/ops/impl/Unsqueeze.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include +#include + +namespace vkcompute { + +void add_unsqueeze_node( + ComputeGraph& graph, + ValueRef in, + ValueRef dim_ref, + ValueRef out) { + vTensorPtr t_in = graph.get_tensor(in); + vTensorPtr t_out = graph.get_tensor(out); + + VK_CHECK_COND( + t_in->dim() < 4, "Cannot unsqueeze a tensor with more than 3 dimensions"); + + int64_t dim = graph.extract_scalar(dim_ref); + int64_t out_dim = t_out->dim(); + + std::vector permute_dims(out_dim); + for (int i = 1; i <= dim; i++) { + permute_dims[i - 1] = i; + } + permute_dims[dim] = 0; + + for (int i = dim + 1; i < out_dim; i++) { + permute_dims[i] = i; + } + + add_permute_node(graph, in, permute_dims, out); +} + +void unsqueeze(ComputeGraph& graph, const std::vector& args) { + return add_unsqueeze_node(graph, args[0], args[1], args[2]); +} + +REGISTER_OPERATORS { + VK_REGISTER_OP(aten.unsqueeze_copy.default, unsqueeze); +} + +} // namespace vkcompute diff --git a/backends/vulkan/runtime/graph/ops/impl/utils/KernelUtils.cpp b/backends/vulkan/runtime/graph/ops/impl/utils/KernelUtils.cpp index d342c4521f..6b823fe30c 100644 --- a/backends/vulkan/runtime/graph/ops/impl/utils/KernelUtils.cpp +++ b/backends/vulkan/runtime/graph/ops/impl/utils/KernelUtils.cpp @@ -26,7 +26,7 @@ api::utils::ivec2 make_ivec2_kernel_size( } } -KernelParams create_kernel_params( +Kernel2dParams create_kernel2d_params( ComputeGraph& graph, const ValueRef weight, const bool kernel_size_only, diff --git a/backends/vulkan/runtime/graph/ops/impl/utils/KernelUtils.h b/backends/vulkan/runtime/graph/ops/impl/utils/KernelUtils.h index fafb00e126..eb0215bfd5 100644 --- a/backends/vulkan/runtime/graph/ops/impl/utils/KernelUtils.h +++ b/backends/vulkan/runtime/graph/ops/impl/utils/KernelUtils.h @@ -16,14 +16,23 @@ namespace vkcompute { -struct KernelParams final { +struct Kernel2dParams final { api::utils::ivec2 kernel_size; api::utils::ivec2 stride; api::utils::ivec2 padding; api::utils::ivec2 dilation; }; -KernelParams create_kernel_params( +struct Kernel1dParams final { + int kernel_size; + int stride; + int padding; + int dilation; + int in_group_size; + int out_group_size; +}; + +Kernel2dParams create_kernel2d_params( ComputeGraph& graph, const ValueRef weight, const bool kernel_size_only, @@ -31,6 +40,14 @@ KernelParams create_kernel_params( const ValueRef padding, const ValueRef dilation); +int64_t calc_out_size( + const int64_t in_size, + const int64_t kernel_size, + const int64_t stride, + const int64_t padding, + const int64_t dilation, + const bool ceil_mode); + std::vector calc_out_sizes_hw( ComputeGraph& graph, const std::vector& in_sizes, diff --git a/backends/vulkan/test/op_tests/cases.py b/backends/vulkan/test/op_tests/cases.py index 3df9140ef4..2a100b92e3 100644 --- a/backends/vulkan/test/op_tests/cases.py +++ b/backends/vulkan/test/op_tests/cases.py @@ -135,6 +135,17 @@ def get_conv_inputs(): [0], 6, ), + ( + (2, 20, 30), + (10, 4, 6), + (10,), + [5], + [5], + [3], + False, + [0], + 5, + ), ( (1, 9, 11), (9, 1, 3), @@ -146,6 +157,17 @@ def get_conv_inputs(): [0], 9, ), + ( + (5, 15, 30), + (20, 3, 3), + None, + [3], + [5], + [7], + False, + [0], + 5, + ), ] ) return test_suite @@ -214,6 +236,7 @@ def get_permute_inputs(): ((9, 2), [1, 0]), ] ) + test_suite.layouts = ["api::kChannelsPacked"] return test_suite @@ -312,6 +335,99 @@ def get_slice_inputs(): return test_suite +def get_unsqueeze_inputs(): + test_suite = VkTestSuite( + [ + ((2, 3, 4), 0), + ((1, 1, 1), 0), + ((1, 1, 1), 1), + ((1, 1, 1), 2), + ((1, 1, 1), 3), + ((9, 9, 9), 0), + ((9, 9, 9), 1), + ((9, 9, 9), 2), + ((9, 9, 9), 3), + ((9, 9), 0), + ((9, 9), 1), + ((9, 9), 2), + ((9,), 0), + ((9,), 1), + ] + ) + test_suite.layouts = [ + "api::kChannelsPacked", + ] + test_suite.data_gen = "make_seq_tensor" + return test_suite + + +def get_clone_inputs(): + test_suite = VkTestSuite( + [ + ((S2, S1, S2, S1),), + ((S2, S1, S2),), + ((S2, S1),), + ((S2,),), + ((XS, S1, XS, S1),), + ((XS, S1, XS),), + ((S1, XS, S1),), + ((XS, S1),), + ((S1, XS),), + ((S1,),), + ((XS,),), + ] + ) + test_suite.layouts = [ + "api::kChannelsPacked", + ] + test_suite.data_gen = "make_seq_tensor" + return test_suite + + +def get_repeat_inputs(): + test_suite = VkTestSuite( + [ + # Repeat channels only (most challenging case) + ((3, XS, S), [2, 1, 1]), + ((7, XS, S), [4, 1, 1]), + ((1, 7, XS, S), [1, 4, 1, 1]), + ((3, 7, XS, S), [1, 4, 1, 1]), + # Repat channels with other dims + ((1, 7, XS, S), [1, 4, 1, 3]), + ((3, 7, XS, S), [1, 4, 1, 3]), + ((3, 7, XS, S), [1, 4, 3, 1]), + ((3, 7, XS, S), [1, 4, 3, 3]), + # Repeat Batch + ((3, 7, XS, S), [3, 4, 3, 3]), + ((3, 7, XS, S), [3, 1, 3, 3]), + # More other cases + ((3, 7, 1, 1), [1, 4, 1, 1]), + ((2, 3), [1, 4]), + ((2, 3), [4, 1]), + ((2, 3), [4, 4]), + ((S1, S2, S2), [1, 3, 1]), + ((S1, S2, S2), [1, 3, 3]), + ((S1, S2, S2), [3, 3, 1]), + ((S1, S2, S2), [3, 3, 3]), + ((S1, S2, S2, S2), [1, 1, 3, 1]), + ((S1, S2, S2, S2), [1, 1, 1, 3]), + ((S1, S2, S2, S2), [1, 1, 3, 3]), + ((S1, S2, S2, S2), [1, 3, 1, 3]), + ((S1, S2, S2, S2), [3, 3, 3, 3]), + ((S1, S2, S2, S2), [3, 3, 1, 1]), + # Expanding cases + ((2, 3), [3, 1, 4]), + ((2, 3), [3, 3, 2, 4]), + ] + ) + test_suite.layouts = [ + "api::kChannelsPacked", + ] + test_suite.data_gen = "make_seq_tensor" + test_suite.dtypes = ["at::kFloat"] + return test_suite + + test_suites = { "aten.add.Tensor": get_binary_elementwise_inputs(), "aten.sub.Tensor": get_binary_elementwise_inputs(), @@ -328,4 +444,7 @@ def get_slice_inputs(): "aten.permute_copy.default": get_permute_inputs(), "aten.view_copy.default": get_view_inputs(), "aten.slice_copy.Tensor": get_slice_inputs(), + "aten.unsqueeze_copy.default": get_unsqueeze_inputs(), + "aten.clone.default": get_clone_inputs(), + "aten.repeat.default": get_repeat_inputs(), } diff --git a/backends/vulkan/test/op_tests/utils/codegen.py b/backends/vulkan/test/op_tests/utils/codegen.py index b1c08e6d0d..f0e5547b4f 100644 --- a/backends/vulkan/test/op_tests/utils/codegen.py +++ b/backends/vulkan/test/op_tests/utils/codegen.py @@ -21,7 +21,8 @@ OPT_DEVICE, OPT_INT64, OPT_LAYOUT, - OPT_SCALARTYPE, + OPT_MEMORY_FORMAT, + OPT_SCALAR_TYPE, TestSuite, TestSuiteGen, THREE_TENSOR_TUPLE, @@ -30,8 +31,10 @@ from torchgen.api import cpp from torchgen.api.types import CppSignatureGroup -from torchgen.gen import generate_static_dispatch_backend_call -from torchgen.model import NativeFunction +from torchgen.gen import generate_static_dispatch_backend_call, translate_args + +from torchgen.gen_aoti_c_shim import gen_static_dispatch_backend_call_signature +from torchgen.model import NativeFunction, Variant ################################## ## Custom Test Suite Definition ## @@ -182,10 +185,23 @@ def create_aten_fn_call(self) -> str: func_call = generate_static_dispatch_backend_call( self.f_sig, self.f, TestSuiteGen.backend_key )[7:].replace("::cpu", "") + + return func_call + + def create_aten_method_call(self) -> str: + # For functions with only Method variant, we fallback to the function + # declared in MethodOperators.h. The method is declared as + # at::_ops::{name}::call(*), and ATEN_FN is a handly macro. + cpp_sig = gen_static_dispatch_backend_call_signature(self.f_sig, self.f) + exprs = translate_args(self.f_sig, cpp_sig) + func_call = f"ATEN_FN({self.f_sig.name()})({exprs});" return func_call def create_out_src(self) -> str: - return f"{self.out.cpp_type} out = " + self.create_aten_fn_call() + if Variant.function in self.f.variants: + return f"{self.out.cpp_type} out = " + self.create_aten_fn_call() + "\n" + else: + return f"{self.out.cpp_type} out = " + self.create_aten_method_call() + "\n" ## Graph code generation utils @@ -250,10 +266,11 @@ def create_value_for(self, ref: ValueRefList) -> str: # noqa: C901 elif ref.src_cpp_type == DOUBLE: ret_str += f"add_scalar({ref.src_cpp_name}); \n" elif ( - ref.src_cpp_type == OPT_SCALARTYPE + ref.src_cpp_type == OPT_SCALAR_TYPE or ref.src_cpp_type == OPT_LAYOUT or ref.src_cpp_type == OPT_DEVICE or ref.src_cpp_type == OPT_BOOL + or ref.src_cpp_type == OPT_MEMORY_FORMAT ): ret_str += "add_none(); \n" elif ref.src_cpp_type == TWO_TENSOR_TUPLE: @@ -351,7 +368,6 @@ def check_graph_out(self, ref: ValueRefList) -> str: def gen_graph_build_code(self) -> str: graph_build = self.create_out_src() - for aten_arg in self.args: graph_build += self.create_value_for(self.refs[aten_arg.name]) diff --git a/backends/vulkan/test/op_tests/utils/codegen_base.py b/backends/vulkan/test/op_tests/utils/codegen_base.py index 986526fbdc..d5feada1df 100644 --- a/backends/vulkan/test/op_tests/utils/codegen_base.py +++ b/backends/vulkan/test/op_tests/utils/codegen_base.py @@ -25,7 +25,8 @@ OPT_INT64 = "::std::optional" OPT_DEVICE = "::std::optional" OPT_LAYOUT = "::std::optional" -OPT_SCALARTYPE = "::std::optional" +OPT_MEMORY_FORMAT = "::std::optional" +OPT_SCALAR_TYPE = "::std::optional" TWO_TENSOR_TUPLE = "::std::tuple" THREE_TENSOR_TUPLE = "::std::tuple" @@ -149,10 +150,11 @@ def create_input_data(self, arg: Argument, data: Any) -> str: # noqa: C901 else: ret_str += f"{str(data)};" elif ( - cpp_type == OPT_SCALARTYPE + cpp_type == OPT_SCALAR_TYPE or cpp_type == OPT_LAYOUT or cpp_type == OPT_DEVICE or cpp_type == OPT_BOOL + or cpp_type == OPT_MEMORY_FORMAT ): ret_str += "std::nullopt;" else: diff --git a/backends/vulkan/test/test_vulkan_delegate.py b/backends/vulkan/test/test_vulkan_delegate.py index 1cce125a81..a458fc1c24 100644 --- a/backends/vulkan/test/test_vulkan_delegate.py +++ b/backends/vulkan/test/test_vulkan_delegate.py @@ -653,10 +653,13 @@ class Conv1dModule(torch.nn.Module): def __init__(self): super().__init__() self.conv = torch.nn.Conv1d( - in_channels=6, - out_channels=6, - kernel_size=3, - groups=6, + in_channels=20, + out_channels=10, + kernel_size=6, + stride=5, + padding=5, + dilation=3, + groups=5, bias=True, ) @@ -664,7 +667,7 @@ def forward(self, x): return self.conv(x) conv1d_module = Conv1dModule() - sample_inputs = (torch.randn(size=(1, 6, 7), dtype=torch.float32),) + sample_inputs = (torch.randn(size=(3, 20, 30), dtype=torch.float32),) self.lower_module_and_test_output( conv1d_module, @@ -729,3 +732,69 @@ def forward(self, x): sample_inputs, memory_layouts=[vk_graph_schema.VkMemoryLayout.TENSOR_CHANNELS_PACKED], ) + + def test_vulkan_backend_reshape(self): + class ReshapeModule(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, x): + return torch.reshape(x, [-1, x.size(-1)]) + + sample_inputs = (torch.randn(size=(5, 3, 4), dtype=torch.float32),) + + self.lower_module_and_test_output( + ReshapeModule(), + sample_inputs, + memory_layouts=[vk_graph_schema.VkMemoryLayout.TENSOR_CHANNELS_PACKED], + ) + + def test_vulkan_backend_view(self): + class ViewModule(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, x): + return x.view([-1, x.size(-1)]) + + sample_inputs = (torch.randn(size=(3, 2, 3, 4), dtype=torch.float32),) + + self.lower_module_and_test_output( + ViewModule(), + sample_inputs, + memory_layouts=[vk_graph_schema.VkMemoryLayout.TENSOR_CHANNELS_PACKED], + ) + + def test_vulkan_backend_unsqueeze(self): + class UnsqueezeModule(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, x): + x = torch.unsqueeze(x, 1) + x = torch.unsqueeze(x, 0) + return x + + sample_inputs = (torch.randn(size=(3,), dtype=torch.float32),) + + self.lower_module_and_test_output( + UnsqueezeModule(), + sample_inputs, + memory_layouts=[vk_graph_schema.VkMemoryLayout.TENSOR_CHANNELS_PACKED], + ) + + def test_vulkan_backend_select(self): + class SelectModule(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, x): + return x[0][3] + + sample_inputs = (torch.randn(size=(3, 6, 2, 7), dtype=torch.float32),) + + self.lower_module_and_test_output( + SelectModule(), + sample_inputs, + memory_layouts=[vk_graph_schema.VkMemoryLayout.TENSOR_CHANNELS_PACKED], + ) diff --git a/backends/xnnpack/README.md b/backends/xnnpack/README.md index fe359b7adc..6e1731799d 100644 --- a/backends/xnnpack/README.md +++ b/backends/xnnpack/README.md @@ -7,8 +7,11 @@ mechanism for leveraging the XNNPACK library to accelerate operators running on CPU. ## Layout -- `runtime/` : Runtime logic used at inference. This contains all the cpp files - used to build the runtime graph and execute the XNNPACK model +- `cmake/` : CMake related files +- `operators`: the directory to store all of op visitors + - `node_visitor.py`: Implementation of serializing each lowerable operator + node + - ... - `partition/`: Partitioner is used to identify operators in model's graph that are suitable for lowering to XNNPACK delegate - `xnnpack_partitioner.py`: Contains partitioner that tags graph patterns @@ -16,10 +19,8 @@ CPU. - `configs.py`: Contains lists of op/modules for XNNPACK lowering - `passes/`: Contains passes which are used before preprocessing to prepare the graph for XNNPACK lowering -- `operators`: the directory to store all of op visitors - - `node_visitor.py`: Implementation of serializing each lowerable operator - node - - ... +- `runtime/` : Runtime logic used at inference. This contains all the cpp files + used to build the runtime graph and execute the XNNPACK model - `serialization/`: Contains files related to serializing the XNNPACK graph representation of the PyTorch model - `schema.fbs`: Flatbuffer schema of serialization format @@ -28,64 +29,107 @@ CPU. - `xnnpack_graph_serialize`: Implementation for serializing dataclasses from graph schema to flatbuffer - `test/`: Tests for XNNPACK Delegate +- `third-party/`: third-party libraries used by XNNPACK Delegate - `xnnpack_preprocess.py`: Contains preprocess implementation which is called by `to_backend` on the graph or subgraph of a model returning a preprocessed blob responsible for executing the graph or subgraph at runtime +## End to End Example + +To further understand the features of the XNNPACK Delegate and how to use it, consider the following end to end example with MobilenetV2. + +### Lowering a model to XNNPACK +```python +import torch +import torchvision.models as models + +from torch.export import export, ExportedProgram +from torchvision.models.mobilenetv2 import MobileNet_V2_Weights +from executorch.backends.xnnpack.partition.xnnpack_partitioner import XnnpackPartitioner +from executorch.exir import EdgeProgramManager, ExecutorchProgramManager, to_edge +from executorch.exir.backend.backend_api import to_backend + + +mobilenet_v2 = models.mobilenetv2.mobilenet_v2(weights=MobileNet_V2_Weights.DEFAULT).eval() +sample_inputs = (torch.randn(1, 3, 224, 224), ) + +exported_program: ExportedProgram = export(mobilenet_v2, sample_inputs) +edge: EdgeProgramManager = to_edge(exported_program) + +edge = edge.to_backend(XnnpackPartitioner()) +``` + +We will go through this example with the [MobileNetV2](https://pytorch.org/hub/pytorch_vision_mobilenet_v2/) pretrained model downloaded from the TorchVision library. The flow of lowering a model starts after exporting the model `to_edge`. We call the `to_backend` api with the `XnnpackPartitioner`. The partitioner identifies the subgraphs suitable for XNNPACK backend delegate to consume. Afterwards, the identified subgraphs will be serialized with the XNNPACK Delegate flatbuffer schema and each subgraph will be replaced with a call to the XNNPACK Delegate. + +```python +>>> print(edge.exported_program().graph_module) +GraphModule( + (lowered_module_0): LoweredBackendModule() + (lowered_module_1): LoweredBackendModule() +) + +def forward(self, arg314_1): + lowered_module_0 = self.lowered_module_0 + executorch_call_delegate = torch.ops.higher_order.executorch_call_delegate(lowered_module_0, arg314_1); lowered_module_0 = arg314_1 = None + getitem = executorch_call_delegate[0]; executorch_call_delegate = None + aten_view_copy_default = executorch_exir_dialects_edge__ops_aten_view_copy_default(getitem, [1, 1280]); getitem = None + aten_clone_default = executorch_exir_dialects_edge__ops_aten_clone_default(aten_view_copy_default); aten_view_copy_default = None + lowered_module_1 = self.lowered_module_1 + executorch_call_delegate_1 = torch.ops.higher_order.executorch_call_delegate(lowered_module_1, aten_clone_default); lowered_module_1 = aten_clone_default = None + getitem_1 = executorch_call_delegate_1[0]; executorch_call_delegate_1 = None + return (getitem_1,) +``` + +We print the graph after lowering above to show the new nodes that were inserted to call the XNNPACK Delegate. The subgraphs which are being delegated to XNNPACK are the first argument at each call site. It can be observed that the majority of `convolution-relu-add` blocks and `linear` blocks were able to be delegated to XNNPACK. We can also see the operators which were not able to be lowered to the XNNPACK delegate, such as `clone` and `view_copy`. + +```python +exec_prog = edge.to_executorch() + +with open("xnnpack_mobilenetv2.pte", "wb") as file: + exec_prog.write_to_file(file) +``` +After lowering to the XNNPACK Program, we can then prepare it for executorch and save the model as a `.pte` file. `.pte` is a binary format that stores the serialized ExecuTorch graph. + + +### Running the XNNPACK Model with CMake +After exporting the XNNPACK Delegated model, we can now try running it with example inputs using CMake. We can build and use the xnn_executor_runner, which is a sample wrapper for the ExecuTorch Runtime and XNNPACK Backend. We first begin by configuring the CMake build like such: +```bash +# cd to the root of executorch repo +cd executorch + +# Get a clean cmake-out directory +rm- -rf cmake-out +mkdir cmake-out + +# Configure cmake +cmake \ + -DCMAKE_INSTALL_PREFIX=cmake-out \ + -DCMAKE_BUILD_TYPE=Release \ + -DEXECUTORCH_BUILD_EXTENSION_MODULE=ON \ + -DEXECUTORCH_BUILD_XNNPACK=ON \ + -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=ON \ + -DEXECUTORCH_ENABLE_LOGGING=1 \ + -DPYTHON_EXECUTABLE=python \ + -Bcmake-out . +``` +Then you can build the runtime componenets with + +```bash +cmake --build cmake-out -j9 --target install --config Release +``` + +Now you should be able to find the executable built at `./cmake-out/backends/xnnpack/xnn_executor_runner` you can run the executable with the model you generated as such +```bash +./cmake-out/backends/xnnpack/xnn_executor_runner --model_path=./mv2_xnnpack_fp32.pte +``` + ## Help & Improvements If you have problems or questions, or have suggestions for ways to make implementation and testing better, please reach out to the PyTorch Edge team or create an issue on [github](https://www.github.com/pytorch/executorch/issues). -## Contributing - -Please follow the following steps and guidelines when adding a new operator -implementation to this library. The goals of these guidelines are to -- Make it straightforward to add new XNNPACK operators. -- Ensure that the newly added operators are of high quality, and are easy to - maintain -- Make it easy for users to find available operator implementations, and to - trust in their quality and behavioral stability. - -### AoT and Serialization Overview -#### Serialization: -XNNPACK delegate uses flatbuffer to serialize its nodes and values. In order to -add -[preprocessing](https://github.com/pytorch/executorch/blob/main/backends/xnnpack/xnnpack_preprocess.py) -support for a new operator, we must add the operator in both the flatbuffer -[schema](https://github.com/pytorch/executorch/blob/main/backends/xnnpack/serialization/schema.fbs), -as well as the mirrored python [data -class](https://github.com/pytorch/executorch/blob/main/backends/xnnpack/serialization/xnnpack_graph_schema.py). -These tables are based on the arguments to the XNNPACK Subgraph APIs. These -APIs can be found -[here](https://github.com/google/xnnpack/blob/master/include/xnnpack.h). We -essentially serialize all the static arguments we need to call `define_{new -operator}()`. - -#### AoT Preprocess: -To add logic to preprocess new operators for the XNNPACK Delegate, we can -create new node_visitors that perform the serialization of the new operator. An -example can be found [here](). The function of these node_visitors is to -serialize all the data we define to need in the schema above. - -#### AoT Partitioner: -XnnpackPartitioner is used to select the pattern (like the linear module -graph) in a big graph such that the selected nodes will be delegated to -XNNPACK. To support a new op (for example, sigmoid), add the corresponding op -or module to the -[config.py](https://github.com/pytorch/executorch/blob/main/backends/xnnpack/partition/configs.py), -which captures the sigmoid op. - -#### How does it work? -- Tag the nodes: in the XNNPACK partitioner's config, which lists all ops that - are supported by the current XNNPACK backend in executorch. When call - `XnnpackPartitioner.partition()`, it will tag all the nodes that matches the - patterns listed in self.pattern -- Lower the nodes; when we call `to_backend(graph_module, XnnpackPartitioner)`, - it will loop through all the tagged nodes, and lower the group with the same - tag. - - -#### Adding Tests for newly minted operators -To test newly added operators, we can add unit tests in: -[tests](https://github.com/pytorch/executorch/tree/main/backends/xnnpack/test) + +## See Also +For more information about the XNNPACK Delegate, please check out the following resources: +- [ExecuTorch XNNPACK Delegate](https://pytorch.org/executorch/0.2/native-delegates-executorch-xnnpack-delegate.html) +- [Building and Running ExecuTorch with XNNPACK Backend](https://pytorch.org/executorch/0.2/native-delegates-executorch-xnnpack-delegate.html) diff --git a/backends/xnnpack/test/tester/tester.py b/backends/xnnpack/test/tester/tester.py index e0115a29ee..8812d5e501 100644 --- a/backends/xnnpack/test/tester/tester.py +++ b/backends/xnnpack/test/tester/tester.py @@ -595,7 +595,7 @@ def _assert_outputs_equal(model_output, ref_output, atol=1e-03, rtol=1e-03): f"Output {i} does not match reference output.\n" f"\tGiven atol: {atol}, rtol: {rtol}.\n" f"\tOutput tensor shape: {model.shape}, dtype: {model.dtype}\n" - f"\tDifference: max: {torch.max(model-ref)}, abs: {torch.max(torch.abs(model-ref))}.\n" + f"\tDifference: max: {torch.max(model-ref)}, abs: {torch.max(torch.abs(model-ref))}, mean abs error: {torch.mean(torch.abs(model-ref))}.\n" f"\t-- Model vs. Reference --\n" f"\t Numel: {model.numel()}, {ref.numel()}\n" f"\tMedian: {model.median()}, {ref.median()}\n" diff --git a/docs/source/_static/img/print_data_tabular.png b/docs/source/_static/img/print_data_tabular.png index 593ea4088cab4ab6a8598f0a7e4b529b83e7e022..7e20b129bb417c3c9047a1e1bb08d0ae677c7b7c 100644 GIT binary patch literal 280808 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

dbwTEwM>!=6@{p!jYh*dF9A2=jm8sU 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 VyJh^N=o;{+uA-w{`sfAx{{SVDUfKWv literal 68673 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 diff --git a/docs/source/build-run-coreml.md b/docs/source/build-run-coreml.md index 86a7cb9d41..da830e542c 100644 --- a/docs/source/build-run-coreml.md +++ b/docs/source/build-run-coreml.md @@ -1,6 +1,6 @@ # Building and Running ExecuTorch with Core ML Backend -Core ML delegate uses Core ML APIs to enable running neural networks via Apple's hardware acceleration. For more about coreml you can read [here](https://developer.apple.com/documentation/coreml). In this tutorial, we will walk through the steps of lowering a PyTorch model to Core ML delegate +Core ML delegate uses Core ML APIs to enable running neural networks via Apple's hardware acceleration. For more about Core ML you can read [here](https://developer.apple.com/documentation/coreml). In this tutorial, we will walk through the steps of lowering a PyTorch model to Core ML delegate ::::{grid} 2 @@ -24,8 +24,8 @@ Core ML delegate uses Core ML APIs to enable running neural networks via Apple's In order to be able to successfully build and run the ExecuTorch's Core ML backend you'll need the following hardware and software components. ### Hardware: -- A [mac](https://www.apple.com/mac/]) system for building. -- A [mac](https://www.apple.com/mac/]) or [iPhone](https://www.apple.com/iphone/) or [iPad](https://www.apple.com/ipad/) or [Apple TV](https://www.apple.com/tv-home/) device for running the model. +- A [mac](https://www.apple.com/mac/) system for building. +- A [mac](https://www.apple.com/mac/) or [iPhone](https://www.apple.com/iphone/) or [iPad](https://www.apple.com/ipad/) or [Apple TV](https://www.apple.com/tv-home/) device for running the model. ### Software: @@ -100,7 +100,7 @@ python3 -m examples.apple.coreml.scripts.export --model_name mv3 --generate_etre # Builds `coreml_executor_runner`. ./examples/apple/coreml/scripts/build_executor_runner.sh ``` -3. Run and generate an [ETDump](./sdk-etdump.md). +3. Run and generate an [ETDump](./sdk-etdump.md). ```bash cd executorch diff --git a/docs/source/build-run-xtensa.md b/docs/source/build-run-xtensa.md index cd58d09a0d..17fd6049f9 100644 --- a/docs/source/build-run-xtensa.md +++ b/docs/source/build-run-xtensa.md @@ -150,8 +150,7 @@ In order to run the CMake build, you need the path to the following: cd executorch rm -rf cmake-out # prebuild and install executorch library -cmake -DBUCK2=buck2 \ - -DCMAKE_TOOLCHAIN_FILE=/examples/cadence/cadence.cmake \ +cmake -DCMAKE_TOOLCHAIN_FILE=/examples/cadence/cadence.cmake \ -DCMAKE_INSTALL_PREFIX=cmake-out \ -DCMAKE_BUILD_TYPE=Debug \ -DPYTHON_EXECUTABLE=python3 \ diff --git a/docs/source/compiler-memory-planning.md b/docs/source/compiler-memory-planning.md index 86c0c13630..1dad3b032f 100644 --- a/docs/source/compiler-memory-planning.md +++ b/docs/source/compiler-memory-planning.md @@ -9,7 +9,7 @@ MemoryPlanning is the very last action taken before taking an `ExportedProgram` Concretely, there are three passes related to memory planning: * `SpecPropPass` computes a TensorSpec for each tensor in the graph (inputs, intermediates or outputs). The most important field of the tensor spec is a symbolic expression of the shapes of the tensor, where the initial set of symbols comes from the dimensions of input tensors, intermediate tensor shapes’ symbolic expression is propagated via tensor operations. The dimensions can be marked as either dynamic or static by users and when the dims are dynamic, users are required to annotate the dim with a ValueRange. -* `SymShapEvalPass` evaluates the symbolic expressions to concrete integers with their upper bounds. There are two ways to doing the upper bound specialization: +* `SymShapeEvalPass` evaluates the symbolic expressions to concrete integers with their upper bounds. There are two ways to doing the upper bound specialization: HintBasedSymShapeEval (to be deprecated) is the old way of evaluating the upper bound. It doesn’t look at the ValueRange of the symbols but uses the shapes of example inputs to replace all the symbols. We call it “hint based“ because the example inputs’ shapes are just hints of what the input shapes might be at run time and are used for tracing only. ValueRangeBasedSymShapeEval is the recommended way of doing UpperBoundMemory planning. It will actually look at the ValueRange of the symbols and do an inference over the ranges to get a real upper bound. * `MemoryPlanningPass` does the actual memory planning given all tensors get a TensorSpec with concrete integer shapes. @@ -18,9 +18,9 @@ HintBasedSymShapeEval (to be deprecated) is the old way of evaluating the upper ExecuTorch provides two options for memory planning algorithms out of the box, but users can define their own if the provided options are inappropriate or insufficient for their use case. -* The naive algorithm simply concatenates all the tensors together in a linear memory without considering any memory re-use. It serves as an upper bound for total memory consumption and serves as a baseline. +* The naive algorithm simply concatenates all the tensors together in a linear memory block without considering memory re-use. It serves as an upper bound for total memory consumption and serves as a baseline. -* The Greedy algorithm tries to re-use the already allocated memory and choose based on the best-fit criteria. Specifically: +* The Greedy algorithm tries to re-use the already allocated memory based on the best-fit criteria. Specifically: When there isn’t an allocated memory whose lifetime doesn’t overlap with the current tensor that we try to do memory planning for, we allocate a new memory buffer with the same size and lifetime as the current tensor. When there is one or more allocated memory buffer, whose lifetime overlaps with the current tensor, we pick the buffer that has the closest size with current tensor so as to reduce memory fragmentation. Finally, we allocate these memory buffers linearly in memory. @@ -48,7 +48,7 @@ Users can write custom memory plans to take advantage of multiple memory locatio ```python class CustomPoolMemoryPlanningPass(MemoryPlanningPass): - def call(self, graph_module: GraphModule) -> PassResult: + def run(self, graph_module: GraphModule, graph_signature: Optional[ExportGraphSignature]) -> PassResult: for subgm in graph_module.modules(): if not isinstance(subgm, GraphModule): continue @@ -68,7 +68,7 @@ class CustomPoolMemoryPlanningPass(MemoryPlanningPass): elif node.target == torch.ops.aten.mul.out: node.meta["spec"].mem_id = 1 - return super().call(graph_module) + return super().run(graph_module, graph_signature) ``` Then later when lowering to ExecuTorch you can use your custom plan in the following way: @@ -83,4 +83,4 @@ program = edge_program.to_executorch( ) ``` -Users attempting to write a custom memory planning algorithm should start by looking at [the greedy algorithm's implementation](https://github.com/pytorch/executorch/blob/d62c41ca86435e5316e7ed292b6d68aff27a2fb7/exir/memory_planning.py#L459C1-L459C12) +Users attempting to write a custom memory planning algorithm should start by looking at [the greedy algorithm's implementation](https://github.com/pytorch/executorch/blob/d62c41ca86435e5316e7ed292b6d68aff27a2fb7/exir/memory_planning.py#L459C1-L459C12). diff --git a/docs/source/debug-backend-delegate.md b/docs/source/debug-backend-delegate.md new file mode 100644 index 0000000000..ebcf94136c --- /dev/null +++ b/docs/source/debug-backend-delegate.md @@ -0,0 +1,65 @@ +# Debug Backend Delegate + +We provide a list of util functions to give users insights on what happened to the graph modules during the `to_backend()` stage. + +## Get delegation summary +The `get_delegation_info()` method provides a summary of what happened to the model after the `to_backend()` call: + +```python +from executorch.exir.backend.utils import get_delegation_info +from tabulate import tabulate + +# ... After call to to_backend(), but before to_executorch() +graph_module = edge_manager.exported_program().graph_module +delegation_info = get_delegation_info(graph_module) +print(delegation_info.get_summary()) +df = delegation_info.get_operator_delegation_dataframe() +print(tabulate(df, headers="keys", tablefmt="fancy_grid")) +``` + +Example printout: +``` +Total delegated subgraphs: 86 +Number of delegated nodes: 473 +Number of non-delegated nodes: 430 +``` + + +| | op_type | occurrences_in_delegated_graphs | occurrences_in_non_delegated_graphs | +|----|---------------------------------|------- |-----| +| 0 | aten__softmax_default | 12 | 0 | +| 1 | aten_add_tensor | 37 | 0 | +| 2 | aten_addmm_default | 48 | 0 | +| 3 | aten_arange_start_step | 0 | 25 | +| | ... | | | +| 23 | aten_view_copy_default | 170 | 48 | +| | ... | | | +| 26 | Total | 473 | 430 | + +From the table, the operator `aten_view_copy_default` appears 170 times in delegate graphs and 48 times in non-delegated graphs. Users can use information like this to debug. + +## Visualize delegated graph +To see a more detailed view, use the `print_delegated_graph()` method to display a printout of the whole graph: + +```python +from executorch.exir.backend.utils import print_delegated_graph +graph_module = edge_manager.exported_program().graph_module +print(print_delegated_graph(graph_module)) +``` +It will print the whole model as well as the subgraph consumed by the backend. The generic debug function provided by fx like `print_tabular()` or `print_readable()` will only show `call_delegate` but hide the the subgraph consumes by the backend, while this function exposes the contents inside the subgraph. + +In the example printout below, observe that `embedding` and `add` operators are delegated to `XNNPACK` while the `sub` operator is not. + +``` +%aten_unsqueeze_copy_default_22 : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.unsqueeze_copy.default](args = (%aten_arange_start_step_23, -2), kwargs = {}) + %aten_unsqueeze_copy_default_23 : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.unsqueeze_copy.default](args = (%aten_arange_start_step_24, -1), kwargs = {}) + %lowered_module_0 : [num_users=1] = get_attr[target=lowered_module_0] + backend_id: XnnpackBackend + lowered graph(): + %aten_embedding_default : [num_users=1] = placeholder[target=aten_embedding_default] + %aten_embedding_default_1 : [num_users=1] = placeholder[target=aten_embedding_default_1] + %aten_add_tensor : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.add.Tensor](args = (%aten_embedding_default, %aten_embedding_default_1), kwargs = {}) + return (aten_add_tensor,) + %executorch_call_delegate : [num_users=1] = call_function[target=torch.ops.higher_order.executorch_call_delegate](args = (%lowered_module_0, %aten_embedding_default, %aten_embedding_default_1), kwargs = {}) + %aten_sub_tensor : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.sub.Tensor](args = (%aten_unsqueeze_copy_default, %aten_unsqueeze_copy_default_1), kwargs = {}) +``` diff --git a/docs/source/getting-started-setup.md b/docs/source/getting-started-setup.md index ffc3349848..833b5ee0ca 100644 --- a/docs/source/getting-started-setup.md +++ b/docs/source/getting-started-setup.md @@ -184,6 +184,39 @@ Output 0: tensor(sizes=[1], [2.]) To learn how to build a similar program, visit the [ExecuTorch in C++ Tutorial](running-a-model-cpp-tutorial.md). +### [Optional] Setting Up Buck2 +**Buck2** is an open-source build system that some of our examples currently utilize for building and running. + +However, please note that the installation of `Buck2` is optional for using ExecuTorch and we are in the process of transitioning away from `Buck2` and migrating all relevant sections to `cmake`. This section will be removed once we finish the migration. + +To set up `Buck2`, You will need the following prerequisits for this section: +* The `zstd` command line tool — install by running + ```bash + pip3 install zstd + ``` +* Version `${executorch_version:buck2}` of the `buck2` commandline tool — you can download a + prebuilt archive for your system from [the Buck2 + repo](https://github.com/facebook/buck2/releases/tag/2024-02-15). Note that + the version is important, and newer or older versions may not work with the + version of the buck2 prelude used by the ExecuTorch repo. + +Configure Buck2 by decompressing with the following command (filename depends + on your system): + + ```bash + # For example, buck2-x86_64-unknown-linux-musl.zst or buck2-aarch64-apple-darwin.zst + zstd -cdq buck2-DOWNLOADED_FILENAME.zst > /tmp/buck2 && chmod +x /tmp/buck2 + ``` + +You may want to copy the `buck2` binary into your `$PATH` so you can run it + as `buck2`. + +After the installation, you can run the `add.pte` program by following `buck2` command: + +```bash +/tmp/buck2 run //examples/portable/executor_runner:executor_runner -- --model_path add.pte +``` + ## Next Steps Congratulations! You have successfully exported, built, and run your first diff --git a/docs/source/index.rst b/docs/source/index.rst index cb78b01285..210839cf57 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -187,6 +187,7 @@ Topics in this section will help you get started with ExecuTorch. native-delegates-executorch-vulkan-delegate backend-delegates-integration backend-delegates-dependencies + debug-backend-delegate .. toctree:: :glob: diff --git a/docs/source/kernel-library-custom-aten-kernel.md b/docs/source/kernel-library-custom-aten-kernel.md index 4b0794ea1c..4d391b1a94 100644 --- a/docs/source/kernel-library-custom-aten-kernel.md +++ b/docs/source/kernel-library-custom-aten-kernel.md @@ -86,10 +86,88 @@ ATen operator with a dtype/dim order specialized kernel (works for `Double` dtyp kernel_name: torch::executor::add_out ``` +### Custom Ops C++ API + +For a custom kernel that implements a custom operator, we provides 2 ways to register it into ExecuTorch runtime: +1. Using `EXECUTORCH_LIBRARY` and `WRAP_TO_ATEN` C++ macros. +2. Using `functions.yaml` and codegen'd C++ libraries. + +The first option requires C++17 and doesn't have selective build support yet, but it's faster than the second option where we have to go through yaml authoring and build system tweaking. + +The first option is particularly suitable for fast prototyping but can also be used in production. + +Similar to `TORCH_LIBRARY`, `EXECUTORCH_LIBRARY` takes the operator name and the C++ function name and register them into ExecuTorch runtime. + +#### Prepare custom kernel implementation + +Define your custom operator schema for both functional variant (used in AOT compilation) and out variant (used in ExecuTorch runtime). The schema needs to follow PyTorch ATen convention (see native_functions.yaml). For example: + +```yaml +custom_linear(Tensor weight, Tensor input, Tensor(?) bias) -> Tensor +custom_linear.out(Tensor weight, Tensor input, Tensor(?) bias, *, Tensor(a!) out) -> Tensor(a!) +``` + +Then write your custom kernel according to the schema using ExecuTorch types, along with APIs to register to ExecuTorch runtime: + + +```c++ +// custom_linear.h/custom_linear.cpp +#include +Tensor& custom_linear_out(const Tensor& weight, const Tensor& input, optional bias, Tensor& out) { + // calculation + return out; +} +``` +#### Use a C++ macro to register it into PyTorch & ExecuTorch + +Append the following line in the example above: +```c++ +// custom_linear.h/custom_linear.cpp +// opset namespace myop +EXECUTORCH_LIBRARY(myop, "custom_linear.out", custom_linear_out); +``` + +Now we need to write some wrapper for this op to show up in PyTorch, but don’t worry we don’t need to rewrite the kernel. Create a separate .cpp for this purpose: + +```c++ +// custom_linear_pytorch.cpp +#include "custom_linear.h" +#include + +at::Tensor custom_linear(const at::Tensor& weight, const at::Tensor& input, std::optional bias) { + // initialize out + at::Tensor out = at::empty({weight.size(1), input.size(1)}); + // wrap kernel in custom_linear.cpp into ATen kernel + WRAP_TO_ATEN(custom_linear_out, 3)(weight, input, bias, out); + return out; +} +// standard API to register ops into PyTorch +TORCH_LIBRARY(myop, m) { + m.def("custom_linear(Tensor weight, Tensor input, Tensor(?) bias) -> Tensor", custom_linear); + m.def("custom_linear.out(Tensor weight, Tensor input, Tensor(?) bias, *, Tensor(a!) out) -> Tensor(a!)", WRAP_TO_ATEN(custom_linear_out, 3)); +} +``` + +#### Compile and link the custom kernel + +Link it into ExecuTorch runtime: In our `CMakeLists.txt`` that builds the binary/application, we just need to add custom_linear.h/cpp into the binary target. We can build a dynamically loaded library (.so or .dylib) and link it as well. + +Link it into PyTorch runtime: We need to package custom_linear.h, custom_linear.cpp and custom_linear_pytorch.cpp into a dynamically loaded library (.so or .dylib) and load it into our python environment. One way of doing this is: + +```python +import torch +torch.ops.load_library("libcustom_linear.so/dylib") + +# Now we have access to the custom op, backed by kernel implemented in custom_linear.cpp. +op = torch.ops.myop.custom_linear.default +``` + ### Custom Ops Yaml Entry -For custom ops (the ones that are not part of the out variants of core ATen opset) we need to specify the operator schema as well as a `kernel` section. So instead of `op` we use `func` with the operator schema. As an example, here’s a yaml entry for a custom op: +As mentioned above, this option provides more support in terms of selective build and features such as merging operator libraries. + +First we need to specify the operator schema as well as a `kernel` section. So instead of `op` we use `func` with the operator schema. As an example, here’s a yaml entry for a custom op: ```yaml - func: allclose.out(Tensor self, Tensor other, float rtol=1e-05, float atol=1e-08, bool equal_nan=False, bool dummy_param=False, *, Tensor(a!) out) -> Tensor(a!) kernels: @@ -159,6 +237,30 @@ target_link_libraries(executorch_binary generated_lib) ``` +We also provide the ability to merge two yaml files, given a precedence. `merge_yaml(FUNCTIONS_YAML functions_yaml FALLBACK_YAML fallback_yaml OUTPUT_DIR out_dir)` merges functions_yaml and fallback_yaml into a single yaml, if there's duplicate entries in functions_yaml and fallback_yaml, this macro will always take the one in functions_yaml. + +Example: + +```yaml +# functions.yaml +- op: add.out + kernels: + - arg_meta: null + kernel_name: torch::executor::opt_add_out +``` + +And out fallback: + +```yaml +# fallback.yaml +- op: add.out + kernels: + - arg_meta: null + kernel_name: torch::executor::add_out +``` + +The merged yaml will have the entry in functions.yaml. + #### Buck2 `executorch_generated_lib` is the macro that takes the yaml files and depends on the selective build macro `et_operator_library`. For an example: diff --git a/docs/source/llm/getting-started.md b/docs/source/llm/getting-started.md index 3bff5c903f..ae743e8e6d 100644 --- a/docs/source/llm/getting-started.md +++ b/docs/source/llm/getting-started.md @@ -1,5 +1,18 @@ # Getting Started with LLMs via ExecuTorch +Welcome to LLM Manual! This manual is designed to provide a practical example to leverage +ExecuTorch in onboarding your own Large Language Models (LLMs). Our primary goal is to offer + a clear and concise guideline on how to integrate our system with your own LLMs. + +Please note that this project is intended as a demonstration and not as a fully functional +example with optimal performance. As such, certain components such as the sampler, tokenizer, +and others are provided in their bare minimum versions solely for demonstration purposes. +Consequently, the results produced by the model may vary and might not always be optimal. + +We encourage users to use this project as a starting point and adapt it to their specific needs, +which includes creating your own versions of the tokenizer, sampler, acceleration backends, and +other components. We hope this project serves as a useful guide in your journey with LLMs and ExecuTorch. + ### Table Of Contents @@ -77,12 +90,12 @@ cd ../.. ::: :::: -For more information, see [Setting Up ExecuTorch](https://pytorch.org/executorch/stable/getting-started-setup.html). +For more information, see [Setting Up ExecuTorch](../getting-started-setup.md). ## Running a Large Language Model Locally -This example uses Karpathy’s [NanoGPT](https://github.com/karpathy/nanoGPT), which is a minimal implementation of +This example uses Karpathy’s [nanoGPT](https://github.com/karpathy/nanoGPT), which is a minimal implementation of GPT-2 124M. This guide is applicable to other language models, as ExecuTorch is model-invariant. There are two steps to running a model with ExecuTorch: @@ -100,7 +113,7 @@ ExecuTorch runtime. Exporting takes a PyTorch model and converts it into a format that can run efficiently on consumer devices. -For this example, you will need the NanoGPT model and the corresponding tokenizer vocabulary. +For this example, you will need the nanoGPT model and the corresponding tokenizer vocabulary. ::::{tab-set} :::{tab-item} curl @@ -141,13 +154,24 @@ model = GPT.from_pretrained('gpt2') # Create example inputs. This is used in the export process to provide # hints on the expected shape of the model input. -example_inputs = (torch.randint(0, 100, (1, 8), dtype=torch.long), ) +example_inputs = (torch.randint(0, 100, (1, model.config.block_size), dtype=torch.long), ) + +# Set up dynamic shape configuration. This allows the sizes of the input tensors +# to differ from the sizes of the tensors in `example_inputs` during runtime, as +# long as they adhere to the rules specified in the dynamic shape configuration. +# Here we set the range of 0th model input's 1st dimension as +# [0, model.config.block_size]. +# See https://pytorch.org/executorch/main/concepts.html#dynamic-shapes +# for details about creating dynamic shapes. +dynamic_shape = ( + {1: torch.export.Dim("token_dim", max=model.config.block_size)}, +) # Trace the model, converting it to a portable intermediate representation. # The torch.no_grad() call tells PyTorch to exclude training-specific logic. with torch.nn.attention.sdpa_kernel([SDPBackend.MATH]), torch.no_grad(): - m = capture_pre_autograd_graph(model, example_inputs) - traced_model = export(m, example_inputs) + m = capture_pre_autograd_graph(model, example_inputs, dynamic_shapes=dynamic_shape) + traced_model = export(m, example_inputs, dynamic_shapes=dynamic_shape) # Convert the model into a runnable ExecuTorch program. edge_config = EdgeCompileConfig(_check_ir_validity=False) @@ -161,7 +185,7 @@ with open("nanogpt.pte", "wb") as file: To export, run the script with `python export_nanogpt.py` (or python3, as appropriate for your environment). It will generate a `nanogpt.pte` file in the current directory. -For more information, see [Exporting to ExecuTorch](https://pytorch.org/executorch/main/tutorials/export-to-executorch-tutorial.html) and +For more information, see [Exporting to ExecuTorch](../tutorials/export-to-executorch-tutorial) and [torch.export](https://pytorch.org/docs/stable/export.html). ### Step 2. Invoking the Runtime @@ -204,11 +228,15 @@ output token by token. Each generated token is passed as input for the next run. ```cpp // main.cpp +// The value of the gpt2 `<|endoftext|>` token. +#define ENDOFTEXT_TOKEN 50256 + std::string generate( Module& llm_model, std::string& prompt, BasicTokenizer& tokenizer, BasicSampler& sampler, + size_t max_input_length, size_t max_output_length) { // Convert the input text into a list of integers (tokens) that represents @@ -237,14 +265,23 @@ std::string generate( // Sample the next token from the logits. int64_t next_token = sampler.sample(logits); + + // Break if we reached the end of the text. + if (next_token == ENDOFTEXT_TOKEN) { + break; + } + + // Add the next token to the output. output_tokens.push_back(next_token); std::cout << tokenizer.decode({ next_token }); std::cout.flush(); // Update next input. - input_tokens.erase(input_tokens.begin()); input_tokens.push_back(next_token); + if (input_tokens.size() > max_input_length) { + input_tokens.erase(input_tokens.begin()); + } } std::cout << std::endl; @@ -278,7 +315,9 @@ penalties for repeated tokens, and biases to prioritize or de-prioritize specifi int main() { // Set up the prompt. This provides the seed text for the model to elaborate. - std::string prompt = "Once upon a time, there was a"; + std::cout << "Enter model prompt: "; + std::string prompt; + std::getline(std::cin, prompt); // The tokenizer is used to convert between tokens (used by the model) and // human-readable strings. @@ -290,23 +329,23 @@ int main() { // Load the exported nanoGPT program, which was generated via the previous steps. Module model("nanogpt.pte", torch::executor::Module::MlockConfig::UseMlockIgnoreErrors); + const auto max_input_tokens = 1024; const auto max_output_tokens = 30; std::cout << prompt; - generate(model, prompt, tokenizer, sampler, max_output_tokens); + generate(model, prompt, tokenizer, sampler, max_input_tokens, max_output_tokens); } ``` Finally, download the following files into the same directory as main.h: -TODO: This is a placeholder. ``` -curl -O https://raw.githubusercontent.com/GregoryComer/et-tutorials/quantization/nanogpt/managed_tensor.h -curl -O https://raw.githubusercontent.com/GregoryComer/et-tutorials/quantization/nanogpt/basic_tokenizer.h -curl -O https://raw.githubusercontent.com/GregoryComer/et-tutorials/quantization/nanogpt/basic_sampler.h +curl -O https://raw.githubusercontent.com/pytorch/executorch/main/examples/llm_manual/basic_sampler.h +curl -O https://raw.githubusercontent.com/pytorch/executorch/main/examples/llm_manual/basic_tokenizer.h +curl -O https://raw.githubusercontent.com/pytorch/executorch/main/examples/llm_manual/managed_tensor.h ``` -To learn more, see [Running an ExecuTorch Model in C++](https://pytorch.org/executorch/main/running-a-model-cpp-tutorial.html) -and the [ExecuTorch Runtime API Reference](https://pytorch.org/executorch/main/executorch-runtime-api-reference.html). +To learn more, see [Running an ExecuTorch Model in C++](../running-a-model-cpp-tutorial.md) +and the [ExecuTorch Runtime API Reference](../executorch-runtime-api-reference.md). ### Building and Running @@ -363,10 +402,20 @@ cmake --build cmake-out -j10 ./cmake-out/nanogpt_runner ``` -You should see something like the following: +You should see the message: ``` -Once upon a time, there was a man who was a member of the military... +Enter model prompt: +``` + +Type some seed text for the model and press enter. Here we use "Hello world!" as +an example prompt: + +``` +Enter model prompt: Hello world! +Hello world! + +I'm not sure if you've heard of the "Curse of the Dragon" or not, but it's a very popular game in ``` At this point, it is likely to run very slowly. This is because ExecuTorch hasn't been told to optimize for @@ -377,12 +426,12 @@ specific hardware (delegation), and because it is doing all of the calculations While ExecuTorch provides a portable, cross-platform implementation for all operators, it also provides specialized backends for a number of different targets. These include, but are not limited to, x86 and ARM CPU acceleration via -the XNNPACK backend, Apple acceleration via the CoreML backend and Metal +the XNNPACK backend, Apple acceleration via the Core ML backend and Metal Performance Shader (MPS) backend, and GPU acceleration via the Vulkan backend. Because optimizations are specific to a given backend, each pte file is specific to the backend(s) targeted at export. To support multiple devices, such as -XNNPACK acceleration for Android and CoreML for iOS, export a separate PTE file +XNNPACK acceleration for Android and Core ML for iOS, export a separate PTE file for each backend. To delegate to a backend at export time, ExecuTorch provides the `to_backend()` @@ -393,12 +442,12 @@ computation graph that can be accelerated by the target backend,and acceleration and optimization. Any portions of the computation graph not delegated will be executed by the ExecuTorch operator implementations. -To delegate the exported model to the specific backend, we need to import its -partitioner as well as edge compile config from ExecuTorch Codebase first, then +To delegate the exported model to a specific backend, we need to import its +partitioner as well as edge compile config from ExecuTorch codebase first, then call `to_backend` with an instance of partitioner on the `EdgeProgramManager` object `to_edge` function created. -Here's an example of how to delegate NanoGPT to XNNPACK (if you're deploying to an Android Phone for instance): +Here's an example of how to delegate nanoGPT to XNNPACK (if you're deploying to an Android phone for instance): ```python # export_nanogpt.py @@ -417,20 +466,31 @@ from torch._export import capture_pre_autograd_graph from model import GPT -# Load the NanoGPT model. +# Load the nanoGPT model. model = GPT.from_pretrained('gpt2') # Create example inputs. This is used in the export process to provide # hints on the expected shape of the model input. example_inputs = ( - torch.randint(0, 100, (1, 8), dtype=torch.long), + torch.randint(0, 100, (1, model.config.block_size - 1), dtype=torch.long), ) +# Set up dynamic shape configuration. This allows the sizes of the input tensors +# to differ from the sizes of the tensors in `example_inputs` during runtime, as +# long as they adhere to the rules specified in the dynamic shape configuration. +# Here we set the range of 0th model input's 1st dimension as +# [0, model.config.block_size]. +# See https://pytorch.org/executorch/main/concepts.html#dynamic-shapes +# for details about creating dynamic shapes. +dynamic_shape = ( + {1: torch.export.Dim("token_dim", max=model.config.block_size - 1)}, +) + # Trace the model, converting it to a portable intermediate representation. # The torch.no_grad() call tells PyTorch to exclude training-specific logic. with torch.nn.attention.sdpa_kernel([SDPBackend.MATH]), torch.no_grad(): - m = capture_pre_autograd_graph(model, example_inputs) - traced_model = export(m, example_inputs) + m = capture_pre_autograd_graph(model, example_inputs, dynamic_shapes=dynamic_shape) + traced_model = export(m, example_inputs, dynamic_shapes=dynamic_shape) # Convert the model into a runnable ExecuTorch program. # To be further lowered to Xnnpack backend, `traced_model` needs xnnpack-specific edge compile config @@ -481,11 +541,9 @@ target_link_libraries( xnnpack_backend) # Provides the XNNPACK CPU acceleration backend ``` -Keep the rest of the code the same. For more details refer to -[Exporting to ExecuTorch](https://pytorch.org/executorch/main/llm/getting-started.html#step-1-exporting-to-executorch) -and -[Invoking the Runtime](https://pytorch.org/executorch/main/llm/getting-started.html#step-2-invoking-the-runtime) -for more details +Keep the rest of the code the same. For more details refer to [Exporting +to ExecuTorch](#step-1-exporting-to-executorch) and [Invoking the +Runtime](#step-2-invoking-the-runtime) for more details At this point, the working directory should contain the following files: @@ -512,18 +570,28 @@ cmake --build cmake-out -j10 ./cmake-out/nanogpt_runner ``` -You should see something like the following: + +You should see the message: + +``` +Enter model prompt: +``` + +Type some seed text for the model and press enter. Here we use "Hello world!" as +an example prompt: ``` -Once upon a time, there was a man who was a member of the military... +Enter model prompt: Hello world! +Hello world! + +I'm not sure if you've heard of the "Curse of the Dragon" or not, but it's a very popular game in ``` +The delegated model should be noticeably faster compared to the non-delegated model. For more information regarding backend delegateion, see the ExecuTorch guides -for the -[XNNPACK Backend](https://pytorch.org/executorch/stable/tutorial-xnnpack-delegate-lowering.html) -and -[CoreML Backend](https://pytorch.org/executorch/stable/build-run-coreml.html). +for the [XNNPACK Backend](../tutorial-xnnpack-delegate-lowering.md) and [Core ML +Backend](../build-run-coreml.md). ## Quantization @@ -609,7 +677,7 @@ target_link_libraries( xnnpack_backend) # Provides the XNNPACK CPU acceleration backend ``` -For more information, see [Quantization in ExecuTorch](https://pytorch.org/executorch/stable/quantization-overview.html). +For more information, see [Quantization in ExecuTorch](../quantization-overview.md). ## Profiling and Debugging After lowering a model by calling `to_backend()`, you may want to see what got delegated and what didn’t. ExecuTorch @@ -633,7 +701,7 @@ df = delegation_info.get_operator_delegation_dataframe() print(tabulate(df, headers="keys", tablefmt="fancy_grid")) ``` -For NanoGPT targeting the XNNPACK backend, you might see the following: +For nanoGPT targeting the XNNPACK backend, you might see the following: ``` Total delegated subgraphs: 86 Number of delegated nodes: 473 @@ -641,7 +709,7 @@ Number of non-delegated nodes: 430 ``` -| | op_type | occurrences_in_delegated_graphs | occurrences_in_non_delegated_graphs | +| | op_type | # in_delegated_graphs | # in_non_delegated_graphs | |----|---------------------------------|------- |-----| | 0 | aten__softmax_default | 12 | 0 | | 1 | aten_add_tensor | 37 | 0 | @@ -663,7 +731,7 @@ print(print_delegated_graph(graph_module)) This may generate a large amount of output for large models. Consider using "Control+F" or "Command+F" to locate the operator you’re interested in (e.g. “aten_view_copy_default”). Observe which instances are not under lowered graphs. -In the fragment of the output for NanoGPT below, observe that embedding and add operators are delegated to XNNPACK while the sub operator is not. +In the fragment of the output for nanoGPT below, observe that embedding and add operators are delegated to XNNPACK while the sub operator is not. ``` %aten_unsqueeze_copy_default_22 : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.unsqueeze_copy.default](args = (%aten_arange_start_step_23, -2), kwargs = {}) @@ -687,7 +755,7 @@ Through the ExecuTorch SDK, users are able to profile model execution, giving ti ##### ETRecord generation (Optional) -An ETRecord is an artifact generated at the time of export that contains model graphs and source-level metadata linking the ExecuTorch program to the original PyTorch model. You can view all profiling events without an ETRecord, though with an ETRecord, you will also be able to link each event to the types of operators being executed, module hierarchy, and stack traces of the original PyTorch source code. For more information, see [https://pytorch.org/executorch/main/sdk-etrecord.html](https://pytorch.org/executorch/main/sdk-etrecord.html) +An ETRecord is an artifact generated at the time of export that contains model graphs and source-level metadata linking the ExecuTorch program to the original PyTorch model. You can view all profiling events without an ETRecord, though with an ETRecord, you will also be able to link each event to the types of operators being executed, module hierarchy, and stack traces of the original PyTorch source code. For more information, see [the ETRecord docs](../sdk-etrecord.md). In your export script, after calling `to_edge()` and `to_executorch()`, call `generate_etrecord()` with the `EdgeProgramManager` from `to_edge()` and the `ExecuTorchProgramManager` from `to_executorch()`. Make sure to copy the `EdgeProgramManager`, as the call to `to_backend()` mutates the graph in-place. @@ -709,7 +777,7 @@ Run the export script and the ETRecord will be generated as `etrecord.bin`. ##### ETDump generation -An ETDump is an artifact generated at runtime containing a trace of the model execution. For more information, see [https://pytorch.org/executorch/main/sdk-etdump.html](https://pytorch.org/executorch/main/sdk-etdump.html) +An ETDump is an artifact generated at runtime containing a trace of the model execution. For more information, see [the ETDump docs](../sdk-etdump.md). Include the ETDump header in your code. ```cpp @@ -779,7 +847,7 @@ This prints the performance data in a tabular format in “inspector_out.txt”, ![](../_static/img/llm_manual_print_data_tabular.png) View in full size -To learn more about the Inspector and the rich functionality it provides, see the [Inspector API Reference](https://pytorch.org/executorch/main/sdk-inspector.html). +To learn more about the Inspector and the rich functionality it provides, see the [Inspector API Reference](../sdk-inspector.md). ## Custom Kernels With the ExecuTorch custom operator APIs, custom operator and kernel authors can easily bring in their kernel into PyTorch/ExecuTorch. @@ -857,7 +925,7 @@ torch.ops.load_library("libcustom_linear.so") Once loaded, you can use the custom operator in PyTorch code. For more information, see [PyTorch Custom Operators](https://pytorch.org/tutorials/advanced/torch_script_custom_ops.html) and -and [ExecuTorch Kernel Registration](https://pytorch.org/executorch/stable/kernel-library-custom-aten-kernel.html). +and [ExecuTorch Kernel Registration](../kernel-library-custom-aten-kernel.md). ### Using a Custom Operator in a Model @@ -879,9 +947,8 @@ def replace_linear_with_custom_linear(module): The remaining steps are the same as the normal flow. Now you can run this module in eager mode as well as export to ExecuTorch. -## How to build Mobile Apps -You can execute an LLM using ExecuTorch on iOS and Android. - -**For iOS see the [iLLaMA App](https://pytorch.org/executorch/main/llm/llama-demo-ios.html).** +## How to Build Mobile Apps +See the instructions for building and running LLMs using ExecuTorch on iOS and Android. -**For Android, see the [Android Sample App](https://pytorch.org/executorch/main/llm/llama-demo-android.html).** +* **[iOS ExecuTorch LLaMA Demo App](llama-demo-ios.md)** +* **[Android ExecuTorch LLaMA Demo App](llama-demo-android.md)** diff --git a/docs/source/native-delegates-executorch-xnnpack-delegate.md b/docs/source/native-delegates-executorch-xnnpack-delegate.md index 12b2e9c2ba..1d12daef9d 100644 --- a/docs/source/native-delegates-executorch-xnnpack-delegate.md +++ b/docs/source/native-delegates-executorch-xnnpack-delegate.md @@ -74,16 +74,8 @@ 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`. After running the model it will produce basic per-op and total timings. We provide an example of the profiling below. The timings listed are the average across runs, and the units are in microseconds. +We have enabled basic profiling for XNNPACK delegate that can be enabled with the following compiler flag `-DENABLE_XNNPACK_PROFILING`. With ExecuTorch's SDK integration, you can also now use the SDK tools to profile the model. You can follow the steps in [Using the ExecuTorch SDK to Profile a Model](./tutorials/sdk-integration-tutorial) on how to profile ExecuTorch models and use SDK's Inspector API to view XNNPACK's internal profiling information. -``` -Fully Connected (NC, F32) GEMM: 109.510002 -Total Time: 109.510002 -``` - -::::{note} -Profiling is a work in progress, and is planned to be integrated with [SDK Tools](sdk-delegate-integration.md) and Tensorboard. -:::: [comment]: <> (TODO: Refactor quantizer to a more official quantization doc) ## Quantization diff --git a/docs/source/runtime-build-and-cross-compilation.md b/docs/source/runtime-build-and-cross-compilation.md index 22246b8f8c..e328bd1541 100644 --- a/docs/source/runtime-build-and-cross-compilation.md +++ b/docs/source/runtime-build-and-cross-compilation.md @@ -60,7 +60,7 @@ cd executorch # # NOTE: If your `buck2` binary is not on the PATH, you can change this line to # say something like `-DBUCK2=/tmp/buck2` to point directly to the tool. -(rm -rf cmake-out && mkdir cmake-out && cd cmake-out && cmake -DBUCK2=buck2 ..) +(rm -rf cmake-out && mkdir cmake-out && cd cmake-out && cmake ..) ``` Once this is done, you don't need to do it again until you pull from the upstream repo again, or if you modify any CMake-related files. diff --git a/docs/source/sdk-inspector.rst b/docs/source/sdk-inspector.rst index 23c529cb9d..e15c1f2a39 100644 --- a/docs/source/sdk-inspector.rst +++ b/docs/source/sdk-inspector.rst @@ -56,6 +56,7 @@ print_data_tabular inspector.print_data_tabular() .. image:: _static/img/print_data_tabular.png +Note that the unit of delegate profiling events is "cycles". We're working on providing a way to set different units in the future. find_total_for_module diff --git a/docs/source/tutorial-xnnpack-delegate-lowering.md b/docs/source/tutorial-xnnpack-delegate-lowering.md index 4defc3079e..b82a1a9f25 100644 --- a/docs/source/tutorial-xnnpack-delegate-lowering.md +++ b/docs/source/tutorial-xnnpack-delegate-lowering.md @@ -171,7 +171,7 @@ Now you should be able to find the executable built at `./cmake-out/backends/xnn ## Running the XNNPACK Model with Buck -Alternatively, you can use `buck2` to run the `.pte` file with XNNPACK delegate instructions in it on your host platform. You can follow the instructions here to install [buck2](getting-started-setup.md#building-a-runtime). You can now run it with the prebuilt `xnn_executor_runner` provided in the examples. This will run the model on some sample inputs. +Alternatively, you can use `buck2` to run the `.pte` file with XNNPACK delegate instructions in it on your host platform. You can follow the instructions here to install [buck2](getting-started-setup.md#Build-&-Run). You can now run it with the prebuilt `xnn_executor_runner` provided in the examples. This will run the model on some sample inputs. ```bash buck2 run examples/xnnpack:xnn_executor_runner -- --model_path ./mv2_xnnpack_fp32.pte diff --git a/docs/source/tutorials_source/sdk-integration-tutorial.py b/docs/source/tutorials_source/sdk-integration-tutorial.py index 8cf186a8cd..27474c2251 100644 --- a/docs/source/tutorials_source/sdk-integration-tutorial.py +++ b/docs/source/tutorials_source/sdk-integration-tutorial.py @@ -20,7 +20,7 @@ # This tutorial will show a full end-to-end flow of how to utilize the SDK. # Specifically, it will: # -# 1. Generate the artifacts consumed by the SDK (`ETRecord <../sdk-etrecord>`__, `ETDump <../sdk-etdump.html>`__). +# 1. Generate the artifacts consumed by the SDK (`ETRecord <../sdk-etrecord.html>`__, `ETDump <../sdk-etdump.html>`__). # 2. Create an Inspector class consuming these artifacts. # 3. Utilize the Inspector class to analyze the model. @@ -42,7 +42,7 @@ # # ``executorch.sdk.generate_etrecord`` takes in an output file path (str), the # edge dialect model (``EdgeProgramManager``), the ExecuTorch dialect model -# (``ExecutorchProgramManager``), and an optional dictionary of additional models +# (``ExecutorchProgramManager``), and an optional dictionary of additional models. # # In this tutorial, an example model (shown below) is used to demonstrate. @@ -113,9 +113,9 @@ def forward(self, x): ###################################################################### # # .. warning:: -# Users should do a deepcopy of the output of to_edge() and pass in the -# deepcopy to the generate_etrecord API. This is needed because the -# subsequent call, to_executorch(), does an in-place mutation and will +# Users should do a deepcopy of the output of ``to_edge()`` and pass in the +# deepcopy to the ``generate_etrecord`` API. This is needed because the +# subsequent call, ``to_executorch()``, does an in-place mutation and will # lose debug data in the process. # @@ -169,21 +169,10 @@ def forward(self, x): f.write(serialized_bundled_program) ###################################################################### -# We provide 2 ways of executing the Bundled Model to generate the ``ETDump``: -# -# **Option 1:** -# -# Use Buck (follow `these instructions <../getting-started-setup.html#building-a-runtime>`__ to set up buck):: -# -# cd executorch -# buck2 run -c executorch.event_tracer_enabled=true examples/sdk/sdk_example_runner:sdk_example_runner -- --bundled_program_path -# -# **Option 2:** -# -# Use CMake (follow `these instructions <../runtime-build-and-cross-compilation.html#configure-the-cmake-build>`__ to set up cmake):: +# Use CMake (follow `these instructions <../runtime-build-and-cross-compilation.html#configure-the-cmake-build>`__ to set up cmake) to execute the Bundled Program to generate the ``ETDump``:: # # cd executorch -# rm -rf cmake-out && mkdir cmake-out && cd cmake-out && cmake -DBUCK2=buck2 -DEXECUTORCH_BUILD_SDK=1 -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=1 .. +# rm -rf cmake-out && mkdir cmake-out && cd cmake-out && cmake -DEXECUTORCH_BUILD_SDK=1 -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=1 .. # cd .. # cmake --build cmake-out -j8 -t sdk_example_runner # ./cmake-out/examples/sdk/sdk_example_runner --bundled_program_path @@ -308,6 +297,6 @@ def forward(self, x): # ^^^^^^^^^^^^^^^ # # - `ExecuTorch SDK <../sdk-overview.html>`__ -# - `ETRecord <../sdk-etrecord>`__ +# - `ETRecord <../sdk-etrecord.html>`__ # - `ETDump <../sdk-etdump.html>`__ # - `Inspector <../sdk-inspector.html>`__ diff --git a/examples/README.md b/examples/README.md index 6865a5c35a..68f4d550df 100644 --- a/examples/README.md +++ b/examples/README.md @@ -9,6 +9,7 @@ ExecuTorch's extensive support spans from simple modules like "Add" to comprehen ## Directory structure ``` examples +├── llm_manual # A storage place for the files that [LLM Maunal](https://pytorch.org/executorch/main/llm/getting-started.html) needs ├── models # Contains a set of popular and representative PyTorch models ├── portable # Contains end-to-end demos for ExecuTorch in portable mode ├── selective_build # Contains demos of selective build for optimizing the binary size of the ExecuTorch runtime @@ -20,7 +21,7 @@ examples | └── mps # Contains end-to-end demos of MPS backend ├── arm # Contains demos of the Arm TOSA and Ethos-U NPU flows ├── qualcomm # Contains demos of Qualcomm QNN backend -├── xtensa # Contains demos of exporting and running a simple model on Xtensa Hifi4 DSP +├── cadence # Contains demos of exporting and running a simple model on Xtensa DSPs ├── third-party # Third-party libraries required for working on the demos └── README.md # This file ``` @@ -30,9 +31,9 @@ examples A user's journey may commence by exploring the demos located in the [`portable/`](./portable) directory. Here, you will gain insights into the fundamental end-to-end workflow to generate a binary file from a ML model in [portable mode](../docs/source/concepts.md##portable-mode-lean-mode) and run it on the ExecuTorch runtime. -## Demo of Llama2 +## Demo of Llama 2 and Llama 3 -[This page](./models/llama2/README.md) demonstrates how to run a Llama 2 7B model on mobile via ExecuTorch. We use XNNPACK to accelerate the performance and 4-bit groupwise PTQ quantization to fit the model on Android and iOS mobile phones. +[This page](./models/llama2/README.md) demonstrates how to run Llama 2 7B and Llama 3 8B models on mobile via ExecuTorch. We use XNNPACK to accelerate the performance and 4-bit groupwise PTQ quantization to fit the model on Android and iOS mobile phones. ## Demo of Selective Build diff --git a/examples/apple/coreml/scripts/inspector_cli.py b/examples/apple/coreml/scripts/inspector_cli.py index 3f8990bdab..077c8c26ef 100644 --- a/examples/apple/coreml/scripts/inspector_cli.py +++ b/examples/apple/coreml/scripts/inspector_cli.py @@ -7,7 +7,7 @@ import argparse import json -from typing import Any, Dict, Final, List, Tuple +from typing import Any, Dict, Final, List, Tuple, Union from executorch.sdk import Inspector from executorch.sdk.inspector._inspector_utils import compare_results @@ -34,6 +34,12 @@ def parse_coreml_delegate_metadata(delegate_metadatas: List[str]) -> Dict[str, A return {} +def convert_coreml_delegate_time( + event_name: Union[str, int], input_time: Union[int, float] +) -> Union[int, float]: + return input_time / (1000 * 1000) + + def main() -> None: parser = argparse.ArgumentParser() parser.add_argument( @@ -60,6 +66,7 @@ def main() -> None: etrecord=args.etrecord_path, debug_buffer_path=args.debug_buffer_path, delegate_metadata_parser=parse_coreml_delegate_metadata, + delegate_time_scale_converter=convert_coreml_delegate_time, ) inspector.print_data_tabular(include_delegate_debug_data=True) if args.compare_results: diff --git a/examples/apple/mps/scripts/build_mps_executor_runner.sh b/examples/apple/mps/scripts/build_mps_executor_runner.sh new file mode 100755 index 0000000000..16754588b6 --- /dev/null +++ b/examples/apple/mps/scripts/build_mps_executor_runner.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# Copyright (c) 2024 Apple Inc. All rights reserved. +# Provided subject to the LICENSE file in the top level directory. + +set -e + +MODE="Release" +OUTPUT="cmake-out" + +usage() { + echo "Usage: $0 [OPTIONS]" + echo "Build frameworks for Apple platforms." + echo "SOURCE_ROOT_DIR defaults to the current directory if not provided." + echo + echo "Options:" + echo " --output=DIR Output directory. Default: 'cmake-out'" + echo " --Debug Use Debug build mode. Default: 'Release'" + echo "Example:" + echo " $0 --output=cmake-out --Debug" + exit 0 +} + +for arg in "$@"; do + case $arg in + -h|--help) usage ;; + --output=*) OUTPUT="${arg#*=}" ;; + --Debug) MODE="Debug" ;; + *) + if [[ -z "$SOURCE_ROOT_DIR" ]]; then + SOURCE_ROOT_DIR="$arg" + else + echo "Invalid argument: $arg" + exit 1 + fi + ;; + esac +done + +rm -rf "$OUTPUT" + +cmake -DBUCK2="$BUCK" \ + -DCMAKE_INSTALL_PREFIX=cmake-out \ + -DCMAKE_BUILD_TYPE="$MODE" \ + -DEXECUTORCH_BUILD_SDK=ON \ + -DEXECUTORCH_ENABLE_EVENT_TRACER=ON \ + -DEXECUTORCH_BUILD_MPS=ON \ + -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" \ + -Bcmake-out . +cmake --build cmake-out -j9 --target install --config "$MODE" +CMAKE_PREFIX_PATH="${PWD}/cmake-out/lib/cmake/ExecuTorch;${PWD}/cmake-out/third-party/gflags" +# build mps_executor_runner +rm -rf cmake-out/examples/apple/mps +cmake \ + -DCMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH" \ + -DCMAKE_BUILD_TYPE="$MODE" \ + -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" \ + -Bcmake-out/examples/apple/mps \ + examples/apple/mps + +cmake --build cmake-out/examples/apple/mps -j9 --config "$MODE" + +echo "Build succeeded!" + +./cmake-out/examples/apple/mps/mps_executor_runner --model_path mps_logical_not.pte --bundled_program diff --git a/examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmake b/examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmake index 506e78d0bc..10ebdf0945 100644 --- a/examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmake +++ b/examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmake @@ -58,7 +58,13 @@ add_compile_definitions( add_link_options( -mcpu=${GCC_CPU} -mthumb - --specs=nosys.specs) +) + +if(SEMIHOSTING) + add_link_options(--specs=rdimon.specs) +else() + add_link_options(--specs=nosys.specs) +endif() # Set floating point unit if(CMAKE_SYSTEM_PROCESSOR MATCHES "\\+fp") diff --git a/examples/arm/executor_runner/CMakeLists.txt b/examples/arm/executor_runner/CMakeLists.txt index 6836f8a79c..98d136face 100644 --- a/examples/arm/executor_runner/CMakeLists.txt +++ b/examples/arm/executor_runner/CMakeLists.txt @@ -12,6 +12,8 @@ if(NOT DEFINED ET_PTE_FILE_PATH) "model is built into the binary.") endif() +option(SEMIHOSTING "Enable semihosting" OFF) + # Example ExecuTorch demo for bare metal Cortex-M based systems set(ET_DIR_PATH "../../.." CACHE PATH "Path to ExecuTorch dir") @@ -105,3 +107,17 @@ target_include_directories(arm_executor_runner PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) add_dependencies(arm_executor_runner gen_model_header) + + +if(SEMIHOSTING) +target_compile_definitions(arm_executor_runner PUBLIC SEMIHOSTING) +endif() + +# Fixup compilation of retarget.c +if(SEMIHOSTING) +# Remove this when MLBEDSW-8910 is closed. +set_source_files_properties( + ${ETHOS_SDK_PATH}/core_platform/targets/corstone-300/retarget.c + PROPERTIES HEADER_FILE_ONLY TRUE +) +endif() \ No newline at end of file diff --git a/examples/arm/setup.sh b/examples/arm/setup.sh index 72cc4c94a5..f626b4caeb 100755 --- a/examples/arm/setup.sh +++ b/examples/arm/setup.sh @@ -215,7 +215,7 @@ function setup_vela() { if [[ ! -e ethos-u-vela ]]; then git clone https://review.mlplatform.org/ml/ethos-u/ethos-u-vela repo_dir="${root_dir}/ethos-u-vela" - base_rev=b90666d9b43f4b5223bb4dcecdbee87b2ad757c2 + base_rev=92240e7979018a197b42aab2da16dc002d86f224 patch_repo fi cd "${root_dir}/ethos-u-vela" diff --git a/examples/demo-apps/apple_ios/ExecuTorchDemo/README.md b/examples/demo-apps/apple_ios/ExecuTorchDemo/README.md index 8c429af74a..2f9102e7c0 100644 --- a/examples/demo-apps/apple_ios/ExecuTorchDemo/README.md +++ b/examples/demo-apps/apple_ios/ExecuTorchDemo/README.md @@ -40,36 +40,45 @@ pip --version ### 3. Getting Started Tutorial -Before proceeding, follow the [Setting Up ExecuTorch](https://pytorch.org/executorch/stable/getting-started-setup) -tutorial to configure the basic environment. Feel free to skip building anything -just yet. Make sure you have all the required dependencies installed, including -the following tools: +Follow the [Setting Up ExecuTorch](https://pytorch.org/executorch/stable/getting-started-setup) +tutorial to configure the basic environment: -- Buck2 (as `/tmp/buck2`) -- Cmake (`cmake` reachable at `$PATH`) -- FlatBuffers Compiler (`flatc` reachable at `$PATH` or as `$FLATC_EXECUTABLE` - enironment variable) +```bash +git clone -b release/0.2 https://github.com/pytorch/executorch.git +cd executorch +git submodule update --init + +python3 -m venv .venv && source .venv/bin/activate + +./install_requirements.sh --pybind coreml mps xnnpack +``` ### 4. Backend Dependencies -Also, follow the corresponding sections from [Core ML](build-run-coreml.md) and -[MPS](build-run-mps.md) tutorials to install additional dependencies for those -backends. Feel free to skip building anything just yet. +Also, follow the corresponding sections from [Core ML](https://pytorch.org/executorch/stable/build-run-coreml) and +[MPS](https://pytorch.org/executorch/stable/build-run-mps) tutorials to install additional dependencies for those +backends: + +```bash +./backends/apple/coreml/scripts/install_requirements.sh + +./backends/apple/mps/install_requirements.sh +``` ## Models and Labels -Now let's move on to exporting and bundling the MobileNet v3 model. +Now, let's move on to exporting and bundling the MobileNet v3 model. ### 1. Export Model -Export the MobileNet v3 model with Core ML, MPS and XNNPACK delegates, and move +Export the MobileNet v3 model with Core ML, MPS and XNNPACK backends, and move the exported model to a specific location where the Demo App will pick them up: ```bash python3 -m examples.portable.scripts.export --model_name="mv3" -python3 -m examples.xnnpack.aot_compiler --delegate --model_name="mv3" python3 -m examples.apple.coreml.scripts.export --model_name="mv3" python3 -m examples.apple.mps.scripts.mps_example --model_name="mv3" +python3 -m examples.xnnpack.aot_compiler --delegate --model_name="mv3" mkdir -p examples/demo-apps/apple_ios/ExecuTorchDemo/ExecuTorchDemo/Resources/Models/MobileNet/ mv mv3*.pte examples/demo-apps/apple_ios/ExecuTorchDemo/ExecuTorchDemo/Resources/Models/MobileNet/ @@ -84,27 +93,6 @@ curl https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt \ -o examples/demo-apps/apple_ios/ExecuTorchDemo/ExecuTorchDemo/Resources/Models/MobileNet/imagenet_classes.txt ``` -## Build Runtime and Backends - -Next, we will build the necessary -[frameworks](https://developer.apple.com/documentation/xcode/creating-a-multi-platform-binary-framework-bundle) -for ExecuTorch and move them over for app linking. - -### 1. Build Frameworks - -```bash -./build/build_apple_frameworks.sh --Release --coreml --mps --xnnpack -``` - -### 2. Move Frameworks for App Linking - -Make sure to have all the `.xcframework` bundles generated at the previous step -at a specific location where the Demo App will pick them up: - -```bash -mv cmake-out examples/demo-apps/apple_ios/ExecuTorchDemo/ExecuTorchDemo/Frameworks -``` - ## Final Steps We're almost done! Now, we just need to open the project in Xcode, run the diff --git a/examples/demo-apps/apple_ios/LLaMA/README.md b/examples/demo-apps/apple_ios/LLaMA/README.md index 04a6eaef67..ddd542a006 100644 --- a/examples/demo-apps/apple_ios/LLaMA/README.md +++ b/examples/demo-apps/apple_ios/LLaMA/README.md @@ -2,12 +2,20 @@ This app demonstrates the use of the LLaMA chat app demonstrating local inference use case with ExecuTorch. -iOS LLaMA App
- ## Prerequisites -* [Xcode 15](https://developer.apple.com/xcode). -* [iOS 17 SDK](https://developer.apple.com/ios). -* Set up your ExecuTorch repo and environment if you haven’t done so by following the [Setting up ExecuTorch](https://pytorch.org/executorch/stable/getting-started-setup) to set up the repo and dev environment. +* [Xcode 15](https://developer.apple.com/xcode) +* [iOS 17 SDK](https://developer.apple.com/ios) +* Set up your ExecuTorch repo and environment if you haven’t done so by following the [Setting up ExecuTorch](https://pytorch.org/executorch/stable/getting-started-setup) to set up the repo and dev environment: + +```bash +git clone -b release/0.2 https://github.com/pytorch/executorch.git +cd executorch +git submodule update --init + +python3 -m venv .venv && source .venv/bin/activate + +./install_requirements.sh +``` ## Exporting models Please refer to the [ExecuTorch Llama2 docs](https://github.com/pytorch/executorch/blob/main/examples/models/llama2/README.md) to export the model. @@ -16,10 +24,11 @@ Please refer to the [ExecuTorch Llama2 docs](https://github.com/pytorch/executor 1. Open the [project](https://github.com/pytorch/executorch/blob/main/examples/demo-apps/apple_ios/LLaMA/LLaMA.xcodeproj) in Xcode. 2. Run the app (cmd+R). -3. In app UI pick a model and tokenizer to use, type a prompt and tap the arrow buton as on the [video](../_static/img/llama_ios_app.mp4). +3. In app UI pick a model and tokenizer to use, type a prompt and tap the arrow buton ```{note} -ExecuTorch runtime is distributed as a Swift package providing some .xcframework as prebuilt binary targets. Xcode will dowload and cache the package on the first run, which will take some time. +ExecuTorch runtime is distributed as a Swift package providing some .xcframework as prebuilt binary targets. +Xcode will dowload and cache the package on the first run, which will take some time. ``` ## Copy the model to Simulator @@ -33,5 +42,11 @@ ExecuTorch runtime is distributed as a Swift package providing some .xcframework 2. Navigate to the Files tab and drag&drop the model and tokenizer files onto the iLLaMA folder. 3. Wait until the files are copied. +Click the image below to see it in action! + + + iOS app running a LlaMA model + + ## Reporting Issues If you encountered any bugs or issues following this tutorial please file a bug/issue here on [Github](https://github.com/pytorch/executorch/issues/new). diff --git a/examples/llm_manual/CMakeLists.txt b/examples/llm_manual/CMakeLists.txt new file mode 100644 index 0000000000..c605e94740 --- /dev/null +++ b/examples/llm_manual/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +cmake_minimum_required(VERSION 3.19) +project(nanogpt_runner) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Set options for executorch build. +option(EXECUTORCH_BUILD_EXTENSION_DATA_LOADER "" ON) +option(EXECUTORCH_BUILD_EXTENSION_MODULE "" ON) +option(EXECUTORCH_BUILD_OPTIMIZED "" ON) +option(EXECUTORCH_BUILD_XNNPACK "" ON) # Build with Xnnpack backend + +# Include the executorch subdirectory. +add_subdirectory( + ${CMAKE_CURRENT_SOURCE_DIR}/third-party/executorch + ${CMAKE_BINARY_DIR}/executorch) + +# include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) + +add_executable(nanogpt_runner main.cpp) +target_link_libraries( + nanogpt_runner + PRIVATE + executorch + extension_module_static # Provides the Module class + optimized_native_cpu_ops_lib # Provides baseline cross-platform kernels + xnnpack_backend) # Provides the XNNPACK CPU acceleration backend diff --git a/examples/llm_manual/README.md b/examples/llm_manual/README.md new file mode 100644 index 0000000000..0ee6bb6a9f --- /dev/null +++ b/examples/llm_manual/README.md @@ -0,0 +1,3 @@ +# LLM Manual + +This repository is a storage place for the files that [LLM Maunal](https://pytorch.org/executorch/main/llm/getting-started.html) needs. Please refer to the documentation website for more information. diff --git a/examples/llm_manual/basic_sampler.h b/examples/llm_manual/basic_sampler.h new file mode 100644 index 0000000000..a95b823de8 --- /dev/null +++ b/examples/llm_manual/basic_sampler.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +class BasicSampler { + public: + BasicSampler() {} + int64_t sample(std::vector logits) { + // Find the token with the highest log probability. + int64_t max_index = + std::max_element(logits.begin(), logits.end()) - logits.begin(); + return max_index; + } +}; diff --git a/examples/llm_manual/basic_tokenizer.h b/examples/llm_manual/basic_tokenizer.h new file mode 100644 index 0000000000..eb51d15fc5 --- /dev/null +++ b/examples/llm_manual/basic_tokenizer.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include +#include +#include +#include + +class BasicTokenizer { + public: + BasicTokenizer(const std::string& filePath) { + std::ifstream file(filePath); + + if (!file) { + std::cerr << "Unable to open file"; + exit(9); // return with error code + } + std::string str( + (std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + + size_t i = 0u; + i = consume_whitespace(str, i); + i = expect(str, i, '{'); + + while (i < str.size() && str[i] != '}') { + i = consume_field(str, i); + } + + // Build decode map as inverse of encode. + for (auto& i : encode_) { + decode_[i.second] = i.first; + } + } + + std::vector encode(const std::string& prompt) { + std::vector words = parse_prompt(prompt); + std::vector result; + for (auto word : words) { + result.push_back(encode_[word]); + } + return result; + } + + std::string decode(const std::vector& indices) { + std::string result; + for (const auto& index : indices) { + result += decode_[index]; + } + return result; + } + + private: + std::unordered_map encode_; + std::unordered_map decode_; + + // Advance the input string index until a non-whitespace character is found + // or it reaches the end of string. + size_t consume_whitespace(const std::string& data, size_t i) { + while (i < data.size() && std::isspace(data[i])) { + i++; + } + + return i; + } + + // Consumes an JSON field of the form + // "str": id, + size_t consume_field(const std::string& data, size_t i) { + i = consume_whitespace(data, i); + + // Parse the key literal. + i = expect(data, i, '"'); + + auto in_escape = false; + std::string key = ""; + while (i < data.size()) { + if (in_escape) { + key += data[i]; + i++; + in_escape = false; + } else { // !in_escape + if (data[i] == '"') { // End of string literal + i++; + break; + } else if (data[i] == '\\') { // Escaped code point + in_escape = true; + } + key += data[i]; + i++; + } + } + + key = post_process_key(key); + + i = expect(data, i, ':'); + i = consume_whitespace(data, i); + + // Read unsigned integer value + auto value_start = i; + while (i < data.size() && std::isdigit(data[i])) { + i++; + } + auto value = static_cast( + std::stol(data.substr(value_start, i - value_start))); + + encode_[key] = value; + + i = consume_whitespace(data, i); + if (i < data.size() && data[i] == ',') { + i++; + } + + return i; + } + + // Assert that the next character in the input string is equal to c. Increment + // the input string index by one. + size_t expect(const std::string& data, size_t i, char c) { + if (i >= data.size() || data[i] != c) { + std::cerr << "Invalid tokenizer vocabulary file. Expected '" << c + << "' at index " << i << std::endl; + exit(1); + } + + return i + 1; + } + + std::string post_process_key(std::string key) { + // Replace the unicode characters with the corresponding byte encoding + // TODO: adopt byte encoder to handle unicode characters in json file. + + std::unordered_map replacements = { + {"\\u0120", " "}, + {"\\u010a", "\n"}, + }; + + for (const auto& replacement : replacements) { + size_t pos = 0; + // While loop through all instances of the substring in the string + while ((pos = key.find(replacement.first, pos)) != std::string::npos) { + key.replace(pos, replacement.first.length(), replacement.second); + pos += replacement.second.length(); + } + } + + // remove duplicate backslashes + for (size_t idx = 0; idx < key.length(); idx++) { + if (key[idx] == '\\') { + key.erase(idx, 1); + if (key[idx] == '\\') { + // If there are two backslashes, keep the second one + idx += 1; + } + } + } + + return key; + } + std::vector parse_prompt(const std::string& prompt) { + std::vector result; + std::string word; + for (char c : prompt) { + if (c == ' ') { + if (!word.empty()) { + result.push_back(word); + word.clear(); + } + word += c; + } else if (ispunct(c)) { + if (!word.empty()) { + result.push_back(word); + word.clear(); + } + result.push_back(std::string(1, c)); + } else { + word += c; + } + } + if (!word.empty()) { + result.push_back(word); + } + return result; + } +}; diff --git a/examples/llm_manual/export_nanogpt.py b/examples/llm_manual/export_nanogpt.py new file mode 100644 index 0000000000..cf29a69c08 --- /dev/null +++ b/examples/llm_manual/export_nanogpt.py @@ -0,0 +1,45 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# export_nanogpt.py + +# Load partitioner for Xnnpack backend +import torch +from executorch.backends.xnnpack.partition.xnnpack_partitioner import XnnpackPartitioner + +# Model to be delegated to specific backend should use specific edge compile config +from executorch.backends.xnnpack.utils.configs import get_xnnpack_edge_compile_config +from executorch.exir import to_edge + +from model import GPT +from torch._export import capture_pre_autograd_graph +from torch.export import export +from torch.nn.attention import sdpa_kernel, SDPBackend + +model = GPT.from_pretrained("gpt2") # use gpt2 weight as pretrained weight +example_inputs = ( + torch.randint(0, 100, (1, model.config.block_size), dtype=torch.long), +) +dynamic_shape = ({1: torch.export.Dim("token_dim", max=model.config.block_size)},) + +# Trace the model, converting it to a portable intermediate representation. +# The torch.no_grad() call tells PyTorch to exclude training-specific logic. +with sdpa_kernel([SDPBackend.MATH]), torch.no_grad(): + m = capture_pre_autograd_graph(model, example_inputs, dynamic_shapes=dynamic_shape) + traced_model = export(m, example_inputs, dynamic_shapes=dynamic_shape) + +# Convert the model into a runnable ExecuTorch program. +# To be further lowered to Xnnpack backend, `traced_model` needs xnnpack-specific edge compile config +edge_config = get_xnnpack_edge_compile_config() +edge_manager = to_edge(traced_model, compile_config=edge_config) + +# Delegate exported model to Xnnpack backend by invoking `to_backend` function with Xnnpack partitioner. +edge_manager = edge_manager.to_backend(XnnpackPartitioner()) +et_program = edge_manager.to_executorch() + +# Save the Xnnpack-delegated ExecuTorch program to a file. +with open("nanogpt.pte", "wb") as file: + file.write(et_program.buffer) diff --git a/examples/llm_manual/main.cpp b/examples/llm_manual/main.cpp new file mode 100644 index 0000000000..2b336059cf --- /dev/null +++ b/examples/llm_manual/main.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +// main.cpp + +#include +#include +#include +#include + +#include "basic_sampler.h" +#include "basic_tokenizer.h" +#include "managed_tensor.h" + +#include +#include +#include +#include +#include + +using namespace torch::executor; + +using SizesType = exec_aten::SizesType; +using DimOrderType = exec_aten::DimOrderType; +using StridesType = exec_aten::StridesType; + +// main.cpp + +#define ENDOFTEXT 50256 + +std::string generate( + Module& llm_model, + std::string& prompt, + BasicTokenizer& tokenizer, + BasicSampler& sampler, + size_t max_input_length, + size_t max_output_length) { + // Convert the input text into a list of integers (tokens) that represents + // it, using the string-to-token mapping that the model was trained on. + // Each token is an integer that represents a word or part of a word. + std::vector input_tokens = tokenizer.encode(prompt); + std::vector output_tokens; + + for (auto i = 0u; i < max_output_length; i++) { + // Convert the input_tokens from a vector of int64_t to EValue. + // EValue is a unified data type in the ExecuTorch runtime. + ManagedTensor tensor_tokens( + input_tokens.data(), + {1, static_cast(input_tokens.size())}, + ScalarType::Long); + std::vector inputs = {tensor_tokens.get_tensor()}; + + // Run the model. It will return a tensor of logits (log-probabilities). + Result> logits_evalue = llm_model.forward(inputs); + + // Convert the output logits from EValue to std::vector, which is what + // the sampler expects. + Tensor logits_tensor = logits_evalue.get()[0].toTensor(); + std::vector logits( + logits_tensor.data_ptr(), + logits_tensor.data_ptr() + logits_tensor.numel()); + + // Sample the next token from the logits. + int64_t next_token = sampler.sample(logits); + + // Break if we reached the end of the text. + if (next_token == ENDOFTEXT) { + break; + } + + // Add the next token to the output. + output_tokens.push_back(next_token); + + std::cout << tokenizer.decode({next_token}); + std::cout.flush(); + + // Update next input. + input_tokens.push_back(next_token); + if (input_tokens.size() > max_input_length) { + input_tokens.erase(input_tokens.begin()); + } + } + + std::cout << std::endl; + + // Convert the output tokens into a human-readable string. + std::string output_string = tokenizer.decode(output_tokens); + return output_string; +} + +// main.cpp + +int main() { + // Set up the prompt. This provides the seed text for the model to elaborate. + std::cout << "Prompt: "; + std::string prompt; + std::getline(std::cin, prompt); + + // The tokenizer is used to convert between tokens (used by the model) and + // human-readable strings. + BasicTokenizer tokenizer("vocab.json"); + + // The sampler is used to sample the next token from the logits. + BasicSampler sampler = BasicSampler(); + + // Load the exported nanoGPT program, which was generated via the previous + // steps. + Module model( + "nanogpt.pte", + torch::executor::Module::MlockConfig::UseMlockIgnoreErrors); + + const auto max_input_tokens = 1024; + const auto max_output_tokens = 30; + std::cout << prompt; + generate( + model, prompt, tokenizer, sampler, max_input_tokens, max_output_tokens); +} diff --git a/examples/llm_manual/managed_tensor.h b/examples/llm_manual/managed_tensor.h new file mode 100644 index 0000000000..d401ae4d18 --- /dev/null +++ b/examples/llm_manual/managed_tensor.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include +#include + +#include + +#pragma once + +namespace torch { +namespace executor { + +/** + * A tensor wrapper takes ownership of all the memory of the necessary metadata + * for torch::executor::Tensor. Note that it doesn't own the data memory. + */ +class ManagedTensor { + public: + /// The type used for elements of `sizes()`. + using SizesType = exec_aten::SizesType; + /// The type used for elements of `dim_order()`. + using DimOrderType = exec_aten::DimOrderType; + /// The type used for elements of `strides()`. + using StridesType = exec_aten::StridesType; + ManagedTensor() = delete; + + explicit ManagedTensor( + void* data, + const std::vector& sizes, + ScalarType dtype) + : dtype_(dtype), sizes_(sizes), data_ptr_(data) { + ssize_t dim = sizes.size(); + dim_order_.resize(dim); + strides_.resize(dim); + for (size_t i = 0; i < dim; ++i) { + dim_order_[i] = i; + } + dim_order_to_stride_nocheck( + sizes.data(), dim_order_.data(), dim, strides_.data()); + tensor_impl_ = std::make_unique( + dtype_, + dim, + sizes_.data(), + data_ptr_, + dim_order_.data(), + strides_.data(), + TensorShapeDynamism::DYNAMIC_BOUND); + } + + /** + * Get the Tensor object managed by this class. + */ + Tensor get_tensor() { + return Tensor(tensor_impl_.get()); + } + + private: + void* data_ptr_ = nullptr; + std::unique_ptr tensor_impl_; + std::vector sizes_; + std::vector strides_; + std::vector dim_order_; + ScalarType dtype_; +}; +} // namespace executor +} // namespace torch diff --git a/examples/models/llama2/README.md b/examples/models/llama2/README.md index b996e89cce..f3c3951b4c 100644 --- a/examples/models/llama2/README.md +++ b/examples/models/llama2/README.md @@ -33,21 +33,22 @@ We evaluated WikiText perplexity using [LM Eval](https://github.com/EleutherAI/l Note that groupsize less than 128 was not enabled, since such model were still too large. This is because our current efforts have focused on enabling FP32 and support for FP16 is under way. What this implies for model size is that 1) embedding table is in FP32 and 2) quantized weights scales are FP32. +## Enablement + +We have verified running Llama 2 7B [mobile applications](#step-6-build-mobile-apps) efficiently on select devices including the iPhone 15 Pro, iPhone 15 Pro Max, Samsung Galaxy S22 and S24, and OnePlus 12. + +For Llama 3 8B, we have verified so far on iPhone 15 Pro Max and OnePlus 12 (with 16GB RAM). + ## Performance -Performance was measured on Samsung Galaxy S22, S24, One Plus 12 and iPhone 15 max Pro. Measurement performance is in terms of tokens/second. +Llama2 7B performance was measured on the Samsung Galaxy S22, S24, and OnePlus 12 devices. The performance measurement is expressed in terms of tokens per second using an [adb binary-based approach](#step-5-run-benchmark-on). |Device | Groupwise 4-bit (128) | Groupwise 4-bit (256) |--------| ---------------------- | --------------- -|Galaxy S22* | 8.15 tokens/second | 8.3 tokens/second | -|Galaxy S24* | 10.66 tokens/second | 11.26 tokens/second | -|One plus 12* | 11.55 tokens/second | 11.6 tokens/second | -|Galaxy S22** | 5.5 tokens/second | 5.9 tokens/second | -|iPhone 15 pro** | ~6 tokens/second | ~6 tokens/second | +|Galaxy S22 | 8.15 tokens/second | 8.3 tokens/second | +|Galaxy S24 | 10.66 tokens/second | 11.26 tokens/second | +|OnePlus 12 | 11.55 tokens/second | 11.6 tokens/second | -*: Measured via adb binary based [workflow](#step-5-run-benchmark-on) - -**: Measured via app based [workflow](#step-6-build-mobile-apps) # Instructions @@ -111,9 +112,11 @@ You can export and run the original Llama3 8B model. 2. Export model and generate `.pte` file ``` - python -m examples.models.llama2.export_llama --checkpoint -p -d=fp32 -X -qmode 8da4w -kv --use_sdpa_with_kv_cache --output_name="llama3_kv_sdpa_xnn_qe_4_32.pte" group_size 128 --metadata '{"get_bos_id":128000, "get_eos_id":128001}' --embedding-quantize 4,32 + python -m examples.models.llama2.export_llama --checkpoint -p -kv --use_sdpa_with_kv_cache -X -qmode 8da4w --group_size 128 -d fp32 --metadata '{"get_bos_id":128000, "get_eos_id":128001}' --embedding-quantize 4,32 --output_name="llama3_kv_sdpa_xnn_qe_4_32.pte" ``` + Due to the larger vocabulary size of Llama3, we recommend quantizing the embeddings with `--embedding-quantize 4,32` to further reduce the model size. + ## (Optional) Finetuning If you want to finetune your model based on a specific dataset, PyTorch provides [TorchTune](https://github.com/pytorch/torchtune) - a native-Pytorch library for easily authoring, fine-tuning and experimenting with LLMs. diff --git a/examples/models/llama2/custom_ops/TARGETS b/examples/models/llama2/custom_ops/TARGETS index 199cbe363d..195df3bb93 100644 --- a/examples/models/llama2/custom_ops/TARGETS +++ b/examples/models/llama2/custom_ops/TARGETS @@ -15,6 +15,7 @@ runtime.python_test( ], preload_deps = [ ":custom_ops_aot_lib", + ":custom_ops_aot_py", ], deps = [ "//caffe2:torch", diff --git a/examples/models/llama2/custom_ops/test_sdpa_with_kv_cache.py b/examples/models/llama2/custom_ops/test_sdpa_with_kv_cache.py index 949fdeab2c..abf3abc028 100644 --- a/examples/models/llama2/custom_ops/test_sdpa_with_kv_cache.py +++ b/examples/models/llama2/custom_ops/test_sdpa_with_kv_cache.py @@ -9,6 +9,8 @@ import torch import torch.nn.functional as F +from .sdpa_with_kv_cache import custom_ops_lib # noqa + class SDPATest(unittest.TestCase): diff --git a/examples/models/llama2/lib/partitioner_lib.py b/examples/models/llama2/lib/partitioner_lib.py index f7f8d34947..1638a35757 100644 --- a/examples/models/llama2/lib/partitioner_lib.py +++ b/examples/models/llama2/lib/partitioner_lib.py @@ -108,7 +108,7 @@ def get_qnn_partitioner(args, quant_dtype): ) except ImportError: raise ImportError( - "Please install the Qualcomm backend follwing https://pytorch.org/executorch/main/build-run-qualcomm.html" + "Please install the Qualcomm backend follwing https://pytorch.org/executorch/main/build-run-qualcomm-ai-engine-direct-backend.html" ) use_fp16 = True diff --git a/examples/portable/scripts/test_demo_backend_delegation.sh b/examples/portable/scripts/test_demo_backend_delegation.sh index 7044026d60..d1ecf9150f 100644 --- a/examples/portable/scripts/test_demo_backend_delegation.sh +++ b/examples/portable/scripts/test_demo_backend_delegation.sh @@ -22,8 +22,7 @@ build_cmake_executor_runner() { (rm -rf ${CMAKE_OUTPUT_DIR} \ && mkdir ${CMAKE_OUTPUT_DIR} \ && cd ${CMAKE_OUTPUT_DIR} \ - && retry cmake -DBUCK2=buck2 \ - -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" ..) + && retry cmake -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" ..) cmake --build ${CMAKE_OUTPUT_DIR} -j4 } diff --git a/examples/sdk/CMakeLists.txt b/examples/sdk/CMakeLists.txt index d7ca7679e3..ec65bef8f5 100644 --- a/examples/sdk/CMakeLists.txt +++ b/examples/sdk/CMakeLists.txt @@ -38,6 +38,9 @@ set(_common_include_directories ${EXECUTORCH_ROOT}/..) # Find prebuilt libraries. executorch package should contain # portable_ops_lib, etdump, bundled_program. find_package(executorch CONFIG REQUIRED) +target_link_options_shared_lib(executorch) +target_link_options_shared_lib(portable_ops_lib) + target_include_directories(executorch INTERFACE ${_common_include_directories}) find_package( @@ -48,18 +51,6 @@ add_executable(sdk_example_runner sdk_example_runner/sdk_example_runner.cpp) target_compile_options(executorch INTERFACE -DET_EVENT_TRACER_ENABLED) -# portable_ops_lib -gen_selected_ops("" "" "ON") -# Expect gen_selected_ops output file to be selected_operators.yaml -generate_bindings_for_kernels( - FUNCTIONS_YAML ${EXECUTORCH_ROOT}/kernels/portable/functions.yaml -) -gen_operators_lib( - "portable_ops_lib" - KERNEL_LIBS portable_kernels - DEPS executorch) - -target_compile_options(portable_ops_lib INTERFACE -DET_EVENT_TRACER_ENABLED) target_include_directories( etdump INTERFACE @@ -72,6 +63,8 @@ target_link_libraries( gflags etdump extension_data_loader - flatcc bundled_program - portable_ops_lib) + flatccrt + portable_ops_lib + portable_kernels +) diff --git a/examples/sdk/README.md b/examples/sdk/README.md index 8d42b6f603..735776be87 100644 --- a/examples/sdk/README.md +++ b/examples/sdk/README.md @@ -56,7 +56,7 @@ Running the program will generate an `ETDump` file (`.etdp`) at the location spe ```bash cd executorch - rm -rf cmake-out && mkdir cmake-out && cd cmake-out && cmake -DBUCK2=buck2 -DEXECUTORCH_BUILD_SDK=1 -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=1 .. + rm -rf cmake-out && mkdir cmake-out && cd cmake-out && cmake -DEXECUTORCH_BUILD_SDK=1 -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=1 .. cd .. cmake --build cmake-out -j8 -t sdk_example_runner ./cmake-out/examples/sdk/sdk_example_runner --bundled_program_path mv2_bundled.bpte --etdump_path mv2_etdump.etdp diff --git a/examples/sdk/test_sdk_example_runner.sh b/examples/sdk/test_sdk_example_runner.sh index 2f1044f42c..5185def655 100644 --- a/examples/sdk/test_sdk_example_runner.sh +++ b/examples/sdk/test_sdk_example_runner.sh @@ -18,8 +18,7 @@ cmake_install_executorch_sdk_lib() { echo "Installing libexecutorch.a, libportable_kernels.a, libetdump.a, libbundled_program.a" rm -rf cmake-out - retry cmake -DBUCK2="$BUCK" \ - -DCMAKE_INSTALL_PREFIX=cmake-out \ + retry cmake -DCMAKE_INSTALL_PREFIX=cmake-out \ -DCMAKE_BUILD_TYPE=Release \ -DEXECUTORCH_BUILD_SDK=ON \ -DEXECUTORCH_ENABLE_EVENT_TRACER=ON \ diff --git a/kernels/portable/cpu/op_isinf.cpp b/kernels/portable/cpu/op_isinf.cpp index da8599d5fa..068f402c07 100644 --- a/kernels/portable/cpu/op_isinf.cpp +++ b/kernels/portable/cpu/op_isinf.cpp @@ -15,7 +15,10 @@ namespace executor { namespace native { Tensor& isinf_out(RuntimeContext& ctx, const Tensor& in, Tensor& out) { - return internal::unary_ufunc_realhb_to_bool(std::isinf, ctx, in, out); + // Lambda is syntactic sugar needed to workaround compilation on some older + // non-compatible distros where isnan is returning int rather than bool + return internal::unary_ufunc_realhb_to_bool( + [](double x) -> bool { return std::isinf(x); }, ctx, in, out); } } // namespace native diff --git a/kernels/portable/cpu/op_isnan.cpp b/kernels/portable/cpu/op_isnan.cpp index 2a82b127d3..09fb4f5f8a 100644 --- a/kernels/portable/cpu/op_isnan.cpp +++ b/kernels/portable/cpu/op_isnan.cpp @@ -15,7 +15,10 @@ namespace executor { namespace native { Tensor& isnan_out(RuntimeContext& ctx, const Tensor& in, Tensor& out) { - return internal::unary_ufunc_realhb_to_bool(std::isnan, ctx, in, out); + // Lambda is syntactic sugar needed to workaround compilation on some older + // non-compatible distros where isnan is returning int rather than bool + return internal::unary_ufunc_realhb_to_bool( + [](double x) -> bool { return std::isnan(x); }, ctx, in, out); } } // namespace native diff --git a/runtime/core/memory_allocator.h b/runtime/core/memory_allocator.h index ec9315a5c2..8613b9ac64 100644 --- a/runtime/core/memory_allocator.h +++ b/runtime/core/memory_allocator.h @@ -63,7 +63,7 @@ class MemoryAllocator { /** * Allocates `size` bytes of memory. * - * @param[in] size Number of memory chunks to allocate. + * @param[in] size Number of bytes to allocate. * @param[in] alignment Minimum alignment for the returned pointer. Must be a * power of 2. * diff --git a/runtime/core/portable_type/targets.bzl b/runtime/core/portable_type/targets.bzl index d4d79b121c..fc03c36c57 100644 --- a/runtime/core/portable_type/targets.bzl +++ b/runtime/core/portable_type/targets.bzl @@ -49,6 +49,7 @@ def define_common_targets(): "bits_types.h", ], visibility = [ + "//executorch/extension/...", "//executorch/runtime/core/exec_aten/util/...", "//executorch/kernels/...", ], diff --git a/runtime/executor/method.cpp b/runtime/executor/method.cpp index 0a3a64a13c..59a00ebd3a 100644 --- a/runtime/executor/method.cpp +++ b/runtime/executor/method.cpp @@ -1013,11 +1013,14 @@ Error Method::execute_instruction() { EXECUTORCH_SCOPE_PROF("OPERATOR_CALL"); internal::EventTracerProfileScope event_tracer_scope = internal::EventTracerProfileScope(event_tracer_, "OPERATOR_CALL"); - // TODO(T147221312): Also expose the temp allocator and tensor resizer - // via the context. - KernelRuntimeContext context(event_tracer_); + // TODO(T147221312): Also expose tensor resizer via the context. + // The temp_allocator passed can be null, but calling allocate_temp will + // fail + KernelRuntimeContext context( + event_tracer_, memory_manager_->temp_allocator()); auto args = chain.argument_lists_[step_state_.instr_idx]; chain.kernels_[step_state_.instr_idx](context, args.data()); + // We reset the temp_allocator after the switch statement err = context.failure_state(); if (err != Error::Ok) { // We know that instr_args_as_KernelCall is non-null because it was diff --git a/runtime/kernel/kernel_runtime_context.h b/runtime/kernel/kernel_runtime_context.h index 8b51ca5ed0..7317315b52 100644 --- a/runtime/kernel/kernel_runtime_context.h +++ b/runtime/kernel/kernel_runtime_context.h @@ -10,6 +10,8 @@ #include #include +#include +#include #include namespace torch { @@ -24,10 +26,21 @@ namespace executor { class KernelRuntimeContext { public: /** - * Construct a new kernel runtime context along with an optional event tracer. + * Construct a new kernel runtime context. + * + * KernelRuntimeContext does not take ownership + * of these pointers, so they must outlive the context instance. + * + * @param[in] event_tracer The optional EventTracer to use for + * profiling/debugging + * @param[in] temp_allocator The optional MemoryAllocator used to allocate + * temporary memory for the kernel. If not provided, an error will be + * returned when calling allocate_temp. */ - KernelRuntimeContext(EventTracer* event_tracer = nullptr) - : event_tracer_(event_tracer) {} + KernelRuntimeContext( + EventTracer* event_tracer = nullptr, + MemoryAllocator* temp_allocator = nullptr) + : event_tracer_(event_tracer), temp_allocator_(temp_allocator) {} /** * Tells the runtime that the kernel call has failed. Prefer this over * ET_CHECK_*(), which fatally panics the process/system. @@ -60,12 +73,37 @@ class KernelRuntimeContext { return event_tracer_; } - // TODO(T147221312): Add a way to allocate temporary memory. + /** + * Allocates temporary memory that will be freed when the kernel returns. This + * returns a pointer to the allocated memory or an error if the allocation + * fails. + * + * @param[in] size Number of bytes to allocate. + * @param[in] alignment Minimum alignment for the returned pointer. Must be a + * power of 2. + * + * @returns A result object containing either a pointer to the allocated + * memory or an error to indicate failure + */ + Result allocate_temp( + size_t size, + size_t alignment = MemoryAllocator::kDefaultAlignment) { + ET_CHECK_OR_RETURN_ERROR( + temp_allocator_ != nullptr, NotFound, "No temp allocator provided"); + void* temp_memory = temp_allocator_->allocate(size, alignment); + ET_CHECK_OR_RETURN_ERROR( + temp_memory != nullptr, + MemoryAllocationFailed, + "Failed to allocate temp memory. Bytes requested: %zu", + size); + return temp_memory; + } // TODO(T147221312): Add a way to resize a tensor. private: EventTracer* event_tracer_ = nullptr; + MemoryAllocator* temp_allocator_ = nullptr; Error failure_state_ = Error::Ok; }; diff --git a/runtime/kernel/targets.bzl b/runtime/kernel/targets.bzl index a8f9eb5052..0bf45321dc 100644 --- a/runtime/kernel/targets.bzl +++ b/runtime/kernel/targets.bzl @@ -55,6 +55,7 @@ def define_common_targets(): exported_deps = [ "//executorch/runtime/core:core", "//executorch/runtime/platform:platform", + "//executorch/runtime/core:memory_allocator", "//executorch/runtime/core:event_tracer" + aten_suffix, # TODO(T147221312): This will eventually depend on exec_aten # once KernelRuntimeContext support tensor resizing, which is diff --git a/runtime/kernel/test/kernel_runtime_context_test.cpp b/runtime/kernel/test/kernel_runtime_context_test.cpp index 7147dc2a16..15709d52bf 100644 --- a/runtime/kernel/test/kernel_runtime_context_test.cpp +++ b/runtime/kernel/test/kernel_runtime_context_test.cpp @@ -15,6 +15,8 @@ using namespace ::testing; using torch::executor::Error; using torch::executor::KernelRuntimeContext; +using torch::executor::MemoryAllocator; +using torch::executor::Result; class KernelRuntimeContextTest : public ::testing::Test { public: @@ -23,6 +25,17 @@ class KernelRuntimeContextTest : public ::testing::Test { } }; +class TestMemoryAllocator : public MemoryAllocator { + public: + TestMemoryAllocator(uint32_t size, uint8_t* base_address) + : MemoryAllocator(size, base_address), last_seen_alignment(0) {} + void* allocate(size_t size, size_t alignment) override { + last_seen_alignment = alignment; + return MemoryAllocator::allocate(size, alignment); + } + size_t last_seen_alignment; +}; + TEST_F(KernelRuntimeContextTest, FailureStateDefaultsToOk) { KernelRuntimeContext context; @@ -47,3 +60,43 @@ TEST_F(KernelRuntimeContextTest, FailureStateReflectsFailure) { context.fail(Error::Ok); EXPECT_EQ(context.failure_state(), Error::Ok); } + +TEST_F(KernelRuntimeContextTest, FailureNoMemoryAllocatorProvided) { + KernelRuntimeContext context; + Result allocated_memory = context.allocate_temp(4); + EXPECT_EQ(allocated_memory.error(), Error::NotFound); +} + +TEST_F(KernelRuntimeContextTest, SuccessfulMemoryAllocation) { + constexpr size_t temp_memory_allocator_pool_size = 4; + auto temp_memory_allocator_pool = + std::make_unique(temp_memory_allocator_pool_size); + MemoryAllocator temp_allocator( + temp_memory_allocator_pool_size, temp_memory_allocator_pool.get()); + KernelRuntimeContext context(nullptr, &temp_allocator); + Result allocated_memory = context.allocate_temp(4); + EXPECT_EQ(allocated_memory.ok(), true); +} + +TEST_F(KernelRuntimeContextTest, FailureMemoryAllocationInsufficientSpace) { + constexpr size_t temp_memory_allocator_pool_size = 4; + auto temp_memory_allocator_pool = + std::make_unique(temp_memory_allocator_pool_size); + MemoryAllocator temp_allocator( + temp_memory_allocator_pool_size, temp_memory_allocator_pool.get()); + KernelRuntimeContext context(nullptr, &temp_allocator); + Result allocated_memory = context.allocate_temp(8); + EXPECT_EQ(allocated_memory.error(), Error::MemoryAllocationFailed); +} + +TEST_F(KernelRuntimeContextTest, MemoryAllocatorAlignmentPassed) { + constexpr size_t temp_memory_allocator_pool_size = 4; + auto temp_memory_allocator_pool = + std::make_unique(temp_memory_allocator_pool_size); + TestMemoryAllocator temp_allocator( + temp_memory_allocator_pool_size, temp_memory_allocator_pool.get()); + KernelRuntimeContext context(nullptr, &temp_allocator); + Result allocated_memory = context.allocate_temp(4, 2); + EXPECT_EQ(allocated_memory.ok(), true); + EXPECT_EQ(temp_allocator.last_seen_alignment, 2); +} diff --git a/sdk/inspector/_inspector.py b/sdk/inspector/_inspector.py index 91492643c8..45fe272cbb 100644 --- a/sdk/inspector/_inspector.py +++ b/sdk/inspector/_inspector.py @@ -312,6 +312,9 @@ class Event: _instruction_id: Optional[int] = None _delegate_metadata_parser: Optional[Callable[[List[str]], Dict[str, Any]]] = None + _delegate_time_scale_converter: Optional[ + Callable[[Union[int, str], Union[int, float]], Union[int, float]] + ] = None @cached_property def delegate_debug_metadatas(self) -> Union[List[str], Dict[str, Any]]: @@ -391,6 +394,9 @@ def _gen_from_inference_events( delegate_metadata_parser: Optional[ Callable[[List[str]], Dict[str, Any]] ] = None, + delegate_time_scale_converter: Optional[ + Callable[[Union[int, str], Union[int, float]], Union[int, float]] + ] = None, ) -> "Event": """ Given an EventSignature and a list of Events with that signature, @@ -411,6 +417,7 @@ def _gen_from_inference_events( name="", _instruction_id=signature.instruction_id, _delegate_metadata_parser=delegate_metadata_parser, + _delegate_time_scale_converter=delegate_time_scale_converter, ) # Populate fields from profile events @@ -476,14 +483,35 @@ def _populate_profiling_related_fields( f"Expected exactly one profile event per InstructionEvent when generating Inspector Event, but got {len(profile_events)}" ) + profile_event = profile_events[0] + # Scale factor should only be applied to non-delegated ops - scale_factor_updated = 1 if ret_event.is_delegated_op else scale_factor + if ( + ret_event.is_delegated_op + and ret_event._delegate_time_scale_converter is not None + ): + scaled_time = ret_event._delegate_time_scale_converter( + ret_event.name, + profile_event.end_time, + # pyre-ignore + ) - ret_event._delegate_time_scale_converter( + ret_event.name, profile_event.start_time + ) + # If it's not a delegated op then we can just use the raw time values + # and then scale them according to the scale factor that was passed in. + elif not ret_event.is_delegated_op: + scaled_time = ( + float(profile_event.end_time - profile_event.start_time) + / scale_factor + ) + # If there was no scale factor passed in just take a difference of the + # end and start times. + else: + scaled_time = float( + profile_event.end_time - profile_event.start_time + ) - profile_event = profile_events[0] - data.append( - float(profile_event.end_time - profile_event.start_time) - / scale_factor_updated - ) + data.append(scaled_time) delegate_debug_metadatas.append( profile_event.delegate_debug_metadata if profile_event.delegate_debug_metadata @@ -646,6 +674,9 @@ def _gen_from_etdump( delegate_metadata_parser: Optional[ Callable[[List[str]], Dict[str, Any]] ] = None, + delegate_time_scale_converter: Optional[ + Callable[[Union[int, str], Union[int, float]], Union[int, float]] + ] = None, ) -> List["EventBlock"]: """ Given an etdump, generate a list of EventBlocks corresponding to the @@ -743,6 +774,7 @@ class GroupedRunInstances: scale_factor, output_buffer, delegate_metadata_parser, + delegate_time_scale_converter, ) for signature, instruction_events in run_group.items() ] @@ -875,6 +907,9 @@ def __init__( delegate_metadata_parser: Optional[ Callable[[List[str]], Dict[str, Any]] ] = None, + delegate_time_scale_converter: Optional[ + Callable[[Union[int, str], Union[int, float]], Union[int, float]] + ] = None, enable_module_hierarchy: bool = False, ) -> None: r""" @@ -930,6 +965,7 @@ def __init__( self._target_time_scale, output_buffer, delegate_metadata_parser=delegate_metadata_parser, + delegate_time_scale_converter=delegate_time_scale_converter, ) # Connect ETRecord to EventBlocks diff --git a/sdk/inspector/tests/TARGETS b/sdk/inspector/tests/TARGETS index 0e6d06e776..374d2ea753 100644 --- a/sdk/inspector/tests/TARGETS +++ b/sdk/inspector/tests/TARGETS @@ -9,6 +9,7 @@ python_unittest( "//executorch/exir:lib", "//executorch/sdk:lib", "//executorch/sdk/debug_format:et_schema", + "//executorch/sdk/etdump:schema_flatcc", "//executorch/sdk/etrecord/tests:etrecord_test_library", "//executorch/sdk/inspector:inspector", "//executorch/sdk/inspector:lib", diff --git a/sdk/inspector/tests/inspector_test.py b/sdk/inspector/tests/inspector_test.py index 472f56f767..e1625bec75 100644 --- a/sdk/inspector/tests/inspector_test.py +++ b/sdk/inspector/tests/inspector_test.py @@ -17,9 +17,15 @@ from executorch.exir import ExportedProgram from executorch.sdk import generate_etrecord, parse_etrecord from executorch.sdk.debug_format.et_schema import OperatorNode +from executorch.sdk.etdump.schema_flatcc import ProfileEvent from executorch.sdk.etrecord.tests.etrecord_test import TestETRecord from executorch.sdk.inspector import _inspector, Event, EventBlock, Inspector, PerfData +from executorch.sdk.inspector._inspector import ( + InstructionEvent, + InstructionEventSignature, + ProfileEventSignature, +) OP_TYPE = "aten::add" @@ -183,6 +189,49 @@ def test_inspector_associate_with_op_graph_nodes_multiple_debug_handles(self): expected_ops = ["op_0", "op_1"] self.assertEqual(event_with_multiple_debug_handles.op_types, expected_ops) + def test_inspector_delegate_time_scale_converter(self): + def time_scale_converter(event_name, time): + return time / 10 + + event = Event( + name="", + _delegate_metadata_parser=None, + _delegate_time_scale_converter=None, + ) + event_signature = ProfileEventSignature( + name="", + instruction_id=0, + delegate_id_str="test_event", + ) + instruction_events = [ + InstructionEvent( + signature=InstructionEventSignature(0, 0), + profile_events=[ + ProfileEvent( + name="test_event", + chain_index=0, + instruction_id=0, + delegate_debug_id_int=None, + delegate_debug_id_str="test_event_delegated", + start_time=100, + end_time=200, + delegate_debug_metadata=None, + ) + ], + ) + ] + Event._populate_profiling_related_fields( + event, event_signature, instruction_events, 1 + ) + # Value of the perf data before scaling is done. + self.assertEqual(event.perf_data.raw[0], 100) + event._delegate_time_scale_converter = time_scale_converter + Event._populate_profiling_related_fields( + event, event_signature, instruction_events, 1 + ) + # Value of the perf data after scaling is done. 200/10 - 100/10. + self.assertEqual(event.perf_data.raw[0], 10) + def test_inspector_get_exported_program(self): # Create a context manager to patch functions called by Inspector.__init__ with patch.object( diff --git a/setup.py b/setup.py index 6591123d5e..5666428176 100644 --- a/setup.py +++ b/setup.py @@ -389,6 +389,7 @@ def run(self): if ShouldBuild.llama_custom_ops: cmake_args += [ + "-DEXECUTORCH_BUILD_CUSTOM=ON", # add llama sdpa ops to pybindings. "-DEXECUTORCH_BUILD_CUSTOM_OPS_AOT=ON", ] build_args += ["--target", "custom_ops_aot_lib"]

-n;c=&(|FRf6FZ7^*&yEQ|F4j}Ni&Md9PafbtI%bT5V2)hd2Nf7yu4B-q`L5hGc zTM&SL2k%a?>AMq100eG+CWIi!vI0mB5t!H2rhMHp6;XktFCsbsNuV^~*+Z~9^~^g3 z-wv>!#Oc}xOK82-cetMm7JN#9XMnX6KmMHdqSC@iE z&rt^O!6g(Z%FVixMGR#QcY|piFd6<9f4&?n2>WomAqvCxM3vYo{~|_QjOQ$gdI!r3>Y86?3LEMu=hh$jZNj>gS$AYct(WYN%6ROZ zA^J~;vcF!X@}RAR3g}4I1l*aZY3b zSx4J5sEt5f%B5G^Q$R;=Ppf)OI!<)#Jt5@8ie6l`5acH|Ag5#D!t0xcuK7xozBJGA z3Gbd7Bc^lFGOBPC2srzFGqV%i$9raN>pGS>-oEmy_oWwhpj0RnL0rza+HAXT-T6Xj zIF3Ck`zCypQB>+ip^u-2=1#3c9_p<_=+&0l(f&z6tG&uFE6c31Squ`39VDVZpmve= z*#vrLv9RBMIE+HuEm@wNMqxitF2|{I3htBO`Q*CIB2VN>_QZSp08a#Q2lrf)ymaG3&WWX8B!lG#E}=9%X0z_T zV^S3et>m9JxSCopbNpo8wF|(`Sv@Eiw!MW8wM#&Ysfv9ZSy%tf~rn-J!)u^n@ zNn!O%@$M2=lun^8lDI+6E^Xc*F-l5oU@fTvq&7G`Uu|T05_IMjqpuamwP4XLoD~#@ zvR^e~E~}O9BNT5SGh8M{9R+f%@&p>F&n=C95mq0)QCf)|{l;9{-*pDBaaF|tcke#L zQ9<}KZF$&^V%^{}T2?wjf5S)tKo#pF>w2xWG;`1nVHYt}x3^Yd2gn^k1&o}l2m?(1 zM9MJCb~LshhQcD5dpcPI{tZaL%X;-hyRh1ne}jyeAvgG$Wd*R)9~kXa#f15M?A&-vvRO8GnSLl3)N|Q-dQ=B`aDlDI1 zz%Tz>YWnwcE2q`93FXZRH~tLvJIr69>tXEHZ9XMysZB!|#@V)B6tW?3?tMzcq9e87 z8D<(e-y{<}zuBm82Ts^j#@n7p?lUkjp>yF*%klzH`PF03U?*v+nRCIdPw?y};Q4}u z-Sd|tktgRtCrdrYE;&galZjuBsJkoN&6?}wy=lxD>)kM$qtSElKE)7SIk{VZSvei> z9`)dDcLh{-cYtuSX@*+~vLMa2Buj5S2RE$L@#MGgM{Oe|+w$dY9KuLQSVGz84F!yv zIHwIcNb5hAJU2n;drLr1bx_At#$wVhm=Pk z$Ps*lcs=upnjfYYNxa1DMqZixVmS !8=>IIdc2o%;79XNp<(X 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