diff --git a/.ci/docker/ci_commit_pins/pytorch.txt b/.ci/docker/ci_commit_pins/pytorch.txt index 242371cbebe..401a0594d98 100644 --- a/.ci/docker/ci_commit_pins/pytorch.txt +++ b/.ci/docker/ci_commit_pins/pytorch.txt @@ -1 +1 @@ -release/2.12 +release/2.13 diff --git a/.ci/docker/common/install_pytorch.sh b/.ci/docker/common/install_pytorch.sh index 3c80d093ab2..c55e3ac878d 100755 --- a/.ci/docker/common/install_pytorch.sh +++ b/.ci/docker/common/install_pytorch.sh @@ -16,6 +16,21 @@ install_domains() { pip_install --no-build-isolation --user "git+https://github.com/pytorch/vision.git@${TORCHVISION_VERSION}" } +configure_pytorch_compiler() { + local cxx_version + cxx_version=$(/usr/bin/c++ --version | head -n1) + if [[ "${cxx_version}" == *clang* ]]; then + local clang_major + clang_major=$(/usr/bin/c++ -dumpversion | cut -d. -f1) + if [[ "${clang_major}" =~ ^[0-9]+$ && "${clang_major}" -lt 16 ]] && + command -v gcc >/dev/null && + command -v g++ >/dev/null; then + export CC=gcc + export CXX=g++ + fi + fi +} + install_pytorch_and_domains() { git clone https://github.com/pytorch/pytorch.git @@ -33,6 +48,7 @@ install_pytorch_and_domains() { if [[ -n "${PYTORCH_BUILD_MAX_JOBS:-}" ]]; then export MAX_JOBS="${PYTORCH_BUILD_MAX_JOBS}" fi + configure_pytorch_compiler # Then build and install PyTorch conda_run python setup.py bdist_wheel pip_install "$(echo dist/*.whl)" @@ -40,7 +56,7 @@ install_pytorch_and_domains() { # Grab the pinned audio and vision commits from PyTorch TORCHAUDIO_VERSION=release/2.11 export TORCHAUDIO_VERSION - TORCHVISION_VERSION=release/0.27 + TORCHVISION_VERSION=release/0.28 export TORCHVISION_VERSION install_domains diff --git a/.ci/scripts/setup-windows.ps1 b/.ci/scripts/setup-windows.ps1 index 2cf28e0cdfe..a38d43bbfd8 100644 --- a/.ci/scripts/setup-windows.ps1 +++ b/.ci/scripts/setup-windows.ps1 @@ -14,6 +14,10 @@ conda activate et # Install test dependencies pip install -r .ci/docker/requirements-ci.txt +# PyTorch 2.13 headers use designated initializers in c10, which require C++20 +# with MSVC. +$env:CMAKE_ARGS = "$env:CMAKE_ARGS -DCMAKE_CXX_STANDARD=20" + # The Windows CI image ships CUDA toolkits on PATH, so install_executorch # (setup.py) auto-enables EXECUTORCH_BUILD_CUDA whenever the detected nvcc # version is in SUPPORTED_CUDA_VERSIONS. CPU-only jobs install CPU torch, so a diff --git a/.ci/scripts/test_model_e2e_windows.ps1 b/.ci/scripts/test_model_e2e_windows.ps1 index 73a78c92112..5a2d8eccf00 100644 --- a/.ci/scripts/test_model_e2e_windows.ps1 +++ b/.ci/scripts/test_model_e2e_windows.ps1 @@ -172,22 +172,35 @@ try { "-DCUDAToolkit_ROOT=$env:CUDA_HOME" ) } + $cmakeCommonArgs = @("-DCMAKE_CXX_STANDARD=20") Write-Host "::endgroup::" Write-Host "::group::Build ExecuTorch (CUDA)" $numCores = [Math]::Max([Environment]::ProcessorCount - 1, 1) + $cmakeOut = Join-Path -Path $executorchRoot -ChildPath "cmake-out" + $executorchCmakeDir = Join-Path -Path $cmakeOut -ChildPath "lib\cmake\ExecuTorch" + $executorchConfig = Join-Path -Path $executorchCmakeDir -ChildPath "executorch-config.cmake" # EXECUTORCH_BUILD_EXTENSION_IMAGE defaults OFF in the preset, so it must be # enabled explicitly here (matching the Makefile CUDA target used on Linux). # The dinov2 runner links the installed extension_image.lib; without this the # main install never builds it and dinov2_runner fails to link (LNK1181). - cmake --preset llm-release-cuda -DEXECUTORCH_BUILD_EXTENSION_IMAGE=ON @cmakeCudaArgs - cmake --build cmake-out --target install --config Release -j $numCores + cmake --preset llm-release-cuda -DEXECUTORCH_BUILD_EXTENSION_IMAGE=ON @cmakeCommonArgs @cmakeCudaArgs + cmake --build $cmakeOut --config Release -j $numCores + cmake --install $cmakeOut --config Release + if (-not (Test-Path -Path $executorchConfig -PathType Leaf)) { + throw "ExecuTorch CMake package config not found after install: $executorchConfig" + } Write-Host "::endgroup::" Write-Host "::group::Build $runnerTarget" + $cmakePackageArgs = @( + "-DCMAKE_FIND_ROOT_PATH=$cmakeOut", + "-DCMAKE_PREFIX_PATH=$cmakeOut;$executorchCmakeDir", + "-Dexecutorch_DIR=$executorchCmakeDir" + ) Push-Location (Join-Path -Path $executorchRoot -ChildPath "examples\models\$runnerPath") try { - cmake --preset $runnerPreset @cmakeCudaArgs + cmake --preset $runnerPreset @cmakeCommonArgs @cmakePackageArgs @cmakeCudaArgs cmake --build (Join-Path -Path $executorchRoot -ChildPath "cmake-out\examples\models\$runnerPath") --target $runnerTarget --config Release -j $numCores } finally { diff --git a/.ci/scripts/utils.sh b/.ci/scripts/utils.sh index b312d0ede83..234e162e48e 100644 --- a/.ci/scripts/utils.sh +++ b/.ci/scripts/utils.sh @@ -107,7 +107,7 @@ install_pytorch_and_domains() { local torch_release=$(cat version.txt) # Download key must match the upload key below (basename of dist/*.whl, # which always carries setup.py's resolved +gitHASH). Branch-ref pins - # like `release/2.12` would otherwise produce `+gitrelease` here and + # like `release/2.13` would otherwise produce `+gitrelease` here and # never hit the cache. local torch_short_hash=$(git rev-parse --short=7 HEAD) local torch_wheel_path="cached_artifacts/pytorch/executorch/pytorch_wheels/${system_name}/${python_version}" @@ -178,7 +178,7 @@ install_pytorch_and_domains() { # Grab the pinned audio and vision commits from PyTorch TORCHAUDIO_VERSION=release/2.11 export TORCHAUDIO_VERSION - TORCHVISION_VERSION=release/0.27 + TORCHVISION_VERSION=release/0.28 export TORCHVISION_VERSION install_domains diff --git a/.github/workflows/mlx.yml b/.github/workflows/mlx.yml index 40ebabbcf28..0de68134c4e 100644 --- a/.github/workflows/mlx.yml +++ b/.github/workflows/mlx.yml @@ -158,10 +158,10 @@ jobs: --prompt-len 4 \ --max-new-tokens 5 2>&1) echo "$OUTPUT" - if echo "$OUTPUT" | grep -q "Generated token ids: \[167, 94, 253, 88, 227\]"; then + if echo "$OUTPUT" | grep -q "Generated token ids: \[167, 180, 167, 180, 167\]"; then echo "Success: Qwen 3.5 MoE MLX export + inference completed with expected output" else - echo "Failed: unexpected output (expected [167, 94, 253, 88, 227])" + echo "Failed: unexpected output (expected [167, 180, 167, 180, 167])" exit 1 fi echo "::endgroup::" diff --git a/backends/aoti/common_shims.cpp b/backends/aoti/common_shims.cpp index 49d445e955d..f3a34a09987 100644 --- a/backends/aoti/common_shims.cpp +++ b/backends/aoti/common_shims.cpp @@ -154,6 +154,11 @@ AOTITorchError aoti_torch_get_dim(Tensor* tensor, int64_t* ret_dim) { return Error::Ok; } +AOTITorchError aoti_torch_get_numel(Tensor* tensor, int64_t* ret_numel) { + *ret_numel = static_cast(tensor->numel()); + return Error::Ok; +} + // Device and layout utility functions int32_t aoti_torch_device_type_cpu() { // Let's say cpu is 0 for ET as well diff --git a/backends/aoti/common_shims.h b/backends/aoti/common_shims.h index 869aece5a3b..d057279e22a 100644 --- a/backends/aoti/common_shims.h +++ b/backends/aoti/common_shims.h @@ -59,6 +59,9 @@ aoti_torch_get_device_index(Tensor* tensor, int32_t* ret_device_index); AOTI_SHIM_EXPORT AOTITorchError aoti_torch_get_dim(Tensor* tensor, int64_t* ret_dim); +AOTI_SHIM_EXPORT AOTITorchError +aoti_torch_get_numel(Tensor* tensor, int64_t* ret_numel); + // Utility functions for device and layout information AOTI_SHIM_EXPORT int32_t aoti_torch_device_type_cpu(); AOTI_SHIM_EXPORT int32_t aoti_torch_layout_strided(); diff --git a/backends/aoti/common_shims_slim.cpp b/backends/aoti/common_shims_slim.cpp index f4b50400778..c8c7408aa62 100644 --- a/backends/aoti/common_shims_slim.cpp +++ b/backends/aoti/common_shims_slim.cpp @@ -60,6 +60,14 @@ AOTITorchError aoti_torch_get_dim(Tensor* tensor, int64_t* ret_dim) { return Error::Ok; } +AOTITorchError aoti_torch_get_numel(Tensor* tensor, int64_t* ret_numel) { + if (tensor == nullptr || ret_numel == nullptr) { + return Error::InvalidArgument; + } + *ret_numel = static_cast(tensor->numel()); + return Error::Ok; +} + int32_t aoti_torch_layout_strided() { // Slimtensor only support strided layout, the return value will always be 0, // a.k.a at::Layout::Strided; diff --git a/backends/aoti/common_shims_slim.h b/backends/aoti/common_shims_slim.h index 3ec867bcf6e..c5a5cab9413 100644 --- a/backends/aoti/common_shims_slim.h +++ b/backends/aoti/common_shims_slim.h @@ -48,6 +48,9 @@ aoti_torch_get_dtype(Tensor* tensor, int32_t* ret_dtype); AOTI_SHIM_EXPORT AOTITorchError aoti_torch_get_dim(Tensor* tensor, int64_t* ret_dim); +AOTI_SHIM_EXPORT AOTITorchError +aoti_torch_get_numel(Tensor* tensor, int64_t* ret_numel); + AOTI_SHIM_EXPORT int32_t aoti_torch_layout_strided(); // ============================================================ diff --git a/backends/aoti/tests/test_common_shims.cpp b/backends/aoti/tests/test_common_shims.cpp index 0fd1b057f99..b2dc2c01dc2 100644 --- a/backends/aoti/tests/test_common_shims.cpp +++ b/backends/aoti/tests/test_common_shims.cpp @@ -241,6 +241,21 @@ TEST_F(CommonShimsTest, ScalarTensor) { error = aoti_torch_get_sizes(tensor_0d, &sizes_ptr2); EXPECT_EQ(error, Error::Ok); EXPECT_EQ(sizes_ptr, sizes_ptr2); + + int64_t numel = -1; + error = aoti_torch_get_numel(tensor_0d, &numel); + EXPECT_EQ(error, Error::Ok); + EXPECT_EQ(numel, 1); +} + +TEST_F(CommonShimsTest, GetNumel) { + auto tensor = create_tracked_tensor({2, 3, 4}); + + int64_t numel = -1; + AOTITorchError error = aoti_torch_get_numel(tensor, &numel); + + EXPECT_EQ(error, Error::Ok); + EXPECT_EQ(numel, 24); } // Test large tensor dimensions @@ -266,6 +281,11 @@ TEST_F(CommonShimsTest, LargeTensorDimensions) { EXPECT_EQ(strides_ptr[1], 120000); EXPECT_EQ(strides_ptr[2], 400); EXPECT_EQ(strides_ptr[3], 1); + + int64_t numel = -1; + error = aoti_torch_get_numel(tensor, &numel); + EXPECT_EQ(error, Error::Ok); + EXPECT_EQ(numel, 2400000000); } // Test that cleanup_tensor_metadata clears the cache diff --git a/backends/aoti/tests/test_common_shims_slim.cpp b/backends/aoti/tests/test_common_shims_slim.cpp index 94319c6f94d..2dd8475a4d8 100644 --- a/backends/aoti/tests/test_common_shims_slim.cpp +++ b/backends/aoti/tests/test_common_shims_slim.cpp @@ -289,6 +289,50 @@ void runGetDimTest(slim_c10::DeviceType device_type) { } } +void runGetNumelTest(slim_c10::DeviceType device_type) { + slim_c10::Device device(device_type, 0); + + // Test 0D tensor (scalar) + { + std::vector sizes = {}; + std::vector strides = {}; + + Tensor* tensor = new Tensor(slim::empty_strided( + slim::makeArrayRef(sizes), + slim::makeArrayRef(strides), + slim_c10::ScalarType::Float, + device)); + + int64_t ret_numel = -1; + AOTITorchError error = aoti_torch_get_numel(tensor, &ret_numel); + + EXPECT_EQ(error, Error::Ok); + EXPECT_EQ(ret_numel, 1); + + delete tensor; + } + + // Test 3D tensor + { + std::vector sizes = {2, 3, 4}; + std::vector strides = calculateContiguousStrides(sizes); + + Tensor* tensor = new Tensor(slim::empty_strided( + slim::makeArrayRef(sizes), + slim::makeArrayRef(strides), + slim_c10::ScalarType::Float, + device)); + + int64_t ret_numel = -1; + AOTITorchError error = aoti_torch_get_numel(tensor, &ret_numel); + + EXPECT_EQ(error, Error::Ok); + EXPECT_EQ(ret_numel, 24); + + delete tensor; + } +} + // ============================================================================ // Storage & Device Property Tests // ============================================================================ @@ -400,6 +444,10 @@ TEST_F(CommonShimsSlimTest, GetDim_CPU) { runGetDimTest(slim_c10::DeviceType::CPU); } +TEST_F(CommonShimsSlimTest, GetNumel_CPU) { + runGetNumelTest(slim_c10::DeviceType::CPU); +} + TEST_F(CommonShimsSlimTest, GetStorageOffset_CPU) { runGetStorageOffsetTest(slim_c10::DeviceType::CPU); } @@ -456,6 +504,13 @@ TEST_F(CommonShimsSlimTest, GetDim_CUDA) { runGetDimTest(slim_c10::DeviceType::CUDA); } +TEST_F(CommonShimsSlimTest, GetNumel_CUDA) { + if (!isCudaAvailable()) { + GTEST_SKIP() << "CUDA not available"; + } + runGetNumelTest(slim_c10::DeviceType::CUDA); +} + TEST_F(CommonShimsSlimTest, GetStorageOffset_CUDA) { if (!isCudaAvailable()) { GTEST_SKIP() << "CUDA not available"; @@ -495,6 +550,7 @@ TEST_F(CommonShimsSlimTest, NullTensorArgument) { int64_t* strides = nullptr; int32_t dtype = -1; int64_t dim = -1; + int64_t numel = -1; EXPECT_EQ( aoti_torch_get_data_ptr(nullptr, &data_ptr), Error::InvalidArgument); @@ -502,6 +558,7 @@ TEST_F(CommonShimsSlimTest, NullTensorArgument) { EXPECT_EQ(aoti_torch_get_strides(nullptr, &strides), Error::InvalidArgument); EXPECT_EQ(aoti_torch_get_dtype(nullptr, &dtype), Error::InvalidArgument); EXPECT_EQ(aoti_torch_get_dim(nullptr, &dim), Error::InvalidArgument); + EXPECT_EQ(aoti_torch_get_numel(nullptr, &numel), Error::InvalidArgument); } TEST_F(CommonShimsSlimTest, NullReturnPointer) { @@ -512,6 +569,7 @@ TEST_F(CommonShimsSlimTest, NullReturnPointer) { EXPECT_EQ(aoti_torch_get_strides(tensor, nullptr), Error::InvalidArgument); EXPECT_EQ(aoti_torch_get_dtype(tensor, nullptr), Error::InvalidArgument); EXPECT_EQ(aoti_torch_get_dim(tensor, nullptr), Error::InvalidArgument); + EXPECT_EQ(aoti_torch_get_numel(tensor, nullptr), Error::InvalidArgument); } // ============================================================================ @@ -534,6 +592,7 @@ TEST_F(CommonShimsSlimTest, ScalarTensor) { int64_t* ret_sizes = nullptr; int64_t* ret_strides = nullptr; int64_t ret_dim = -1; + int64_t ret_numel = -1; EXPECT_EQ(aoti_torch_get_sizes(tensor, &ret_sizes), Error::Ok); EXPECT_NE(ret_sizes, nullptr); @@ -543,6 +602,9 @@ TEST_F(CommonShimsSlimTest, ScalarTensor) { EXPECT_EQ(aoti_torch_get_dim(tensor, &ret_dim), Error::Ok); EXPECT_EQ(ret_dim, 0); + + EXPECT_EQ(aoti_torch_get_numel(tensor, &ret_numel), Error::Ok); + EXPECT_EQ(ret_numel, 1); } TEST_F(CommonShimsSlimTest, LargeTensor) { @@ -559,6 +621,7 @@ TEST_F(CommonShimsSlimTest, LargeTensor) { int64_t* ret_sizes = nullptr; int64_t* ret_strides = nullptr; + int64_t ret_numel = -1; EXPECT_EQ(aoti_torch_get_sizes(tensor, &ret_sizes), Error::Ok); EXPECT_EQ(ret_sizes[0], 100); @@ -569,6 +632,9 @@ TEST_F(CommonShimsSlimTest, LargeTensor) { EXPECT_EQ(ret_strides[0], 60000); // 200 * 300 EXPECT_EQ(ret_strides[1], 300); // 300 EXPECT_EQ(ret_strides[2], 1); + + EXPECT_EQ(aoti_torch_get_numel(tensor, &ret_numel), Error::Ok); + EXPECT_EQ(ret_numel, 6000000); } TEST_F(CommonShimsSlimTest, ConsistentPointerReturn) { diff --git a/backends/cuda/cuda_backend.py b/backends/cuda/cuda_backend.py index ef746830bed..bf28077c62c 100644 --- a/backends/cuda/cuda_backend.py +++ b/backends/cuda/cuda_backend.py @@ -263,6 +263,13 @@ def _move_to_device_resize_kv(ep, location): return ep +def _on_off_compile_spec_value(spec: CompileSpec) -> bool: + value = spec.value.decode("utf-8").upper() + if value not in ["ON", "OFF"]: + raise ValueError(f"Invalid {spec.key}: {value}. Expected 'ON' or 'OFF'.") + return value == "ON" + + @final @experimental( "This API and all of cuda backend related functionality are experimental." @@ -496,12 +503,25 @@ def get_aoti_compile_options( # Parse compile_specs to check for platform platform = "linux" + emulate_precision_casts = True + max_autotune = True + autotune_at_compile_time = None shim_library_path = None for spec in compile_specs: if spec.key == "platform": platform = spec.value.decode("utf-8") - if spec.key == "shim_library_path": + elif spec.key == "emulate_precision_casts": + emulate_precision_casts = _on_off_compile_spec_value(spec) + elif spec.key == "max_autotune": + max_autotune = _on_off_compile_spec_value(spec) + elif spec.key == "autotune_at_compile_time": + autotune_at_compile_time = _on_off_compile_spec_value(spec) + elif spec.key == "shim_library_path": shim_library_path = spec.value.decode("utf-8") + options["emulate_precision_casts"] = emulate_precision_casts + options["max_autotune"] = max_autotune + if autotune_at_compile_time is not None: + options["triton.autotune_at_compile_time"] = autotune_at_compile_time # Add platform-specific options if platform == "windows": diff --git a/backends/cuda/runtime/aoti_cuda_shims.lib b/backends/cuda/runtime/aoti_cuda_shims.lib index 63c8d79bd7a..8bb03cc1c1e 100644 Binary files a/backends/cuda/runtime/aoti_cuda_shims.lib and b/backends/cuda/runtime/aoti_cuda_shims.lib differ diff --git a/backends/cuda/tests/test_cuda_export.py b/backends/cuda/tests/test_cuda_export.py index 83d792187d1..2bd3d4db109 100644 --- a/backends/cuda/tests/test_cuda_export.py +++ b/backends/cuda/tests/test_cuda_export.py @@ -16,6 +16,47 @@ from torch.export import export +class TestCudaBackendCompileOptions(unittest.TestCase): + def test_emulate_precision_casts_compile_spec(self): + options = CudaBackend.get_aoti_compile_options( + [CompileSpec(key="emulate_precision_casts", value=b"OFF")] + ) + + self.assertFalse(options["emulate_precision_casts"]) + + def test_invalid_emulate_precision_casts_compile_spec(self): + with self.assertRaisesRegex(ValueError, "Invalid emulate_precision_casts"): + CudaBackend.get_aoti_compile_options( + [CompileSpec(key="emulate_precision_casts", value=b"MAYBE")] + ) + + def test_max_autotune_compile_spec(self): + options = CudaBackend.get_aoti_compile_options( + [CompileSpec(key="max_autotune", value=b"OFF")] + ) + + self.assertFalse(options["max_autotune"]) + + def test_invalid_max_autotune_compile_spec(self): + with self.assertRaisesRegex(ValueError, "Invalid max_autotune"): + CudaBackend.get_aoti_compile_options( + [CompileSpec(key="max_autotune", value=b"MAYBE")] + ) + + def test_autotune_at_compile_time_compile_spec(self): + options = CudaBackend.get_aoti_compile_options( + [CompileSpec(key="autotune_at_compile_time", value=b"OFF")] + ) + + self.assertFalse(options["triton.autotune_at_compile_time"]) + + def test_invalid_autotune_at_compile_time_compile_spec(self): + with self.assertRaisesRegex(ValueError, "Invalid autotune_at_compile_time"): + CudaBackend.get_aoti_compile_options( + [CompileSpec(key="autotune_at_compile_time", value=b"MAYBE")] + ) + + class TestCudaExport(unittest.TestCase): """Test CUDA export functionality for various operations using to_edge_transform_and_lower.""" diff --git a/examples/models/dinov2/CMakeLists.txt b/examples/models/dinov2/CMakeLists.txt index a2af3002a35..8e83ee44873 100644 --- a/examples/models/dinov2/CMakeLists.txt +++ b/examples/models/dinov2/CMakeLists.txt @@ -7,7 +7,9 @@ cmake_minimum_required(VERSION 3.24) project(dinov2_runner) -set(CMAKE_CXX_STANDARD 17) +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(EXECUTORCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../..) diff --git a/examples/models/llama/install_requirements.sh b/examples/models/llama/install_requirements.sh index 8a2fa25d244..7692306c50b 100755 --- a/examples/models/llama/install_requirements.sh +++ b/examples/models/llama/install_requirements.sh @@ -11,7 +11,18 @@ # Install snakeviz for cProfile flamegraph # Install lm-eval for Model Evaluation with lm-evalution-harness. # Install safetensors to load safetensors checkpoints (currently adapter only). -pip install hydra-core huggingface_hub tiktoken torchtune sentencepiece tokenizers snakeviz lm_eval==0.4.5 blobfile safetensors +TORCHTUNE_COMMIT=3c1872ace149f03ef4ffec765d3f0a5fd0399497 +pip install \ + hydra-core \ + huggingface_hub \ + tiktoken \ + "torchtune @ git+https://github.com/pytorch/torchtune.git@${TORCHTUNE_COMMIT}" \ + sentencepiece \ + tokenizers \ + snakeviz \ + lm_eval==0.4.5 \ + blobfile \ + safetensors # Call the install helper for further setup python examples/models/llama/install_requirement_helper.py diff --git a/examples/models/parakeet/CMakeLists.txt b/examples/models/parakeet/CMakeLists.txt index a2b798de557..9449583c613 100644 --- a/examples/models/parakeet/CMakeLists.txt +++ b/examples/models/parakeet/CMakeLists.txt @@ -7,7 +7,9 @@ cmake_minimum_required(VERSION 3.24) project(parakeet_runner) -set(CMAKE_CXX_STANDARD 17) +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(EXECUTORCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../..) diff --git a/examples/models/qwen3_5_moe/export.py b/examples/models/qwen3_5_moe/export.py index d2c3914c6a2..9485a5ee32c 100644 --- a/examples/models/qwen3_5_moe/export.py +++ b/examples/models/qwen3_5_moe/export.py @@ -1073,6 +1073,8 @@ def _export_cuda(model, config, args): "enable_dynamic_shape": True, "get_mutable_buffer_metadata": _mutable_buffer_metadata_json(model), } + # Avoid a PyTorch 2.13 Triton codegen bug in Qwen's fused cast/reduction + # compile-time autotune path while keeping the CUDA backend defaults unchanged. et_prog = to_edge_transform_and_lower( {"decode": decode_ep, "prefill": prefill_ep}, partitioner={ @@ -1081,6 +1083,9 @@ def _export_cuda(model, config, args): [ CudaBackend.generate_method_name_compile_spec("decode"), CompileSpec("low_memory_mode", b"ON"), + CompileSpec("emulate_precision_casts", b"OFF"), + CompileSpec("max_autotune", b"OFF"), + CompileSpec("autotune_at_compile_time", b"OFF"), ] ) ], @@ -1089,6 +1094,9 @@ def _export_cuda(model, config, args): [ CudaBackend.generate_method_name_compile_spec("prefill"), CompileSpec("low_memory_mode", b"ON"), + CompileSpec("emulate_precision_casts", b"OFF"), + CompileSpec("max_autotune", b"OFF"), + CompileSpec("autotune_at_compile_time", b"OFF"), ] ) ], diff --git a/examples/models/sortformer/CMakeLists.txt b/examples/models/sortformer/CMakeLists.txt index bdc0fbfbbe5..171eb726ed2 100644 --- a/examples/models/sortformer/CMakeLists.txt +++ b/examples/models/sortformer/CMakeLists.txt @@ -7,7 +7,9 @@ cmake_minimum_required(VERSION 3.24) project(sortformer_runner) -set(CMAKE_CXX_STANDARD 17) +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(EXECUTORCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../..) diff --git a/examples/models/voxtral_realtime/CMakeLists.txt b/examples/models/voxtral_realtime/CMakeLists.txt index 28545f407ca..93fa9249cad 100644 --- a/examples/models/voxtral_realtime/CMakeLists.txt +++ b/examples/models/voxtral_realtime/CMakeLists.txt @@ -7,7 +7,9 @@ cmake_minimum_required(VERSION 3.24) project(voxtral_realtime) -set(CMAKE_CXX_STANDARD 17) +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(EXECUTORCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../..) diff --git a/extension/llm/export/nvfp4.py b/extension/llm/export/nvfp4.py index feeb95f50a6..b0671d11173 100644 --- a/extension/llm/export/nvfp4.py +++ b/extension/llm/export/nvfp4.py @@ -161,7 +161,7 @@ def _exportable_nvfp4_transform(module: nn.Module, config: ExportableNVFP4Config f"NVFP4 requires weight dims divisible by 16, got {weight.shape}" ) - per_tensor_scale = 1.0 + per_tensor_scale = None if config.use_per_tensor_scale: tensor_amax = torch.max(torch.abs(weight)) per_tensor_scale = per_tensor_amax_to_scale(tensor_amax) @@ -173,7 +173,11 @@ def _exportable_nvfp4_transform(module: nn.Module, config: ExportableNVFP4Config qdata_u32 = qdata_packed.view(torch.uint32) scales_u8 = scales_fp8.view(torch.uint8) - pts = torch.tensor(per_tensor_scale, dtype=torch.float32) + pts = torch.as_tensor( + 1.0 if per_tensor_scale is None else per_tensor_scale, + dtype=torch.float32, + device=weight.device, + ) quantized_weight = ExportableNVFP4Tensor( qdata_u32, scales_u8, diff --git a/install_requirements.py b/install_requirements.py index 53204ffd3ee..94485804a42 100644 --- a/install_requirements.py +++ b/install_requirements.py @@ -15,6 +15,8 @@ # The pip repository that hosts nightly torch packages. # This will be dynamically set based on CUDA availability and CUDA backend enabled/disabled. TORCH_URL_BASE = "https://download.pytorch.org/whl/test" +TORCHAO_URL_BASE = "https://download.pytorch.org/whl/nightly" +TORCHAO_NIGHTLY_VERSION = "0.18.0.dev20260715" # Since ExecuTorch often uses main-branch features of pytorch, only the nightly # pip versions will have the required features. @@ -43,13 +45,15 @@ def install_requirements(use_pytorch_nightly): # Determine the appropriate PyTorch URL based on CUDA delegate status torch_url = determine_torch_url(TORCH_URL_BASE) + torchao_url = determine_torch_url(TORCHAO_URL_BASE) # pip packages needed by exir. TORCH_PACKAGE = [ # Setting use_pytorch_nightly to false to test the pinned PyTorch commit. Note # that we don't need to set any version number there because they have already # been installed on CI before this step, so pip won't reinstall them - ("torch==2.12.0" if use_pytorch_nightly else "torch"), + ("torch==2.13.0" if use_pytorch_nightly else "torch"), + f"torchao=={TORCHAO_NIGHTLY_VERSION}", ] # Install the requirements for core ExecuTorch package. @@ -66,44 +70,45 @@ def install_requirements(use_pytorch_nightly): *TORCH_PACKAGE, "--extra-index-url", torch_url, + "--extra-index-url", + torchao_url, ], check=True, ) - LOCAL_REQUIREMENTS = [ - "third-party/ao", # We need the latest kernels for fast iteration, so not relying on pypi. - ] + ( - [ - "extension/llm/tokenizers", # TODO(larryliu0820): Setup a pypi package for this. - ] - if sys.platform != "win32" - else [] - ) # TODO(gjcomer): Re-enable when buildable on Windows. - - # Install packages directly from local copy instead of pypi. - # This is usually not recommended. - new_env = os.environ.copy() - if ("EXECUTORCH_BUILD_KERNELS_TORCHAO" not in new_env) or ( - new_env["EXECUTORCH_BUILD_KERNELS_TORCHAO"] == "0" - ): - new_env["USE_CPP"] = "0" - else: - assert new_env["EXECUTORCH_BUILD_KERNELS_TORCHAO"] == "1" - new_env["USE_CPP"] = "1" - new_env["CMAKE_POLICY_VERSION_MINIMUM"] = "3.5" - subprocess.run( - [ - sys.executable, - "-m", - "pip", - "install", - # Without --no-build-isolation, setup.py can't find the torch module. - "--no-build-isolation", - *LOCAL_REQUIREMENTS, - ], - env=new_env, - check=True, - ) + LOCAL_REQUIREMENTS = [] + if os.environ.get("TORCHAO_BUILD_EXPERIMENTAL_MPS") == "1": + LOCAL_REQUIREMENTS.append("third-party/ao") + if sys.platform != "win32": + # TODO(larryliu0820): Setup a pypi package for this. + LOCAL_REQUIREMENTS.append("extension/llm/tokenizers") + # TODO(gjcomer): Re-enable when buildable on Windows. + + if LOCAL_REQUIREMENTS: + # Install packages directly from local copy instead of pypi. + # This is usually not recommended. + new_env = os.environ.copy() + if ("EXECUTORCH_BUILD_KERNELS_TORCHAO" not in new_env) or ( + new_env["EXECUTORCH_BUILD_KERNELS_TORCHAO"] == "0" + ): + new_env["USE_CPP"] = "0" + else: + assert new_env["EXECUTORCH_BUILD_KERNELS_TORCHAO"] == "1" + new_env["USE_CPP"] = "1" + new_env["CMAKE_POLICY_VERSION_MINIMUM"] = "3.5" + subprocess.run( + [ + sys.executable, + "-m", + "pip", + "install", + # Without --no-build-isolation, setup.py can't find the torch module. + "--no-build-isolation", + *LOCAL_REQUIREMENTS, + ], + env=new_env, + check=True, + ) def install_optional_example_requirements(use_pytorch_nightly): @@ -112,7 +117,7 @@ def install_optional_example_requirements(use_pytorch_nightly): print("Installing torch domain libraries") DOMAIN_LIBRARIES = [ - ("torchvision==0.27.0" if use_pytorch_nightly else "torchvision"), + ("torchvision==0.28.0" if use_pytorch_nightly else "torchvision"), ("torchaudio==2.11.0" if use_pytorch_nightly else "torchaudio"), ] # Then install domain libraries diff --git a/requirements-examples.txt b/requirements-examples.txt index f1579edcb14..1f36b7c6108 100644 --- a/requirements-examples.txt +++ b/requirements-examples.txt @@ -3,5 +3,5 @@ datasets == 3.6.0 # 4.0.0 deprecates trust_remote_code and load scripts. For now pin to 3.6.0 timm == 1.0.7 torchsr == 1.0.4 -torchtune @ git+https://github.com/pytorch/torchtune.git@6f2aa7254458145f99d7004cbd6ebc8e53a06404 +torchtune @ git+https://github.com/pytorch/torchtune.git@3c1872ace149f03ef4ffec765d3f0a5fd0399497 transformers == 5.0.0rc1 diff --git a/runtime/core/portable_type/c10/c10/util/BFloat16-math.h b/runtime/core/portable_type/c10/c10/util/BFloat16-math.h index 8291cd74481..bce89e8acd9 100644 --- a/runtime/core/portable_type/c10/c10/util/BFloat16-math.h +++ b/runtime/core/portable_type/c10/c10/util/BFloat16-math.h @@ -181,7 +181,7 @@ template < typename T, typename std::enable_if_t, int> = 0> inline T rsqrt(T a) { - return 1.0 / std::sqrt(float(a)); + return 1.0f / std::sqrt(float(a)); } template < typename T, diff --git a/runtime/core/portable_type/c10/c10/util/complex_math.h b/runtime/core/portable_type/c10/c10/util/complex_math.h index d369df50592..2b9bbea6c71 100644 --- a/runtime/core/portable_type/c10/c10/util/complex_math.h +++ b/runtime/core/portable_type/c10/c10/util/complex_math.h @@ -327,7 +327,7 @@ C10_HOST_DEVICE inline c10::complex atanh(const c10::complex& x) { template C10_HOST_DEVICE inline c10::complex log1p(const c10::complex& z) { #if defined(__APPLE__) || defined(__MACOSX) || defined(__CUDACC__) || \ - defined(__HIPCC__) + defined(__HIPCC__) || defined(__SYCL_DEVICE_ONLY__) // For Mac, the new implementation yielded a high relative error. Falling back // to the old version for now. // See https://github.com/numpy/numpy/pull/22611#issuecomment-1667945354 diff --git a/runtime/core/portable_type/c10/torch/headeronly/util/Half.h b/runtime/core/portable_type/c10/torch/headeronly/util/Half.h index a9c0b166ba2..e5aa622656c 100644 --- a/runtime/core/portable_type/c10/torch/headeronly/util/Half.h +++ b/runtime/core/portable_type/c10/torch/headeronly/util/Half.h @@ -236,7 +236,7 @@ C10_HOST_DEVICE inline float fp16_ieee_to_fp32_value(uint16_t h) { /* * - Choose either results of conversion of input as a normalized number, or * as a denormalized number, depending on the input exponent. The variable - * two_w contains input exponent in bits 27-31, therefore if its smaller than + * two_w contains input exponent in bits 27-31, therefore if it's smaller than * 2**27, the input is either a denormal number, or zero. * - Combine the result of conversion of exponent and mantissa with the sign * of the input number. diff --git a/third-party/ao b/third-party/ao index 02105d46c61..4aa81011346 160000 --- a/third-party/ao +++ b/third-party/ao @@ -1 +1 @@ -Subproject commit 02105d46c61dc80a8c9d39d5836e827ba3af8439 +Subproject commit 4aa810113463571cabb7342d1a00a1f3de9c54f2 diff --git a/torch_pin.py b/torch_pin.py index 0c5cd50fe6d..ca593b1ef05 100644 --- a/torch_pin.py +++ b/torch_pin.py @@ -1,2 +1,2 @@ -TORCH_VERSION = "2.12.0" +TORCH_VERSION = "2.13.0" # NIGHTLY_VERSION = "dev20260318" Temporarily pinning to stable release candidate. Revert https://github.com/pytorch/executorch/pull/18287