Phoenix Meadowlark | be1589d | 2020-10-06 21:48:40 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | # Copyright 2020 Google LLC |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # https://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | """Runs all E2E TensorFlow tests and extracts their benchmarking artifacts. |
| 17 | |
Phoenix Meadowlark | ab21d2a | 2020-10-23 09:30:43 -0700 | [diff] [blame] | 18 | Example usages: |
| 19 | # Run all test suites and collect their artifacts: |
| 20 | python3 ./scripts/get_e2e_artifacts.py |
| 21 | |
| 22 | # Run the e2e_tests test suite and collect its artifacts: |
| 23 | python3 ./scripts/get_e2e_artifacts.py --test_suites=e2e_tests |
Phoenix Meadowlark | be1589d | 2020-10-06 21:48:40 -0700 | [diff] [blame] | 24 | """ |
| 25 | |
| 26 | import fileinput |
| 27 | import os |
| 28 | import re |
| 29 | import subprocess |
| 30 | import tempfile |
| 31 | from typing import Dict, Set |
| 32 | import zipfile |
| 33 | |
| 34 | import utils |
| 35 | |
| 36 | from absl import app |
| 37 | from absl import flags |
| 38 | |
| 39 | SUITE_NAME_TO_TARGET = { |
| 40 | 'e2e_tests': |
| 41 | '//integrations/tensorflow/e2e:e2e_tests', |
| 42 | 'mobile_bert_squad_tests': |
| 43 | '//integrations/tensorflow/e2e:mobile_bert_squad_tests', |
Phoenix Meadowlark | ab4cb7c | 2020-11-06 15:03:30 -0800 | [diff] [blame] | 44 | 'layers_tests': |
| 45 | '//integrations/tensorflow/e2e/keras/layers:layers_tests', |
| 46 | 'layers_dynamic_batch_tests': |
| 47 | '//integrations/tensorflow/e2e/keras/layers:layers_dynamic_batch_tests', |
| 48 | 'layers_training_tests': |
| 49 | '//integrations/tensorflow/e2e/keras/layers:layers_training_tests', |
Phoenix Meadowlark | a4ee4ff | 2020-11-05 10:42:36 -0800 | [diff] [blame] | 50 | 'keyword_spotting_tests': |
| 51 | '//integrations/tensorflow/e2e/keras:keyword_spotting_tests', |
| 52 | 'keyword_spotting_internal_streaming_tests': |
| 53 | '//integrations/tensorflow/e2e/keras:keyword_spotting_internal_streaming_tests', |
Phoenix Meadowlark | 8c8b972 | 2020-10-28 11:26:45 -0700 | [diff] [blame] | 54 | 'imagenet_non_hermetic_tests': |
Phoenix Meadowlark | 1f86f0e | 2020-12-01 14:42:32 -0800 | [diff] [blame] | 55 | '//integrations/tensorflow/e2e/keras/applications:imagenet_non_hermetic_tests', |
Phoenix Meadowlark | ab21d2a | 2020-10-23 09:30:43 -0700 | [diff] [blame] | 56 | 'slim_vision_tests': |
| 57 | '//integrations/tensorflow/e2e/slim_vision_models:slim_vision_tests', |
Phoenix Meadowlark | be1589d | 2020-10-06 21:48:40 -0700 | [diff] [blame] | 58 | } |
| 59 | SUITES_HELP = [f'`{name}`' for name in SUITE_NAME_TO_TARGET] |
| 60 | SUITES_HELP = f'{", ".join(SUITES_HELP[:-1])} and {SUITES_HELP[-1]}' |
| 61 | |
| 62 | FLAGS = flags.FLAGS |
| 63 | |
| 64 | flags.DEFINE_bool( |
| 65 | 'dry_run', False, |
| 66 | 'Run without extracting files. Useful for quickly checking for artifact ' |
| 67 | 'collisions.') |
| 68 | flags.DEFINE_string( |
| 69 | 'artifacts_dir', os.path.join(tempfile.gettempdir(), 'iree', 'modules'), |
| 70 | 'Directory to transfer the benchmarking artifacts to. Defaults to ' |
| 71 | '/tmp/iree/modules/') |
| 72 | flags.DEFINE_bool('run_test_suites', True, 'Run any specified test suites.') |
| 73 | flags.DEFINE_list('test_suites', list(SUITE_NAME_TO_TARGET.keys()), |
| 74 | f'Any combination of {SUITES_HELP}.') |
| 75 | |
| 76 | EXPECTED_COLLISIONS = [ |
| 77 | '/tf_ref/', 'tf_input.mlir', 'iree_input.mlir', '/saved_model/' |
| 78 | ] |
| 79 | |
| 80 | |
| 81 | def _target_to_testlogs_path(target: str) -> str: |
| 82 | """Convert target into the path where Bazel stores the artifacts we want.""" |
| 83 | return os.path.join('bazel-testlogs', |
| 84 | target.replace('//', '').replace(':', os.sep)) |
| 85 | |
| 86 | |
| 87 | def _target_to_test_name(target: str, test_suite_path: str) -> str: |
| 88 | """Get test_name from `suite_name_test_name__tf__backend_name`.""" |
| 89 | return target.split('__')[0].replace(f'{test_suite_path}_', '') |
| 90 | |
| 91 | |
| 92 | def get_test_paths_and_names(test_suite_path: str): |
| 93 | """Get the paths Bazel stores test outputs in and the matching test names.""" |
| 94 | targets = utils.get_test_targets(test_suite_path) |
| 95 | test_paths = [_target_to_testlogs_path(target) for target in targets] |
| 96 | test_names = [ |
| 97 | _target_to_test_name(target, test_suite_path) for target in targets |
| 98 | ] |
| 99 | return test_paths, test_names |
| 100 | |
| 101 | |
| 102 | def check_collision(filename: str, test_name: str, written_paths: Set[str], |
| 103 | paths_to_tests: Dict[str, str]): |
| 104 | """Check that we aren't overwriting files unless we expect to.""" |
| 105 | # Note: We can't use a check that the files have identical contents because |
| 106 | # tf_input.mlir can have random numbers appended to its function names. |
| 107 | # See https://github.com/google/iree/issues/3375 |
| 108 | |
| 109 | expected_collision = any([name in filename for name in EXPECTED_COLLISIONS]) |
| 110 | if filename in written_paths and not expected_collision: |
| 111 | raise ValueError(f'Collision found on {filename} between {test_name}.py ' |
| 112 | f'and {paths_to_tests[filename]}.py') |
| 113 | else: |
| 114 | written_paths.add(filename) |
| 115 | paths_to_tests[filename] = test_name |
| 116 | |
| 117 | |
| 118 | def update_path(archive_path: str): |
Phoenix Meadowlark | 3d8e9ca | 2020-10-12 14:29:32 -0700 | [diff] [blame] | 119 | """Update the --module_file flag with the new location of the compiled.vmfb""" |
Phoenix Meadowlark | be1589d | 2020-10-06 21:48:40 -0700 | [diff] [blame] | 120 | backend_path = archive_path.split('traces')[0] # 'ModuleName/backend_name'. |
| 121 | compiled_path = os.path.join(FLAGS.artifacts_dir, backend_path, |
| 122 | 'compiled.vmfb') |
| 123 | flagfile_path = os.path.join(FLAGS.artifacts_dir, archive_path) |
| 124 | for line in fileinput.input(files=[flagfile_path], inplace=True): |
Phoenix Meadowlark | 3d8e9ca | 2020-10-12 14:29:32 -0700 | [diff] [blame] | 125 | if line.strip().startswith('--module_file'): |
| 126 | print(f'--module_file={compiled_path}\n', end='') |
Phoenix Meadowlark | be1589d | 2020-10-06 21:48:40 -0700 | [diff] [blame] | 127 | else: |
| 128 | print(line, end='') |
| 129 | |
| 130 | |
| 131 | def extract_artifacts(test_path: str, test_name: str, written_paths: Set[str], |
| 132 | paths_to_tests: Dict[str, str]): |
| 133 | """Unzips all of the benchmarking artifacts for a given test and backend.""" |
| 134 | outputs = os.path.join(test_path, 'test.outputs', 'outputs.zip') |
Phoenix Meadowlark | ab21d2a | 2020-10-23 09:30:43 -0700 | [diff] [blame] | 135 | if FLAGS.dry_run and not os.path.exists(outputs): |
| 136 | # The artifacts may or may not be present on disk during a dry run. If they |
| 137 | # are then we want to collision check them, but if they aren't that's fine. |
| 138 | return |
| 139 | |
Phoenix Meadowlark | be1589d | 2020-10-06 21:48:40 -0700 | [diff] [blame] | 140 | archive = zipfile.ZipFile(outputs) |
| 141 | # Filter out directory names. |
| 142 | filenames = [name for name in archive.namelist() if name[-1] != os.sep] |
| 143 | |
| 144 | for filename in filenames: |
| 145 | # Check for collisions. |
| 146 | check_collision(filename, test_name, written_paths, paths_to_tests) |
| 147 | |
| 148 | # Extract and update flagfile path. |
| 149 | if not FLAGS.dry_run: |
| 150 | archive.extract(filename, FLAGS.artifacts_dir) |
| 151 | if filename.endswith('flagfile'): |
| 152 | update_path(filename) |
| 153 | |
| 154 | |
| 155 | def main(argv): |
| 156 | del argv # Unused. |
| 157 | |
| 158 | # Convert test suite shorthands to full test suite targets. |
| 159 | test_suites = [SUITE_NAME_TO_TARGET[suite] for suite in FLAGS.test_suites] |
| 160 | |
Phoenix Meadowlark | ab21d2a | 2020-10-23 09:30:43 -0700 | [diff] [blame] | 161 | if FLAGS.run_test_suites: |
| 162 | # Use bazel test to execute all of the test suites in parallel. |
Phoenix Meadowlark | 589dfa7 | 2020-11-21 20:19:52 -0800 | [diff] [blame] | 163 | command = ['bazel', 'test', *test_suites, '--color=yes'] |
Phoenix Meadowlark | ab21d2a | 2020-10-23 09:30:43 -0700 | [diff] [blame] | 164 | print(f'Running: `{" ".join(command)}`') |
| 165 | if not FLAGS.dry_run: |
| 166 | subprocess.check_call(command) |
| 167 | print() |
| 168 | |
Phoenix Meadowlark | be1589d | 2020-10-06 21:48:40 -0700 | [diff] [blame] | 169 | written_paths = set() |
| 170 | paths_to_tests = dict() |
| 171 | |
| 172 | for test_suite in test_suites: |
Phoenix Meadowlark | be1589d | 2020-10-06 21:48:40 -0700 | [diff] [blame] | 173 | # Extract all of the artifacts for this test suite. |
| 174 | test_paths, test_names = get_test_paths_and_names(test_suite) |
| 175 | for i, (test_path, test_name) in enumerate(zip(test_paths, test_names)): |
| 176 | print(f'\rTransfering {test_suite} {i + 1}/{len(test_paths)}', end='') |
| 177 | extract_artifacts(test_path, test_name, written_paths, paths_to_tests) |
| 178 | print('\n') |
| 179 | |
| 180 | |
| 181 | if __name__ == '__main__': |
| 182 | app.run(main) |