blob: 0adc89a18f642ab0bd6e09c4386274d5fda5a878 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2022 The IREE Authors
#
# Licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""Runs all matched benchmark suites on a Linux device."""
import sys
import pathlib
# Add build_tools python dir to the search path.
sys.path.insert(0, str(pathlib.Path(__file__).parent.with_name("python")))
from typing import Any, List, Optional, Sequence
import atexit
import json
import requests
import shutil
import subprocess
import tarfile
from common import benchmark_suite as benchmark_suite_module
from common.benchmark_driver import BenchmarkDriver
from common.benchmark_suite import BenchmarkCase, BenchmarkSuite
from common.benchmark_config import BenchmarkConfig
from common import benchmark_definition
from common.benchmark_definition import (
execute_cmd,
execute_cmd_and_get_output,
get_git_commit_hash,
get_iree_benchmark_module_arguments,
wait_for_iree_benchmark_module_start,
parse_iree_benchmark_metrics,
)
from common.linux_device_utils import get_linux_device_info
from e2e_test_artifacts import iree_artifacts
import common.common_arguments
class LinuxBenchmarkDriver(BenchmarkDriver):
"""Linux benchmark driver."""
def __init__(self, gpu_id: str, *args, **kwargs):
self.gpu_id = gpu_id
super().__init__(*args, **kwargs)
def run_benchmark_case(
self,
benchmark_case: BenchmarkCase,
benchmark_results_filename: Optional[pathlib.Path],
) -> None:
module_dir = benchmark_case.module_dir
local_module_dir = module_dir.get_local_path()
if local_module_dir:
case_tmp_dir = local_module_dir
module_path = local_module_dir / iree_artifacts.MODULE_FILENAME
else:
module_rel_dir = iree_artifacts.get_module_dir_path(
benchmark_case.run_config.module_generation_config
)
case_tmp_dir = self.config.tmp_dir / module_rel_dir
case_tmp_dir.mkdir(parents=True, exist_ok=True)
module_url = (module_dir / iree_artifacts.MODULE_FILENAME).get_url()
assert module_url is not None
module_path = self.__fetch_file(
uri=module_url, dest=case_tmp_dir / iree_artifacts.MODULE_FILENAME
)
inputs_dir = None
expected_output_dir = None
if benchmark_case.input_uri:
inputs_dir = self.__fetch_and_unpack_npy(
uri=benchmark_case.input_uri, dest_dir=case_tmp_dir / "inputs_npy"
)
if benchmark_case.expected_output_uri:
expected_output_dir = self.__fetch_and_unpack_npy(
uri=benchmark_case.expected_output_uri,
dest_dir=case_tmp_dir / "expected_outputs_npy",
)
external_params = []
if benchmark_case.external_param_urls:
for param_url in benchmark_case.external_param_urls:
scope, url, filename = benchmark_definition.parse_external_param_url(
param_url
)
param_path = self.__fetch_file(uri=url, dest=case_tmp_dir / filename)
param_arg = f"{scope}={param_path}" if scope else param_path
external_params.append(param_arg)
if benchmark_results_filename:
if self.config.benchmark_tool_dir is None:
raise ValueError("benchmark_tool_dir can't be None.")
if self.config.verify and expected_output_dir:
if not inputs_dir:
raise ValueError(f"Input data is missing for {benchmark_case}.")
self.__run_verify(
tool_dir=self.config.benchmark_tool_dir,
benchmark_case=benchmark_case,
module_path=module_path,
inputs_dir=inputs_dir,
expected_outputs_dir=expected_output_dir,
external_params=external_params,
)
self.__run_benchmark(
tool_dir=self.config.benchmark_tool_dir,
benchmark_case=benchmark_case,
module_path=module_path,
results_filename=benchmark_results_filename,
external_params=external_params,
)
def __build_tool_cmds(
self,
benchmark_case: BenchmarkCase,
tool_path: pathlib.Path,
module_path: pathlib.Path,
inputs_dir: Optional[pathlib.Path] = None,
external_params: Sequence[str] = (),
) -> List[Any]:
run_config = benchmark_case.run_config
cmds = [tool_path, f"--module={module_path}"]
cmds += run_config.materialize_run_flags(
gpu_id=self.gpu_id,
inputs_dir=inputs_dir,
external_params=external_params,
)
cpu_params = run_config.target_device_spec.device_parameters.cpu_params
if cpu_params:
raise ValueError("CPU pinning is not supported yet.")
return cmds
def __fetch_and_unpack_npy(self, uri: str, dest_dir: pathlib.Path) -> pathlib.Path:
out_dir = self.__unpack_file(
src=self.__fetch_file(
uri=uri,
dest=dest_dir.with_suffix(".tgz"),
),
dest=dest_dir,
)
return out_dir.absolute()
def __fetch_file(self, uri: str, dest: pathlib.Path) -> pathlib.Path:
"""Check and fetch file if needed."""
if dest.exists():
return dest
req = requests.get(uri, stream=True, timeout=60)
if not req.ok:
raise RuntimeError(f"Failed to fetch {uri}: {req.status_code} - {req.text}")
with dest.open("wb") as dest_file:
for data in req.iter_content(chunk_size=64 * 1024 * 1024):
dest_file.write(data)
return dest
def __unpack_file(self, src: pathlib.Path, dest: pathlib.Path) -> pathlib.Path:
"""Unpack tar with/without compression."""
if dest.exists():
return dest
with tarfile.open(src) as tar_file:
tar_file.extractall(dest)
return dest
def __run_verify(
self,
tool_dir: pathlib.Path,
benchmark_case: BenchmarkCase,
module_path: pathlib.Path,
inputs_dir: pathlib.Path,
expected_outputs_dir: pathlib.Path,
external_params: Sequence[str] = (),
):
cmd = self.__build_tool_cmds(
benchmark_case=benchmark_case,
tool_path=tool_dir / "iree-run-module",
module_path=module_path,
inputs_dir=inputs_dir,
external_params=external_params,
)
# Currently only support single output.
cmd.append(f'--expected_output=@{expected_outputs_dir / "output_0.npy"}')
cmd += benchmark_case.verify_params
execute_cmd_and_get_output(cmd, verbose=self.verbose)
def __run_benchmark(
self,
tool_dir: pathlib.Path,
benchmark_case: BenchmarkCase,
module_path: pathlib.Path,
results_filename: pathlib.Path,
external_params: Sequence[str] = (),
):
tool_name = benchmark_case.benchmark_tool_name
cmd = self.__build_tool_cmds(
benchmark_case=benchmark_case,
tool_path=tool_dir / tool_name,
module_path=module_path,
external_params=external_params,
)
if tool_name == "iree-benchmark-module":
cmd.extend(
get_iree_benchmark_module_arguments(
driver_info=benchmark_case.driver_info,
benchmark_min_time=self.config.benchmark_min_time,
)
)
benchmark_stdout, benchmark_stderr = execute_cmd_and_get_output(
cmd, verbose=self.verbose
)
benchmark_metrics = parse_iree_benchmark_metrics(
benchmark_stdout, benchmark_stderr
)
if self.verbose:
print(benchmark_metrics)
results_filename.write_text(json.dumps(benchmark_metrics.to_json_object()))
def main(args):
device_info = get_linux_device_info(
args.device_model, args.cpu_uarch, args.gpu_id, args.verbose
)
if args.verbose:
print(device_info)
commit = get_git_commit_hash("HEAD")
benchmark_config = BenchmarkConfig.build_from_args(args, commit)
benchmark_groups = json.loads(args.execution_benchmark_config.read_text())
run_configs = benchmark_suite_module.get_run_configs_by_target_and_shard(
benchmark_groups, args.target_device_name, args.shard_index
)
benchmark_suite = BenchmarkSuite.load_from_run_configs(
run_configs=run_configs, root_benchmark_dir=benchmark_config.root_benchmark_dir
)
benchmark_driver = LinuxBenchmarkDriver(
gpu_id=args.gpu_id,
device_info=device_info,
benchmark_config=benchmark_config,
benchmark_suite=benchmark_suite,
benchmark_grace_time=1.0,
verbose=args.verbose,
)
if args.pin_cpu_freq:
raise NotImplementedError("CPU freq pinning is not supported yet.")
if args.pin_gpu_freq:
raise NotImplementedError("GPU freq pinning is not supported yet.")
if not args.no_clean:
atexit.register(shutil.rmtree, args.tmp_dir)
benchmark_driver.run()
benchmark_results = benchmark_driver.get_benchmark_results()
if args.output is not None:
with args.output.open("w") as f:
f.write(benchmark_results.to_json_str())
if args.verbose:
print(benchmark_results.commit)
print(benchmark_results.benchmarks)
benchmark_errors = benchmark_driver.get_benchmark_errors()
if benchmark_errors:
print("Benchmarking completed with errors", file=sys.stderr)
raise RuntimeError(benchmark_errors)
def parse_argument():
arg_parser = common.common_arguments.Parser()
arg_parser.add_argument("--device_model", default="Unknown", help="Device model")
arg_parser.add_argument(
"--cpu_uarch", default=None, help="CPU microarchitecture, e.g., CascadeLake"
)
arg_parser.add_argument(
"--gpu_id",
type=str,
default="0",
help="GPU ID to run the benchmark, e.g., '0' or 'GPU-<UUID>'",
)
return arg_parser.parse_args()
if __name__ == "__main__":
main(parse_argument())