blob: fa05805d60cb51fc01846c96122ab0826cd42f90 [file] [log] [blame]
# 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
"""Utilities for handling the benchmark suite.
See https://iree.dev/developers/performance/benchmark-suites/ for how to build
the benchmark suite.
"""
import pathlib
import re
import urllib.parse
import urllib.request
import dataclasses
from dataclasses import dataclass
from typing import Dict, List, Optional, Sequence, Tuple
from common import benchmark_definition
from common.benchmark_definition import IREE_DRIVERS_INFOS, DriverInfo
from e2e_test_artifacts import iree_artifacts
from e2e_test_framework.definitions import common_definitions, iree_definitions
from e2e_test_framework import serialization
@dataclass
class BenchmarkCase:
"""Represents a benchmark case.
model_name: the source model, e.g., 'MobileSSD'.
model_tags: the source model tags, e.g., ['f32'].
bench_mode: the benchmark mode, e.g., '1-thread,big-core'.
target_arch: the target CPU/GPU architature.
driver_info: the IREE driver configuration.
benchmark_tool_name: the benchmark tool, e.g., 'iree-benchmark-module'.
run_config: the run config from e2e test framework.
module_dir: path/URL of the module directory.
input_uri: URI to find the input npy.
expected_output_uri: URI to find the expected output npy.
"""
model_name: str
model_tags: Sequence[str]
bench_mode: Sequence[str]
target_arch: common_definitions.DeviceArchitecture
driver_info: DriverInfo
benchmark_tool_name: str
run_config: iree_definitions.E2EModelRunConfig
module_dir: benchmark_definition.ResourceLocation
input_uri: Optional[str] = None
expected_output_uri: Optional[str] = None
verify_params: List[str] = dataclasses.field(default_factory=list)
# A map from execution config to driver info. This is temporary during migration
# before we can drop the DriverInfo.
EXECUTION_CONFIG_TO_DRIVER_INFO_KEY_MAP: Dict[
Tuple[iree_definitions.RuntimeDriver, iree_definitions.RuntimeLoader], str
] = {
(
iree_definitions.RuntimeDriver.LOCAL_TASK,
iree_definitions.RuntimeLoader.EMBEDDED_ELF,
): "iree-llvm-cpu",
(
iree_definitions.RuntimeDriver.LOCAL_SYNC,
iree_definitions.RuntimeLoader.EMBEDDED_ELF,
): "iree-llvm-cpu-sync",
(
iree_definitions.RuntimeDriver.LOCAL_TASK,
iree_definitions.RuntimeLoader.VMVX_MODULE,
): "iree-vmvx",
(
iree_definitions.RuntimeDriver.LOCAL_SYNC,
iree_definitions.RuntimeLoader.VMVX_MODULE,
): "iree-vmvx-sync",
(
iree_definitions.RuntimeDriver.VULKAN,
iree_definitions.RuntimeLoader.NONE,
): "iree-vulkan",
(
iree_definitions.RuntimeDriver.CUDA,
iree_definitions.RuntimeLoader.NONE,
): "iree-cuda",
}
class BenchmarkSuite(object):
"""Represents the benchmarks in benchmark suite directory."""
def __init__(self, benchmark_cases: Sequence[BenchmarkCase]):
"""Construct a benchmark suite.
Args:
benchmark_cases: list of benchmark cases.
"""
self.benchmark_cases = list(benchmark_cases)
def filter_benchmarks(
self,
available_drivers: Optional[Sequence[str]] = None,
available_loaders: Optional[Sequence[str]] = None,
target_architectures: Optional[
Sequence[common_definitions.DeviceArchitecture]
] = None,
driver_filter: Optional[str] = None,
mode_filter: Optional[str] = None,
model_name_filter: Optional[str] = None,
) -> Sequence[BenchmarkCase]:
"""Filters benchmarks.
Args:
available_drivers: list of drivers supported by the tools. None means to
match any driver.
available_loaders: list of executable loaders supported by the tools.
None means to match any loader.
target_architectures: list of target architectures to be included. None
means no filter.
driver_filter: driver filter regex.
mode_filter: benchmark mode regex.
model_name_filter: model name regex.
Returns:
A list of matched benchmark cases.
"""
chosen_cases = []
for benchmark_case in self.benchmark_cases:
driver_info = benchmark_case.driver_info
driver_name = driver_info.driver_name
matched_available_driver = (
available_drivers is None or driver_name in available_drivers
)
matched_driver_filter = (
driver_filter is None
or re.match(driver_filter, driver_name) is not None
)
matched_driver = matched_available_driver and matched_driver_filter
matched_loader = (
not driver_info.loader_name
or available_loaders is None
or (driver_info.loader_name in available_loaders)
)
if target_architectures is None:
matched_arch = True
else:
matched_arch = benchmark_case.target_arch in target_architectures
bench_mode = ",".join(benchmark_case.bench_mode)
matched_mode = (
mode_filter is None or re.match(mode_filter, bench_mode) is not None
)
model_name_with_tags = benchmark_case.model_name
if len(benchmark_case.model_tags) > 0:
model_name_with_tags += f"-{','.join(benchmark_case.model_tags)}"
matched_model_name = (
model_name_filter is None
or re.match(model_name_filter, model_name_with_tags) is not None
)
if (
matched_driver
and matched_loader
and matched_arch
and matched_model_name
and matched_mode
):
chosen_cases.append(benchmark_case)
return chosen_cases
@staticmethod
def load_from_run_configs(
run_configs: Sequence[iree_definitions.E2EModelRunConfig],
root_benchmark_dir: benchmark_definition.ResourceLocation,
):
"""Loads the benchmarks from the run configs.
Args:
run_configs: list of benchmark run configs.
root_benchmark_dir: path/URL of the root benchmark directory.
Returns:
A benchmark suite.
"""
benchmark_cases = []
for run_config in run_configs:
module_gen_config = run_config.module_generation_config
module_exec_config = run_config.module_execution_config
target_device_spec = run_config.target_device_spec
driver_info_key = EXECUTION_CONFIG_TO_DRIVER_INFO_KEY_MAP.get(
(module_exec_config.driver, module_exec_config.loader)
)
if driver_info_key is None:
raise ValueError(
f"Can't map execution config to driver info: {module_exec_config}."
)
driver_info = IREE_DRIVERS_INFOS[driver_info_key]
target_arch = target_device_spec.architecture
model = module_gen_config.imported_model.model
module_rel_dir = iree_artifacts.get_module_dir_path(module_gen_config)
module_dir = root_benchmark_dir / module_rel_dir
benchmark_case = BenchmarkCase(
model_name=model.name,
model_tags=model.tags,
bench_mode=module_exec_config.tags,
target_arch=target_arch,
driver_info=driver_info,
benchmark_tool_name=run_config.tool.value,
module_dir=module_dir,
input_uri=model.input_url,
expected_output_uri=model.expected_output_url,
verify_params=model.verify_params,
run_config=run_config,
)
benchmark_cases.append(benchmark_case)
return BenchmarkSuite(benchmark_cases=benchmark_cases)
def get_run_configs_by_target_and_shard(
benchmark_groups: Dict, target_device_name: str, shard_index: Optional[int] = None
):
"""Returns a flat list of run_configs from `benchmark_groups`, filtered by the given `target_device_name`.
If a `shard_index` is given, only the run configs for the given shard are returned, otherwise all the run configs are returned.
"""
benchmark_group = benchmark_groups.get(target_device_name)
if benchmark_group is None:
raise ValueError(
"Target device '{}' not found in the benchmark config.".format(
target_device_name
)
)
if shard_index is None:
# In case no shard index was given we will run ALL benchmarks from ALL shards
packed_run_configs = [
shard["run_configs"] for shard in benchmark_group["shards"]
]
else:
# Otherwise we will only run the benchmarks from the given shard
benchmark_shard = next(
(
shard
for shard in benchmark_group["shards"]
if shard["index"] == shard_index
),
None,
)
if benchmark_shard is None:
raise ValueError(
"Given shard (index={}) not found in the benchmark config group. Available indexes: [{}].".format(
shard_index,
", ".join(
str(shard["index"]) for shard in benchmark_group["shards"]
),
)
)
packed_run_configs = [benchmark_shard["run_configs"]]
# When no `shard_index` is given we might have more than one shard to process.
# We do this by deserializing the `run_config` field from each shard separately
# and then merge the unpacked flat lists of `E2EModelRunConfig`.
return [
run_config
for packed_run_config in packed_run_configs
for run_config in serialization.unpack_and_deserialize(
data=packed_run_config,
root_type=List[iree_definitions.E2EModelRunConfig],
)
]