| #!/usr/bin/env python3 |
| |
| # Copyright 2020 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 E2E TensorFlow tests and extracts their benchmarking artifacts. |
| |
| Example usages: |
| # Run all test suites and collect their artifacts: |
| python3 ./build_tools/scripts/get_e2e_artifacts.py |
| |
| # Run the e2e_tests test suite and collect its artifacts: |
| python3 ./build_tools/scripts/get_e2e_artifacts.py --test_suites=e2e_tests |
| """ |
| |
| import fileinput |
| import os |
| import re |
| import subprocess |
| import tempfile |
| from typing import Dict, Set |
| import zipfile |
| |
| import utils |
| |
| from absl import app |
| from absl import flags |
| |
| SUITE_NAME_TO_TARGET = { |
| "e2e_tests": "//integrations/tensorflow/e2e:e2e_tests", |
| "mobile_bert_squad_tests": "//integrations/tensorflow/e2e:mobile_bert_squad_tests", |
| "layers_tests": "//integrations/tensorflow/e2e/keras/layers:layers_tests", |
| "layers_dynamic_batch_tests": "//integrations/tensorflow/e2e/keras/layers:layers_dynamic_batch_tests", |
| "layers_training_tests": "//integrations/tensorflow/e2e/keras/layers:layers_training_tests", |
| "keyword_spotting_tests": "//integrations/tensorflow/e2e/keras:keyword_spotting_tests", |
| "keyword_spotting_internal_streaming_tests": "//integrations/tensorflow/e2e/keras:keyword_spotting_internal_streaming_tests", |
| "imagenet_non_hermetic_tests": "//integrations/tensorflow/e2e/keras/applications:imagenet_non_hermetic_tests", |
| "slim_vision_tests": "//integrations/tensorflow/e2e/slim_vision_models:slim_vision_tests", |
| } |
| SUITES_HELP = [f"`{name}`" for name in SUITE_NAME_TO_TARGET] |
| SUITES_HELP = f'{", ".join(SUITES_HELP[:-1])} and {SUITES_HELP[-1]}' |
| |
| FLAGS = flags.FLAGS |
| |
| flags.DEFINE_bool( |
| "dry_run", |
| False, |
| "Run without extracting files. Useful for quickly checking for artifact " |
| "collisions.", |
| ) |
| flags.DEFINE_string( |
| "artifacts_dir", |
| os.path.join(tempfile.gettempdir(), "iree", "modules"), |
| "Directory to transfer the benchmarking artifacts to. Defaults to " |
| "/tmp/iree/modules/", |
| ) |
| flags.DEFINE_bool("run_test_suites", True, "Run any specified test suites.") |
| flags.DEFINE_list( |
| "test_suites", |
| list(SUITE_NAME_TO_TARGET.keys()), |
| f"Any combination of {SUITES_HELP}.", |
| ) |
| |
| EXPECTED_COLLISIONS = ["/tf_ref/", "tf_input.mlir", "iree_input.mlir", "/saved_model/"] |
| |
| |
| def _target_to_testlogs_path(target: str) -> str: |
| """Convert target into the path where Bazel stores the artifacts we want.""" |
| return os.path.join("bazel-testlogs", target.replace("//", "").replace(":", os.sep)) |
| |
| |
| def _target_to_test_name(target: str, test_suite_path: str) -> str: |
| """Get test_name from `suite_name_test_name__tf__backend_name`.""" |
| return target.split("__")[0].replace(f"{test_suite_path}_", "") |
| |
| |
| def get_test_paths_and_names(test_suite_path: str): |
| """Get the paths Bazel stores test outputs in and the matching test names.""" |
| targets = utils.get_test_targets(test_suite_path) |
| test_paths = [_target_to_testlogs_path(target) for target in targets] |
| test_names = [_target_to_test_name(target, test_suite_path) for target in targets] |
| return test_paths, test_names |
| |
| |
| def check_collision( |
| filename: str, |
| test_name: str, |
| written_paths: Set[str], |
| paths_to_tests: Dict[str, str], |
| ): |
| """Check that we aren't overwriting files unless we expect to.""" |
| # Note: We can't use a check that the files have identical contents because |
| # tf_input.mlir can have random numbers appended to its function names. |
| # See https://github.com/openxla/iree/issues/3375 |
| |
| expected_collision = any([name in filename for name in EXPECTED_COLLISIONS]) |
| if filename in written_paths and not expected_collision: |
| raise ValueError( |
| f"Collision found on {filename} between {test_name}.py " |
| f"and {paths_to_tests[filename]}.py" |
| ) |
| else: |
| written_paths.add(filename) |
| paths_to_tests[filename] = test_name |
| |
| |
| def update_path(archive_path: str): |
| """Update the --module flag with the new location of the compiled.vmfb""" |
| backend_path = archive_path.split("traces")[0] # 'ModuleName/backend_name'. |
| compiled_path = os.path.join(FLAGS.artifacts_dir, backend_path, "compiled.vmfb") |
| flagfile_path = os.path.join(FLAGS.artifacts_dir, archive_path) |
| for line in fileinput.input(files=[flagfile_path], inplace=True): |
| if line.strip().startswith("--module"): |
| print(f"--module={compiled_path}\n", end="") |
| else: |
| print(line, end="") |
| |
| |
| def extract_artifacts( |
| test_path: str, |
| test_name: str, |
| written_paths: Set[str], |
| paths_to_tests: Dict[str, str], |
| ): |
| """Unzips all of the benchmarking artifacts for a given test and backend.""" |
| outputs = os.path.join(test_path, "test.outputs", "outputs.zip") |
| if FLAGS.dry_run and not os.path.exists(outputs): |
| # The artifacts may or may not be present on disk during a dry run. If they |
| # are then we want to collision check them, but if they aren't that's fine. |
| return |
| |
| archive = zipfile.ZipFile(outputs) |
| # Filter out directory names. |
| filenames = [name for name in archive.namelist() if name[-1] != os.sep] |
| |
| for filename in filenames: |
| # Check for collisions. |
| check_collision(filename, test_name, written_paths, paths_to_tests) |
| |
| # Extract and update flagfile path. |
| if not FLAGS.dry_run: |
| archive.extract(filename, FLAGS.artifacts_dir) |
| if filename.endswith("flagfile"): |
| update_path(filename) |
| |
| |
| def main(argv): |
| del argv # Unused. |
| |
| print( |
| "The bazel integrations build and tests are deprecated. This script " |
| "may be reworked in the future. For the time being refer to " |
| "https://github.com/openxla/iree/blob/main/docs/developers/developing_iree/e2e_benchmarking.md " |
| "for information on how to run TensorFlow benchmarks." |
| ) |
| exit(1) |
| |
| # Convert test suite shorthands to full test suite targets. |
| test_suites = [SUITE_NAME_TO_TARGET[suite] for suite in FLAGS.test_suites] |
| |
| if FLAGS.run_test_suites: |
| # Use bazel test to execute all of the test suites in parallel. |
| command = ["bazel", "test", *test_suites, "--color=yes"] |
| print(f'Running: `{" ".join(command)}`') |
| if not FLAGS.dry_run: |
| subprocess.run(command, check=True) |
| print() |
| |
| written_paths = set() |
| paths_to_tests = dict() |
| |
| for test_suite in test_suites: |
| # Extract all of the artifacts for this test suite. |
| test_paths, test_names = get_test_paths_and_names(test_suite) |
| for i, (test_path, test_name) in enumerate(zip(test_paths, test_names)): |
| print(f"\rTransfering {test_suite} {i + 1}/{len(test_paths)}", end="") |
| extract_artifacts(test_path, test_name, written_paths, paths_to_tests) |
| print("\n") |
| |
| |
| if __name__ == "__main__": |
| app.run(main) |