diff --git a/.github/requirements/conda-env-Linux-X64 b/.github/requirements/conda-env-Linux-X64.txt similarity index 100% rename from .github/requirements/conda-env-Linux-X64 rename to .github/requirements/conda-env-Linux-X64.txt diff --git a/.github/workflows/_run_android_tests.yml b/.github/workflows/_run_android_tests.yml index 5f1a1c46bc07..8121abe40aee 100644 --- a/.github/workflows/_run_android_tests.yml +++ b/.github/workflows/_run_android_tests.yml @@ -41,9 +41,17 @@ jobs: strategy: matrix: ${{ fromJSON(needs.filter.outputs.test-matrix) }} fail-fast: false + # NB: This job can only run on GitHub Linux runner atm. This is an ok thing though + # because that runner is ephemeral and could access upload secrets runs-on: ${{ matrix.runner }} + env: + # GitHub runner installs Android SDK on this path + ANDROID_ROOT: /usr/local/lib/android + ANDROID_NDK_VERSION: '21.4.7075529' + BUILD_LITE_INTERPRETER: ${{ matrix.use_lite_interpreter }} + # 4 of them are supported atm: armeabi-v7a, arm64-v8a, x86, x86_64 + SUPPORT_ABI: '${{ matrix.support_abi }}' steps: - # [see note: pytorch repo ref] - name: Checkout PyTorch uses: pytorch/pytorch/.github/actions/checkout-pytorch@main @@ -51,7 +59,7 @@ jobs: uses: pytorch/test-infra/.github/actions/setup-miniconda@main with: python-version: 3.8 - environment-file: .github/requirements/conda-env-${{ runner.os }}-${{ runner.arch }} + environment-file: .github/requirements/conda-env-${{ runner.os }}-${{ runner.arch }}.txt - name: Install NDK uses: nick-fields/retry@v2.8.2 @@ -60,12 +68,12 @@ jobs: max_attempts: 3 retry_wait_seconds: 90 command: | + set -eux + # Install NDK 21 after GitHub update # https://github.com/actions/virtual-environments/issues/5595 - ANDROID_ROOT="/usr/local/lib/android" ANDROID_SDK_ROOT="${ANDROID_ROOT}/sdk" ANDROID_NDK="${ANDROID_SDK_ROOT}/ndk-bundle" - ANDROID_NDK_VERSION="21.4.7075529" SDKMANAGER="${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager" # NB: This step downloads and installs NDK, thus it could be flaky. @@ -86,8 +94,10 @@ jobs: - name: Build PyTorch Android run: | + set -eux + echo "CMAKE_PREFIX_PATH=${CONDA_PREFIX:-"$(dirname "$(which conda)")/../"}" >> "${GITHUB_ENV}" - ${CONDA_RUN} ./scripts/build_pytorch_android.sh x86 + ${CONDA_RUN} ./scripts/build_pytorch_android.sh "${SUPPORT_ABI}" - name: Run tests uses: reactivecircus/android-emulator-runner@v2 diff --git a/.github/workflows/build-android-binaries.yml b/.github/workflows/build-android-binaries.yml new file mode 100644 index 000000000000..7bf786522795 --- /dev/null +++ b/.github/workflows/build-android-binaries.yml @@ -0,0 +1,48 @@ +name: Build Android binaries + +on: + push: + branches: + - nightly + tags: + # NOTE: Binary build pipelines should only get triggered on release candidate builds + # Release candidate tags look like: v1.11.0-rc1 + - v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+ + paths: + - .github/workflows/build-android-binaries.yml + - .github/workflows/_run_android_tests.yml + - android/** + pull_request: + paths: + - .github/workflows/build-android-binaries.yml + - .github/workflows/_run_android_tests.yml + - android/** + # NB: We can use this workflow dispatch to test and build the binaries manually + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.event_name == 'workflow_dispatch' }} + cancel-in-progress: true + +jobs: + android-build-test: + name: android-build-test + uses: ./.github/workflows/_run_android_tests.yml + with: + test-matrix: | + { include: [ + { config: 'default', + shard: 1, + num_shards: 1, + runner: 'ubuntu-20.04-16x', + use_lite_interpreter: 1, + support_abi: 'armeabi-v7a,arm64-v8a,x86,x86_64', + }, + { config: 'default', + shard: 1, + num_shards: 1, + runner: 'ubuntu-20.04-16x', + use_lite_interpreter: 0, + support_abi: 'armeabi-v7a,arm64-v8a,x86,x86_64', + }, + ]} diff --git a/.github/workflows/periodic.yml b/.github/workflows/periodic.yml index 1555c8cdac39..c3f678558fb6 100644 --- a/.github/workflows/periodic.yml +++ b/.github/workflows/periodic.yml @@ -189,7 +189,14 @@ jobs: with: test-matrix: | { include: [ - { config: "default", shard: 1, num_shards: 1, runner: "ubuntu-20.04-16x" }, + { config: 'default', + shard: 1, + num_shards: 1, + runner: 'ubuntu-20.04-16x', + use_lite_interpreter: 1, + # Just set x86 for testing here + support_abi: 'x86', + }, ]} linux-vulkan-focal-py3_11-clang10-build: diff --git a/.gitignore b/.gitignore index a374a14337dd..0b97756e4d33 100644 --- a/.gitignore +++ b/.gitignore @@ -365,3 +365,7 @@ venv/ # Log files *.log sweep/ + +# Android build artifacts +android/pytorch_android/.cxx +android/pytorch_android_torchvision/.cxx diff --git a/android/pytorch_android/src/androidTest/java/org/pytorch/PytorchTestBase.java b/android/pytorch_android/src/androidTest/java/org/pytorch/PytorchTestBase.java index 9abcbcbda8a6..d2dfa93da17a 100644 --- a/android/pytorch_android/src/androidTest/java/org/pytorch/PytorchTestBase.java +++ b/android/pytorch_android/src/androidTest/java/org/pytorch/PytorchTestBase.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.junit.Test; +import org.junit.Ignore; public abstract class PytorchTestBase { private static final String TEST_MODULE_ASSET_NAME = "android_api_module.ptl"; @@ -413,7 +414,10 @@ public abstract class PytorchTestBase { } @Test + @Ignore public void testSpectralOps() throws IOException { + // NB: This model fails without lite interpreter. The error is as follows: + // RuntimeError: stft requires the return_complex parameter be given for real inputs runModel("spectral_ops"); } diff --git a/android/test_app/app/build.gradle b/android/test_app/app/build.gradle index 71c58d4a5b90..963ff1a7a426 100644 --- a/android/test_app/app/build.gradle +++ b/android/test_app/app/build.gradle @@ -41,6 +41,11 @@ android { buildConfigField("long[]", "INPUT_TENSOR_SHAPE", "new long[]{1, 3, 224, 224}") buildConfigField("boolean", "NATIVE_BUILD", 'false') buildConfigField("boolean", "USE_VULKAN_DEVICE", 'false') + buildConfigField( + "int", + "BUILD_LITE_INTERPRETER", + System.env.BUILD_LITE_INTERPRETER != null ? System.env.BUILD_LITE_INTERPRETER : "1" + ) addManifestPlaceholders([APP_NAME: "@string/app_name", MAIN_ACTIVITY: "org.pytorch.testapp.MainActivity"]) } buildTypes { @@ -63,14 +68,15 @@ android { mnet { dimension "model" applicationIdSuffix ".mnet" - buildConfigField("String", "MODULE_ASSET_NAME", "\"mnet.pt\"") + buildConfigField("String", "MODULE_ASSET_NAME", "\"mobilenet_v2.ptl\"") addManifestPlaceholders([APP_NAME: "MNET"]) buildConfigField("String", "LOGCAT_TAG", "\"pytorch-mnet\"") } + // NB: This is not working atm https://github.com/pytorch/pytorch/issues/102966 mnetVulkan { dimension "model" applicationIdSuffix ".mnet_vulkan" - buildConfigField("String", "MODULE_ASSET_NAME", "\"mnet_vulkan.pt\"") + buildConfigField("String", "MODULE_ASSET_NAME", "\"mobilenet_v2_vulkan.ptl\"") buildConfigField("boolean", "USE_VULKAN_DEVICE", 'true') addManifestPlaceholders([APP_NAME: "MNET_VULKAN"]) buildConfigField("String", "LOGCAT_TAG", "\"pytorch-mnet-vulkan\"") @@ -78,7 +84,7 @@ android { resnet18 { dimension "model" applicationIdSuffix ".resnet18" - buildConfigField("String", "MODULE_ASSET_NAME", "\"resnet18.pt\"") + buildConfigField("String", "MODULE_ASSET_NAME", "\"resnet18.ptl\"") addManifestPlaceholders([APP_NAME: "RN18"]) buildConfigField("String", "LOGCAT_TAG", "\"pytorch-resnet18\"") } @@ -149,8 +155,8 @@ dependencies { //nativeBuildImplementation(name: 'pytorch_android_torchvision-release', ext: 'aar') //extractForNativeBuild(name: 'pytorch_android-release', ext: 'aar') - nightlyImplementation 'org.pytorch:pytorch_android:1.12.0-SNAPSHOT' - nightlyImplementation 'org.pytorch:pytorch_android_torchvision:1.12.0-SNAPSHOT' + nightlyImplementation 'org.pytorch:pytorch_android:2.2.0-SNAPSHOT' + nightlyImplementation 'org.pytorch:pytorch_android_torchvision:2.2.0-SNAPSHOT' aarImplementation(name:'pytorch_android', ext:'aar') aarImplementation(name:'pytorch_android_torchvision', ext:'aar') diff --git a/test/mobile/model_test/gen_test_model.py b/test/mobile/model_test/gen_test_model.py index 7c6b780e8d6d..a5365bc4be9b 100644 --- a/test/mobile/model_test/gen_test_model.py +++ b/test/mobile/model_test/gen_test_model.py @@ -47,7 +47,11 @@ from tensor_ops import ( TensorViewOpsModule, ) from torch.jit.mobile import _load_for_lite_interpreter -from torchvision_models import MobileNetV2Module +from torchvision_models import ( + MobileNetV2Module, + MobileNetV2VulkanModule, + Resnet18Module, +) test_path_ios = "ios/TestApp/models/" test_path_android = "android/pytorch_android/src/androidTest/assets/" @@ -98,6 +102,8 @@ all_modules = { "torchscript_collection_ops": TSCollectionOpsModule(), # vision "mobilenet_v2": MobileNetV2Module(), + "mobilenet_v2_vulkan": MobileNetV2VulkanModule(), + "resnet18": Resnet18Module(), # android api module "android_api_module": AndroidAPIModule(), } diff --git a/test/mobile/model_test/torchvision_models.py b/test/mobile/model_test/torchvision_models.py index 8684724d4771..25c4ab15c5d0 100644 --- a/test/mobile/model_test/torchvision_models.py +++ b/test/mobile/model_test/torchvision_models.py @@ -19,3 +19,37 @@ class MobileNetV2Module: ) optimized_module(example) return optimized_module + + +class MobileNetV2VulkanModule: + def getModule(self): + model = torchvision.models.mobilenet_v2(pretrained=True) + model.eval() + example = torch.zeros(1, 3, 224, 224) + traced_script_module = torch.jit.trace(model, example) + optimized_module = optimize_for_mobile(traced_script_module, backend="vulkan") + augment_model_with_bundled_inputs( + optimized_module, + [ + (example, ), + ], + ) + optimized_module(example) + return optimized_module + + +class Resnet18Module: + def getModule(self): + model = torchvision.models.resnet18(pretrained=True) + model.eval() + example = torch.zeros(1, 3, 224, 224) + traced_script_module = torch.jit.trace(model, example) + optimized_module = optimize_for_mobile(traced_script_module) + augment_model_with_bundled_inputs( + optimized_module, + [ + (example, ), + ], + ) + optimized_module(example) + return optimized_module