提交 5553184f 编写于 作者: Y yejianwu

encrypt cl source, extract embed code for tuning and get opencl program

上级 b6ad52d6
......@@ -5,8 +5,6 @@ package(
default_visibility = ["//visibility:public"],
)
load("//mace:mace.bzl", "if_embed_binary_program")
cc_library(
name = "generated_models_lib",
srcs = glob(["models/*/*.cc"]),
......@@ -15,11 +13,10 @@ cc_library(
deps = [
"//mace/core",
"//mace/ops",
'//mace/codegen:version_lib',
] + if_embed_binary_program([
'//mace/codegen:generated_opencl_lib',
'//mace/codegen:generated_tuning_lib',
]),
"//mace/codegen:version_lib",
"//mace/codegen:generated_opencl_lib",
"//mace/codegen:generated_tuning_lib",
],
)
cc_library(
......
......@@ -7,7 +7,7 @@ package(
licenses(["notice"]) # Apache 2.0
load("//mace:mace.bzl", "if_android", "if_profiling_enabled", "if_embed_binary_program")
load("//mace:mace.bzl", "if_android", "if_profiling_enabled")
cc_library(
name = "core",
......@@ -22,8 +22,7 @@ cc_library(
"runtime/opencl/*.h",
]),
copts = ["-std=c++11", "-D_GLIBCXX_USE_C99_MATH_TR1", "-Werror=return-type"] +
if_profiling_enabled(["-DMACE_OPENCL_PROFILING"]) +
if_embed_binary_program(["-DMACE_EMBED_BINARY_PROGRAM"]),
if_profiling_enabled(["-DMACE_OPENCL_PROFILING"]),
linkopts = if_android(["-pie", "-ldl"]),
deps = [
"//mace/utils:utils_hdrs",
......
......@@ -174,40 +174,48 @@ std::string OpenCLRuntime::GenerateCLBinaryFilenamePrefix(
return filename_prefix;
}
void OpenCLRuntime::BuildProgram(const std::string &program_file_name,
extern bool GetOpenCLProgram(
const std::string &program_name,
const std::string &binary_file_name_prefix,
std::vector<unsigned char> *program_vec,
bool *is_opencl_binary);
const std::vector<unsigned char>
OpenCLRuntime::DecryptOpenCLSource(const std::vector<unsigned char> &src) {
std::vector<unsigned char> res;
res.reserve(src.size());
std::string decrypt_lookup_table = "Xiaomi-AI-Platform-Mace";
size_t lookup_table_size = decrypt_lookup_table.size();
for (int i = 0; i < src.size(); i++) {
res.push_back(src[i] ^ decrypt_lookup_table[i % lookup_table_size]);
}
return res;
}
void OpenCLRuntime::BuildProgram(const std::string &program_name,
const std::string &binary_file_name_prefix,
const std::string &build_options,
cl::Program *program) {
MACE_CHECK_NOTNULL(program);
#ifdef MACE_EMBED_BINARY_PROGRAM
extern const std::map<std::string, std::vector<unsigned char>> 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<unsigned char> binary = it_binary->second;
*program = cl::Program(this->context(), {device()}, {binary});
#else
std::string source_filename = kernel_path_ + program_file_name;
// Create program
if (std::ifstream(source_filename).is_open()) {
VLOG(1) << "Create program with source: " << source_filename;
std::vector<unsigned char> kernel_source;
MACE_CHECK(ReadFile(source_filename, false, &kernel_source));
bool is_opencl_binary = false;
std::vector<unsigned char> program_vec;
const bool is_success = GetOpenCLProgram(program_name,
binary_file_name_prefix,
&program_vec,
&is_opencl_binary);
MACE_CHECK(is_success, "Failed in GetOpenCLProgram!");
if (is_opencl_binary) {
*program = cl::Program(this->context(), {device()}, {program_vec});
VLOG(1) << "Use opencl binaries";
} else {
cl::Program::Sources sources;
sources.push_back(std::string(kernel_source.begin(), kernel_source.end()));
const std::vector<unsigned char> decrypt_source =
DecryptOpenCLSource(program_vec);
sources.push_back(std::string(decrypt_source.begin(), decrypt_source.end()));
*program = cl::Program(this->context(), sources);
} else {
LOG(FATAL) << "Failed to open kernel file " << source_filename;
VLOG(1) << "Use opencl sources";
}
#endif
// Build program
std::string build_options_str =
......@@ -225,41 +233,40 @@ void OpenCLRuntime::BuildProgram(const std::string &program_file_name,
LOG(FATAL) << "Build program failed: " << ret;
}
#ifndef MACE_EMBED_BINARY_PROGRAM
// Write binary if necessary
std::string binary_filename = kernel_path_ + binary_file_name_prefix + ".bin";
size_t device_list_size = 1;
std::unique_ptr<size_t[]> program_binary_sizes(
new size_t[device_list_size]);
cl_int err = clGetProgramInfo((*program)(), CL_PROGRAM_BINARY_SIZES,
sizeof(size_t) * device_list_size,
program_binary_sizes.get(), nullptr);
MACE_CHECK(err == CL_SUCCESS) << "Error code: " << err;
std::unique_ptr<std::unique_ptr<unsigned char[]>[]> program_binaries(
new std::unique_ptr<unsigned char[]>[device_list_size]);
for (cl_uint i = 0; i < device_list_size; ++i) {
program_binaries[i] = std::unique_ptr<unsigned char[]>(
new unsigned char[program_binary_sizes[i]]);
}
if (!is_opencl_binary) {
// Write binary if necessary
std::string binary_filename = kernel_path_ + binary_file_name_prefix + ".bin";
size_t device_list_size = 1;
std::unique_ptr<size_t[]> program_binary_sizes(
new size_t[device_list_size]);
cl_int err = clGetProgramInfo((*program)(), CL_PROGRAM_BINARY_SIZES,
sizeof(size_t) * device_list_size,
program_binary_sizes.get(), nullptr);
MACE_CHECK(err == CL_SUCCESS) << "Error code: " << err;
std::unique_ptr<std::unique_ptr<unsigned char[]>[]> program_binaries(
new std::unique_ptr<unsigned char[]>[device_list_size]);
for (cl_uint i = 0; i < device_list_size; ++i) {
program_binaries[i] = std::unique_ptr<unsigned char[]>(
new unsigned char[program_binary_sizes[i]]);
}
err = clGetProgramInfo((*program)(), CL_PROGRAM_BINARIES,
sizeof(unsigned char *) * device_list_size,
program_binaries.get(), nullptr);
MACE_CHECK(err == CL_SUCCESS) << "Error code: " << err;
std::vector<unsigned char> content(
reinterpret_cast<unsigned char const *>(program_binaries[0].get()),
reinterpret_cast<unsigned char const *>(program_binaries[0].get()) +
program_binary_sizes[0]);
err = clGetProgramInfo((*program)(), CL_PROGRAM_BINARIES,
sizeof(unsigned char *) * device_list_size,
program_binaries.get(), nullptr);
MACE_CHECK(err == CL_SUCCESS) << "Error code: " << err;
std::vector<unsigned char> content(
reinterpret_cast<unsigned char const *>(program_binaries[0].get()),
reinterpret_cast<unsigned char const *>(program_binaries[0].get()) +
program_binary_sizes[0]);
MACE_CHECK(WriteFile(binary_filename, true, content));
#endif
MACE_CHECK(WriteFile(binary_filename, true, content));
}
}
cl::Kernel OpenCLRuntime::BuildKernel(
const std::string &program_name,
const std::string &kernel_name,
const std::set<std::string> &build_options) {
std::string program_file_name = program_name + ".cl";
std::string build_options_str;
for (auto &option : build_options) {
build_options_str += " " + option;
......@@ -274,7 +281,7 @@ cl::Kernel OpenCLRuntime::BuildKernel(
} else {
std::string binary_file_name_prefix =
GenerateCLBinaryFilenamePrefix(built_program_key);
this->BuildProgram(program_file_name, binary_file_name_prefix,
this->BuildProgram(program_name, binary_file_name_prefix,
build_options_str, &program);
built_program_map_.emplace(built_program_key, program);
}
......
......@@ -55,6 +55,7 @@ class OpenCLRuntime {
const std::string &build_options,
cl::Program *program);
std::string GenerateCLBinaryFilenamePrefix(const std::string &filename_msg);
const std::vector<unsigned char> DecryptOpenCLSource(const std::vector<unsigned char> &src);
private:
// All OpenCL object must be a pointer and manually deleted before unloading
......
# Examples
load("//mace:mace.bzl", "if_android", "if_enable_neon", "if_embed_binary_program")
load("//mace:mace.bzl", "if_android", "if_enable_neon")
cc_binary(
name = "helloworld",
......
......@@ -7,7 +7,7 @@ package(
licenses(["notice"]) # Apache 2.0
load("//mace:mace.bzl", "if_android", "if_enable_neon", "if_embed_binary_program")
load("//mace:mace.bzl", "if_android", "if_enable_neon")
cc_library(
name = "kernels",
......@@ -22,7 +22,6 @@ cc_library(
"-D_GLIBCXX_USE_C99_MATH_TR1",
"-Werror=return-type",
] +
if_embed_binary_program(["-DMACE_EMBED_BINARY_PROGRAM"]) +
if_enable_neon(["-fopenmp"]),
linkopts = if_android(["-lm"]),
deps = [
......
......@@ -30,12 +30,6 @@ def if_profiling_enabled(a):
"//conditions:default": [],
})
def if_embed_binary_program(a):
return select({
"//mace:embed_binary_program": a,
"//conditions:default": [],
})
def if_enable_neon(a):
return select({
"//mace:enable_neon": a,
......
//
// Copyright (c) 2017 XiaoMi All rights reserved.
//
// This is a generated file, DO NOT EDIT
#include <map>
#include <string>
#include <vector>
namespace mace {
extern const std::map<std::string, std::vector<{{data_type}}>> {{variable_name}}=
{
{% for key, value in maps.iteritems() %}
{
"{{key}}",
{
{%- for ele in value -%}
{{ele}},
{%- endfor -%}
}
}, // {{key}}
{% endfor %}
};
} // namespace
......@@ -38,14 +38,14 @@ def generate_cpp_source():
idx += params_size
env = jinja2.Environment(loader=jinja2.FileSystemLoader(sys.path[0]))
return env.get_template('binary.cc.tmpl').render(
return env.get_template('embed_code.cc.tmpl').render(
maps = data_map,
data_type = 'int',
variable_name = FLAGS.variable_name
data_type = 'unsigned int',
variable_name = FLAGS.variable_name,
mode="tuning_binary"
)
def main(unused_args):
cpp_binary_source = generate_cpp_source()
if os.path.isfile(FLAGS.output_path):
os.remove(FLAGS.output_path)
......
//
// Copyright (c) 2017 XiaoMi All rights reserved.
//
// This is a generated file, DO NOT EDIT
{% if mode == "read_tuning_config" %}
#include <fstream>
#include <string>
#include <vector>
#include <unordered_map>
namespace mace {
bool GetTuningParams(const char *path,
std::unordered_map<std::string, std::vector<{{data_type}}>> *param_table) {
if (path != nullptr) {
std::ifstream ifs(path, std::ios::binary | std::ios::in);
if (ifs.is_open()) {
int32_t key_size = 0;
int32_t params_size = 0;
int32_t params_count = 0;
int64_t num_pramas = 0;
ifs.read(reinterpret_cast<char *>(&num_pramas), sizeof(num_pramas));
while (num_pramas--) {
ifs.read(reinterpret_cast<char *>(&key_size), sizeof(key_size));
std::string key(key_size, ' ');
ifs.read(&key[0], key_size);
ifs.read(reinterpret_cast<char *>(&params_size), sizeof(params_size));
params_count = params_size / sizeof({{data_type}});
std::vector<{{data_type}}> params(params_count);
for (int i = 0; i < params_count; ++i) {
ifs.read(reinterpret_cast<char *>(&params[i]), sizeof({{data_type}}));
}
param_table->emplace(key, params);
}
ifs.close();
} else {
return false;
}
}
return true;
}
} // namespace
{% else %}
#include <map>
#include <string>
#include <vector>
{% if mode == "tuning_binary" %}
#include <unordered_map>
{% endif %}
namespace mace {
extern const std::map<std::string, std::vector<{{data_type}}>> {{variable_name}}=
{
{% for key, value in maps.iteritems() %}
{
"{{key}}",
{
{%- for ele in value -%}
{{ele}},
{%- endfor -%}
}
}, // {{key}}
{% endfor %}
};
{% if mode == "cl_encrypt" %}
bool GetOpenCLProgram(const std::string &program_name,
const std::string &binary_file_name_prefix,
std::vector<unsigned char> *program_vec,
bool *is_binary) {
*is_binary = false;
auto it_source = {{variable_name}}.find(program_name);
if (it_source == {{variable_name}}.end()) {
return false;
}
*program_vec = it_source->second;
return true;
}
{% elif mode == "cl_binary" %}
bool GetOpenCLProgram(const std::string &program_name,
const std::string &binary_file_name_prefix,
std::vector<unsigned char> *program_vec,
bool *is_binary) {
*is_binary = true;
auto it_source = {{variable_name}}.find(binary_file_name_prefix);
if (it_source == {{variable_name}}.end()) {
return false;
}
*program_vec = it_source->second;
return true;
}
{% elif mode == "tuning_binary" %}
bool GetTuningParams(const char *path,
std::unordered_map<std::string, std::vector<{{data_type}}>> *param_table) {
for (auto it = kTuningParamsData.begin(); it != kTuningParamsData.end(); ++it) {
param_table->emplace(it->first, std::vector<{{data_type}}>(it->second.begin(), it->second.end()));
}
return true;
}
{% endif %}
} // namespace
{% endif %}
import argparse
import os
import sys
import jinja2
# python encrypt_opencl_codegen.py --cl_kernel_dir=./mace/kernels/opencl/cl/ \
# --output_path=./mace/codegen/opencl_encrypt/opencl_encrypted_program.cc
FLAGS = None
encrypt_lookup_table = "Xiaomi-AI-Platform-Mace"
def encrypt_code(code_str):
encrypted_arr = []
for i in range(len(code_str)):
encrypted_char = hex(ord(code_str[i]) ^ ord(encrypt_lookup_table[i % len(encrypt_lookup_table)]))
encrypted_arr.append(encrypted_char)
return encrypted_arr
def main(unused_args):
if not os.path.exists(FLAGS.cl_kernel_dir):
print("Input cl_kernel_dir " + FLAGS.cl_kernel_dir + " doesn't exist!")
header_code = ""
for file_name in os.listdir(FLAGS.cl_kernel_dir):
file_path = os.path.join(FLAGS.cl_kernel_dir, file_name)
if file_path[-2:] == ".h":
f = open(file_path, "r")
header_code += f.read()
encrypted_code_maps = {}
for file_name in os.listdir(FLAGS.cl_kernel_dir):
file_path = os.path.join(FLAGS.cl_kernel_dir, file_name)
if file_path[-3:] == ".cl":
f = open(file_path, "r")
code_str = ""
for line in f.readlines():
if "#include <common.h>" in line:
code_str += header_code
else:
code_str += line
encrypted_code_arr = encrypt_code(code_str)
encrypted_code_maps[file_name[:-3]] = encrypted_code_arr
env = jinja2.Environment(loader=jinja2.FileSystemLoader(sys.path[0]))
cpp_cl_encrypted_kernel = env.get_template('embed_code.cc.tmpl').render(
maps=encrypted_code_maps,
data_type='unsigned char',
variable_name='kEncryptedProgramMap',
mode='cl_encrypt')
if os.path.isfile(FLAGS.output_path):
os.remove(FLAGS.output_path)
w_file = open(FLAGS.output_path, "w")
w_file.write(cpp_cl_encrypted_kernel)
w_file.close()
print("Generate encrypted opencl source done!")
def parse_args():
"""Parses command line arguments."""
parser = argparse.ArgumentParser()
parser.add_argument(
"--cl_kernel_dir",
type=str,
default="./mace/kernels/opencl/cl/",
help="The cl kernels directory.")
parser.add_argument(
"--output_path",
type=str,
default="./mace/examples/codegen/opencl/opencl_encrypted_program.cc",
help="The path of encrypted opencl kernels.")
return parser.parse_known_args()
if __name__ == '__main__':
FLAGS, unparsed = parse_args()
main(unused_args=[sys.argv[0]] + unparsed)
import argparse
import os
import subprocess
import sys
import numpy as np
......@@ -28,10 +27,11 @@ def generate_cpp_source():
maps[file_name[:-4]].append(hex(ele))
env = jinja2.Environment(loader=jinja2.FileSystemLoader(sys.path[0]))
return env.get_template('binary.cc.tmpl').render(
return env.get_template('embed_code.cc.tmpl').render(
maps = maps,
data_type = 'unsigned char',
variable_name = 'kCompiledProgramMap'
variable_name = 'kCompiledProgramMap',
mode="cl_binary"
)
......
import argparse
import os
import sys
import jinja2
# python mace/python/tools/read_tuning_codegen.py \
# --output_path=./mace/codegen/tuning/read_tuning_params.cc
FLAGS = None
def main(unused_args):
env = jinja2.Environment(loader=jinja2.FileSystemLoader(sys.path[0]))
cpp_cl_encrypted_kernel = env.get_template('embed_code.cc.tmpl').render(
data_type='unsigned int',
mode='read_tuning_config')
if os.path.isfile(FLAGS.output_path):
os.remove(FLAGS.output_path)
w_file = open(FLAGS.output_path, "w")
w_file.write(cpp_cl_encrypted_kernel)
w_file.close()
print("Generate reading tuning method done!")
def parse_args():
"""Parses command line arguments."""
parser = argparse.ArgumentParser()
parser.add_argument(
"--output_path",
type=str,
default="./mace/examples/codegen/tuning/read_tuning_params.cc",
help="The path of codes to read tuning params.")
return parser.parse_known_args()
if __name__ == '__main__':
FLAGS, unparsed = parse_args()
main(unused_args=[sys.argv[0]] + unparsed)
......@@ -18,6 +18,9 @@
namespace mace {
extern bool GetTuningParams(const char *path,
std::unordered_map<std::string, std::vector<unsigned int>> *param_table);
template <typename param_type>
class Tuner {
public:
......@@ -99,40 +102,10 @@ class Tuner {
}
inline void ReadRunParamters() {
#ifdef MACE_EMBED_BINARY_PROGRAM
extern const std::map<std::string, std::vector<int>> kTuningParamsData;
VLOG(1) << "Read tuning parameters from source";
for (auto it = kTuningParamsData.begin(); it != kTuningParamsData.end(); ++it) {
param_table_.emplace(it->first, std::vector<param_type>(it->second.begin(), it->second.end()));
}
#else
if (path_ != nullptr) {
std::ifstream ifs(path_, std::ios::binary | std::ios::in);
if (ifs.is_open()) {
int32_t key_size = 0;
int32_t params_size = 0;
int32_t params_count = 0;
int64_t num_pramas = 0;
ifs.read(reinterpret_cast<char *>(&num_pramas), sizeof(num_pramas));
while (num_pramas--) {
ifs.read(reinterpret_cast<char *>(&key_size), sizeof(key_size));
std::string key(key_size, ' ');
ifs.read(&key[0], key_size);
ifs.read(reinterpret_cast<char *>(&params_size), sizeof(params_size));
params_count = params_size / sizeof(param_type);
std::vector<param_type> params(params_count);
for (int i = 0; i < params_count; ++i) {
ifs.read(reinterpret_cast<char *>(&params[i]), sizeof(param_type));
}
param_table_.emplace(key, params);
}
ifs.close();
} else {
LOG(WARNING) << "Read run parameter file failed.";
}
bool success = GetTuningParams(path_, &param_table_);
if (!success) {
LOG(WARNING) << "Read run parameter failed.";
}
#endif
}
template <typename RetType>
......
......@@ -35,9 +35,6 @@ VERSION_SOURCE_PATH=${CODEGEN_DIR}/version
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 \
......@@ -47,10 +44,6 @@ build_and_run()
--copt=-DMACE_MODEL_FUNCTION=Create${MODEL_TAG}
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}
......@@ -104,10 +97,20 @@ rm -rf ${VERSION_SOURCE_PATH}
mkdir -p ${VERSION_SOURCE_PATH}
bash mace/tools/git/gen_version_source.sh ${VERSION_SOURCE_PATH}/version.cc
echo "Step 4: Run model on the phone with files"
echo "Step 4: Generate encrypted opencl source and read tuning method"
rm -rf ${CL_CODEGEN_DIR}
rm -rf ${TUNING_CODEGEN_DIR}
mkdir -p ${CL_CODEGEN_DIR}
mkdir -p ${TUNING_CODEGEN_DIR}
python mace/python/tools/encrypt_opencl_codegen.py \
--cl_kernel_dir=./mace/kernels/opencl/cl/ --output_path=${CL_CODEGEN_DIR}/opencl_encrypt_program.cc
python mace/python/tools/read_tuning_codegen.py \
--output_path=${TUNING_CODEGEN_DIR}/read_tuning_params.cc
echo "Step 5: Run model on the phone with files"
build_and_run false
echo "Step 5: Generate OpenCL binary program and config code"
echo "Step 6: Generate OpenCL binary program and config code"
rm -rf ${CL_BIN_DIR}
adb pull ${KERNEL_DIR} ${CL_BIN_DIR}
rm -rf ${CL_CODEGEN_DIR}
......@@ -115,20 +118,21 @@ mkdir -p ${CL_CODEGEN_DIR}
python mace/python/tools/opencl_codegen.py \
--cl_binary_dir=${CL_BIN_DIR} --output_path=${CL_CODEGEN_DIR}/opencl_compiled_program.cc
echo "Step 6: Generate tuning source file"
echo "Step 7: Generate tuning source file"
adb pull ${PHONE_DATA_DIR}/mace_run.config ${CL_BIN_DIR}
rm -rf ${TUNING_CODEGEN_DIR}
mkdir -p ${TUNING_CODEGEN_DIR}
python mace/python/tools/binary_codegen.py \
--binary_file=${CL_BIN_DIR}/mace_run.config --output_path=${TUNING_CODEGEN_DIR}/tuning_params.cc
echo "Step 7: Run model on the phone using binary"
echo "Step 8: Run model on the phone using binary"
build_and_run true
echo "Step 8: Pull the mace run result."
echo "Step 9: Pull the mace run result."
rm -rf ${MODEL_DIR}/${OUTPUT_FILE_NAME}
adb </dev/null pull ${PHONE_DATA_DIR}/${OUTPUT_FILE_NAME} ${MODEL_DIR}
echo "Step 9: Validate the result"
echo "Step 10: Validate the result"
python tools/validate.py --model_file ${TF_MODEL_FILE_PATH} \
--input_file ${MODEL_DIR}/${INPUT_FILE_NAME} \
--mace_out_file ${MODEL_DIR}/${OUTPUT_FILE_NAME} \
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册