diff --git a/.gitignore b/.gitignore index 4f6a819247b68822cda94ea099f9d51de56c3a24..a36f7a1cefce9bec3c17b7930b079c8a8b401f6a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ mace/codegen/opencl_bin/ mace/codegen/tuning/ mace/codegen/version/ mace/codegen/engine/ +mace/codegen/lib/ build/ docs/_build/ *.a diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c9545fd88f94342e10c9db9f6814eceddb528d0d..d8dc2dc9d8301aef497e0614473badd3263458c0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -69,8 +69,6 @@ extra_tests: platform_compatible_tests: stage: platform_compatible_tests script: - - mkdir -p mace/codegen/version && bash mace/tools/git/gen_version_source.sh mace/codegen/version/version.cc - - mkdir -p mace/codegen/tuning && python mace/python/tools/binary_codegen.py --output_path=mace/codegen/tuning/tuning_params.cc - bazel build mace/core:core ndk_versions_compatible_tests: @@ -101,5 +99,8 @@ python_tools_tests: - rm -rf mace-models - GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" git clone git@github.com:XiaoMi/mace-models.git - CONF_FILE=mace-models/mobilenet-v2/mobilenet-v2.yml - - sh -c "python tools/converter.py build --config=${CONF_FILE} --disable_tuning && python tools/converter.py run --config=${CONF_FILE} --round=1 --validate && python tools/converter.py run --config=${CONF_FILE} --example --round=1 --validate" || exit 1 + - > + python tools/converter.py convert --config=${CONF_FILE} --model_graph_format=file --model_data_format=file || exit 1; + python tools/converter.py run --config=${CONF_FILE} --round=1 --validate --model_graph_format=file --model_data_format=file || exit 1; + python tools/converter.py run --config=${CONF_FILE} --example --round=1 --validate --model_graph_format=file --model_data_format=file || exit 1; - rm -rf mace-models diff --git a/WORKSPACE b/WORKSPACE index 2f9fe438ccfe5598b879359afdcf84026b52d76f..1176c1ba39895a74f1571bb64a0b5f62a216fbce 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,5 +1,12 @@ workspace(name = "mace") +# generate version and opencl kernel code. +load("//repository/git:git_configure.bzl", "git_version_repository") +load("//repository/opencl-kernel:opencl_kernel_configure.bzl", "encrypt_opencl_kernel_repository") + +git_version_repository(name="local_version_config") +encrypt_opencl_kernel_repository(name="local_opencl_kernel_encrypt") + # proto_library rules implicitly depend on @com_google_protobuf//:protoc, # which is the proto-compiler. # This statement defines the @com_google_protobuf repo. diff --git a/mace/BUILD b/mace/BUILD index ebc0e3653dde3a0f0cb796510e36aa602f79bcea..c1ad71119cfcfe85fda19010cde257c323d423ec 100644 --- a/mace/BUILD +++ b/mace/BUILD @@ -73,8 +73,33 @@ cc_library( visibility = ["//visibility:public"], ) -cc_library( +genrule( name = "libmace_static", - srcs = ["libmace.a"], + srcs = [ + "//mace/codegen:generated_opencl", + "//mace/codegen:generated_version", + "//mace/core", + "//mace/kernels", + "//mace/ops", + "//mace/utils", + "//mace/proto:mace_cc", + "@com_google_protobuf//:protobuf_lite", + ], + outs = ["libmace.a"], + cmd = "tmp_mri_file=$$(mktemp mace-static-lib-mri.XXXXXXXXXX);" + + "mri_stream=$$(python $(location //mace/python/tools:archive_static_lib) " + + "$(locations //mace/codegen:generated_opencl) " + + "$(locations //mace/codegen:generated_version) " + + "$(locations //mace/core:core) " + + "$(locations //mace/kernels:kernels) " + + "$(locations //mace/ops:ops) " + + "$(locations //mace/utils:utils) " + + "$(locations //mace/proto:mace_cc) " + + "$(locations @com_google_protobuf//:protobuf_lite) " + + "$@ " + + "$$tmp_mri_file);" + + "$(AR) -M <$$tmp_mri_file;" + + "rm -rf $$tmp_mri_file;", + tools = ["//mace/python/tools:archive_static_lib"], visibility = ["//visibility:public"], ) diff --git a/mace/benchmark/BUILD b/mace/benchmark/BUILD index b601a5d355ea6f9982e6935367f0a22377f50348..201c46dcb1148df6c45bdc14b2672feda427232a 100644 --- a/mace/benchmark/BUILD +++ b/mace/benchmark/BUILD @@ -36,11 +36,12 @@ cc_binary( "//external:gflags_nothreads", "//mace/codegen:generated_models", "//mace/codegen:generated_mace_engine_factory", + "//mace/ops:ops", ], ) cc_binary( - name = "benchmark_model_shared", + name = "benchmark_model_dynamic", srcs = [ "benchmark_model.cc", ], diff --git a/mace/benchmark/benchmark_model.cc b/mace/benchmark/benchmark_model.cc index 7d2c17f6b7b7fba760369dad4514cce038f1b105..b6e2a9c248af622628e279ac771d6ea705745c34 100644 --- a/mace/benchmark/benchmark_model.cc +++ b/mace/benchmark/benchmark_model.cc @@ -26,7 +26,9 @@ #include "mace/utils/logging.h" #include "mace/utils/utils.h" #include "mace/benchmark/statistics.h" +#ifdef MODEL_GRAPH_FORMAT_CODE #include "mace/codegen/engine/mace_engine_factory.h" +#endif namespace mace { namespace benchmark { @@ -191,6 +193,9 @@ DEFINE_int32(warmup_runs, 1, "how many runs to initialize model"); DEFINE_string(opencl_binary_file, "", "compiled opencl binary file path"); +DEFINE_string(opencl_parameter_file, + "", + "tuned OpenCL parameter file path"); DEFINE_string(model_data_file, "", "model data file name, used when EMBED_MODEL_DATA set to 0"); DEFINE_string(model_file, "", @@ -267,6 +272,8 @@ int Main(int argc, char **argv) { std::vector opencl_binary_paths = {FLAGS_opencl_binary_file}; mace::SetOpenCLBinaryPaths(opencl_binary_paths); + + mace::SetOpenCLParameterPath(FLAGS_opencl_parameter_file); } #endif // MACE_ENABLE_OPENCL @@ -285,27 +292,30 @@ int Main(int argc, char **argv) { // Create Engine const char *model_data_file_ptr = FLAGS_model_data_file.empty() ? nullptr : FLAGS_model_data_file.c_str(); + + std::vector model_pb_data; if (FLAGS_model_file != "") { - std::vector model_pb_data; if (!mace::ReadBinaryFile(&model_pb_data, FLAGS_model_file)) { LOG(FATAL) << "Failed to read file: " << FLAGS_model_file; } - create_engine_status = - CreateMaceEngineFromProto(model_pb_data, - model_data_file_ptr, - input_names, - output_names, - device_type, - &engine); - } else { - create_engine_status = + } +#ifdef MODEL_GRAPH_FORMAT_CODE + create_engine_status = CreateMaceEngineFromCode(FLAGS_model_name, model_data_file_ptr, input_names, output_names, device_type, &engine); - } +#else + create_engine_status = + CreateMaceEngineFromProto(model_pb_data, + model_data_file_ptr, + input_names, + output_names, + device_type, + &engine); +#endif if (create_engine_status != MaceStatus::MACE_SUCCESS) { LOG(FATAL) << "Create engine error, please check the arguments"; } diff --git a/mace/codegen/BUILD b/mace/codegen/BUILD index a38f32954603129dc99841da27880452e4936818..e42a1b13049cc6d24503b2398a1cb19ffcc87a3c 100644 --- a/mace/codegen/BUILD +++ b/mace/codegen/BUILD @@ -5,6 +5,8 @@ package( default_visibility = ["//visibility:public"], ) +load("//mace:mace.bzl", "mace_version_genrule", "encrypt_opencl_kernel_genrule") + cc_library( name = "generated_models", srcs = glob(["models/*/*.cc"]), @@ -12,19 +14,16 @@ cc_library( copts = ["-Werror", "-Wextra", "-Wno-missing-field-initializers"], deps = [ "//mace/core", - "//mace/ops", ], ) -cc_library( - name = "generated_opencl", - srcs = glob(["opencl/*.cc"]), - copts = ["-Werror", "-Wextra", "-Wno-missing-field-initializers"], -) +mace_version_genrule() + +encrypt_opencl_kernel_genrule() cc_library( - name = "generated_tuning_params", - srcs = ["tuning/tuning_params.cc"], + name = "generated_opencl", + srcs = ["opencl/encrypt_opencl_kernel.cc"], copts = ["-Werror", "-Wextra", "-Wno-missing-field-initializers"], ) @@ -36,9 +35,22 @@ cc_library( cc_library( name = "generated_mace_engine_factory", - hdrs = ["engine/mace_engine_factory.h"], + hdrs = glob(["engine/*.h"]), copts = ["-Werror", "-Wextra", "-Wno-missing-field-initializers"], deps = [ "//mace/public", ], ) + +cc_library( + name = "generated_libmace", + srcs = glob(["lib/*.so"]), + visibility = ["//visibility:public"], +) + +cc_library( + name = "generated_libmace_static", + srcs = glob(["lib/*.a"]), + linkstatic = 1, + visibility = ["//visibility:public"], +) diff --git a/mace/core/BUILD b/mace/core/BUILD index c819d80033a66e18ed20b0522bbc62ff94658ef4..8e3fb50ffd91106348808ce05bf63330e60ee2b1 100644 --- a/mace/core/BUILD +++ b/mace/core/BUILD @@ -61,7 +61,6 @@ cc_library( ]), deps = [ "//mace/codegen:generated_version", - "//mace/codegen:generated_tuning_params", "//mace/proto:mace_cc", "//mace/utils", ] + if_android([ diff --git a/mace/core/mace_runtime.cc b/mace/core/mace_runtime.cc index 8fe2b6c3b5222b7a9f68229349db040a58ea6b4d..dd7469a8b18e48d31ae6c0c6185ca1b297a8105c 100644 --- a/mace/core/mace_runtime.cc +++ b/mace/core/mace_runtime.cc @@ -24,4 +24,10 @@ void SetKVStorageFactory(std::shared_ptr storage_factory) { kStorageFactory = storage_factory; } +std::string kOpenCLParameterPath; // NOLINT(runtime/string) + +void SetOpenCLParameterPath(const std::string &path) { + kOpenCLParameterPath = path; +} + }; // namespace mace diff --git a/mace/examples/README.md b/mace/examples/README.md deleted file mode 100644 index 328833fd5a0bcca9bc779c0e4eab912c95d318e3..0000000000000000000000000000000000000000 --- a/mace/examples/README.md +++ /dev/null @@ -1,32 +0,0 @@ -Examples -======= - -* Build the example (e.g., with armeabi-v7a target) - -``` -# To enable debug mode build, use '-c dbg' flag. -# To check the underlying commands executed, use '-s' flag. -# TO check the failed command, use '--verbose_failures' flag. - -bazel build -c opt mace/examples:helloworld \ - --crosstool_top=//external:android/crosstool \ - --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \ - --cpu=arm64-v8a -``` - -* To run adb inside docker, the container network should use 'host' -``` -docker run -it --net=host mace-dev /bin/bash -``` - -* Push and run the example -``` -adb shell "mkdir /data/local/tmp" -adb push bazel-bin/mace/examples/helloworld /data/local/tmp/ -adb shell /data/local/tmp/helloworld -``` - -* Check the logs -``` -adb logcat | grep native -``` diff --git a/mace/examples/cli/BUILD b/mace/examples/cli/BUILD index 95086ad74d523e87a2c5c4e2c7bed8d2c4f8bb88..c6f0d32be9139a94df797a61c24775ddfbbfe66f 100644 --- a/mace/examples/cli/BUILD +++ b/mace/examples/cli/BUILD @@ -23,14 +23,14 @@ cc_binary( deps = [ "//external:gflags_nothreads", "//mace/codegen:generated_mace_engine_factory", - "//mace:libmace_static", + "//mace/codegen:generated_libmace_static", ] + if_hexagon_enabled([ "//third_party/nnlib:libhexagon", ]), ) cc_binary( - name = "example_shared", + name = "example_dynamic", srcs = ["example.cc"], copts = [ "-Werror", @@ -39,13 +39,20 @@ cc_binary( ] + if_android([ "-DMACE_ENABLE_OPENCL", ]), - linkopts = ["-lm", "-pie", "-fPIE"] + if_openmp_enabled(["-fopenmp"]), + linkopts = [ + "-lm", + ] + if_openmp_enabled([ + "-fopenmp" + ]) + if_android([ + "-ldl", + "-pie", + "-llog", + ]), linkstatic = 0, deps = [ "//external:gflags_nothreads", "//mace/codegen:generated_mace_engine_factory", - "//mace/utils:utils", - "//mace:libmace", + "//mace/codegen:generated_libmace", ], ) diff --git a/mace/examples/cli/README.md b/mace/examples/cli/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ba70a7e61dcaef7a550a68262cc872fc333142f5 --- /dev/null +++ b/mace/examples/cli/README.md @@ -0,0 +1,23 @@ +Examples +======= + +* Convert model + +``` +python tools/converter.py convert --config=/path/to/your/model_deployment_file +``` + +* Run example +``` +python tools/converter.py run --config=/path/to/your/model_deployment_file --example +``` + +* Validate result +``` +python tools/converter.py run --config=/path/to/your/model_deployment_file --example --example +``` + +* Check the logs +``` +adb logcat +``` diff --git a/mace/examples/cli/example.cc b/mace/examples/cli/example.cc index 323730585db159a10b21cb152e80b5257f83e3ed..2270f295bb1317992c1f3b01d343edb3fffe9e5e 100644 --- a/mace/examples/cli/example.cc +++ b/mace/examples/cli/example.cc @@ -23,7 +23,7 @@ #include "mace/public/mace.h" #include "mace/public/mace_runtime.h" // if convert model to code. -#ifdef CODE_TYPE +#ifdef MODEL_GRAPH_FORMAT_CODE #include "mace/codegen/engine/mace_engine_factory.h" #endif @@ -108,6 +108,9 @@ DEFINE_string(output_file, DEFINE_string(opencl_binary_file, "", "compiled opencl binary file path"); +DEFINE_string(opencl_parameter_file, + "", + "tuned OpenCL parameter file path"); DEFINE_string(model_data_file, "", "model data file name, used when EMBED_MODEL_DATA set to 0"); @@ -123,7 +126,7 @@ DEFINE_int32(gpu_priority_hint, 3, "0:DEFAULT/1:LOW/2:NORMAL/3:HIGH"); DEFINE_int32(omp_num_threads, -1, "num of openmp threads"); DEFINE_int32(cpu_affinity_policy, 1, "0:AFFINITY_NONE/1:AFFINITY_BIG_ONLY/2:AFFINITY_LITTLE_ONLY"); -#ifndef CODE_TYPE +#ifndef MODEL_GRAPH_FORMAT_CODE namespace { bool ReadBinaryFile(std::vector *data, const std::string &filename) { @@ -172,6 +175,8 @@ bool RunModel(const std::vector &input_names, // you should update the binary when OpenCL Driver changed. std::vector opencl_binary_paths = {FLAGS_opencl_binary_file}; mace::SetOpenCLBinaryPaths(opencl_binary_paths); + + mace::SetOpenCLParameterPath(FLAGS_opencl_parameter_file); } #endif // MACE_ENABLE_OPENCL @@ -191,7 +196,7 @@ bool RunModel(const std::vector &input_names, MaceStatus create_engine_status; // Only choose one of the two type based on the `build_type` // in model deployment file(.yml). -#ifdef CODE_TYPE +#ifdef MODEL_GRAPH_FORMAT_CODE create_engine_status = CreateMaceEngineFromCode(FLAGS_model_name, FLAGS_model_data_file, diff --git a/mace/mace.bzl b/mace/mace.bzl index 9e81ee40c599028becfa3e611d2f409542491c4e..506f1dbaf0431d56a4dcca7e3e5c4f968f60155e 100644 --- a/mace/mace.bzl +++ b/mace/mace.bzl @@ -47,3 +47,21 @@ def if_openmp_enabled(a): "//mace:openmp_enabled": a, "//conditions:default": [], }) + + +def mace_version_genrule(): + native.genrule( + name = "mace_version_gen", + srcs = [str(Label("@local_version_config//:gen/version"))], + outs = ["version/version.cc"], + cmd = "cat $(SRCS) > $@;" + ) + +def encrypt_opencl_kernel_genrule(): + native.genrule( + name = "encrypt_opencl_kernel_gen", + srcs = [str(Label("@local_opencl_kernel_encrypt//:gen/encrypt_opencl_kernel"))], + outs = ["opencl/encrypt_opencl_kernel.cc"], + cmd = "cat $(SRCS) > $@;" + ) + diff --git a/mace/mace_version_script.lds b/mace/mace_version_script.lds index db9797ee637e9f2ab36e55acc492ea9d617af169..27cc2caa361a930d28c6311a48244f463186f228 100644 --- a/mace/mace_version_script.lds +++ b/mace/mace_version_script.lds @@ -6,6 +6,7 @@ mace { *FileStorageFactory*; *SetKVStorageFactory*; *SetOpenCLBinaryPaths*; + *SetOpenCLParameterPath*; *SetGPUHints*; *SetOpenMPThreadPolicy*; *SetOpenMPThreadAffinity*; diff --git a/mace/public/mace_runtime.h b/mace/public/mace_runtime.h index f7fbf430dddbce6747fd3b953c9778d655c523c0..807155bb4a82e87cd6bccc49d5fadd9cba7f62d5 100644 --- a/mace/public/mace_runtime.h +++ b/mace/public/mace_runtime.h @@ -90,6 +90,14 @@ void SetKVStorageFactory(std::shared_ptr storage_factory); __attribute__((visibility("default"))) void SetOpenCLBinaryPaths(const std::vector &paths); +// Just call once. (Not thread-safe) +// Set the path of Generated OpenCL parameter file +// if you use gpu for specific soc. +// The parameters is the local work group size tuned for specific SOC, which +// may be faster than the general parameters. +__attribute__((visibility("default"))) +void SetOpenCLParameterPath(const std::string &path); + // Set GPU hints, currently only supports Adreno GPU. // // Caution: this function may hurt performance if improper parameters provided. diff --git a/mace/python/tools/BUILD b/mace/python/tools/BUILD index bcbe98e099173c9b3d3ad23d5983975c21977e24..f7ccc9dc4cd616000833b7da763c6076f9cd1ad4 100644 --- a/mace/python/tools/BUILD +++ b/mace/python/tools/BUILD @@ -48,3 +48,10 @@ py_binary( "@six_archive//:six", ], ) + +py_binary( + name = "archive_static_lib", + srcs = ["archive_static_lib.py"], + srcs_version = "PY2AND3", + visibility = ["//visibility:public"], +) diff --git a/mace/python/tools/archive_static_lib.py b/mace/python/tools/archive_static_lib.py new file mode 100644 index 0000000000000000000000000000000000000000..30cc2b31f0e84680243e0daecf6897bd9df8d455 --- /dev/null +++ b/mace/python/tools/archive_static_lib.py @@ -0,0 +1,38 @@ +# Copyright 2018 Xiaomi, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + + +def is_static_lib(lib_name): + return lib_name.endswith('.a') or lib_name.endswith('.lo') + + +def merge_libs(input_libs, + output_lib_path, + mri_script): + # make static library + mri_stream = "" + mri_stream += "create %s\n" % output_lib_path + for lib in input_libs: + if is_static_lib(lib): + mri_stream += ("addlib %s\n" % lib) + mri_stream += "save\n" + mri_stream += "end\n" + with open(mri_script, 'w') as tmp: + tmp.write(mri_stream) + + +if __name__ == '__main__': + merge_libs(sys.argv[1:-2], sys.argv[-2], sys.argv[-1]) diff --git a/mace/python/tools/converter.py b/mace/python/tools/converter.py index 1da642629f97003456ba73a0a9a32ad7b7e8de6e..d023358a87c80eff668e2cddab56383629c91f57 100644 --- a/mace/python/tools/converter.py +++ b/mace/python/tools/converter.py @@ -198,7 +198,7 @@ def main(unused_args): FLAGS.output_dir, FLAGS.runtime, FLAGS.embed_model_data, FLAGS.winograd, FLAGS.data_type, - FLAGS.model_build_type) + FLAGS.model_graph_format) def str2bool(v): @@ -277,10 +277,10 @@ def parse_args(): default=True, help="embed model data.") parser.add_argument( - "--model_build_type", + "--model_graph_format", type=str, - default="code", - help="[proto|code] build models to code" + + default="file", + help="[file|code] build models to code" + "or `Protobuf` file.") parser.add_argument( "--data_type", diff --git a/mace/python/tools/encrypt_opencl_codegen.py b/mace/python/tools/encrypt_opencl_codegen.py index 38e3fea9216028425ffd9ae5b28807c28d61896f..cfb9c91e1c7f3faf9bcc26e5a17be22796f81f4f 100644 --- a/mace/python/tools/encrypt_opencl_codegen.py +++ b/mace/python/tools/encrypt_opencl_codegen.py @@ -14,6 +14,7 @@ import argparse import os +import shutil import sys import jinja2 @@ -68,8 +69,21 @@ def encrypt_opencl_codegen(cl_kernel_dir, output_path): data_type='unsigned char', variable_name='kEncryptedProgramMap') - if os.path.isfile(output_path): - os.remove(output_path) + output_dir = os.path.dirname(output_path) + if os.path.exists(output_dir): + if os.path.isdir(output_dir): + try: + shutil.rmtree(output_dir) + except OSError: + raise RuntimeError( + "Cannot delete directory %s due to permission " + "error, inspect and remove manually" % output_dir) + else: + raise RuntimeError( + "Cannot delete non-directory %s, inspect ", + "and remove manually" % output_dir) + os.makedirs(output_dir) + with open(output_path, "w") as w_file: w_file.write(cpp_cl_encrypted_kernel) diff --git a/mace/python/tools/mace_engine_factory.h.jinja2 b/mace/python/tools/mace_engine_factory.h.jinja2 index 354668765d114060a8212a98775dfefc43a0fdff..9262dc6712fa3b61aa593869d87750e5f92643fe 100644 --- a/mace/python/tools/mace_engine_factory.h.jinja2 +++ b/mace/python/tools/mace_engine_factory.h.jinja2 @@ -24,7 +24,6 @@ namespace mace { -{% if model_type == 'code' %} {% for tag in model_tags %} namespace {{tag}} { @@ -59,7 +58,6 @@ MaceStatus CreateMaceEngineFromCode( if (engine == nullptr) { return MaceStatus::MACE_INVALID_ARGS; } - const unsigned char * model_data = nullptr; std::shared_ptr net_def; MaceStatus status = MaceStatus::MACE_SUCCESS; switch (model_name_map[model_name]) { @@ -69,7 +67,7 @@ MaceStatus CreateMaceEngineFromCode( engine->reset(new mace::MaceEngine(device_type)); {% if embed_model_data %} (void)model_data_file; - model_data = + const unsigned char * model_data = mace::{{model_tags[i]}}::LoadModelData(); status = (*engine)->Init(net_def.get(), input_nodes, output_nodes, model_data); {% else %} @@ -83,22 +81,5 @@ MaceStatus CreateMaceEngineFromCode( return status; } -{% else %} -MaceStatus CreateMaceEngineFromCode( - const std::string &model_name, - const std::string &model_data_file, - const std::vector &input_nodes, - const std::vector &output_nodes, - const DeviceType device_type, - std::shared_ptr *engine) { - (void)(model_name); - (void)(model_data_file); - (void)(input_nodes); - (void)(output_nodes); - (void)(device_type); - (void)(engine); - return MaceStatus::MACE_INVALID_ARGS; -} -{% endif %} } // namespace mace diff --git a/mace/python/tools/mace_engine_factory_codegen.py b/mace/python/tools/mace_engine_factory_codegen.py index 36b24eaefc96f65101938d1c4358157f0aafd88a..ce91006506cecc14d2838a880f5c5398e8676886 100644 --- a/mace/python/tools/mace_engine_factory_codegen.py +++ b/mace/python/tools/mace_engine_factory_codegen.py @@ -20,7 +20,7 @@ from jinja2 import Environment, FileSystemLoader FLAGS = None -def gen_mace_engine_factory(model_tags, template_dir, model_type, +def gen_mace_engine_factory(model_tags, template_dir, embed_model_data, output_dir): # Create the jinja2 environment. j2_env = Environment( @@ -30,33 +30,6 @@ def gen_mace_engine_factory(model_tags, template_dir, model_type, source = j2_env.get_template(template_name).render( model_tags=model_tags, embed_model_data=embed_model_data, - model_type=model_type, ) with open(output_dir + '/mace_engine_factory.h', "wb") as f: f.write(source) - - -def parse_args(): - """Parses command line arguments.""" - parser = argparse.ArgumentParser() - parser.add_argument( - "--model_tag", - type=str, - default="", - help="model tag") - parser.add_argument( - "--template_dir", type=str, default="", help="template path") - parser.add_argument( - "--output_dir", type=str, default="", help="output path") - parser.add_argument( - "--model_type", - type=str, - default="", - help="[source|pb] model load type") - return parser.parse_known_args() - - -if __name__ == '__main__': - FLAGS, unparsed = parse_args() - gen_mace_engine_creator(FLAGS.model_tag, FLAGS.template_dir, - FLAGS.model_type, FLAGS.output_dir) diff --git a/mace/python/tools/model.jinja2 b/mace/python/tools/model.jinja2 index 8ee59882698a68d382ff9aeeaa5e62d45b4c8f6a..2d2ad8ec483a8f3c5da8b4d50a48c4af916d6524 100644 --- a/mace/python/tools/model.jinja2 +++ b/mace/python/tools/model.jinja2 @@ -14,7 +14,6 @@ // This is a generated file. DO NOT EDIT! -#include #include #include "mace/core/macros.h" diff --git a/mace/python/tools/model_saver.py b/mace/python/tools/model_saver.py index f41a9479d39d07df6de4d5d7500a57ee1a8a5add..0b849c76b95584b94d4b8c9b3a2161a54db3ff46 100644 --- a/mace/python/tools/model_saver.py +++ b/mace/python/tools/model_saver.py @@ -33,6 +33,11 @@ GPUDataType = \ Enum('GPUDataType', [(ele, ele) for ele in GPUDataTypeStrs], type=str) +class ModelFormat(object): + file = "file" + code = "code" + + def generate_obfuscated_name(namespace, name): md5 = hashlib.md5() md5.update(namespace) @@ -240,15 +245,15 @@ def save_model_to_code(net_def, model_tag, runtime, counter += 1 # generate tensor data - model_data = extract_model_data(net_def) - template_name = 'tensor_data.jinja2' - source = j2_env.get_template(template_name).render( - tag=model_tag, - embed_model_data=embed_model_data, - model_data_size=len(model_data), - model_data=model_data) - with open(output_dir + 'tensor_data' + '.cc', "wb") as f: - f.write(source) + if embed_model_data: + model_data = extract_model_data(net_def) + template_name = 'tensor_data.jinja2' + source = j2_env.get_template(template_name).render( + tag=model_tag, + model_data_size=len(model_data), + model_data=model_data) + with open(output_dir + 'tensor_data' + '.cc', "wb") as f: + f.write(source) # generate op source files template_name = 'operator.jinja2' @@ -293,7 +298,7 @@ def save_model_to_code(net_def, model_tag, runtime, def save_model(net_def, model_checksum, weight_checksum, template_dir, obfuscate, model_tag, output_dir, runtime, embed_model_data, - winograd_conv, data_type, model_build_type): + winograd_conv, data_type, model_graph_format): if obfuscate: obfuscate_name(net_def) else: @@ -303,10 +308,10 @@ def save_model(net_def, model_checksum, weight_checksum, template_dir, # update tensor type update_tensor_infos(net_def, runtime, data_type) - if model_build_type == 'proto' or not embed_model_data: + if model_graph_format == ModelFormat.file or not embed_model_data: save_model_data(net_def, model_tag, output_dir) - if model_build_type == 'proto': + if model_graph_format == ModelFormat.file: save_model_to_proto(net_def, model_tag, output_dir) else: save_model_to_code(net_def, model_tag, runtime, diff --git a/mace/python/tools/tensor_data.jinja2 b/mace/python/tools/tensor_data.jinja2 index ca267d6d43f1dfadf4748641777c196d7f0bcab3..e4a50c7a605094e71c7a58dc87bbd66a3532e7d2 100644 --- a/mace/python/tools/tensor_data.jinja2 +++ b/mace/python/tools/tensor_data.jinja2 @@ -14,28 +14,17 @@ // This is a generated file. DO NOT EDIT! -#include -#include - -#include "mace/core/macros.h" -#include "mace/public/mace.h" -#include "mace/utils/env_time.h" -#include "mace/utils/logging.h" namespace mace { namespace {{tag}} { -{% if embed_model_data %} alignas(4) const unsigned char model_data[{{ model_data_size }}] = { {% for d in model_data %}{{"0x%02X, " % d }}{%endfor%} }; -{% endif %} -{% if embed_model_data %} const unsigned char *LoadModelData() { return model_data; } -{% endif %} } // namespace {{tag}} } // namespace mace diff --git a/mace/python/tools/tensor_source.jinja2 b/mace/python/tools/tensor_source.jinja2 index e982e41f45c7fe02a3eda75e761e14fe30d1eb51..ef59c8bcdcaf35abb6b4f9da6988525637cbdbf6 100644 --- a/mace/python/tools/tensor_source.jinja2 +++ b/mace/python/tools/tensor_source.jinja2 @@ -14,9 +14,6 @@ // This is a generated file. DO NOT EDIT! -#include -#include - #include "mace/proto/mace.pb.h" #include "mace/public/mace.h" #include "mace/utils/env_time.h" diff --git a/mace/tools/git/gen_version_source.sh b/mace/tools/git/gen_version_source.sh index 8dde7a106301a7d7863224da94e44a5e721a780a..10309b1d1ee4df05308ef074afb3676b1789d98e 100644 --- a/mace/tools/git/gen_version_source.sh +++ b/mace/tools/git/gen_version_source.sh @@ -13,12 +13,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +MACE_SOURCE_DIR=$(dirname $0) + OUTPUT_FILENAME=$1 if [[ -z "${OUTPUT_FILENAME}}" ]]; then echo "Usage: $0 " exit 1 fi +OUTPUT_DIR=$(dirname $OUTPUT_FILENAME) +if [ -d $OUTPUT_DIR ]; then + rm -rf $OUTPUT_DIR +fi + +mkdir -p $OUTPUT_DIR + +pushd $MACE_SOURCE_DIR + DATE_STR=$(date +%Y%m%d) GIT_VERSION=$(git describe --long --tags) if [[ $? != 0 ]]; then @@ -49,3 +60,5 @@ __attribute__((visibility("default"))) const char *MaceVersion() { return "MACEVER-${GIT_VERSION}" + 8; } } // namespace mace EOF + +popd diff --git a/mace/tools/validation/BUILD b/mace/tools/validation/BUILD index 3e341220ddf49e3e611c326954bca78f017efa76..a1ba419b27ea448b2d53ea2b75fa31fdcac83af5 100644 --- a/mace/tools/validation/BUILD +++ b/mace/tools/validation/BUILD @@ -16,11 +16,12 @@ cc_binary( "//external:gflags_nothreads", "//mace/codegen:generated_mace_engine_factory", "//mace/codegen:generated_models", + "//mace/ops:ops", ], ) cc_binary( - name = "mace_run_shared", + name = "mace_run_dynamic", srcs = ["mace_run.cc"], copts = [ "-Werror", @@ -33,7 +34,7 @@ cc_binary( deps = [ "//external:gflags_nothreads", "//mace/codegen:generated_mace_engine_factory", - "//mace/utils:utils", "//mace:libmace", + "//mace/utils:utils", ], ) diff --git a/mace/tools/validation/mace_run.cc b/mace/tools/validation/mace_run.cc index c50f7502c4abacd77cca8e32898df14f63d48fae..54532d328ae47e1d915358141d08f0f371494065 100644 --- a/mace/tools/validation/mace_run.cc +++ b/mace/tools/validation/mace_run.cc @@ -38,7 +38,9 @@ #include "mace/utils/logging.h" #include "mace/utils/utils.h" +#ifdef MODEL_GRAPH_FORMAT_CODE #include "mace/codegen/engine/mace_engine_factory.h" +#endif namespace mace { namespace tools { @@ -175,6 +177,9 @@ DEFINE_string(output_file, DEFINE_string(opencl_binary_file, "", "compiled opencl binary file path"); +DEFINE_string(opencl_parameter_file, + "", + "tuned OpenCL parameter file path"); DEFINE_string(model_data_file, "", "model data file name, used when EMBED_MODEL_DATA set to 0 or 2"); @@ -212,6 +217,8 @@ bool RunModel(const std::string &model_name, std::vector opencl_binary_paths = {FLAGS_opencl_binary_file}; mace::SetOpenCLBinaryPaths(opencl_binary_paths); + + mace::SetOpenCLParameterPath(FLAGS_opencl_parameter_file); } #endif // MACE_ENABLE_OPENCL @@ -238,23 +245,24 @@ bool RunModel(const std::string &model_name, while (true) { // Create Engine int64_t t0 = NowMicros(); - if (FLAGS_model_file != "") { - create_engine_status = - CreateMaceEngineFromProto(model_pb_data, - FLAGS_model_data_file, - input_names, - output_names, - device_type, - &engine); - } else { - create_engine_status = +#ifdef MODEL_GRAPH_FORMAT_CODE + create_engine_status = CreateMaceEngineFromCode(model_name, FLAGS_model_data_file, input_names, output_names, device_type, &engine); - } +#else + (void)(model_name); + create_engine_status = + CreateMaceEngineFromProto(model_pb_data, + FLAGS_model_data_file, + input_names, + output_names, + device_type, + &engine); +#endif int64_t t1 = NowMicros(); if (create_engine_status != MACE_SUCCESS) { @@ -311,23 +319,23 @@ bool RunModel(const std::string &model_name, LOG(ERROR) << "Warmup runtime error, retry ... errcode: " << warmup_status; do { - if (FLAGS_model_file != "") { - create_engine_status = - CreateMaceEngineFromProto(model_pb_data, - FLAGS_model_data_file, - input_names, - output_names, - device_type, - &engine); - } else { - create_engine_status = - CreateMaceEngineFromCode(model_name, - FLAGS_model_data_file, - input_names, - output_names, - device_type, - &engine); - } +#ifdef MODEL_GRAPH_FORMAT_CODE + create_engine_status = + CreateMaceEngineFromCode(model_name, + FLAGS_model_data_file, + input_names, + output_names, + device_type, + &engine); +#else + create_engine_status = + CreateMaceEngineFromProto(model_pb_data, + FLAGS_model_data_file, + input_names, + output_names, + device_type, + &engine); +#endif } while (create_engine_status != MACE_SUCCESS); } else { int64_t t4 = NowMicros(); @@ -351,23 +359,23 @@ bool RunModel(const std::string &model_name, LOG(ERROR) << "Mace run model runtime error, retry ... errcode: " << run_status; do { - if (FLAGS_model_file != "") { - create_engine_status = - CreateMaceEngineFromProto(model_pb_data, - FLAGS_model_data_file, - input_names, - output_names, - device_type, - &engine); - } else { - create_engine_status = - CreateMaceEngineFromCode(model_name, - FLAGS_model_data_file, - input_names, - output_names, - device_type, - &engine); - } +#ifdef MODEL_GRAPH_FORMAT_CODE + create_engine_status = + CreateMaceEngineFromCode(model_name, + FLAGS_model_data_file, + input_names, + output_names, + device_type, + &engine); +#else + create_engine_status = + CreateMaceEngineFromProto(model_pb_data, + FLAGS_model_data_file, + input_names, + output_names, + device_type, + &engine); +#endif } while (create_engine_status != MACE_SUCCESS); } else { int64_t t1 = NowMicros(); diff --git a/mace/utils/BUILD b/mace/utils/BUILD index 69794347f21a71a8c0b24cd65038b2e06fb6af9a..c383eb87d1d56ae3ae0e7aec13517a43128ad66a 100644 --- a/mace/utils/BUILD +++ b/mace/utils/BUILD @@ -31,7 +31,6 @@ cc_library( copts = ["-Werror", "-Wextra", "-Wno-missing-field-initializers"], deps = [ "//mace/public", - "//mace/codegen:generated_tuning_params", ], ) @@ -49,6 +48,7 @@ cc_test( linkstatic = 1, deps = [ ":utils", + "//mace/core", "@gtest//:gtest", "@gtest//:gtest_main", ], diff --git a/mace/utils/tuner.h b/mace/utils/tuner.h index e2a83aac8e76c778ad7d159ece057163f0ff1d82..e4007b6694a415e7a058b5f6f33a93a2ba485e8e 100644 --- a/mace/utils/tuner.h +++ b/mace/utils/tuner.h @@ -117,13 +117,31 @@ class Tuner { } inline void ReadRunParamters() { - extern const std::map> - kTuningParamsData; - if (!kTuningParamsData.empty()) { - for (auto it = kTuningParamsData.begin(); it != kTuningParamsData.end(); - ++it) { - param_table_.emplace(it->first, std::vector( - it->second.begin(), it->second.end())); + extern std::string kOpenCLParameterPath; + if (!kOpenCLParameterPath.empty()) { + std::ifstream ifs(kOpenCLParameterPath, std::ios::binary | std::ios::in); + if (ifs.is_open()) { + int64_t num_params = 0; + ifs.read(reinterpret_cast(&num_params), sizeof(num_params)); + while (num_params--) { + int32_t key_size = 0; + ifs.read(reinterpret_cast(&key_size), sizeof(key_size)); + std::string key(key_size, ' '); + ifs.read(&key[0], key_size); + + int32_t params_size = 0; + ifs.read(reinterpret_cast(¶ms_size), sizeof(params_size)); + int32_t params_count = params_size / sizeof(unsigned int); + std::vector params(params_count); + for (int i = 0; i < params_count; ++i) { + ifs.read(reinterpret_cast(¶ms[i]), + sizeof(unsigned int)); + } + param_table_.emplace(key, params); + } + ifs.close(); + } else { + LOG(WARNING) << "Read OpenCL tuned parameters file failed."; } } else { LOG(INFO) << "There is no tuned parameters."; diff --git a/repository/git/BUILD b/repository/git/BUILD new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/repository/git/BUILD.tpl b/repository/git/BUILD.tpl new file mode 100644 index 0000000000000000000000000000000000000000..820dfa71e8c129fabe3b96711fffb04bd7076fcc --- /dev/null +++ b/repository/git/BUILD.tpl @@ -0,0 +1,11 @@ +# Description: +# Borrow from tensorflow +# Exports generated files used to generate mace/codegen/version/version.cc + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +exports_files( + glob(["gen/*"]), +) \ No newline at end of file diff --git a/repository/git/git_configure.bzl b/repository/git/git_configure.bzl new file mode 100644 index 0000000000000000000000000000000000000000..b46c74e557fdc061fb3e71216d9938c9eab5b81d --- /dev/null +++ b/repository/git/git_configure.bzl @@ -0,0 +1,21 @@ +"""Repository rule for Git autoconfiguration, borrow from tensorflow +""" +def _git_version_conf_impl(repository_ctx): + repository_ctx.template( + "BUILD", + Label("//repository/git:BUILD.tpl")) + + mace_root_path = str(repository_ctx.path(Label("@mace//:BUILD")))[:-len("BUILD")] + + generated_files_path = repository_ctx.path("gen") + + repository_ctx.execute([ + 'bash', '%s/mace/tools/git/gen_version_source.sh' % mace_root_path + , '%s/version' % generated_files_path + ], quiet=False) + + +git_version_repository = repository_rule( + implementation = _git_version_conf_impl, + local=True, +) diff --git a/repository/opencl-kernel/BUILD b/repository/opencl-kernel/BUILD new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/repository/opencl-kernel/BUILD.tpl b/repository/opencl-kernel/BUILD.tpl new file mode 100644 index 0000000000000000000000000000000000000000..e205fd35f39046188a983cbd13dc21fc38a2e2dc --- /dev/null +++ b/repository/opencl-kernel/BUILD.tpl @@ -0,0 +1,10 @@ +# Description: +# Exports generated files used to generate mace/codegen/opencl/opencl_encrypt_program.cc + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +exports_files( + glob(["gen/*"]), +) \ No newline at end of file diff --git a/repository/opencl-kernel/opencl_kernel_configure.bzl b/repository/opencl-kernel/opencl_kernel_configure.bzl new file mode 100644 index 0000000000000000000000000000000000000000..a0c715c75eaf2478bd18a7b283915b704855029c --- /dev/null +++ b/repository/opencl-kernel/opencl_kernel_configure.bzl @@ -0,0 +1,24 @@ +"""Repository rule for opencl encrypt kernel autoconfiguration, borrow from tensorflow +""" +def _opencl_encrypt_kernel_impl(repository_ctx): + repository_ctx.template( + "BUILD", + Label("//repository/opencl-kernel:BUILD.tpl")) + + mace_root_path = str(repository_ctx.path(Label("@mace//:BUILD")))[:-len("BUILD")] + + generated_files_path = repository_ctx.path("gen") + + python_bin_path = repository_ctx.which("python") + + repository_ctx.execute([ + python_bin_path, '%s/mace/python/tools/encrypt_opencl_codegen.py' % mace_root_path, + '--cl_kernel_dir=%s/mace/kernels/opencl/cl' % mace_root_path, + '--output_path=%s/encrypt_opencl_kernel' % generated_files_path + ], quiet=False) + + +encrypt_opencl_kernel_repository = repository_rule( + implementation = _opencl_encrypt_kernel_impl, + local=True, +) diff --git a/tools/bazel_adb_run.py b/tools/bazel_adb_run.py index 166a0edcbf41aac2d9c6be877ca708d08fb23800..7cf5e79fb3106b2eec0b80474d720770acbc15ea 100644 --- a/tools/bazel_adb_run.py +++ b/tools/bazel_adb_run.py @@ -130,11 +130,6 @@ def main(unused_args): host_bin_path, bin_name = sh_commands.bazel_target_to_bin(target) target_abis = FLAGS.target_abis.split(',') - # generate sources - sh_commands.gen_encrypted_opencl_source() - sh_commands.gen_mace_version() - sh_commands.gen_tuning_param_code([]) - for target_abi in target_abis: sh_commands.bazel_build(target, abi=target_abi, enable_neon=FLAGS.enable_neon, diff --git a/tools/build-standalone-lib.sh b/tools/build-standalone-lib.sh index 169a20087b51b39133e80cf45ab3718147145e78..f18ef67663975e286f8f772047ecd56554bc8abe 100755 --- a/tools/build-standalone-lib.sh +++ b/tools/build-standalone-lib.sh @@ -21,28 +21,44 @@ python mace/python/tools/binary_codegen.py --output_path=mace/codegen/tuning/tun # copy include headers cp mace/public/*.h $INCLUDE_DIR/ -echo "build lib for armeabi-v7a" +# make directories rm -rf $LIB_DIR/armeabi-v7a mkdir -p $LIB_DIR/armeabi-v7a -bazel build --config android --config optimization mace:libmace --cpu=armeabi-v7a --define neon=true --define openmp=true -cp bazel-bin/mace/libmace.so $LIB_DIR/armeabi-v7a/ - -echo "build lib for armeabi-v7a with hexagon dsp" -rm -rf $LIB_DIR/armeabi-v7a/hexagon-dsp -mkdir -p $LIB_DIR/armeabi-v7a/hexagon-dsp -bazel build --config android --config optimization mace:libmace --cpu=armeabi-v7a --define neon=true --define openmp=true --define hexagon=true -cp bazel-bin/mace/libmace.so $LIB_DIR/armeabi-v7a/hexagon-dsp/ -cp third_party/nnlib/*so $LIB_DIR/armeabi-v7a/hexagon-dsp/ -echo "build lib for arm64-v8a" rm -rf $LIB_DIR/arm64-v8a mkdir -p $LIB_DIR/arm64-v8a -bazel build --config android --config optimization mace:libmace --cpu=arm64-v8a --define neon=true --define openmp=true -cp bazel-bin/mace/libmace.so $LIB_DIR/arm64-v8a/ -echo "build lib for linux-x86-64" rm -rf $LIB_DIR/linux-x86-64 mkdir -p $LIB_DIR/linux-x86-64 -bazel build --config optimization mace:libmace --define openmp=true + +# build shared libraries +echo "build shared lib for armeabi-v7a" +bazel build --config android --config optimization mace:libmace --define neon=true --define openmp=true --define hexagon=true --cpu=armeabi-v7a +cp bazel-bin/mace/libmace.so $LIB_DIR/armeabi-v7a/ +cp third_party/nnlib/*so $LIB_DIR/armeabi-v7a/ + +echo "build shared lib for arm64-v8a" +bazel build --config android --config optimization mace:libmace --define neon=true --define openmp=true --cpu=arm64-v8a +cp bazel-bin/mace/libmace.so $LIB_DIR/arm64-v8a/ + +echo "build shared lib for linux-x86-64" +bazel build mace:libmace --config optimization --define openmp=true cp bazel-bin/mace/libmace.so $LIB_DIR/linux-x86-64/ + +# build static libraries +echo "build static lib for armeabi-v7a" +bazel build --config android --config optimization mace:libmace_static --define neon=true --define openmp=true --define hexagon=true --cpu=armeabi-v7a +cp bazel-genfiles/mace/libmace.a $LIB_DIR/armeabi-v7a/ +cp third_party/nnlib/*so $LIB_DIR/armeabi-v7a/ + +echo "build static lib for arm64-v8a" +bazel build --config android --config optimization mace:libmace_static --define neon=true --define openmp=true --cpu=arm64-v8a +cp bazel-genfiles/mace/libmace.a $LIB_DIR/arm64-v8a/ + +echo "build static lib for linux-x86-64" +bazel build mace:libmace --config optimization --define openmp=true +cp bazel-genfiles/mace/libmace.a $LIB_DIR/linux-x86-64/ + +echo "LIB PATH: $LIB_DIR" +echo "INCLUDE FILE PATH: $INCLUDE_DIR" diff --git a/tools/converter.py b/tools/converter.py index 2e2d62be9c5e54402e8b96f8d7aee64348493718..8c0ea3406368a78cc158a1e934147207f1829dc9 100644 --- a/tools/converter.py +++ b/tools/converter.py @@ -28,6 +28,7 @@ from enum import Enum import sh_commands from sh_commands import BuildType +from sh_commands import ModelFormat from common import CaffeEnvType from common import DeviceType @@ -53,16 +54,33 @@ BUILD_TMP_GENERAL_OUTPUT_DIR_NAME = 'general' OUTPUT_LIBRARY_DIR_NAME = 'lib' OUTPUT_OPENCL_BINARY_DIR_NAME = 'opencl' OUTPUT_OPENCL_BINARY_FILE_NAME = 'compiled_opencl_kernel' +OUTPUT_OPENCL_PARAMETER_FILE_NAME = 'tuned_opencl_parameter' CL_COMPILED_BINARY_FILE_NAME = "mace_cl_compiled_program.bin" +CL_TUNED_PARAMETER_FILE_NAME = "mace_run.config" CODEGEN_BASE_DIR = 'mace/codegen' MODEL_CODEGEN_DIR = CODEGEN_BASE_DIR + '/models' +ENGINE_CODEGEN_DIR = CODEGEN_BASE_DIR + '/engine' +LIB_CODEGEN_DIR = CODEGEN_BASE_DIR + '/lib' LIBMACE_SO_TARGET = "//mace:libmace.so" +LIBMACE_STATIC_TARGET = "//mace:libmace_static" +LIBMACE_STATIC_PATH = "bazel-genfiles/mace/libmace.a" +LIBMACE_DYNAMIC_PATH = "bazel-bin/mace/libmace.so" +MODEL_LIB_TARGET = "//mace/codegen:generated_models" +MODEL_LIB_PATH = "bazel-bin/mace/codegen/libgenerated_models.a" MACE_RUN_STATIC_NAME = "mace_run_static" -MACE_RUN_SHARED_NAME = "mace_run_shared" -EXAMPLE_STATIC_NAME = "example_static" -EXAMPLE_SHARED_NAME = "example_shared" +MACE_RUN_DYNAMIC_NAME = "mace_run_dynamic" MACE_RUN_STATIC_TARGET = "//mace/tools/validation:" + MACE_RUN_STATIC_NAME -MACE_RUN_SHARED_TARGET = "//mace/tools/validation:" + MACE_RUN_SHARED_NAME +MACE_RUN_DYNAMIC_TARGET = "//mace/tools/validation:" + MACE_RUN_DYNAMIC_NAME +EXAMPLE_STATIC_NAME = "example_static" +EXAMPLE_DYNAMIC_NAME = "example_dynamic" +EXAMPLE_STATIC_TARGET = "//mace/examples/cli:" + EXAMPLE_STATIC_NAME +EXAMPLE_DYNAMIC_TARGET = "//mace/examples/cli:" + EXAMPLE_DYNAMIC_NAME +BM_MODEL_STATIC_NAME = "benchmark_model_static" +BM_MODEL_DYNAMIC_NAME = "benchmark_model_dynamic" +BM_MODEL_STATIC_TARGET = "//mace/benchmark:" + BM_MODEL_STATIC_NAME +BM_MODEL_DYNAMIC_TARGET = "//mace/benchmark:" + BM_MODEL_DYNAMIC_NAME +DEVICE_INTERIOR_DIR = PHONE_DATA_DIR + "/interior" +BUILD_TMP_OPENCL_BIN_DIR = 'opencl_bin' ALL_SOC_TAG = 'all' ABITypeStrs = [ @@ -78,6 +96,17 @@ class ABIType(object): host = 'host' +ModelFormatStrs = [ + "file", + "code", +] + + +class MACELibType(object): + static = 0 + dynamic = 1 + + PlatformTypeStrs = [ "tensorflow", "caffe", @@ -126,6 +155,7 @@ WinogradParameters = [0, 2, 4] class DefaultValues(object): + mace_lib_type = MACELibType.static omp_num_threads = -1, cpu_affinity_policy = 1, gpu_perf_hint = 3, @@ -136,9 +166,8 @@ class YAMLKeyword(object): library_name = 'library_name' target_abis = 'target_abis' target_socs = 'target_socs' - build_type = 'build_type' - embed_model_data = 'embed_model_data' - linkshared = 'linkshared' + model_graph_format = 'model_graph_format' + model_data_format = 'model_data_format' models = 'models' platform = 'platform' model_file_path = 'model_file_path' @@ -281,45 +310,30 @@ def format_model_config(flags): "Build specified SOC library, " "you must plug in a phone using the SOC") - build_type = BuildType.code - if flags.build_type: - build_type_str = flags.build_type + if flags.model_graph_format: + model_graph_format = flags.model_graph_format else: - build_type_str = configs.get(YAMLKeyword.build_type, "") - if build_type_str == BuildType.proto: - build_type = BuildType.proto - elif build_type_str == BuildType.code: - build_type = BuildType.code + model_graph_format = configs.get(YAMLKeyword.model_graph_format, "") + mace_check(model_graph_format in ModelFormatStrs, + ModuleName.YAML_CONFIG, + 'You must set model_graph_format and ' + "model_graph_format must be in " + str(ModelFormatStrs)) + configs[YAMLKeyword.model_graph_format] = model_graph_format + if flags.model_data_format: + model_data_format = flags.model_data_format else: - MaceLogger.error(ModuleName.YAML_CONFIG, - "Invalid build type " + build_type_str - + ". only support [proto|code] format, " - + "proto for converting model to ProtoBuf file, " - + "code for converting model to c++ code.") - configs[YAMLKeyword.build_type] = build_type - embed_model_data = configs.get(YAMLKeyword.embed_model_data, "") - if embed_model_data == "" or not isinstance(embed_model_data, int) or \ - embed_model_data < 0 or embed_model_data > 1: - MaceLogger.error(ModuleName.YAML_CONFIG, - "embed_model_data must be 0 or 1. " - "0 for embed model data to code, 1 not.") - if build_type == BuildType.proto: - configs[YAMLKeyword.embed_model_data] = 0 - - linkshared = configs.get(YAMLKeyword.linkshared, "") - if linkshared == "": - configs[YAMLKeyword.linkshared] = 0 - linkshared = 0 - if not isinstance(linkshared, int) or linkshared < 0 or \ - linkshared > 1: - MaceLogger.error(ModuleName.YAML_CONFIG, - "linkshared must be 0 or 1. " - "default is 0, for link mace lib statically, " - "1 for dynamic linking.") - if build_type == BuildType.code and linkshared == 1: - MaceLogger.error(ModuleName.YAML_CONFIG, - "'linkshared == 1' only support when " - "'build_type == proto'") + model_data_format = configs.get(YAMLKeyword.model_data_format, "") + configs[YAMLKeyword.model_data_format] = model_data_format + mace_check(model_data_format in ModelFormatStrs, + ModuleName.YAML_CONFIG, + 'You must set model_data_format and ' + "model_data_format must be in " + str(ModelFormatStrs)) + + mace_check(not (model_graph_format == ModelFormat.file + and model_data_format == ModelFormat.code), + ModuleName.YAML_CONFIG, + "If model_graph format is 'file'," + " the model_data_format must be 'file' too") model_names = configs.get(YAMLKeyword.models, []) mace_check(len(model_names) > 0, ModuleName.YAML_CONFIG, @@ -450,18 +464,9 @@ def format_model_config(flags): return configs -def get_build_binary_dir(library_name, target_abi, target_soc, - serial_num): - if not target_soc or not serial_num: - binary_path_digest = md5sum(target_abi) - binary_path_digest = "%s_%s" % (target_abi, binary_path_digest) - else: - device_name = sh_commands.adb_get_device_name_by_serialno(serial_num) - binary_path_digest = md5sum(target_abi + target_soc + serial_num) - binary_path_digest = "%s_%s_%s" % \ - (device_name, target_soc, binary_path_digest) +def get_build_binary_dir(library_name, target_abi): return "%s/%s/%s/%s" % ( - BUILD_OUTPUT_DIR, library_name, BUILD_TMP_DIR_NAME, binary_path_digest) + BUILD_OUTPUT_DIR, library_name, BUILD_TMP_DIR_NAME, target_abi) def get_build_model_dirs(library_name, model_name, target_abi, target_soc, @@ -505,24 +510,62 @@ def get_opencl_binary_output_path(library_name, target_abi, target_soc) -def get_shared_library_dir(library_name, abi): - return '%s/%s/%s/%s' % (BUILD_OUTPUT_DIR, - library_name, - OUTPUT_LIBRARY_DIR_NAME, - abi) +def get_opencl_parameter_output_path(library_name, target_abi, + target_soc, serial_num): + device_name = \ + sh_commands.adb_get_device_name_by_serialno(serial_num) + return '%s/%s/%s/%s/%s_%s.%s.%s.bin' % \ + (BUILD_OUTPUT_DIR, + library_name, + OUTPUT_OPENCL_BINARY_DIR_NAME, + target_abi, + library_name, + OUTPUT_OPENCL_PARAMETER_FILE_NAME, + device_name, + target_soc) -################################ -# build -################################ -def pull_opencl_binary_and_tuning_param(target_abi, - serialno, - model_output_dirs): - sh_commands.pull_binaries(target_abi, serialno, model_output_dirs, - CL_COMPILED_BINARY_FILE_NAME) +def clear_build_dirs(library_name): + # make build dir + if not os.path.exists(BUILD_OUTPUT_DIR): + os.makedirs(BUILD_OUTPUT_DIR) + # clear temp build dir + tmp_build_dir = os.path.join(BUILD_OUTPUT_DIR, library_name, + BUILD_TMP_DIR_NAME) + if os.path.exists(tmp_build_dir): + sh.rm('-rf', tmp_build_dir) + os.makedirs(tmp_build_dir) + # clear lib dir + lib_output_dir = os.path.join( + BUILD_OUTPUT_DIR, library_name, OUTPUT_LIBRARY_DIR_NAME) + if os.path.exists(lib_output_dir): + sh.rm('-rf', lib_output_dir) + + +def check_model_converted(library_name, model_name, + model_graph_format, model_data_format): + model_output_dir = \ + '%s/%s/%s' % (BUILD_OUTPUT_DIR, library_name, MODEL_OUTPUT_DIR_NAME) + if model_graph_format == ModelFormat.file: + mace_check(os.path.exists("%s/%s.pb" % (model_output_dir, model_name)), + ModuleName.RUN, + "You shuold convert model first.") + else: + mace_check(os.path.exists("%s/%s.a" % + (model_output_dir, library_name)), + ModuleName.RUN, + "You shuold convert model first.") + if model_data_format == ModelFormat.file: + mace_check(os.path.exists("%s/%s.data" % + (model_output_dir, model_name)), + ModuleName.RUN, + "You shuold convert model first.") -def print_configuration(flags, configs): +################################ +# convert +################################ +def print_configuration(configs): title = "Common Configuration" header = ["key", "value"] data = list() @@ -532,13 +575,10 @@ def print_configuration(flags, configs): configs[YAMLKeyword.target_abis]]) data.append([YAMLKeyword.target_socs, configs[YAMLKeyword.target_socs]]) - data.append([YAMLKeyword.build_type, - configs[YAMLKeyword.build_type]]) - data.append([YAMLKeyword.embed_model_data, - configs[YAMLKeyword.embed_model_data]]) - data.append([YAMLKeyword.linkshared, - configs[YAMLKeyword.linkshared]]) - data.append(["Tuning", flags.disable_tuning]) + data.append([YAMLKeyword.model_graph_format, + configs[YAMLKeyword.model_graph_format]]) + data.append([YAMLKeyword.model_data_format, + configs[YAMLKeyword.model_data_format]]) MaceLogger.summary(StringFormatter.table(header, data, title)) @@ -596,26 +636,29 @@ def convert_model(configs): '%s/%s/%s' % (BUILD_OUTPUT_DIR, library_name, MODEL_OUTPUT_DIR_NAME) model_header_dir = \ '%s/%s/%s' % (BUILD_OUTPUT_DIR, library_name, MODEL_HEADER_DIR_PATH) + # clear output dir if os.path.exists(model_output_dir): sh.rm("-rf", model_output_dir) os.makedirs(model_output_dir) if os.path.exists(model_header_dir): sh.rm("-rf", model_header_dir) - os.makedirs(model_header_dir) - # copy header files - sh.cp("-f", glob.glob("mace/public/*.h"), model_header_dir) - - sh_commands.gen_mace_engine_factory_source( - configs[YAMLKeyword.models].keys(), - configs[YAMLKeyword.build_type], - configs[YAMLKeyword.embed_model_data]) - if configs[YAMLKeyword.build_type] == BuildType.code: + + embed_model_data = \ + configs[YAMLKeyword.model_data_format] == ModelFormat.code + + if os.path.exists(MODEL_CODEGEN_DIR): + sh.rm("-rf", MODEL_CODEGEN_DIR) + if os.path.exists(ENGINE_CODEGEN_DIR): + sh.rm("-rf", ENGINE_CODEGEN_DIR) + + if configs[YAMLKeyword.model_graph_format] == ModelFormat.code: + os.makedirs(model_header_dir) + sh_commands.gen_mace_engine_factory_source( + configs[YAMLKeyword.models].keys(), + embed_model_data) sh.cp("-f", glob.glob("mace/codegen/engine/*.h"), model_header_dir) - embed_model_data = configs[YAMLKeyword.embed_model_data] - - sh_commands.clear_model_codegen() for model_name in configs[YAMLKeyword.models]: MaceLogger.header( StringFormatter.block("Convert %s model" % model_name)) @@ -650,11 +693,11 @@ def convert_model(configs): embed_model_data, model_config[YAMLKeyword.winograd], model_config[YAMLKeyword.obfuscate], - configs[YAMLKeyword.build_type], + configs[YAMLKeyword.model_graph_format], data_type, ",".join(model_config.get(YAMLKeyword.graph_optimize_options, []))) - if configs[YAMLKeyword.build_type] == BuildType.proto: + if configs[YAMLKeyword.model_graph_format] == ModelFormat.file: sh.mv("-f", '%s/%s.pb' % (model_codegen_dir, model_name), model_output_dir) @@ -673,202 +716,34 @@ def convert_model(configs): StringFormatter.block("Model %s converted" % model_name)) -def build_specific_lib(target_abi, target_soc, serial_num, - configs, tuning, enable_openmp, - address_sanitizer): - library_name = configs[YAMLKeyword.library_name] - build_type = configs[YAMLKeyword.build_type] - embed_model_data = configs[YAMLKeyword.embed_model_data] - linkshared = configs[YAMLKeyword.linkshared] - hexagon_mode = get_hexagon_mode(configs) - model_output_dirs = [] - - build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi, - target_soc, serial_num) - if os.path.exists(build_tmp_binary_dir): - sh.rm("-rf", build_tmp_binary_dir) - os.makedirs(build_tmp_binary_dir) - - sh_commands.gen_tuning_param_code(model_output_dirs) - if linkshared == 0: - mace_run_name = MACE_RUN_STATIC_NAME - mace_run_target = MACE_RUN_STATIC_TARGET - else: - mace_run_name = MACE_RUN_SHARED_NAME - mace_run_target = MACE_RUN_SHARED_TARGET - sh_commands.bazel_build( - LIBMACE_SO_TARGET, - abi=target_abi, - hexagon_mode=hexagon_mode, - enable_openmp=enable_openmp, - address_sanitizer=address_sanitizer - ) - sh_commands.update_libmace_shared_library(serial_num, - target_abi, - library_name, - BUILD_OUTPUT_DIR, - OUTPUT_LIBRARY_DIR_NAME) - - sh_commands.bazel_build( - mace_run_target, - abi=target_abi, - hexagon_mode=hexagon_mode, - enable_openmp=enable_openmp, - address_sanitizer=address_sanitizer - ) - sh_commands.update_mace_run_lib(build_tmp_binary_dir, linkshared) - binary_changed = False - - for model_name in configs[YAMLKeyword.models]: - model_config = configs[YAMLKeyword.models][model_name] - model_runtime = model_config[YAMLKeyword.runtime] - # Create model build directory - model_output_base_dir, model_output_dir, mace_model_dir = \ - get_build_model_dirs(library_name, model_name, target_abi, - target_soc, serial_num, - model_config[YAMLKeyword.model_file_path]) - - model_output_dirs.append(model_output_dir) +def get_model_lib_output_path(library_name): + library_out_dir = os.path.join(BUILD_OUTPUT_DIR, library_name, + MODEL_OUTPUT_DIR_NAME) + lib_output_path = "%s/%s.a" % (library_out_dir, library_name) + return lib_output_path - if os.path.exists(model_output_dir): - sh.rm("-rf", model_output_dir) - os.makedirs(model_output_dir) - # build for specified soc - if not address_sanitizer and target_abi != ABIType.host \ - and target_soc is not None and \ - model_runtime in [RuntimeType.gpu, RuntimeType.cpu_gpu]: - sh_commands.clear_phone_data_dir(serial_num, PHONE_DATA_DIR) +def build_model_lib(configs, address_sanitizer): + MaceLogger.header(StringFormatter.block("Building model library")) - subgraphs = model_config[YAMLKeyword.subgraphs] - # generate input data - sh_commands.gen_random_input( - model_output_dir, - subgraphs[0][YAMLKeyword.input_tensors], - subgraphs[0][YAMLKeyword.input_shapes], - subgraphs[0][YAMLKeyword.validation_inputs_data], - input_ranges=subgraphs[0][YAMLKeyword.input_ranges]) - - device_type = parse_device_type(RuntimeType.gpu) - sh_commands.tuning_run( - abi=target_abi, - serialno=serial_num, - target_dir=build_tmp_binary_dir, - target_name=mace_run_name, - vlog_level=0, - embed_model_data=embed_model_data, - model_output_dir=model_output_dir, - input_nodes=subgraphs[0][YAMLKeyword.input_tensors], - output_nodes=subgraphs[0][YAMLKeyword.output_tensors], - input_shapes=subgraphs[0][YAMLKeyword.input_shapes], - output_shapes=subgraphs[0][YAMLKeyword.output_shapes], - mace_model_dir=mace_model_dir, - model_tag=model_name, - device_type=device_type, - running_round=0, - restart_round=1, - limit_opencl_kernel_time=model_config[YAMLKeyword.limit_opencl_kernel_time], # noqa - tuning=tuning, - out_of_range_check=False, - phone_data_dir=PHONE_DATA_DIR, - build_type=build_type, - opencl_binary_file="", - shared_library_dir=get_shared_library_dir(library_name, target_abi), # noqa - linkshared=linkshared, - ) + # create model library dir + library_name = configs[YAMLKeyword.library_name] + model_lib_output_path = get_model_lib_output_path(library_name) + library_out_dir = os.path.dirname(model_lib_output_path) + if not os.path.exists(library_out_dir): + os.makedirs(library_out_dir) - pull_opencl_binary_and_tuning_param(target_abi, serial_num, - [model_output_dir]) - sh_commands.touch_tuned_file_flag(build_tmp_binary_dir) - binary_changed = True + for target_abi in configs[YAMLKeyword.target_abis]: + hexagon_mode = get_hexagon_mode(configs) - if binary_changed: - opencl_output_bin_path = get_opencl_binary_output_path( - library_name, target_abi, target_soc, serial_num - ) - sh_commands.merge_opencl_binaries( - model_output_dirs, CL_COMPILED_BINARY_FILE_NAME, - opencl_output_bin_path) - sh_commands.gen_tuning_param_code(model_output_dirs) sh_commands.bazel_build( - mace_run_target, + MODEL_LIB_TARGET, abi=target_abi, hexagon_mode=hexagon_mode, - enable_openmp=enable_openmp, address_sanitizer=address_sanitizer ) - sh_commands.update_mace_run_lib(build_tmp_binary_dir, linkshared) - - if target_abi == ABIType.host: - sh_commands.build_host_libraries(build_type, target_abi) - - # build benchmark_model binary - sh_commands.build_benchmark_model(target_abi, - build_tmp_binary_dir, - hexagon_mode, - enable_openmp, - linkshared) - - # generate library - if linkshared == 0: - sh_commands.merge_libs(target_soc, - serial_num, - target_abi, - library_name, - BUILD_OUTPUT_DIR, - OUTPUT_LIBRARY_DIR_NAME, - build_type, - hexagon_mode) - - # build example binary - sh_commands.build_example(target_soc, serial_num, target_abi, - library_name, BUILD_OUTPUT_DIR, - OUTPUT_LIBRARY_DIR_NAME, - build_tmp_binary_dir, build_type, - hexagon_mode, enable_openmp, linkshared) - - -def generate_library(configs, tuning, enable_openmp, address_sanitizer): - MaceLogger.header(StringFormatter.block("Building library")) - # generate source - MaceLogger.info('* generate common source files...') - sh_commands.gen_mace_version() - sh_commands.gen_encrypted_opencl_source() - MaceLogger.info('generate common source files done') - - # create build dirs - library_name = configs[YAMLKeyword.library_name] - if not os.path.exists(BUILD_OUTPUT_DIR): - os.makedirs(BUILD_OUTPUT_DIR) - tmp_build_dir = os.path.join(BUILD_OUTPUT_DIR, library_name, - BUILD_TMP_DIR_NAME) - if not os.path.exists(tmp_build_dir): - os.makedirs(tmp_build_dir) - library_out_dir = os.path.join(BUILD_OUTPUT_DIR, library_name, - OUTPUT_LIBRARY_DIR_NAME) - if os.path.exists(library_out_dir): - sh.rm('-rf', library_out_dir) - - target_socs = configs[YAMLKeyword.target_socs] - for target_abi in configs[YAMLKeyword.target_abis]: - if not target_socs or target_abi == ABIType.host: - build_specific_lib(target_abi, None, None, configs, - tuning, enable_openmp, address_sanitizer) - else: - if ALL_SOC_TAG in target_socs: - target_socs = sh_commands.adb_get_all_socs() - for target_soc in target_socs: - serial_nums = \ - sh_commands.get_target_socs_serialnos([target_soc]) - for serial_num in serial_nums: - with sh_commands.device_lock(serial_num): - build_specific_lib(target_abi, target_soc, serial_num, - configs, tuning, enable_openmp, - address_sanitizer) - # package library - sh_commands.packaging_lib(BUILD_OUTPUT_DIR, - configs[YAMLKeyword.library_name]) + sh.cp("-f", MODEL_LIB_PATH, model_lib_output_path) def print_library_summary(configs): @@ -876,21 +751,27 @@ def print_library_summary(configs): title = "Library" header = ["key", "value"] data = list() - data.append(["library package", - "%s/%s/libmace_%s.tar.gz" - % (BUILD_OUTPUT_DIR, library_name, library_name)]) + data.append(["MACE Model Path", + "%s/%s/%s" + % (BUILD_OUTPUT_DIR, library_name, MODEL_OUTPUT_DIR_NAME)]) + if configs[YAMLKeyword.model_graph_format] == ModelFormat.code: + data.append(["MACE Model Header Path", + "%s/%s/%s" + % (BUILD_OUTPUT_DIR, library_name, + MODEL_HEADER_DIR_PATH)]) + MaceLogger.summary(StringFormatter.table(header, data, title)) -def build_library(flags): +def convert_func(flags): configs = format_model_config(flags) - print_configuration(flags, configs) + print_configuration(configs) convert_model(configs) - generate_library(configs, flags.disable_tuning, - flags.disable_openmp, flags.address_sanitizer) + if configs[YAMLKeyword.model_graph_format] == ModelFormat.code: + build_model_lib(configs, flags.address_sanitizer) print_library_summary(configs) @@ -926,7 +807,7 @@ def report_run_statistics(stdout, if not os.path.exists(report_filename): with open(report_filename, 'w') as f: f.write("model_name,device_name,soc,abi,runtime," - "init,warmup,run_avg,tuned\n") + "init(ms),warmup(ms),run_avg(ms),tuned\n") data_str = "{model_name},{device_name},{soc},{abi},{device_type}," \ "{init},{warmup},{run_avg},{tuned}\n" \ @@ -938,52 +819,222 @@ def report_run_statistics(stdout, init=metrics[0], warmup=metrics[1], run_avg=metrics[2], - tuned=tuned, - ) + tuned=tuned) with open(report_filename, 'a') as f: f.write(data_str) +def build_mace_run(configs, target_abi, enable_openmp, address_sanitizer, + mace_lib_type): + library_name = configs[YAMLKeyword.library_name] + hexagon_mode = get_hexagon_mode(configs) + + build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi) + if os.path.exists(build_tmp_binary_dir): + sh.rm("-rf", build_tmp_binary_dir) + os.makedirs(build_tmp_binary_dir) + + mace_run_target = MACE_RUN_STATIC_TARGET + if mace_lib_type == MACELibType.dynamic: + mace_run_target = MACE_RUN_DYNAMIC_TARGET + build_arg = "" + if configs[YAMLKeyword.model_graph_format] == ModelFormat.code: + mace_check(os.path.exists(ENGINE_CODEGEN_DIR), + ModuleName.RUN, + "You shuold convert model first.") + build_arg = "--per_file_copt=mace/tools/validation/mace_run.cc@-DMODEL_GRAPH_FORMAT_CODE" # noqa + + sh_commands.bazel_build( + mace_run_target, + abi=target_abi, + hexagon_mode=hexagon_mode, + enable_openmp=enable_openmp, + address_sanitizer=address_sanitizer, + extra_args=build_arg + ) + sh_commands.update_mace_run_binary(build_tmp_binary_dir, + mace_lib_type == MACELibType.dynamic) + + +def build_example(configs, target_abi, enable_openmp, mace_lib_type): + library_name = configs[YAMLKeyword.library_name] + hexagon_mode = get_hexagon_mode(configs) + + build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi) + if os.path.exists(build_tmp_binary_dir): + sh.rm("-rf", build_tmp_binary_dir) + os.makedirs(build_tmp_binary_dir) + + libmace_target = LIBMACE_STATIC_TARGET + if mace_lib_type == MACELibType.dynamic: + libmace_target = LIBMACE_SO_TARGET + + sh_commands.bazel_build(libmace_target, + abi=target_abi, + enable_openmp=enable_openmp, + hexagon_mode=hexagon_mode) + + if os.path.exists(LIB_CODEGEN_DIR): + sh.rm("-rf", LIB_CODEGEN_DIR) + sh.mkdir("-p", LIB_CODEGEN_DIR) + + build_arg = "" + if mace_lib_type == MACELibType.dynamic: + example_target = EXAMPLE_DYNAMIC_TARGET + sh.cp("-f", LIBMACE_DYNAMIC_PATH, LIB_CODEGEN_DIR) + else: + sh.cp("-f", LIBMACE_STATIC_PATH, LIB_CODEGEN_DIR) + if configs[YAMLKeyword.model_graph_format] == ModelFormat.code: + mace_check(os.path.exists(ENGINE_CODEGEN_DIR), + ModuleName.RUN, + "You shuold convert model first.") + model_lib_path = get_model_lib_output_path(library_name) + sh.cp("-f", model_lib_path, LIB_CODEGEN_DIR) + build_arg = "--per_file_copt=mace/examples/cli/example.cc@-DMODEL_GRAPH_FORMAT_CODE" # noqa + + example_target = EXAMPLE_STATIC_TARGET + + sh_commands.bazel_build(example_target, + abi=target_abi, + enable_openmp=enable_openmp, + hexagon_mode=hexagon_mode, + extra_args=build_arg) + + target_bin = "/".join(sh_commands.bazel_target_to_bin(example_target)) + sh.cp("-f", target_bin, build_tmp_binary_dir) + if os.path.exists(LIB_CODEGEN_DIR): + sh.rm("-rf", LIB_CODEGEN_DIR) + + +def tuning(library_name, model_name, model_config, + model_graph_format, model_data_format, + target_abi, target_soc, serial_num, + mace_lib_type): + print('* Tuning, it may take some time...') + + # clear opencl output dir + opencl_output_dir = os.path.join( + BUILD_OUTPUT_DIR, library_name, OUTPUT_OPENCL_BINARY_DIR_NAME) + if os.path.exists(opencl_output_dir): + sh.rm('-rf', opencl_output_dir) + + build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi) + mace_run_name = MACE_RUN_STATIC_NAME + link_dynamic = False + if mace_lib_type == MACELibType.dynamic: + mace_run_name = MACE_RUN_DYNAMIC_NAME + link_dynamic = True + + embed_model_data = model_data_format == ModelFormat.code + + model_output_base_dir, model_output_dir, mace_model_dir = \ + get_build_model_dirs(library_name, model_name, target_abi, + target_soc, serial_num, + model_config[YAMLKeyword.model_file_path]) + + # build for specified soc + sh_commands.clear_phone_data_dir(serial_num, PHONE_DATA_DIR) + + subgraphs = model_config[YAMLKeyword.subgraphs] + # generate input data + sh_commands.gen_random_input( + model_output_dir, + subgraphs[0][YAMLKeyword.input_tensors], + subgraphs[0][YAMLKeyword.input_shapes], + subgraphs[0][YAMLKeyword.validation_inputs_data], + input_ranges=subgraphs[0][YAMLKeyword.input_ranges]) + + sh_commands.tuning_run( + abi=target_abi, + serialno=serial_num, + target_dir=build_tmp_binary_dir, + target_name=mace_run_name, + vlog_level=0, + embed_model_data=embed_model_data, + model_output_dir=model_output_dir, + input_nodes=subgraphs[0][YAMLKeyword.input_tensors], + output_nodes=subgraphs[0][YAMLKeyword.output_tensors], + input_shapes=subgraphs[0][YAMLKeyword.input_shapes], + output_shapes=subgraphs[0][YAMLKeyword.output_shapes], + mace_model_dir=mace_model_dir, + model_tag=model_name, + device_type=DeviceType.GPU, + running_round=0, + restart_round=1, + limit_opencl_kernel_time=model_config[YAMLKeyword.limit_opencl_kernel_time], # noqa + tuning=True, + out_of_range_check=False, + phone_data_dir=PHONE_DATA_DIR, + model_graph_format=model_graph_format, + opencl_binary_file="", + opencl_parameter_file="", + libmace_dynamic_library_path=LIBMACE_DYNAMIC_PATH, + link_dynamic=link_dynamic, + ) + # pull opencl binary + sh_commands.pull_file_from_device( + serial_num, + DEVICE_INTERIOR_DIR, + CL_COMPILED_BINARY_FILE_NAME, + "%s/%s" % (model_output_dir, BUILD_TMP_OPENCL_BIN_DIR)) + + # pull opencl parameter + sh_commands.pull_file_from_device( + serial_num, + PHONE_DATA_DIR, + CL_TUNED_PARAMETER_FILE_NAME, + "%s/%s" % (model_output_dir, BUILD_TMP_OPENCL_BIN_DIR)) + + print('Tuning done\n') + + def run_specific_target(flags, configs, target_abi, target_soc, serial_num): library_name = configs[YAMLKeyword.library_name] - build_type = configs[YAMLKeyword.build_type] - embed_model_data = configs[YAMLKeyword.embed_model_data] + mace_lib_type = flags.mace_lib_type + embed_model_data = \ + configs[YAMLKeyword.model_data_format] == ModelFormat.code opencl_output_bin_path = "" - linkshared = configs[YAMLKeyword.linkshared] - if not configs[YAMLKeyword.target_socs] or target_abi == ABIType.host: - build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi, - None, None) - else: - build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi, - target_soc, serial_num) + opencl_parameter_path = "" + build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi) + if configs[YAMLKeyword.target_socs] and target_abi != ABIType.host: opencl_output_bin_path = get_opencl_binary_output_path( library_name, target_abi, target_soc, serial_num ) - mace_check(os.path.exists(build_tmp_binary_dir), - ModuleName.RUN, - 'You should build before run.') + opencl_parameter_path = get_opencl_parameter_output_path( + library_name, target_abi, target_soc, serial_num + ) + # get target name for run if flags.example: - if linkshared == 0: + if mace_lib_type == MACELibType.static: target_name = EXAMPLE_STATIC_NAME else: - target_name = EXAMPLE_SHARED_NAME + target_name = EXAMPLE_DYNAMIC_NAME else: - if linkshared == 0: + if mace_lib_type == MACELibType.static: target_name = MACE_RUN_STATIC_NAME else: - target_name = MACE_RUN_SHARED_NAME + target_name = MACE_RUN_DYNAMIC_NAME + + link_dynamic = mace_lib_type == MACELibType.dynamic + model_output_dirs = [] for model_name in configs[YAMLKeyword.models]: + check_model_converted(library_name, model_name, + configs[YAMLKeyword.model_graph_format], + configs[YAMLKeyword.model_data_format]) if target_abi == ABIType.host: device_name = ABIType.host else: - device_name =\ + device_name = \ sh_commands.adb_get_device_name_by_serialno(serial_num) + sh_commands.clear_phone_data_dir(serial_num, PHONE_DATA_DIR) + MaceLogger.header( StringFormatter.block( "Run model %s on %s" % (model_name, device_name))) + model_config = configs[YAMLKeyword.models][model_name] model_runtime = model_config[YAMLKeyword.runtime] subgraphs = model_config[YAMLKeyword.subgraphs] @@ -998,12 +1049,24 @@ def run_specific_target(flags, configs, target_abi, get_build_model_dirs(library_name, model_name, target_abi, target_soc, serial_num, model_config[YAMLKeyword.model_file_path]) - mace_check(os.path.exists(model_output_dir) - and os.path.exists(mace_model_dir), - ModuleName.RUN, - 'You should build before run.') - if target_abi != ABIType.host: - sh_commands.clear_phone_data_dir(serial_num, PHONE_DATA_DIR) + if os.path.exists(model_output_dir): + sh.rm("-rf", model_output_dir) + os.makedirs(model_output_dir) + + # tuning for specified soc + if not flags.address_sanitizer \ + and not flags.example \ + and target_abi != ABIType.host \ + and configs[YAMLKeyword.target_socs] \ + and target_soc \ + and model_runtime in [RuntimeType.gpu, RuntimeType.cpu_gpu] \ + and not flags.disable_tuning: + tuning(library_name, model_name, model_config, + configs[YAMLKeyword.model_graph_format], + configs[YAMLKeyword.model_data_format], + target_abi, target_soc, serial_num, + mace_lib_type) + model_output_dirs.append(model_output_dir) # generate input data sh_commands.gen_random_input( @@ -1012,6 +1075,7 @@ def run_specific_target(flags, configs, target_abi, subgraphs[0][YAMLKeyword.input_shapes], subgraphs[0][YAMLKeyword.validation_inputs_data], input_ranges=subgraphs[0][YAMLKeyword.input_ranges]) + runtime_list = [] if target_abi == ABIType.host: runtime_list.extend([RuntimeType.cpu]) @@ -1021,7 +1085,7 @@ def run_specific_target(flags, configs, target_abi, runtime_list.extend([model_runtime]) for runtime in runtime_list: device_type = parse_device_type(runtime) - + # run for specified soc run_output = sh_commands.tuning_run( abi=target_abi, serialno=serial_num, @@ -1043,7 +1107,7 @@ def run_specific_target(flags, configs, target_abi, tuning=False, out_of_range_check=flags.gpu_out_of_range_check, phone_data_dir=PHONE_DATA_DIR, - build_type=build_type, + model_graph_format=configs[YAMLKeyword.model_graph_format], omp_num_threads=flags.omp_num_threads, cpu_affinity_policy=flags.cpu_affinity_policy, gpu_perf_hint=flags.gpu_perf_hint, @@ -1051,8 +1115,9 @@ def run_specific_target(flags, configs, target_abi, runtime_failure_ratio=flags.runtime_failure_ratio, address_sanitizer=flags.address_sanitizer, opencl_binary_file=opencl_output_bin_path, - shared_library_dir=get_shared_library_dir(library_name, target_abi), # noqa - linkshared=linkshared, + opencl_parameter_file=opencl_parameter_path, + libmace_dynamic_library_path=LIBMACE_DYNAMIC_PATH, + link_dynamic=link_dynamic, ) if flags.validate: model_file_path, weight_file_path = get_model_files( @@ -1077,20 +1142,60 @@ def run_specific_target(flags, configs, target_abi, phone_data_dir=PHONE_DATA_DIR, caffe_env=flags.caffe_env) if flags.report and flags.round > 0: + opencl_parameter_bin_path = get_opencl_parameter_output_path( + library_name, target_abi, target_soc, serial_num + ) + tuned = device_type == DeviceType.GPU\ + and os.path.exists(opencl_parameter_bin_path) report_run_statistics( run_output, target_abi, serial_num, model_name, device_type, flags.report_dir, - sh_commands.is_binary_tuned(build_tmp_binary_dir)) + tuned) + + if model_output_dirs: + opencl_output_bin_path = get_opencl_binary_output_path( + library_name, target_abi, target_soc, serial_num + ) + opencl_parameter_bin_path = get_opencl_parameter_output_path( + library_name, target_abi, target_soc, serial_num + ) + # merge all models' OpenCL binaries together + sh_commands.merge_opencl_binaries( + model_output_dirs, CL_COMPILED_BINARY_FILE_NAME, + opencl_output_bin_path) + # merge all models' OpenCL parameters together + sh_commands.merge_opencl_parameters( + model_output_dirs, CL_TUNED_PARAMETER_FILE_NAME, + opencl_parameter_bin_path) def run_mace(flags): configs = format_model_config(flags) + if flags.mace_lib_type == MACELibType.dynamic and \ + configs[YAMLKeyword.model_graph_format] == ModelFormat.code: + MaceLogger.error(ModuleName.YAML_CONFIG, + "If you want to link MACE dynamic library, " + "you must use file-type MACE model.") + + clear_build_dirs(configs[YAMLKeyword.library_name]) target_socs = configs[YAMLKeyword.target_socs] if not target_socs or ALL_SOC_TAG in target_socs: target_socs = sh_commands.adb_get_all_socs() for target_abi in configs[YAMLKeyword.target_abis]: + # build target + if flags.example: + build_example(configs, target_abi, + not flags.disable_openmp, + flags.mace_lib_type) + else: + build_mace_run(configs, target_abi, + not flags.disable_openmp, + flags.address_sanitizer, + flags.mace_lib_type) + + # run if target_abi == ABIType.host: run_specific_target(flags, configs, target_abi, None, None) else: @@ -1109,26 +1214,64 @@ def run_mace(flags): ################################ # benchmark model ################################ +def build_benchmark_model(configs, target_abi, enable_openmp, mace_lib_type): + library_name = configs[YAMLKeyword.library_name] + hexagon_mode = get_hexagon_mode(configs) + + link_dynamic = mace_lib_type == MACELibType.dynamic + if link_dynamic: + benchmark_target = BM_MODEL_DYNAMIC_TARGET + else: + benchmark_target = BM_MODEL_STATIC_TARGET + + build_arg = "" + if configs[YAMLKeyword.model_graph_format] == ModelFormat.code: + mace_check(os.path.exists(ENGINE_CODEGEN_DIR), + ModuleName.BENCHMARK, + "You shuold convert model first.") + build_arg = "--per_file_copt=mace/benchmark/benchmark_model.cc@-DMODEL_GRAPH_FORMAT_CODE" # noqa + + sh_commands.bazel_build(benchmark_target, + abi=target_abi, + enable_openmp=enable_openmp, + hexagon_mode=hexagon_mode, + extra_args=build_arg) + # clear tmp binary dir + build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi) + if os.path.exists(build_tmp_binary_dir): + sh.rm("-rf", build_tmp_binary_dir) + os.makedirs(build_tmp_binary_dir) + + target_bin = "/".join(sh_commands.bazel_target_to_bin(benchmark_target)) + sh.cp("-f", target_bin, build_tmp_binary_dir) + + def bm_specific_target(flags, configs, target_abi, target_soc, serial_num): library_name = configs[YAMLKeyword.library_name] - build_type = configs[YAMLKeyword.build_type] - embed_model_data = configs[YAMLKeyword.embed_model_data] + embed_model_data = \ + configs[YAMLKeyword.model_data_format] == ModelFormat.code opencl_output_bin_path = "" - linkshared = configs[YAMLKeyword.linkshared] - if not configs[YAMLKeyword.target_socs] or target_abi == ABIType.host: - build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi, - None, None) + opencl_parameter_path = "" + link_dynamic = flags.mace_lib_type == MACELibType.dynamic + + if link_dynamic: + bm_model_binary_name = BM_MODEL_DYNAMIC_NAME else: - build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi, - target_soc, serial_num) + bm_model_binary_name = BM_MODEL_STATIC_NAME + build_tmp_binary_dir = get_build_binary_dir(library_name, target_abi) + + if configs[YAMLKeyword.target_socs] and target_abi != ABIType.host: opencl_output_bin_path = get_opencl_binary_output_path( library_name, target_abi, target_soc, serial_num ) - mace_check(os.path.exists(build_tmp_binary_dir), - ModuleName.BENCHMARK, - 'You should build before benchmark.') + opencl_parameter_path = get_opencl_parameter_output_path( + library_name, target_abi, target_soc, serial_num + ) for model_name in configs[YAMLKeyword.models]: + check_model_converted(library_name, model_name, + configs[YAMLKeyword.model_graph_format], + configs[YAMLKeyword.model_data_format]) if target_abi == ABIType.host: device_name = ABIType.host else: @@ -1151,10 +1294,10 @@ def bm_specific_target(flags, configs, target_abi, target_soc, serial_num): get_build_model_dirs(library_name, model_name, target_abi, target_soc, serial_num, model_config[YAMLKeyword.model_file_path]) - mace_check(os.path.exists(model_output_dir) - and os.path.exists(mace_model_dir), - ModuleName.BENCHMARK, - 'You should build before benchmark.') + if os.path.exists(model_output_dir): + sh.rm("-rf", model_output_dir) + os.makedirs(model_output_dir) + if target_abi != ABIType.host: sh_commands.clear_phone_data_dir(serial_num, PHONE_DATA_DIR) @@ -1177,6 +1320,7 @@ def bm_specific_target(flags, configs, target_abi, target_soc, serial_num): abi=target_abi, serialno=serial_num, benchmark_binary_dir=build_tmp_binary_dir, + benchmark_binary_name=bm_model_binary_name, vlog_level=0, embed_model_data=embed_model_data, model_output_dir=model_output_dir, @@ -1188,24 +1332,37 @@ def bm_specific_target(flags, configs, target_abi, target_soc, serial_num): model_tag=model_name, device_type=device_type, phone_data_dir=PHONE_DATA_DIR, - build_type=build_type, + model_graph_format=configs[YAMLKeyword.model_graph_format], omp_num_threads=flags.omp_num_threads, cpu_affinity_policy=flags.cpu_affinity_policy, gpu_perf_hint=flags.gpu_perf_hint, gpu_priority_hint=flags.gpu_priority_hint, opencl_binary_file=opencl_output_bin_path, - shared_library_dir=get_shared_library_dir(library_name, target_abi), # noqa - linkshared=linkshared) + opencl_parameter_file=opencl_parameter_path, + libmace_dynamic_library_path=LIBMACE_DYNAMIC_PATH, + link_dynamic=link_dynamic) def benchmark_model(flags): configs = format_model_config(flags) + if flags.mace_lib_type == MACELibType.dynamic and \ + configs[YAMLKeyword.model_graph_format] == ModelFormat.code: + MaceLogger.error(ModuleName.YAML_CONFIG, + "If you want to link MACE dynamic library, " + "you must use file-type MACE model.") + + clear_build_dirs(configs[YAMLKeyword.library_name]) target_socs = configs[YAMLKeyword.target_socs] if not target_socs or ALL_SOC_TAG in target_socs: target_socs = sh_commands.adb_get_all_socs() for target_abi in configs[YAMLKeyword.target_abis]: + # build benchmark_model binary + build_benchmark_model(configs, target_abi, + not flags.disable_openmp, + flags.mace_lib_type) + if target_abi == ABIType.host: bm_specific_target(flags, configs, target_abi, None, None) else: @@ -1242,6 +1399,15 @@ def str_to_caffe_env_type(v): raise argparse.ArgumentTypeError('[docker | local] expected.') +def str_to_mace_lib_type(v): + if v.lower() == 'dynamic': + return MACELibType.dynamic + elif v.lower() == 'static': + return MACELibType.static + else: + raise argparse.ArgumentTypeError('[dynamic| static] expected.') + + def parse_args(): """Parses command line arguments.""" all_type_parent_parser = argparse.ArgumentParser(add_help=False) @@ -1252,10 +1418,15 @@ def parse_args(): required=True, help="the path of model yaml configuration file.") all_type_parent_parser.add_argument( - "--build_type", + "--model_graph_format", type=str, default="", - help="Model build type, can be ['proto', 'code'].") + help="[file, code], MACE Model graph format.") + all_type_parent_parser.add_argument( + "--model_data_format", + type=str, + default="", + help="['file', 'code'], MACE Model data format.") all_type_parent_parser.add_argument( "--target_abis", type=str, @@ -1266,12 +1437,21 @@ def parse_args(): type=str, default="", help="Target SOCs, comma seperated list.") - build_run_parent_parser = argparse.ArgumentParser(add_help=False) - build_run_parent_parser.add_argument( + convert_run_parent_parser = argparse.ArgumentParser(add_help=False) + convert_run_parent_parser.add_argument( '--address_sanitizer', action="store_true", help="Whether to use address sanitizer to check memory error") run_bm_parent_parser = argparse.ArgumentParser(add_help=False) + run_bm_parent_parser.add_argument( + "--mace_lib_type", + type=str_to_mace_lib_type, + default=DefaultValues.mace_lib_type, + help="[static | dynamic], Which type MACE library to use.") + run_bm_parent_parser.add_argument( + "--disable_openmp", + action="store_true", + help="Disable openmp for multiple thread.") run_bm_parent_parser.add_argument( "--omp_num_threads", type=int, @@ -1295,25 +1475,21 @@ def parse_args(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers() - build = subparsers.add_parser( - 'build', - parents=[all_type_parent_parser, build_run_parent_parser], - help='build model library and test tools') - build.set_defaults(func=build_library) - build.add_argument( - '--disable_tuning', - action="store_false", - help="Disable tuning the parameters for the GPU of specified SoC.") - build.add_argument( - "--disable_openmp", - action="store_false", - help="Disable openmp for multiple thread.") + convert = subparsers.add_parser( + 'convert', + parents=[all_type_parent_parser, convert_run_parent_parser], + help='convert to mace model (file or code)') + convert.set_defaults(func=convert_func) run = subparsers.add_parser( 'run', parents=[all_type_parent_parser, run_bm_parent_parser, - build_run_parent_parser], + convert_run_parent_parser], help='run model in command line') run.set_defaults(func=run_mace) + run.add_argument( + "--disable_tuning", + action="store_true", + help="Disable tuning for specific thread.") run.add_argument( "--round", type=int, @@ -1364,8 +1540,7 @@ def parse_args(): help="whether to run example.") benchmark = subparsers.add_parser( 'benchmark', - parents=[all_type_parent_parser, run_bm_parent_parser, - build_run_parent_parser], + parents=[all_type_parent_parser, run_bm_parent_parser], help='benchmark model for detail information') benchmark.set_defaults(func=benchmark_model) return parser.parse_known_args() diff --git a/tools/sh_commands.py b/tools/sh_commands.py index ddbd7c6b107711421bfb67a745a368b40beeef38..a59f16761daddac1b20925cb64361d7ac083f73c 100644 --- a/tools/sh_commands.py +++ b/tools/sh_commands.py @@ -85,6 +85,11 @@ class BuildType(object): code = 'code' +class ModelFormat(object): + file = 'file' + code = 'code' + + def stdout_success(stdout): stdout_lines = stdout.split("\n") for line in stdout_lines: @@ -104,11 +109,6 @@ def clear_phone_data_dir(serialno, phone_data_dir): "rm -rf %s" % phone_data_dir) -def clear_model_codegen(model_codegen_dir="mace/codegen/models"): - if os.path.exists(model_codegen_dir): - sh.rm("-rf", model_codegen_dir) - - ################################ # adb commands ################################ @@ -144,23 +144,6 @@ def get_target_socs_serialnos(target_socs=None): return serialnos -def get_soc_serial_number_map(): - serial_numbers = adb_devices() - soc_serial_number_map = {} - for num in serial_numbers: - props = adb_getprop_by_serialno(num) - soc_serial_number_map[props["ro.board.platform"]] = num - return soc_serial_number_map - - -def get_target_soc_serial_number(target_soc): - soc_serial_number_map = get_soc_serial_number_map() - serial_number = None - if target_soc in soc_serial_number_map: - serial_number = soc_serial_number_map[target_soc] - return serial_number - - def adb_getprop_by_serialno(serialno): outputs = sh.adb("-s", serialno, "shell", "getprop") raw_props = split_stdout(outputs) @@ -285,6 +268,12 @@ def find_asan_rt_library(abi, asan_rt_path=''): return "%s/%s" % (asan_rt_path, asan_rt_library_names(abi)) +def find_gnustl_shared_path(abi): + return \ + "%s/sources/cxx-stl/gnu-libstdc++/4.9/libs/%s/libgnustl_shared.so" % \ + (os.environ["ANDROID_NDK_HOME"], abi) + + ################################ # bazel commands ################################ @@ -322,6 +311,7 @@ def bazel_build(target, bazel_args += ("--config", "optimization") if extra_args: bazel_args += (extra_args, ) + print bazel_args sh.bazel( _fg=True, *bazel_args) @@ -360,7 +350,6 @@ def gen_encrypted_opencl_source(codegen_path="mace/codegen"): def gen_mace_engine_factory_source(model_tags, - model_load_type, embed_model_data, codegen_path="mace/codegen"): print("* Generate mace engine creator source") @@ -370,31 +359,18 @@ def gen_mace_engine_factory_source(model_tags, gen_mace_engine_factory( model_tags, "mace/python/tools", - model_load_type, embed_model_data, codegen_tools_dir) print("Generate mace engine creator source done!\n") -def pull_binaries(abi, serialno, model_output_dirs, - cl_built_kernel_file_name): - compiled_opencl_dir = "/data/local/tmp/mace_run/interior/" - mace_run_param_file = "mace_run.config" - - cl_bin_dirs = [] - for d in model_output_dirs: - cl_bin_dirs.append(os.path.join(d, "opencl_bin")) - cl_bin_dirs_str = ",".join(cl_bin_dirs) - if cl_bin_dirs: - cl_bin_dir = cl_bin_dirs_str - if os.path.exists(cl_bin_dir): - sh.rm("-rf", cl_bin_dir) - sh.mkdir("-p", cl_bin_dir) - if abi != "host": - adb_pull(compiled_opencl_dir + cl_built_kernel_file_name, - cl_bin_dir, serialno) - adb_pull("/data/local/tmp/mace_run/%s" % mace_run_param_file, - cl_bin_dir, serialno) +def pull_file_from_device(serial_num, file_path, file_name, output_dir): + if not os.path.exists(output_dir): + sh.mkdir("-p", output_dir) + output_path = "%s/%s" % (output_dir, file_path) + if os.path.exists(output_path): + sh.rm('-rf', output_path) + adb_pull(file_path + '/' + file_name, output_dir, serial_num) def merge_opencl_binaries(binaries_dirs, @@ -454,29 +430,52 @@ def merge_opencl_binaries(binaries_dirs, np.array(output_byte_array).tofile(output_file_path) -def gen_tuning_param_code(model_output_dirs, - codegen_path="mace/codegen"): - mace_run_param_file = "mace_run.config" +def merge_opencl_parameters(binaries_dirs, + cl_parameter_file_name, + output_file_path): cl_bin_dirs = [] - for d in model_output_dirs: + for d in binaries_dirs: cl_bin_dirs.append(os.path.join(d, "opencl_bin")) - cl_bin_dirs_str = ",".join(cl_bin_dirs) + # create opencl binary output dir + opencl_binary_dir = os.path.dirname(output_file_path) + if not os.path.exists(opencl_binary_dir): + sh.mkdir("-p", opencl_binary_dir) + kvs = {} + for binary_dir in cl_bin_dirs: + binary_path = os.path.join(binary_dir, cl_parameter_file_name) + if not os.path.exists(binary_path): + continue - tuning_codegen_dir = "%s/tuning/" % codegen_path - if not os.path.exists(tuning_codegen_dir): - sh.mkdir("-p", tuning_codegen_dir) + print 'generate opencl parameter from', binary_path + with open(binary_path, "rb") as f: + binary_array = np.fromfile(f, dtype=np.uint8) - tuning_param_variable_name = "kTuningParamsData" - tuning_param_codegen(cl_bin_dirs_str, - mace_run_param_file, - "%s/tuning_params.cc" % tuning_codegen_dir, - tuning_param_variable_name) + idx = 0 + size, = struct.unpack("Q", binary_array[idx:idx + 8]) + idx += 8 + for _ in xrange(size): + key_size, = struct.unpack("i", binary_array[idx:idx + 4]) + idx += 4 + key, = struct.unpack( + str(key_size) + "s", binary_array[idx:idx + key_size]) + idx += key_size + value_size, = struct.unpack("i", binary_array[idx:idx + 4]) + idx += 4 + kvs[key] = binary_array[idx:idx + value_size] + idx += value_size + output_byte_array = bytearray() + data_size = len(kvs) + output_byte_array.extend(struct.pack("Q", data_size)) + for key, value in kvs.iteritems(): + key_size = len(key) + output_byte_array.extend(struct.pack("i", key_size)) + output_byte_array.extend(struct.pack(str(key_size) + "s", key)) + value_size = len(value) + output_byte_array.extend(struct.pack("i", value_size)) + output_byte_array.extend(value) -def gen_mace_version(codegen_path="mace/codegen"): - sh.mkdir("-p", "%s/version" % codegen_path) - sh.bash("mace/tools/git/gen_version_source.sh", - "%s/version/version.cc" % codegen_path) + np.array(output_byte_array).tofile(output_file_path) def gen_model_code(model_codegen_dir, @@ -494,7 +493,7 @@ def gen_model_code(model_codegen_dir, embed_model_data, winograd, obfuscate, - model_build_type, + model_graph_format, data_type, graph_optimize_options): bazel_build_common("//mace/python/tools:converter") @@ -521,7 +520,7 @@ def gen_model_code(model_codegen_dir, "--winograd=%s" % winograd, "--obfuscate=%s" % obfuscate, "--output_dir=%s" % model_codegen_dir, - "--model_build_type=%s" % model_build_type, + "--model_graph_format=%s" % model_graph_format, "--data_type=%s" % data_type, "--graph_optimize_options=%s" % graph_optimize_options, _fg=True) @@ -572,58 +571,28 @@ def gen_random_input(model_output_dir, sh.cp("-f", input_file_list[i], dst_input_file) -def update_mace_run_lib(build_tmp_binary_dir, linkshared=0): - if linkshared == 0: - mace_run_filepath = build_tmp_binary_dir + "/mace_run_static" +def update_mace_run_binary(build_tmp_binary_dir, link_dynamic=False): + if link_dynamic: + mace_run_filepath = build_tmp_binary_dir + "/mace_run_dynamic" else: - mace_run_filepath = build_tmp_binary_dir + "/mace_run_shared" + mace_run_filepath = build_tmp_binary_dir + "/mace_run_static" if os.path.exists(mace_run_filepath): sh.rm("-rf", mace_run_filepath) - if linkshared == 0: - sh.cp("-f", "bazel-bin/mace/tools/validation/mace_run_static", + if link_dynamic: + sh.cp("-f", "bazel-bin/mace/tools/validation/mace_run_dynamic", build_tmp_binary_dir) else: - sh.cp("-f", "bazel-bin/mace/tools/validation/mace_run_shared", + sh.cp("-f", "bazel-bin/mace/tools/validation/mace_run_static", build_tmp_binary_dir) -def touch_tuned_file_flag(build_tmp_binary_dir): - sh.touch(build_tmp_binary_dir + '/tuned') - - -def is_binary_tuned(build_tmp_binary_dir): - return os.path.exists(build_tmp_binary_dir + '/tuned') - - def create_internal_storage_dir(serialno, phone_data_dir): internal_storage_dir = "%s/interior/" % phone_data_dir sh.adb("-s", serialno, "shell", "mkdir", "-p", internal_storage_dir) return internal_storage_dir -def update_libmace_shared_library(serial_num, - abi, - project_name, - build_output_dir, - library_output_dir): - library_dir = "%s/%s/%s/%s" % ( - build_output_dir, project_name, library_output_dir, abi) - - if os.path.exists(library_dir): - sh.rm("-rf", library_dir) - sh.mkdir("-p", library_dir) - sh.cp("-f", "bazel-bin/mace/libmace.so", library_dir) - sh.cp("-f", - "%s/sources/cxx-stl/gnu-libstdc++/4.9/libs/%s/libgnustl_shared.so" % - (os.environ["ANDROID_NDK_HOME"], abi), - library_dir) - - if os.path.exists("mace/libmace.so"): - sh.rm("-f", "mace/libmace.so") - sh.cp("-f", "bazel-bin/mace/libmace.so", "mace/") - - def tuning_run(abi, serialno, target_dir, @@ -644,9 +613,10 @@ def tuning_run(abi, tuning, out_of_range_check, phone_data_dir, - build_type, + model_graph_format, opencl_binary_file, - shared_library_dir, + opencl_parameter_file, + libmace_dynamic_library_path, omp_num_threads=-1, cpu_affinity_policy=1, gpu_perf_hint=3, @@ -655,7 +625,7 @@ def tuning_run(abi, output_file_name="model_out", runtime_failure_ratio=0.0, address_sanitizer=False, - linkshared=0): + link_dynamic=False): print("* Run '%s' with round=%s, restart_round=%s, tuning=%s, " "out_of_range_check=%s, omp_num_threads=%s, cpu_affinity_policy=%s, " "gpu_perf_hint=%s, gpu_priority_hint=%s" % @@ -663,7 +633,7 @@ def tuning_run(abi, str(out_of_range_check), omp_num_threads, cpu_affinity_policy, gpu_perf_hint, gpu_priority_hint)) mace_model_path = "" - if build_type == BuildType.proto: + if model_graph_format == ModelFormat.file: mace_model_path = "%s/%s.pb" % (mace_model_dir, model_tag) if abi == "host": p = subprocess.Popen( @@ -695,7 +665,6 @@ def tuning_run(abi, stdout = err + out print stdout print("Running finished!\n") - return stdout else: sh.adb("-s", serialno, "shell", "mkdir", "-p", phone_data_dir) internal_storage_dir = create_internal_storage_dir( @@ -713,24 +682,26 @@ def tuning_run(abi, adb_push("%s/%s.data" % (mace_model_dir, model_tag), phone_data_dir, serialno) - if device_type == common.DeviceType.GPU\ - and os.path.exists(opencl_binary_file): - adb_push(opencl_binary_file, phone_data_dir, serialno) + if device_type == common.DeviceType.GPU: + if os.path.exists(opencl_binary_file): + adb_push(opencl_binary_file, phone_data_dir, serialno) + if os.path.exists(opencl_parameter_file): + adb_push(opencl_parameter_file, phone_data_dir, serialno) adb_push("third_party/nnlib/libhexagon_controller.so", phone_data_dir, serialno) mace_model_phone_path = "" - if build_type == BuildType.proto: + if model_graph_format == ModelFormat.file: mace_model_phone_path = "%s/%s.pb" % (phone_data_dir, model_tag) adb_push(mace_model_path, mace_model_phone_path, serialno) - if linkshared == 1: - adb_push("%s/libmace.so" % shared_library_dir, phone_data_dir, + if link_dynamic: + adb_push(libmace_dynamic_library_path, phone_data_dir, serialno) - adb_push("%s/libgnustl_shared.so" % shared_library_dir, + adb_push(find_gnustl_shared_path(abi), phone_data_dir, serialno) @@ -774,6 +745,8 @@ def tuning_run(abi, "--model_file=%s" % mace_model_phone_path, "--opencl_binary_file=%s/%s" % (phone_data_dir, os.path.basename(opencl_binary_file)), + "--opencl_parameter_file=%s/%s" % + (phone_data_dir, os.path.basename(opencl_parameter_file)), ]) adb_cmd = ' '.join(adb_cmd) cmd_file_name = "%s-%s-%s" % ('cmd_file', model_tag, str(time.time())) @@ -926,152 +899,9 @@ def validate_model(abi, print("Validation done!\n") -def build_host_libraries(model_build_type, abi): - bazel_build("@com_google_protobuf//:protobuf_lite", abi=abi) - bazel_build("//mace/proto:mace_cc", abi=abi) - bazel_build("//mace/codegen:generated_opencl", abi=abi) - bazel_build("//mace/codegen:generated_tuning_params", abi=abi) - bazel_build("//mace/codegen:generated_version", abi=abi) - bazel_build("//mace/utils:utils", abi=abi) - bazel_build("//mace/core:core", abi=abi) - bazel_build("//mace/kernels:kernels", abi=abi) - bazel_build("//mace/ops:ops", abi=abi) - if model_build_type == BuildType.code: - bazel_build( - "//mace/codegen:generated_models", - abi=abi) - - ################################ # library ################################ -def get_lib_path(target_soc, serial_num, abi, project_name, build_output_dir, - library_output_dir): - project_output_dir = "%s/%s" % (build_output_dir, project_name) - library_dir = "%s/%s" % (project_output_dir, library_output_dir) - model_bin_dir = "%s/%s/" % (library_dir, abi) - if abi == "host": - lib_path = "%s/libmace_%s.a" % \ - (model_bin_dir, project_name) - else: - if not target_soc: - lib_path = "%s/libmace_%s.a" % \ - (model_bin_dir, project_name) - else: - device_name = adb_get_device_name_by_serialno(serial_num) - lib_path = "%s/libmace_%s.%s.%s.a" % \ - (model_bin_dir, project_name, - device_name, target_soc) - return lib_path - - -def merge_libs(target_soc, - serial_num, - abi, - project_name, - build_output_dir, - library_output_dir, - model_build_type, - hexagon_mode): - print("* Merge mace lib") - project_output_dir = "%s/%s" % (build_output_dir, project_name) - hexagon_lib_file = "third_party/nnlib/libhexagon_controller.so" - library_dir = "%s/%s" % (project_output_dir, library_output_dir) - model_bin_dir = "%s/%s/" % (library_dir, abi) - - if not os.path.exists(model_bin_dir): - sh.mkdir("-p", model_bin_dir) - if hexagon_mode: - sh.cp("-f", hexagon_lib_file, library_dir) - - lib_path = get_lib_path(target_soc, serial_num, abi, - project_name, build_output_dir, library_output_dir) - # make static library - mri_stream = "" - if abi == "host": - mri_stream += "create %s\n" % lib_path - mri_stream += ( - "addlib " - "bazel-bin/mace/codegen/libgenerated_opencl.pic.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/codegen/libgenerated_tuning_params.pic.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/codegen/libgenerated_version.pic.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/core/libcore.pic.lo\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/kernels/libkernels.pic.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/utils/libutils.pic.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/proto/libmace_cc.pic.a\n") - mri_stream += ( - "addlib " - "bazel-bin/external/com_google_protobuf/libprotobuf_lite.pic.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/ops/libops.pic.lo\n") - if model_build_type == BuildType.code: - mri_stream += ( - "addlib " - "bazel-bin/mace/codegen/libgenerated_models.pic.a\n") - else: - mri_stream += "create %s\n" % lib_path - if model_build_type == BuildType.code: - mri_stream += ( - "addlib " - "bazel-bin/mace/codegen/libgenerated_models.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/codegen/libgenerated_opencl.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/codegen/libgenerated_tuning_params.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/codegen/libgenerated_version.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/core/libcore.lo\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/kernels/libkernels.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/utils/libutils.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/proto/libmace_cc.a\n") - mri_stream += ( - "addlib " - "bazel-bin/external/com_google_protobuf/libprotobuf_lite.a\n") - mri_stream += ( - "addlib " - "bazel-bin/mace/ops/libops.lo\n") - - mri_stream += "save\n" - mri_stream += "end\n" - which_sys = platform.system() - if which_sys == "Linux": - cmd = sh.Command("%s/toolchains/" % os.environ["ANDROID_NDK_HOME"] + - "aarch64-linux-android-4.9/prebuilt/linux-x86_64/" + - "bin/aarch64-linux-android-ar") - elif which_sys == "Darwin": - cmd = sh.Command("%s/toolchains/" % os.environ["ANDROID_NDK_HOME"] + - "aarch64-linux-android-4.9/prebuilt/darwin-x86_64/" + - "bin/aarch64-linux-android-ar") - - cmd("-M", _in=mri_stream) - - print("Libs merged!\n") - - def packaging_lib(libmace_output_dir, project_name): print("* Package libs for %s" % project_name) tar_package_name = "libmace_%s.tar.gz" % project_name @@ -1102,80 +932,13 @@ def packaging_lib(libmace_output_dir, project_name): print("Packaging Done!\n") -################################ -# example -################################ -def build_example(target_soc, - serial_num, - abi, - project_name, - build_output_dir, - library_output_dir, - model_output_dir, - build_type, - hexagon_mode, - enable_openmp, - linkshared=False): - static_lib_name = "mace/libmace.a" - if not linkshared: - target_name = "example_static" - lib_path = get_lib_path(target_soc, serial_num, abi, project_name, - build_output_dir, library_output_dir) - sh.cp("-f", lib_path, static_lib_name) - else: - target_name = "example_shared" - example_target = "//mace/examples/cli:%s" % target_name - - if build_type == BuildType.code: - build_arg = "--per_file_copt=//mace/examples/cli:example.cc@-DCODE_TYPE" # noqa - else: - build_arg = "" - - bazel_build(example_target, - abi=abi, - enable_openmp=enable_openmp, - hexagon_mode=hexagon_mode, - extra_args=build_arg) - - example_binary_file = "%s/%s" % (model_output_dir, target_name) - if os.path.exists(example_binary_file): - sh.rm("-rf", example_binary_file) - - target_bin = "/".join(bazel_target_to_bin(example_target)) - sh.cp("-f", target_bin, model_output_dir) - sh.rm("-rf", static_lib_name) - - ################################ # benchmark ################################ -def build_benchmark_model(abi, - model_output_dir, - hexagon_mode, - enable_openmp, - linkshared=False): - if not linkshared: - target_name = "benchmark_model_static" - else: - target_name = "benchmark_model_shared" - benchmark_target = "//mace/benchmark:%s" % target_name - - bazel_build(benchmark_target, - abi=abi, - enable_openmp=enable_openmp, - hexagon_mode=hexagon_mode) - - benchmark_binary_file = "%s/%s" % (model_output_dir, target_name) - if os.path.exists(benchmark_binary_file): - sh.rm("-rf", benchmark_binary_file) - - target_bin = "/".join(bazel_target_to_bin(benchmark_target)) - sh.cp("-f", target_bin, model_output_dir) - - def benchmark_model(abi, serialno, benchmark_binary_dir, + benchmark_binary_name, vlog_level, embed_model_data, model_output_dir, @@ -1187,31 +950,27 @@ def benchmark_model(abi, model_tag, device_type, phone_data_dir, - build_type, + model_graph_format, opencl_binary_file, - shared_library_dir, + opencl_parameter_file, + libmace_dynamic_library_path, omp_num_threads=-1, cpu_affinity_policy=1, gpu_perf_hint=3, gpu_priority_hint=3, input_file_name="model_input", - linkshared=0): + link_dynamic=False): print("* Benchmark for %s" % model_tag) - if linkshared == 0: - benchmark_model_target = "benchmark_model_static" - else: - benchmark_model_target = "benchmark_model_shared" - mace_model_path = "" - if build_type == BuildType.proto: + if model_graph_format == ModelFormat.file: mace_model_path = "%s/%s.pb" % (mace_model_dir, model_tag) if abi == "host": p = subprocess.Popen( [ "env", "MACE_CPP_MIN_VLOG_LEVEL=%s" % vlog_level, - "%s/%s" % (benchmark_binary_dir, benchmark_model_target), + "%s/%s" % (benchmark_binary_dir, benchmark_binary_name), "--model_name=%s" % model_tag, "--input_node=%s" % ",".join(input_nodes), "--output_node=%s" % ",".join(output_nodes), @@ -1240,23 +999,25 @@ def benchmark_model(abi, if not embed_model_data: adb_push("%s/%s.data" % (mace_model_dir, model_tag), phone_data_dir, serialno) - if device_type == common.DeviceType.GPU \ - and os.path.exists(opencl_binary_file): - adb_push(opencl_binary_file, phone_data_dir, serialno) + if device_type == common.DeviceType.GPU: + if os.path.exists(opencl_binary_file): + adb_push(opencl_binary_file, phone_data_dir, serialno) + if os.path.exists(opencl_parameter_file): + adb_push(opencl_parameter_file, phone_data_dir, serialno) mace_model_phone_path = "" - if build_type == BuildType.proto: + if model_graph_format == ModelFormat.file: mace_model_phone_path = "%s/%s.pb" % (phone_data_dir, model_tag) adb_push(mace_model_path, mace_model_phone_path, serialno) - if linkshared == 1: - adb_push("%s/libmace.so" % shared_library_dir, phone_data_dir, + if link_dynamic: + adb_push(libmace_dynamic_library_path, phone_data_dir, serialno) - adb_push("%s/libgnustl_shared.so" % shared_library_dir, + adb_push(find_gnustl_shared_path(abi), phone_data_dir, serialno) - adb_push("%s/%s" % (benchmark_binary_dir, benchmark_model_target), + adb_push("%s/%s" % (benchmark_binary_dir, benchmark_binary_name), phone_data_dir, serialno) @@ -1267,7 +1028,7 @@ def benchmark_model(abi, phone_data_dir, "MACE_INTERNAL_STORAGE_PATH=%s" % internal_storage_dir, "MACE_OPENCL_PROFILING=1", - "%s/%s" % (phone_data_dir, benchmark_model_target), + "%s/%s" % (phone_data_dir, benchmark_binary_name), "--model_name=%s" % model_tag, "--input_node=%s" % ",".join(input_nodes), "--output_node=%s" % ",".join(output_nodes), @@ -1283,6 +1044,8 @@ def benchmark_model(abi, "--model_file=%s" % mace_model_phone_path, "--opencl_binary_file=%s/%s" % (phone_data_dir, os.path.basename(opencl_binary_file)), + "--opencl_parameter_file=%s/%s" % + (phone_data_dir, os.path.basename(opencl_parameter_file)), ] adb_cmd = ' '.join(adb_cmd) cmd_file_name = "%s-%s-%s" % ('cmd_file', model_tag, str(time.time()))