From 60fda80ea4018380cc5bcada4f0ddaa341e59497 Mon Sep 17 00:00:00 2001 From: Liangliang He Date: Tue, 19 Dec 2017 10:42:18 +0800 Subject: [PATCH] Add opencl binary program support --- mace/BUILD | 8 ++ mace/codegen/BUILD | 26 +++++ mace/core/BUILD | 5 +- mace/core/runtime/opencl/opencl_runtime.cc | 20 +++- mace/examples/BUILD | 7 +- mace/mace.bzl | 8 +- mace/python/tools/opencl_codegen.py | 65 +++++++++++++ .../tools/opencl_compiled_program.cc.tmpl | 31 ++++++ mace/python/tools/source_converter_lib.py | 2 +- tools/validate_gcn.sh | 97 ++++++++++++------- 10 files changed, 221 insertions(+), 48 deletions(-) create mode 100644 mace/codegen/BUILD create mode 100644 mace/python/tools/opencl_codegen.py create mode 100644 mace/python/tools/opencl_compiled_program.cc.tmpl diff --git a/mace/BUILD b/mace/BUILD index b1bcd270..8ae6e6d6 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 00000000..433a4297 --- /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 36b2a121..a3f35682 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 1cc57079..70ca43b6 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 d8978654..7b6a268d 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 af6fe583..d7d10109 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 00000000..7c6d7489 --- /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 00000000..84dd3f5c --- /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 a26d5c13..addcef8c 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 377749ff..46952446 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