pytorch/android
Jiakai Liu ff17b83fd8 [pytorch][ci] add custom selective build flow for android build (#40199)
Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/40199

Mobile custom selective build has already been covered by `test/mobile/custom_build/build.sh`.
It builds a CLI binary with host-toolchain and runs on host machine to
check correctness of the result.

But that custom build test doesn't cover the android/gradle build part.
And we cannot use it to measure and track the in-APK size of custom
build library.

So this PR adds the selective build test coverage for android NDK build.
Also integrate with the CI to upload the custom build size to scuba.

TODO:
Ideally it should build android/test_app and measure the in-APK size.
But the test_app hasn't been covered by any CI yet and is currently
broken, so build & measure AAR instead (which can be inaccurate as we
plan to pack C++ header files into AAR soon).

Sample result: https://fburl.com/scuba/pytorch_binary_size/skxwb1gh
```

+---------------------+-------------+-------------------+-----------+----------+
|     build_mode      |    arch     |        lib        | Build Num |   Size   |
+---------------------+-------------+-------------------+-----------+----------+
| custom-build-single | armeabi-v7a | libpytorch_jni.so |   5901579 | 3.68 MiB |
| prebuild            | armeabi-v7a | libpytorch_jni.so |   5901014 | 6.23 MiB |
| prebuild            | x86_64      | libpytorch_jni.so |   5901014 | 7.67 MiB |
+---------------------+-------------+-------------------+-----------+----------+
```

Test Plan: Imported from OSS

Differential Revision: D22111115

Pulled By: ljk53

fbshipit-source-id: 11d24efbc49a85f851ecd0e481d14123f405b3a9
2020-07-02 21:11:01 -07:00
..
gradle [android] Maven publishing license fix (#32474) 2020-03-20 12:27:08 -07:00
libs [android] Remove android fbjni subproject (#39691) 2020-06-15 15:58:18 -07:00
pytorch_android [Selective Build] Apply query-based on instrumentation_tests 2020-06-26 08:05:53 -07:00
pytorch_android_torchvision [codemod][lint][fbcode] Apply google-java-format 2020-02-13 12:14:14 -08:00
test_app [pytorch][ci] add custom selective build flow for android build (#40199) 2020-07-02 21:11:01 -07:00
.gitignore
build.gradle Revert D22118971: [android] gradle version update 2020-06-19 08:48:21 -07:00
build_test_app.sh [pytorch] consolidate android gradle build scripts (#39999) 2020-06-15 23:55:21 -07:00
build_test_app_custom.sh [pytorch][ci] add custom selective build flow for android build (#40199) 2020-07-02 21:11:01 -07:00
common.sh [pytorch] consolidate android gradle build scripts (#39999) 2020-06-15 23:55:21 -07:00
gradle.properties Bump nightlies to 1.7.0 (#40519) 2020-06-25 22:36:33 -07:00
README.md [android][readme] Aar native linking add fbjni (#40578) 2020-07-01 08:09:17 -07:00
run_tests.sh [pytorch] consolidate android gradle build scripts (#39999) 2020-06-15 23:55:21 -07:00
settings.gradle [android] Remove android fbjni subproject (#39691) 2020-06-15 15:58:18 -07:00

Android

Demo applications and tutorials

Demo applications with code walk-through can be find in this github repo.

Publishing

Release

Release artifacts are published to jcenter:

repositories {
    jcenter()
}

dependencies {
    implementation 'org.pytorch:pytorch_android:1.5.0'
    implementation 'org.pytorch:pytorch_android_torchvision:1.5.0'
}
Nightly

Nightly(snapshots) builds are published every night from master branch to nexus sonatype snapshots repository

To use them repository must be specified explicitly:

repositories {
    maven {
        url "https://oss.sonatype.org/content/repositories/snapshots"
    }
}

dependencies {
    ...
    implementation 'org.pytorch:pytorch_android:1.7.0-SNAPSHOT'
    implementation 'org.pytorch:pytorch_android_torchvision:1.7.0-SNAPSHOT'
    ...
}

The current nightly(snapshots) version is the value of VERSION_NAME in gradle.properties in current folder, at this moment it is 1.7.0-SNAPSHOT.

Building PyTorch Android from Source

In some cases you might want to use a local build of pytorch android, for example you may build custom libtorch binary with another set of operators or to make local changes.

For this you can use ./scripts/build_pytorch_android.sh script.

git clone https://github.com/pytorch/pytorch.git
cd pytorch
git submodule update --init --recursive
sh ./scripts/build_pytorch_android.sh

The workflow contains several steps:

1. Build libtorch for android for all 4 android abis (armeabi-v7a, arm64-v8a, x86, x86_64)

2. Create symbolic links to the results of those builds: android/pytorch_android/src/main/jniLibs/${abi} to the directory with output libraries android/pytorch_android/src/main/cpp/libtorch_include/${abi} to the directory with headers. These directories are used to build libpytorch.so library that will be loaded on android device.

3. And finally run gradle in android/pytorch_android directory with task assembleRelease

Script requires that Android SDK, Android NDK and gradle are installed. They are specified as environment variables:

ANDROID_HOME - path to Android SDK

ANDROID_NDK - path to Android NDK

GRADLE_HOME - path to gradle

After successful build you should see the result as aar file:

$ find pytorch_android/build/ -type f -name *aar
pytorch_android/build/outputs/aar/pytorch_android.aar
pytorch_android_torchvision/build/outputs/aar/pytorch_android.aar

It can be used directly in android projects, as a gradle dependency:

allprojects {
    repositories {
        flatDir {
            dirs 'libs'
        }
    }
}

dependencies {
    implementation(name:'pytorch_android', ext:'aar')
    implementation(name:'pytorch_android_torchvision', ext:'aar')
    ...
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.facebook.soloader:nativeloader:0.8.0'
    implementation 'com.facebook.fbjni:fbjni-java-only:0.0.3'
}

We also have to add all transitive dependencies of our aars. As pytorch_android depends on 'com.android.support:appcompat-v7:28.0.0', 'com.facebook.soloader:nativeloader:0.8.0' and 'com.facebook.fbjni:fbjni-java-only:0.0.3', we need to add them. (In case of using maven dependencies they are added automatically from pom.xml).

You can check out test app example that uses aars directly.

Linking to prebuilt libtorch library from gradle dependency

In some cases, you may want to use libtorch from your android native build. You can do it without building libtorch android, using native libraries from PyTorch android gradle dependency. For that, you will need to add the next lines to your gradle build.

android {
...
    configurations {
       extractForNativeBuild
    }
...
    compileOptions {
        externalNativeBuild {
            cmake {
                arguments "-DANDROID_STL=c++_shared"
            }
        }
    }
...
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    extractForNativeBuild('org.pytorch:pytorch_android:1.6.0')
}

task extractAARForNativeBuild {
    doLast {
        configurations.extractForNativeBuild.files.each {
            def file = it.absoluteFile
            copy {
                from zipTree(file)
                into "$buildDir/$file.name"
                include "headers/**"
                include "jni/**"
            }
        }
    }
}

tasks.whenTaskAdded { task ->
  if (task.name.contains('externalNativeBuild')) {
    task.dependsOn(extractAARForNativeBuild)
  }
}

pytorch_android aar contains headers to link in headers folder and native libraries in jni/$ANDROID_ABI/. As PyTorch native libraries use ANDROID_STL - we should use ANDROID_STL=c++_shared to have only one loaded binary of STL.

The added task will unpack them to gradle build directory.

In your native build you can link to them adding these lines to your CMakeLists.txt:

# Relative path of gradle build directory to CMakeLists.txt
set(build_DIR ${CMAKE_SOURCE_DIR}/build)

file(GLOB PYTORCH_INCLUDE_DIRS "${build_DIR}/pytorch_android*.aar/headers")
file(GLOB PYTORCH_LINK_DIRS "${build_DIR}/pytorch_android*.aar/jni/${ANDROID_ABI}")

set(BUILD_SUBDIR ${ANDROID_ABI})
target_include_directories(${PROJECT_NAME} PRIVATE
  ${PYTORCH_INCLUDE_DIRS}
)

find_library(PYTORCH_LIBRARY pytorch_jni
  PATHS ${PYTORCH_LINK_DIRS}
  NO_CMAKE_FIND_ROOT_PATH)

find_library(FBJNI_LIBRARY fbjni
  PATHS ${PYTORCH_LINK_DIRS}
  NO_CMAKE_FIND_ROOT_PATH)

target_link_libraries(${PROJECT_NAME}
  ${PYTORCH_LIBRARY})
  ${FBJNI_LIBRARY})

If your CMakeLists.txt file is located in the same directory as your build.gradle, set(build_DIR ${CMAKE_SOURCE_DIR}/build) should work for you. But if you have another location of it, you may need to change it.

After that, you can use libtorch C++ API from your native code.

#include <string>
#include <ATen/NativeFunctions.h>
#include <torch/script.h>
namespace pytorch_testapp_jni {
namespace {
    struct JITCallGuard {
      torch::autograd::AutoGradMode no_autograd_guard{false};
      torch::AutoNonVariableTypeMode non_var_guard{true};
      torch::jit::GraphOptimizerEnabledGuard no_optimizer_guard{false};
    };
}

void loadAndForwardModel(const std::string& modelPath) {
  JITCallGuard guard;
  torch::jit::Module module = torch::jit::load(modelPath);
  module.eval();
  torch::Tensor t = torch::randn({1, 3, 224, 224});
  c10::IValue t_out = module.forward({t});
}
}

To load torchscript model for mobile we need some special setup which is placed in struct JITCallGuard in this example. It may change in future, you can track the latest changes keeping an eye in our pytorch android jni code

Example of linking to libtorch from aar

PyTorch Android API Javadoc

You can find more details about the PyTorch Android API in the Javadoc.