blob: 88754389a7dfa7ed094292ad6ba0c48723d00e49 [file] [log] [blame]
#!/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)