mace_tools.py 13.2 KB
Newer Older
1 2
#!/usr/bin/env python

3
# Must run at root dir of libmace project.
4
# python tools/mace_tools.py \
Y
yejianwu 已提交
5
#     --config=tools/example.yaml \
6 7 8 9
#     --round=100 \
#     --mode=all

import argparse
10
import hashlib
11 12 13 14
import os
import shutil
import subprocess
import sys
15
import urllib
Y
yejianwu 已提交
16
import yaml
L
liuqi 已提交
17
import re
18

19
import sh_commands
L
Liangliang He 已提交
20

21 22
from ConfigParser import ConfigParser

Y
yejianwu 已提交
23

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
def run_command(command):
  print("Run command: {}".format(command))
  result = subprocess.Popen(
      command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  out, err = result.communicate()

  if out:
    print("Stdout msg:\n{}".format(out))
  if err:
    print("Stderr msg:\n{}".format(err))

  if result.returncode != 0:
    raise Exception("Exit not 0 from bash with code: {}, command: {}".format(
        result.returncode, command))


Y
yejianwu 已提交
40
def get_global_runtime(configs):
41
  runtime_list = []
Y
yejianwu 已提交
42 43 44
  for model_name in configs["models"]:
    model_runtime = configs["models"][model_name]["runtime"]
    runtime_list.append(model_runtime.lower())
45

Y
yejianwu 已提交
46
  global_runtime = ""
47 48 49 50 51 52
  if "dsp" in runtime_list:
    global_runtime = "dsp"
  elif "gpu" in runtime_list:
    global_runtime = "gpu"
  elif "cpu" in runtime_list:
    global_runtime = "cpu"
李寅 已提交
53 54
  elif "neon" in runtime_list:
    global_runtime = "neon"
55 56 57
  else:
    raise Exception("Not found available RUNTIME in config files!")

Y
yejianwu 已提交
58
  return global_runtime
59 60


Y
yejianwu 已提交
61 62 63
def generate_opencl_and_version_code():
  command = "bash tools/generate_opencl_and_version_code.sh"
  run_command(command)
64

65

Y
yejianwu 已提交
66 67
def clear_env(target_soc):
  command = "bash tools/clear_env.sh {}".format(target_soc)
68 69
  run_command(command)

L
liuqi 已提交
70 71 72
def input_file_name(input_name):
  return os.environ['INPUT_FILE_NAME'] + '_' + \
         re.sub('[^0-9a-zA-Z]+', '_', input_name)
73

L
liuqi 已提交
74 75
def generate_random_input(target_soc, model_output_dir,
                          input_names, input_files):
76
  generate_data_or_not = True
Y
yejianwu 已提交
77 78
  command = "bash tools/validate_tools.sh {} {} {}".format(
      target_soc, model_output_dir, int(generate_data_or_not))
79 80
  run_command(command)

L
liuqi 已提交
81 82 83 84 85
  input_file_list = []
  if isinstance(input_files, list):
    input_file_list.extend(input_files)
  else:
    input_file_list.append(input_files)
L
liuqi 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
  if len(input_file_list) != 0:
    input_name_list = []
    if isinstance(input_names, list):
      input_name_list.extend(input_names)
    else:
      input_name_list.append(input_names)
    if len(input_file_list) != len(input_name_list):
      raise Exception('If input_files set, the input files should match the input names.')
    for i in range(len(input_file_list)):
      if input_file_list[i] is not None:
        dst_input_file = model_output_dir + '/' + input_file_name(input_name_list[i])
        if input_file_list[i].startswith("http://") or \
            input_file_list[i].startswith("https://"):
          urllib.urlretrieve(input_file_list[i], dst_input_file)
        else:
          shutil.copy(input_file_list[i], dst_input_file)
102 103 104

def generate_model_code():
  command = "bash tools/generate_model_code.sh"
Y
yejianwu 已提交
105
  run_command(command)
106 107


108 109 110
def build_mace_run(production_mode, model_output_dir, hexagon_mode):
  command = "bash tools/build_mace_run.sh {} {} {}".format(
      int(production_mode), model_output_dir, int(hexagon_mode))
111 112 113
  run_command(command)


Y
yejianwu 已提交
114 115 116 117 118 119 120
def tuning_run(target_soc,
               model_output_dir,
               running_round,
               tuning,
               production_mode,
               restart_round,
               option_args=''):
Y
yejianwu 已提交
121
  command = "bash tools/tuning_run.sh {} {} {} {} {} {} \"{}\"".format(
Y
yejianwu 已提交
122 123
      target_soc, model_output_dir, running_round, int(tuning),
      int(production_mode), restart_round, option_args)
124 125
  run_command(command)

Y
yejianwu 已提交
126

127 128 129
def benchmark_model(target_soc, model_output_dir, option_args=''):
  command = "bash tools/benchmark.sh {} {} \"{}\"".format(
      target_soc, model_output_dir, option_args)
130
  run_command(command)
131

Y
yejianwu 已提交
132

Y
yejianwu 已提交
133 134 135 136
def run_model(target_soc, model_output_dir, running_round, restart_round,
              option_args):
  tuning_run(target_soc, model_output_dir, running_round, False, False,
             restart_round, option_args)
137 138


Y
yejianwu 已提交
139
def generate_production_code(target_soc, model_output_dirs, pull_or_not):
140 141 142 143
  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)
Y
yejianwu 已提交
144 145
  command = "bash tools/generate_production_code.sh {} {} {}".format(
      target_soc, cl_bin_dirs_str, int(pull_or_not))
146 147 148
  run_command(command)


Y
yejianwu 已提交
149
def build_mace_run_prod(target_soc, model_output_dir, tuning, global_runtime):
Y
yejianwu 已提交
150
  if "dsp" == global_runtime:
151 152 153 154
    hexagon_mode = True
  else:
    hexagon_mode = False

155
  production_or_not = False
156
  build_mace_run(production_or_not, model_output_dir, hexagon_mode)
157
  tuning_run(
Y
yejianwu 已提交
158
      target_soc,
159 160 161
      model_output_dir,
      running_round=0,
      tuning=tuning,
李寅 已提交
162 163
      production_mode=production_or_not,
      restart_round=1)
164 165 166

  production_or_not = True
  pull_or_not = True
Y
yejianwu 已提交
167
  generate_production_code(target_soc, [model_output_dir], pull_or_not)
168
  build_mace_run(production_or_not, model_output_dir, hexagon_mode)
169 170


171 172 173 174
def build_run_throughput_test(target_soc, run_seconds, merged_lib_file,
                              model_input_dir):
  command = "bash tools/build_run_throughput_test.sh {} {} {} {}".format(
      target_soc, run_seconds, merged_lib_file, model_input_dir)
175 176 177
  run_command(command)


Y
fix run  
yejianwu 已提交
178
def validate_model(target_soc, model_output_dir):
179
  generate_data_or_not = False
Y
fix run  
yejianwu 已提交
180 181
  command = "bash tools/validate_tools.sh {} {} {}".format(
      target_soc, model_output_dir, int(generate_data_or_not))
182 183 184 185 186 187 188 189
  run_command(command)


def build_production_code():
  command = "bash tools/build_production_code.sh"
  run_command(command)


Y
yejianwu 已提交
190
def merge_libs_and_tuning_results(target_soc, output_dir, model_output_dirs):
191
  pull_or_not = False
Y
yejianwu 已提交
192
  generate_production_code(target_soc, model_output_dirs, pull_or_not)
193 194 195
  build_production_code()

  model_output_dirs_str = ",".join(model_output_dirs)
Y
yejianwu 已提交
196
  command = "bash tools/merge_libs.sh {} {} {}".format(target_soc, output_dir,
Y
yejianwu 已提交
197
                                                       model_output_dirs_str)
198 199
  run_command(command)

Y
yejianwu 已提交
200

Y
yejianwu 已提交
201 202 203 204
def packaging_lib_file(output_dir):
  command = "bash tools/packaging_lib.sh {}".format(output_dir)
  run_command(command)

205 206

def parse_model_configs():
Y
yejianwu 已提交
207 208 209
  with open(FLAGS.config) as f:
    configs = yaml.load(f)
    return configs
210 211 212 213 214 215 216


def parse_args():
  """Parses command line arguments."""
  parser = argparse.ArgumentParser()
  parser.register("type", "bool", lambda v: v.lower() == "true")
  parser.add_argument(
Y
yejianwu 已提交
217
      "--config",
218 219 220 221
      type=str,
      default="./tool/config",
      help="The global config file of models.")
  parser.add_argument(
222
      "--output_dir", type=str, default="build", help="The output dir.")
223 224
  parser.add_argument(
      "--round", type=int, default=1, help="The model running round.")
李寅 已提交
225
  parser.add_argument(
Y
yejianwu 已提交
226 227 228 229 230 231
      "--run_seconds",
      type=int,
      default=10,
      help="The model throughput test running seconds.")
  parser.add_argument(
      "--restart_round", type=int, default=1, help="The model restart round.")
232 233
  parser.add_argument(
      "--tuning", type="bool", default="true", help="Tune opencl params.")
Y
yejianwu 已提交
234 235 236 237 238
  parser.add_argument(
      "--mode",
      type=str,
      default="all",
      help="[build|run|validate|merge|all|throughput_test].")
L
Liangliang He 已提交
239
  parser.add_argument(
240
      "--target_socs",
L
Liangliang He 已提交
241 242 243
      type=str,
      default="all",
      help="SoCs to build, comma seperated list (getprop ro.board.platform)")
244 245
  return parser.parse_known_args()

L
liuqi 已提交
246 247 248 249 250 251 252
def set_environment(configs):
  os.environ["EMBED_MODEL_DATA"] = str(configs["embed_model_data"])
  os.environ["VLOG_LEVEL"] = str(configs["vlog_level"])
  os.environ["PROJECT_NAME"] = os.path.splitext(os.path.basename(
    FLAGS.config))[0]
  os.environ['INPUT_FILE_NAME'] = "model_input"
  os.environ['OUTPUT_FILE_NAME'] = "model_out"
253 254

def main(unused_args):
Y
yejianwu 已提交
255
  configs = parse_model_configs()
256

257 258
  if FLAGS.mode == "validate":
    FLAGS.round = 1
李寅 已提交
259
    FLAGS.restart_round = 1
260

L
liuqi 已提交
261
  set_environment(configs)
262

Y
yejianwu 已提交
263 264 265 266 267 268 269 270
  if FLAGS.mode == "build" or FLAGS.mode == "all":
    # Remove previous output dirs
    if not os.path.exists(FLAGS.output_dir):
      os.makedirs(FLAGS.output_dir)
    elif os.path.exists(os.path.join(FLAGS.output_dir, "libmace")):
      shutil.rmtree(os.path.join(FLAGS.output_dir, os.environ["PROJECT_NAME"]))
      os.makedirs(os.path.join(FLAGS.output_dir, os.environ["PROJECT_NAME"]))

Y
yejianwu 已提交
271
  generate_opencl_and_version_code()
W
wuchenghui 已提交
272
  option_args = ' '.join([arg for arg in unused_args if arg.startswith('--')])
Y
yejianwu 已提交
273

274
  available_socs = sh_commands.adb_get_all_socs()
L
Liangliang He 已提交
275 276 277 278 279
  target_socs = available_socs
  if hasattr(configs, "target_socs"):
    target_socs = set(configs["target_socs"])
    target_socs = target_socs & available_socs

280 281
  if FLAGS.target_socs != "all":
    socs = set(FLAGS.target_socs.split(','))
L
Liangliang He 已提交
282 283 284 285 286 287 288
    target_socs = target_socs & socs
    missing_socs = socs.difference(target_socs)
    if len(missing_socs) > 0:
      print("Error: devices with SoCs are not connected %s" % missing_socs)
      exit(1)

  for target_soc in target_socs:
289
    for target_abi in configs["target_abis"]:
Y
yejianwu 已提交
290
      global_runtime = get_global_runtime(configs)
291
      # Transfer params by environment
Y
yejianwu 已提交
292 293 294 295 296 297 298
      os.environ["TARGET_ABI"] = target_abi
      model_output_dirs = []
      for model_name in configs["models"]:
        # Transfer params by environment
        os.environ["MODEL_TAG"] = model_name
        print '=======================', model_name, '======================='
        model_config = configs["models"][model_name]
299
        input_file_list = model_config.get("validation_inputs_data", [])
Y
yejianwu 已提交
300
        for key in model_config:
Y
yejianwu 已提交
301 302 303 304 305 306
          if key in ['input_nodes', 'output_nodes'] and isinstance(
              model_config[key], list):
            os.environ[key.upper()] = ",".join(model_config[key])
          elif key in ['input_shapes', 'output_shapes'] and isinstance(
              model_config[key], list):
            os.environ[key.upper()] = ":".join(model_config[key])
Y
yejianwu 已提交
307 308 309 310 311 312
          else:
            os.environ[key.upper()] = str(model_config[key])

        md5 = hashlib.md5()
        md5.update(model_config["model_file_path"])
        model_path_digest = md5.hexdigest()
Y
yejianwu 已提交
313 314 315 316 317
        model_output_dir = "%s/%s/%s/%s/%s/%s/%s" % (FLAGS.output_dir,
                                                     os.environ["PROJECT_NAME"],
                                                     "build", model_name,
                                                     model_path_digest,
                                                     target_soc, target_abi)
Y
yejianwu 已提交
318 319 320 321 322 323 324 325 326 327
        model_output_dirs.append(model_output_dir)

        if FLAGS.mode == "build" or FLAGS.mode == "all":
          if os.path.exists(model_output_dir):
            shutil.rmtree(model_output_dir)
          os.makedirs(model_output_dir)
          clear_env(target_soc)

        # Support http:// and https://
        if model_config["model_file_path"].startswith(
Y
yejianwu 已提交
328 329
            "http://") or model_config["model_file_path"].startswith(
                "https://"):
Y
yejianwu 已提交
330
          os.environ["MODEL_FILE_PATH"] = model_output_dir + "/model.pb"
Y
yejianwu 已提交
331 332
          urllib.urlretrieve(model_config["model_file_path"],
                             os.environ["MODEL_FILE_PATH"])
Y
yejianwu 已提交
333

Y
yejianwu 已提交
334 335 336 337 338 339 340
        if model_config["platform"] == "caffe" and (
            model_config["weight_file_path"].startswith("http://") or
            model_config["weight_file_path"].startswith("https://")):
          os.environ[
              "WEIGHT_FILE_PATH"] = model_output_dir + "/model.caffemodel"
          urllib.urlretrieve(model_config["weight_file_path"],
                             os.environ["WEIGHT_FILE_PATH"])
Y
yejianwu 已提交
341 342 343

        if FLAGS.mode == "build" or FLAGS.mode == "run" or FLAGS.mode == "validate"\
            or FLAGS.mode == "benchmark" or FLAGS.mode == "all":
L
liuqi 已提交
344 345
          generate_random_input(target_soc, model_output_dir,
            model_config['input_nodes'], input_file_list)
Y
yejianwu 已提交
346 347 348

        if FLAGS.mode == "build" or FLAGS.mode == "all":
          generate_model_code()
Y
yejianwu 已提交
349 350
          build_mace_run_prod(target_soc, model_output_dir, FLAGS.tuning,
                              global_runtime)
Y
yejianwu 已提交
351 352

        if FLAGS.mode == "run" or FLAGS.mode == "validate" or FLAGS.mode == "all":
Y
yejianwu 已提交
353 354
          run_model(target_soc, model_output_dir, FLAGS.round,
                    FLAGS.restart_round, option_args)
Y
yejianwu 已提交
355 356

        if FLAGS.mode == "benchmark":
357
          benchmark_model(target_soc, model_output_dir, option_args)
Y
yejianwu 已提交
358

359
        if FLAGS.mode == "validate" or FLAGS.mode == "all":
Y
fix run  
yejianwu 已提交
360
          validate_model(target_soc, model_output_dir)
Y
yejianwu 已提交
361 362

      if FLAGS.mode == "build" or FLAGS.mode == "merge" or FLAGS.mode == "all":
Y
yejianwu 已提交
363 364 365
        merge_libs_and_tuning_results(
            target_soc, FLAGS.output_dir + "/" + os.environ["PROJECT_NAME"],
            model_output_dirs)
Y
yejianwu 已提交
366 367

      if FLAGS.mode == "throughput_test":
368 369
        merged_lib_file = FLAGS.output_dir + "/%s/%s/libmace_%s.%s.a" % \
            (os.environ["PROJECT_NAME"], target_abi, os.environ["PROJECT_NAME"], target_soc)
L
liuqi 已提交
370
        generate_random_input(target_soc, FLAGS.output_dir, [], [])
Y
yejianwu 已提交
371 372 373
        for model_name in configs["models"]:
          runtime = configs["models"][model_name]["runtime"]
          os.environ["%s_MODEL_TAG" % runtime.upper()] = model_name
374 375
        build_run_throughput_test(target_soc, FLAGS.run_seconds,
                                  merged_lib_file, FLAGS.output_dir)
376

377 378
  if FLAGS.mode == "build" or FLAGS.mode == "all":
    packaging_lib_file(FLAGS.output_dir)
Y
yejianwu 已提交
379

380

Y
yejianwu 已提交
381
if __name__ == "__main__":
382 383
  FLAGS, unparsed = parse_args()
  main(unused_args=[sys.argv[0]] + unparsed)
李寅 已提交
384