Skip to content

Commit

Permalink
[iOS] Add tracker support into ios-rpc application (#7876)
Browse files Browse the repository at this point in the history
* [IOS-RPC] Missprint in flag value

Signed-off-by: Alexander Peskov <peskovnn@gmail.com>

* [IOS-RPC] custom_dyld up commit id. Fix mem leak

Signed-off-by: Alexander Peskov <peskovnn@gmail.com>

* [IOS-RPC] build without scheme

Signed-off-by: Alexander Peskov <peskovnn@gmail.com>

* [IOS-RPC] Add tracker support into ios-rpc app

Also containes:
* Links with tvm_runtime.dylib
* Minor improvements from UX perspective
* Add cli args support
* Add caching for url/port/key attributes

Signed-off-by: Alexander Peskov <peskovnn@gmail.com>

* [IOS-RPC] lint fix

Signed-off-by: Alexander Peskov <peskovnn@gmail.com>

* [IOS-RPC] Uniform servers

Also:
- Disabled bit-code
- Enabled ARC
- Use custom DSO loader by default
- Single button to connect/disconnect
- Add verbose flag

Signed-off-by: Alexander Peskov <peskovnn@gmail.com>

* [IOS_RPC] Min changes. Fix warnings

Signed-off-by: Alexander Peskov <peskovnn@gmail.com>

* [IOS-RPC] Fix review comments

Signed-off-by: Alexander Peskov <peskovnn@gmail.com>

* Fix RPC connection to tracker

* Fix typo

* Fix build for local developer profile

* Add tvmrpc xcode scheme

* Revert unnecessary change

* Remove old mechanism of reloading libs

* Display ip and port for PureRPC mode

* Update tests

* Remove tvmLauncher from ios_rpc

* Add updating tvm_build_dir in init_proj script

* Update default bundle

* Update README.md for ios_rpc

* Fix lint

* Apply comments

* Rename PureRPC to Standalone

Co-authored-by: Egor Churaev <egor.churaev@gmail.com>
  • Loading branch information
apeskov and echuraev committed Sep 22, 2021
1 parent 287ee40 commit 76d0534
Show file tree
Hide file tree
Showing 21 changed files with 1,778 additions and 906 deletions.
16 changes: 11 additions & 5 deletions apps/ios_rpc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ endif()
# It is required to load unsigned shared modules on real iOS devices
ExternalProject_Add(custom_dso_loader
GIT_REPOSITORY https://github.com/octoml/macho-dyld.git
GIT_TAG 48d1e8b5c40c7f5b744cb089634af17dd86125b2
GIT_TAG 0742b8129de7df1130be355b74faa8c036265bfc
PREFIX custom_dso_loader
LOG_DOWNLOAD TRUE
LOG_CONFIGURE TRUE
Expand All @@ -54,24 +54,30 @@ ExternalProject_Add(custom_dso_loader
-DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=${CMAKE_BUILD_WITH_INSTALL_NAME_DIR}
)

if(NOT CMAKE_IOS_RPC_BUNDLE)
set(CMAKE_IOS_RPC_BUNDLE org.apache.tvmrpc)
endif()

# iOS RPC Xcode project wrapper to integrate into Cmake
ExternalProject_Add(ios_rpc
PREFIX ios_rpc
DEPENDS custom_dso_loader
DEPENDS custom_dso_loader tvm_runtime
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
BUILD_COMMAND xcodebuild
-scheme tvmrpc
-target tvmrpc
-configuration ${CMAKE_BUILD_TYPE}
-project <SOURCE_DIR>/tvmrpc.xcodeproj
-derivedDataPath <BINARY_DIR>
-sdk ${CMAKE_OSX_SYSROOT}
-arch ${CMAKE_OSX_ARCHITECTURES}
-hideShellScriptEnvironment
-allowProvisioningUpdates
build
SYMROOT=<BINARY_DIR>
IPHONEOS_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}
DEVELOPMENT_TEAM=${CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM}
TVM_BUILD_DIR=${CMAKE_BINARY_DIR}
USE_CUSTOM_DSO_LOADER=YES
USE_CUSTOM_DSO_LOADER=1
PRODUCT_BUNDLE_IDENTIFIER=${CMAKE_IOS_RPC_BUNDLE}
)
296 changes: 210 additions & 86 deletions apps/ios_rpc/README.md

Large diffs are not rendered by default.

16 changes: 6 additions & 10 deletions apps/ios_rpc/init_proj.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import re

default_team_id = "3FR42MXLK9"
default_bundle_identifier = "org.apache.tvmrpc"
default_tvm_build_dir = "path-to-tvm-ios-build-folder"

parser = argparse.ArgumentParser(
description="Update tvmrpc.xcodeproj\
Expand All @@ -38,26 +38,22 @@
)

parser.add_argument(
"--bundle_identifier",
"--tvm_build_dir",
type=str,
required=False,
default=default_bundle_identifier,
help="The new bundle identifier\n\
(example: {})".format(
default_bundle_identifier
),
required=True,
help="Path to directory with libtvm_runtime.dylib",
)

args = parser.parse_args()
team_id = args.team_id
bundle_identifier = args.bundle_identifier
tvm_build_dir = args.tvm_build_dir

fi = open("tvmrpc.xcodeproj/project.pbxproj")
proj_config = fi.read()
fi.close()

proj_config = proj_config.replace(default_team_id, team_id)
proj_config = proj_config.replace(default_bundle_identifier, bundle_identifier)
proj_config = proj_config.replace(default_tvm_build_dir, tvm_build_dir)
fo = open("tvmrpc.xcodeproj/project.pbxproj", "w")
fo.write(proj_config)
fo.close()
46 changes: 25 additions & 21 deletions apps/ios_rpc/tests/ios_rpc_mobilenet.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,7 @@
from mxnet import gluon
from PIL import Image
import coremltools

# Set to be address of tvm proxy.
proxy_host = os.environ["TVM_IOS_RPC_PROXY_HOST"]
# Set your desination via env variable.
# Should in format "platform=iOS,id=<the test device uuid>"
destination = os.environ["TVM_IOS_RPC_DESTINATION"]

if not re.match(r"^platform=.*,id=.*$", destination):
print("Bad format: {}".format(destination))
print("Example of expected string: platform=iOS,id=1234567890abcabcabcabc1234567890abcabcab")
sys.exit(1)

proxy_port = 9090
key = "iphone"
import argparse

# Change target configuration, this is setting for iphone6s
# arch = "x86_64"
Expand All @@ -54,6 +41,8 @@
sdk = "iphoneos"
target_host = "llvm -mtriple=%s-apple-darwin" % arch

MODES = {"proxy": rpc.connect, "tracker": rpc.connect_tracker, "standalone": rpc.connect}

# override metal compiler to compile to iphone
@tvm.register_func("tvm_callback_metal_compile")
def compile_metal(src):
Expand Down Expand Up @@ -97,7 +86,7 @@ def get_model(model_name, data_shape):
return func, params


def test_mobilenet():
def test_mobilenet(host, port, key, mode):
temp = utils.tempdir()
image, synset = prepare_input()
model, params = get_model("mobilenetv2_1.0", image.shape)
Expand All @@ -107,13 +96,13 @@ def run(mod, target):
lib = relay.build(mod, target=target, target_host=target_host, params=params)
path_dso = temp.relpath("deploy.dylib")
lib.export_library(path_dso, xcode.create_dylib, arch=arch, sdk=sdk)
xcode.codesign(path_dso)

# Start RPC test server that contains the compiled library.
xcode.popen_test_rpc(proxy_host, proxy_port, key, destination=destination, libs=[path_dso])

# connect to the proxy
remote = rpc.connect(proxy_host, proxy_port, key=key)
if mode == "tracker":
remote = MODES[mode](host, port).request(key)
else:
remote = MODES[mode](host, port, key=key)
remote.upload(path_dso)

if target == "metal":
dev = remote.metal(0)
Expand Down Expand Up @@ -175,4 +164,19 @@ def annotate(func, compiler):


if __name__ == "__main__":
test_mobilenet()
parser = argparse.ArgumentParser(description="Demo app demonstrates how ios_rpc works.")
parser.add_argument("--host", required=True, type=str, help="Adress of rpc server")
parser.add_argument("--port", type=int, default=9090, help="rpc port (default: 9090)")
parser.add_argument("--key", type=str, default="iphone", help="device key (default: iphone)")
parser.add_argument(
"--mode",
type=str,
default="tracker",
help="type of RPC connection (default: tracker), possible values: {}".format(
", ".join(MODES.keys())
),
)

args = parser.parse_args()
assert args.mode in MODES.keys()
test_mobilenet(args.host, args.port, args.key, args.mode)
91 changes: 28 additions & 63 deletions apps/ios_rpc/tests/ios_rpc_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,22 @@
from tvm import rpc
from tvm.contrib import utils, xcode
import numpy as np

# Set to be address of tvm proxy.
proxy_host = os.environ["TVM_IOS_RPC_PROXY_HOST"]
# Set your destination via env variable.
# Should in format "platform=iOS,id=<the test device uuid>"
destination = os.environ["TVM_IOS_RPC_DESTINATION"]

if not re.match(r"^platform=.*,id=.*$", destination):
print("Bad format: {}".format(destination))
print("Example of expected string: platform=iOS,id=1234567890abcabcabcabc1234567890abcabcab")
sys.exit(1)

proxy_port = 9090
key = "iphone"
import argparse

# Change target configuration, this is setting for iphone6s
arch = "arm64"
sdk = "iphoneos"
target = "llvm -mtriple=%s-apple-darwin" % arch

MODES = {"proxy": rpc.connect, "tracker": rpc.connect_tracker, "standalone": rpc.connect}

# override metal compiler to compile to iphone
@tvm.register_func("tvm_callback_metal_compile")
def compile_metal(src):
return xcode.compile_metal(src, sdk=sdk)


def test_rpc_module():
def test_rpc_module(host, port, key, mode):
# graph
n = tvm.runtime.convert(1024)
A = te.placeholder((n,), name="A")
Expand All @@ -69,7 +58,6 @@ def test_rpc_module():
f = tvm.build(s, [A, B], "metal", target_host=target, name="myadd")
path_dso1 = temp.relpath("dev_lib.dylib")
f.export_library(path_dso1, xcode.create_dylib, arch=arch, sdk=sdk)
xcode.codesign(path_dso1)

s = te.create_schedule(B.op)
xo, xi = s[B].split(B.op.axis[0], factor=64)
Expand All @@ -79,72 +67,49 @@ def test_rpc_module():
f = tvm.build(s, [A, B], target, name="myadd_cpu")
path_dso2 = temp.relpath("cpu_lib.dylib")
f.export_library(path_dso2, xcode.create_dylib, arch=arch, sdk=sdk)
xcode.codesign(path_dso2)

# Start RPC test server that contains the compiled library.
server = xcode.popen_test_rpc(
proxy_host, proxy_port, key, destination=destination, libs=[path_dso1, path_dso2]
)

# connect to the proxy
remote = rpc.connect(proxy_host, proxy_port, key=key)
if mode == "tracker":
remote = MODES[mode](host, port).request(key)
else:
remote = MODES[mode](host, port, key=key)
remote.upload(path_dso1)
dev = remote.metal(0)
f1 = remote.load_module("dev_lib.dylib")
a_np = np.random.uniform(size=1024).astype(A.dtype)
a = tvm.nd.array(a_np, dev)
b = tvm.nd.array(np.zeros(1024, dtype=A.dtype), dev)
time_f = f1.time_evaluator(f1.entry_name, dev, number=10)
cost = time_f(a, b).mean
print("%g secs/op" % cost)
print("Metal: %g secs/op" % cost)
np.testing.assert_equal(b.numpy(), a.numpy() + 1)
# CPU
dev = remote.cpu(0)
remote.upload(path_dso2)
f2 = remote.load_module("cpu_lib.dylib")
a_np = np.random.uniform(size=1024).astype(A.dtype)
a = tvm.nd.array(a_np, dev)
b = tvm.nd.array(np.zeros(1024, dtype=A.dtype), dev)
time_f = f2.time_evaluator(f2.entry_name, dev, number=10)
cost = time_f(a, b).mean
print("%g secs/op" % cost)
np.testing.assert_equal(b.numpy(), a.numpy() + 1)


def test_rpc_module_with_upload():
server = xcode.popen_test_rpc(proxy_host, proxy_port, key, destination=destination)

remote = rpc.connect(proxy_host, proxy_port, key=key)
try:
remote.get_function("runtime.module.loadfile_dylib_custom")
except AttributeError as e:
print(e)
print("Skip test. You are using iOS RPC without custom DSO loader enabled.")
return

n = tvm.runtime.convert(1024)
A = te.placeholder((n,), name="A")
B = te.compute(A.shape, lambda *i: A(*i) + 1.0, name="B")
temp = utils.tempdir()
s = te.create_schedule(B.op)
xo, xi = s[B].split(B.op.axis[0], factor=64)
s[B].parallel(xi)
s[B].pragma(xo, "parallel_launch_point")
s[B].pragma(xi, "parallel_barrier_when_finish")
f = tvm.build(s, [A, B], target, name="myadd_cpu")
path_dso = temp.relpath("cpu_lib.dylib")
f.export_library(path_dso, xcode.create_dylib, arch=arch, sdk=sdk)

dev = remote.cpu(0)
remote.upload(path_dso)
f = remote.load_module("cpu_lib.dylib")
a_np = np.random.uniform(size=1024).astype(A.dtype)
a = tvm.nd.array(a_np, dev)
b = tvm.nd.array(np.zeros(1024, dtype=A.dtype), dev)
time_f = f.time_evaluator(f.entry_name, dev, number=10)
cost = time_f(a, b).mean
print("%g secs/op" % cost)
print("CPU: %g secs/op" % cost)
np.testing.assert_equal(b.numpy(), a.numpy() + 1)


if __name__ == "__main__":
test_rpc_module()
test_rpc_module_with_upload()
parser = argparse.ArgumentParser(description="Demo app demonstrates how ios_rpc works.")
parser.add_argument("--host", required=True, type=str, help="Adress of rpc server")
parser.add_argument("--port", type=int, default=9090, help="rpc port (default: 9090)")
parser.add_argument("--key", type=str, default="iphone", help="device key (default: iphone)")
parser.add_argument(
"--mode",
type=str,
default="tracker",
help="type of RPC connection (default: tracker), possible values: {}".format(
", ".join(MODES.keys())
),
)

args = parser.parse_args()
assert args.mode in MODES.keys()
test_rpc_module(args.host, args.port, args.key, args.mode)
Loading

0 comments on commit 76d0534

Please sign in to comment.