diff --git a/mace/BUILD b/mace/BUILD index b1bcd27021878e20e35be0ce9c1f4b2e58f095bd..8ae6e6d6adbcfca7b87ed14a308e186fccfa3114 100644 --- a/mace/BUILD +++ b/mace/BUILD @@ -31,3 +31,11 @@ config_setting( }, visibility = ["//visibility:public"], ) + +config_setting( + name = "embed_binary_program", + define_values = { + "embed_binary_program": "true", + }, + visibility = ["//visibility:public"], +) diff --git a/mace/codegen/BUILD b/mace/codegen/BUILD new file mode 100644 index 0000000000000000000000000000000000000000..433a42976c76298b955920a35aa4176e8e1b0b6e --- /dev/null +++ b/mace/codegen/BUILD @@ -0,0 +1,26 @@ +# Description: +# Generated model and runtime code. +# +package( + default_visibility = ["//visibility:public"], +) + +load("//mace:mace.bzl", "if_embed_binary_program") + +cc_library( + name = "generated_models_lib", + srcs = glob(["models/*/*.cc"]), + copts = ["-std=c++11", "-D_GLIBCXX_USE_C99_MATH_TR1"], + linkstatic = 1, + deps = [ + "//mace/core", + "//mace/ops", + ] + if_embed_binary_program(['//mace/codegen:generated_opencl_lib']), +) + +cc_library( + name = "generated_opencl_lib", + srcs = glob(["opencl/*.cc"]), + copts = ["-std=c++11", "-D_GLIBCXX_USE_C99_MATH_TR1"], + linkstatic = 1, +) diff --git a/mace/core/BUILD b/mace/core/BUILD index 36b2a121e2c3ab4c7ad0e16468502134328777e0..a3f35682fd2f287f7f6b0a55aa88395803ac716d 100644 --- a/mace/core/BUILD +++ b/mace/core/BUILD @@ -7,7 +7,7 @@ package( licenses(["notice"]) # Apache 2.0 -load("//mace:mace.bzl", "if_android", "if_profiling_enabled") +load("//mace:mace.bzl", "if_android", "if_profiling_enabled", "if_embed_binary_program") cc_library( name = "opencl_runtime", @@ -19,7 +19,8 @@ cc_library( "runtime/opencl/*.h", ]), copts = ["-std=c++11", "-D_GLIBCXX_USE_C99_MATH_TR1"] + - if_profiling_enabled(["-DMACE_OPENCL_PROFILING"]), + if_profiling_enabled(["-DMACE_OPENCL_PROFILING"]) + + if_embed_binary_program(["-DMACE_EMBED_BINARY_PROGRAM"]), linkopts = ["-ldl"], deps = [ ":core", diff --git a/mace/core/runtime/opencl/opencl_runtime.cc b/mace/core/runtime/opencl/opencl_runtime.cc index 1cc57079410bc014f2f9be131d491cde4a504d11..70ca43b67efcfdfea95e5d8246f08d61450cc053 100644 --- a/mace/core/runtime/opencl/opencl_runtime.cc +++ b/mace/core/runtime/opencl/opencl_runtime.cc @@ -180,6 +180,17 @@ void OpenCLRuntime::BuildProgram(const std::string &program_file_name, cl::Program *program) { MACE_CHECK_NOTNULL(program); +#ifdef MACE_EMBED_BINARY_PROGRAM + extern const std::map> kCompiledProgramMap; + VLOG(1) << "Create program with merged binary map"; + auto it_binary = kCompiledProgramMap.find(binary_file_name_prefix); + if (it_binary == kCompiledProgramMap.end()) { + LOG(FATAL) << "Cannot found the binary key '" << binary_file_name_prefix + << "' in kCompiledProgramMap"; + } + std::vector binary = it_binary->second; + *program = cl::Program(this->context(), {device()}, {binary}); +#else std::string source_filename = kernel_path_ + program_file_name; std::string binary_filename = kernel_path_ + binary_file_name_prefix + ".bin"; @@ -187,10 +198,10 @@ void OpenCLRuntime::BuildProgram(const std::string &program_file_name, bool is_binary_filename_exist = std::ifstream(binary_filename).is_open(); if (is_binary_filename_exist) { VLOG(1) << "Create program with binary: " << binary_filename; - std::vector binaries; - MACE_CHECK(ReadFile(binary_filename, true, &binaries)); + std::vector binary; + MACE_CHECK(ReadFile(binary_filename, true, &binary)); - *program = cl::Program(this->context(), {device()}, {binaries}); + *program = cl::Program(this->context(), {device()}, {binary}); } else if (std::ifstream(source_filename).is_open()) { VLOG(1) << "Create program with source: " << source_filename; @@ -206,6 +217,7 @@ void OpenCLRuntime::BuildProgram(const std::string &program_file_name, LOG(FATAL) << "Failed to open kernel file " << binary_filename << " or " << source_filename; } +#endif // Build program std::string build_options_str = @@ -223,6 +235,7 @@ void OpenCLRuntime::BuildProgram(const std::string &program_file_name, LOG(FATAL) << "Build program failed: " << ret; } +#ifndef MACE_EMBED_BINARY_PROGRAM // Write binary if necessary if (!is_binary_filename_exist) { size_t device_list_size = 1; @@ -250,6 +263,7 @@ void OpenCLRuntime::BuildProgram(const std::string &program_file_name, MACE_CHECK(WriteFile(binary_filename, true, content)); } +#endif } cl::Kernel OpenCLRuntime::BuildKernel( diff --git a/mace/examples/BUILD b/mace/examples/BUILD index d8978654d8bc59e48f2f28c1668b5cf2bc65cabf..7b6a268d56a78bce9879d9707d71f9f8eb4f29c6 100644 --- a/mace/examples/BUILD +++ b/mace/examples/BUILD @@ -1,5 +1,5 @@ # Examples -load("//mace:mace.bzl", "if_android") +load("//mace:mace.bzl", "if_android", "if_embed_binary_program") cc_binary( name = "helloworld", @@ -30,13 +30,12 @@ cc_test( cc_binary( name = "mace_run", - srcs = glob(["models/*/*.cc"] + ["mace_run.cc"]), + srcs = ["mace_run.cc"], copts = ["-std=c++11", "-D_GLIBCXX_USE_C99_MATH_TR1"], linkopts = ["-fopenmp"], linkstatic = 1, deps = [ - "//mace/core", - "//mace/ops", + "//mace/codegen:generated_models_lib", "//mace/utils:command_line_flags", ], ) diff --git a/mace/mace.bzl b/mace/mace.bzl index af6fe583544d4d80dcc3c794b25480b7fb82da88..d7d10109f75e2d7ba630528242ed53a0e79aecf6 100644 --- a/mace/mace.bzl +++ b/mace/mace.bzl @@ -28,4 +28,10 @@ def if_profiling_enabled(a): return select({ "//mace:profiling_enabled": a, "//conditions:default": [], -}) + }) + +def if_embed_binary_program(a): + return select({ + "//mace:embed_binary_program": a, + "//conditions:default": [], + }) diff --git a/mace/python/tools/opencl_codegen.py b/mace/python/tools/opencl_codegen.py new file mode 100644 index 0000000000000000000000000000000000000000..7c6d74892f7b535714dd00f3719e4987df3c20d0 --- /dev/null +++ b/mace/python/tools/opencl_codegen.py @@ -0,0 +1,65 @@ +import argparse +import os +import subprocess +import sys + +import numpy as np + +import jinja2 + +# python mace/python/tools/opencl_codegen.py \ +# --cl_binary_dir=${CL_BIN_DIR} --output_path=${CL_HEADER_PATH} + +FLAGS = None + + +def generate_cpp_source(): + maps = {"binary_maps": []} + for file_name in os.listdir(FLAGS.cl_binary_dir): + file_path = os.path.join(FLAGS.cl_binary_dir, file_name) + if file_path[-4:] == ".bin": + # read binary + f = open(file_path, "rb") + binary_array = np.fromfile(f, dtype=np.uint8) + f.close() + + binary_dict = {"name": file_name[:-4], "content": []} + for ele in binary_array: + binary_dict["content"].append(hex(ele)) + maps["binary_maps"].append(binary_dict) + + env = jinja2.Environment(loader=jinja2.FileSystemLoader(sys.path[0])) + return env.get_template('opencl_compiled_program.cc.tmpl').render(maps) + + +def main(unused_args): + if not os.path.exists(FLAGS.cl_binary_dir): + print("Input cl_binary_dir " + FLAGS.cl_binary_dir + " doesn't exist!") + + cpp_cl_binary_source = generate_cpp_source() + if os.path.isfile(FLAGS.output_path): + os.remove(FLAGS.output_path) + w_file = open(FLAGS.output_path, "w") + w_file.write(cpp_cl_binary_source) + w_file.close() + + +def parse_args(): + """Parses command line arguments.""" + parser = argparse.ArgumentParser() + parser.add_argument( + "--cl_binary_dir", + type=str, + default="./cl_bin/", + help="The cl binaries directory.") + parser.add_argument( + "--output_path", + type=str, + default="./mace/examples/codegen/opencl/opencl_compiled_program.cc", + help="The path of generated C++ header file which contains cl binaries.") + return parser.parse_known_args() + + +if __name__ == '__main__': + FLAGS, unparsed = parse_args() + main(unused_args=[sys.argv[0]] + unparsed) diff --git a/mace/python/tools/opencl_compiled_program.cc.tmpl b/mace/python/tools/opencl_compiled_program.cc.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..84dd3f5c6fc6d8e655db4b3414a43c11595d3c60 --- /dev/null +++ b/mace/python/tools/opencl_compiled_program.cc.tmpl @@ -0,0 +1,31 @@ +// +// Copyright (c) 2017 XiaoMi All rights reserved. +// + +// This is a generated file, DO NOT EDIT + +#include +#include +#include + +namespace mace { + +{% for map in binary_maps %} +// {{map.name}} +{% endfor %} + +extern const std::map> kCompiledProgramMap = +{ + {% for map in binary_maps %} + { + "{{map.name}}", + { + {%- for ele in map.content -%} + {{ele}}, + {%- endfor -%} + } + }, // {{map.name}} +{% endfor %} +}; + +} // namespace diff --git a/mace/python/tools/source_converter_lib.py b/mace/python/tools/source_converter_lib.py index a26d5c13cb7c585a7b99740d45b73604036763e7..addcef8c7ffeb5c6037c8ac587ad0950e0b649dc 100644 --- a/mace/python/tools/source_converter_lib.py +++ b/mace/python/tools/source_converter_lib.py @@ -106,7 +106,7 @@ def convert_to_source(net_def, template, confuse, model_tag, output): tag = model_tag, mode = 0, ) - with gfile.GFile(output_dir + str(counter) + '.cc', "wb") as f: + with gfile.GFile(output_dir + 'tensor' + str(counter) + '.cc', "wb") as f: f.write(source) counter += 1 diff --git a/tools/validate_gcn.sh b/tools/validate_gcn.sh index 377749ff55b4631f05817cef27f4c9343bf5f0e0..46952446b77d09dea7e72f20bce2e4964b3e514b 100644 --- a/tools/validate_gcn.sh +++ b/tools/validate_gcn.sh @@ -21,20 +21,62 @@ PHONE_DATA_DIR="/data/local/tmp/${MACE_MODEL_NAME}" KERNEL_DIR="${PHONE_DATA_DIR}/cl/" IMAGE_SIZE=$2 MODEL_TAG=GCN${IMAGE_SIZE} +CODEGEN_DIR=${MACE_SOURCE_DIR}/mace/codegen +MODEL_CODEGEN_DIR=${CODEGEN_DIR}/models/gcn-$IMAGE_SIZE +CL_CODEGEN_DIR=${CODEGEN_DIR}/opencl +CL_BIN_DIR=${CODEGEN_DIR}/opencl_bin + +build_and_run() +{ + EMBED_OPENCL_BINARY=$1 + if [ "$EMBED_OPENCL_BINARY" = true ]; then + EMBED_OPENCL_BINARY_BUILD_FLAGS="--define embed_binary_program=true" + fi + + bazel build -c opt --strip always mace/examples:mace_run \ + --crosstool_top=//external:android/crosstool \ + --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \ + --cpu=arm64-v8a \ + $EMBED_OPENCL_BINARY_BUILD_FLAGS \ + --copt=-DMACE_MODEL_FUNCTION=Create${MODEL_TAG} + + adb shell "rm -rf ${PHONE_DATA_DIR}" + adb shell "mkdir -p ${PHONE_DATA_DIR}" + if [ "$EMBED_OPENCL_BINARY" = false ]; then + adb shell "mkdir -p ${KERNEL_DIR}" + adb push mace/kernels/opencl/cl/ ${KERNEL_DIR} + fi + adb push ${MODEL_DIR}/${INPUT_FILE_NAME} ${PHONE_DATA_DIR} + adb push bazel-bin/mace/examples/mace_run ${PHONE_DATA_DIR} + + num_threads=${1:-4} + + adb