| #!/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) |