Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS] Add tracker support into ios-rpc application #7876

Merged
merged 23 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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