blob: 2b7d005100581e4269b75b1776d98bcc2bf04452 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import os
import platform
import pytest
import shutil
import logging
import sys
from unittest import mock
import tvm
from tvm.driver.tvmc.main import _main
from tvm.driver.tvmc.model import TVMCException
from tvm.driver.tvmc import compiler
from unittest.mock import MagicMock
@pytest.mark.skipif(
platform.machine() == "aarch64",
reason="Currently failing on AArch64 - see https://github.com/apache/tvm/issues/10673",
)
def test_tvmc_cl_workflow(keras_simple, tmpdir_factory):
pytest.importorskip("tensorflow")
tmpdir = tmpdir_factory.mktemp("data")
# Test model tuning
log_path = os.path.join(tmpdir, "keras-autotuner_records.json")
tuning_str = (
f"tvmc tune --target llvm --output {log_path} "
f"--trials 2 --enable-autoscheduler {keras_simple}"
)
tuning_args = tuning_str.split(" ")[1:]
_main(tuning_args)
assert os.path.exists(log_path)
# Test model compilation
package_path = os.path.join(tmpdir, "keras-tvm.tar")
compile_str = (
f"tvmc compile --target llvm --tuning-records {log_path} "
f"--output {package_path} {keras_simple}"
)
compile_args = compile_str.split(" ")[1:]
_main(compile_args)
assert os.path.exists(package_path)
# Test running the model
output_path = os.path.join(tmpdir, "predictions.npz")
run_str = f"tvmc run --end-to-end --outputs {output_path} {package_path}"
run_args = run_str.split(" ")[1:]
_main(run_args)
assert os.path.exists(output_path)
@pytest.mark.skipif(
platform.machine() == "aarch64",
reason="Currently failing on AArch64 - see https://github.com/apache/tvm/issues/10673",
)
def test_tvmc_cl_workflow_json_config(keras_simple, tmpdir_factory):
pytest.importorskip("tensorflow")
tune_config_file = "tune_config_test"
tmpdir = tmpdir_factory.mktemp("data")
# Test model tuning
log_path = os.path.join(tmpdir, "keras-autotuner_records.json")
tuning_str = (
f"tvmc tune --config {tune_config_file} --output {log_path} "
f"--enable-autoscheduler {keras_simple}"
)
tuning_args = tuning_str.split(" ")[1:]
_main(tuning_args)
assert os.path.exists(log_path)
# Test model compilation
package_path = os.path.join(tmpdir, "keras-tvm.tar")
compile_str = (
f"tvmc compile --tuning-records {log_path} " f"--output {package_path} {keras_simple}"
)
compile_args = compile_str.split(" ")[1:]
_main(compile_args)
assert os.path.exists(package_path)
# Test running the model
output_path = os.path.join(tmpdir, "predictions.npz")
run_str = f"tvmc run --outputs {output_path} {package_path}"
run_args = run_str.split(" ")[1:]
_main(run_args)
assert os.path.exists(output_path)
@pytest.fixture
def missing_file():
missing_file_name = "missing_file_as_invalid_input.tfite"
return missing_file_name
@pytest.fixture
def broken_symlink(tmp_path):
broken_symlink = "broken_symlink_as_invalid_input.tflite"
os.symlink("non_existing_file", tmp_path / broken_symlink)
yield broken_symlink
os.unlink(tmp_path / broken_symlink)
@pytest.fixture
def fake_directory(tmp_path):
dir_as_invalid = "dir_as_invalid_input.tflite"
os.mkdir(tmp_path / dir_as_invalid)
yield dir_as_invalid
shutil.rmtree(tmp_path / dir_as_invalid)
@pytest.mark.parametrize(
"invalid_input",
["missing_file", "broken_symlink", "fake_directory"],
)
def test_tvmc_compile_file_check(capsys, invalid_input, request):
invalid_input = request.getfixturevalue(invalid_input)
compile_cmd = f"tvmc compile --target 'c' {invalid_input}"
run_arg = compile_cmd.split(" ")[1:]
_main(run_arg)
captured = capsys.readouterr()
expected_err = (
f"Error: Input file '{invalid_input}' doesn't exist, "
"is a broken symbolic link, or a directory.\n"
)
on_assert_error = f"'tvmc compile' failed to check invalid FILE: {invalid_input}"
assert captured.err == expected_err, on_assert_error
@pytest.mark.parametrize(
"invalid_input",
["missing_file", "broken_symlink", "fake_directory"],
)
def test_tvmc_tune_file_check(capsys, invalid_input, request):
invalid_input = request.getfixturevalue(invalid_input)
tune_cmd = f"tvmc tune --target 'llvm' --output output.json {invalid_input}"
run_arg = tune_cmd.split(" ")[1:]
_main(run_arg)
captured = capsys.readouterr()
expected_err = (
f"Error: Input file '{invalid_input}' doesn't exist, "
"is a broken symbolic link, or a directory.\n"
)
on_assert_error = f"'tvmc tune' failed to check invalid FILE: {invalid_input}"
assert captured.err == expected_err, on_assert_error
@mock.patch("tvm.relay.build", side_effect=tvm.relay.build)
@mock.patch("tvm.driver.tvmc.model.TVMCPackage.__init__", return_value=None)
def test_tvmc_workspace_pools_check(mock_pkg, mock_relay, keras_simple, tmpdir_factory):
pytest.importorskip("tensorflow")
tmpdir = tmpdir_factory.mktemp("data")
# Test model compilation
package_path = os.path.join(tmpdir, "keras-tvm.tar")
compile_str = (
f"tvmc compile --target=llvm --workspace-pools=sram "
f"--workspace-pools-targets=sram:llvm "
f"--output={package_path} {keras_simple}"
)
compile_args = compile_str.split(" ")[1:]
_main(compile_args)
assert os.path.exists(package_path)
assert mock_relay.call_count == 1
assert mock_relay.call_args_list[0][1]["workspace_memory_pools"].pools[0].pool_name == "sram"
@pytest.fixture
def paddle_model(paddle_resnet50):
# If we can't import "paddle" module, skip testing paddle as the input model.
if pytest.importorskip("paddle", reason="'paddle' module not installed"):
return paddle_resnet50
@pytest.mark.parametrize(
"model",
[
"paddle_model",
],
)
# compile_model() can take too long and is tested elsewhere, hence it's mocked below
@mock.patch.object(compiler, "compile_model")
# @mock.patch.object(compiler, "compile_model")
def test_tvmc_compile_input_model(mock_compile_model, tmpdir_factory, model, request):
model = request.getfixturevalue(model)
output_dir = tmpdir_factory.mktemp("output")
output_file = output_dir / "model.tar"
compile_cmd = (
f"tvmc compile --target 'llvm' {model} --model-format paddle --output {output_file}"
)
run_arg = compile_cmd.split(" ")[1:]
_main(run_arg)
mock_compile_model.assert_called_once()
def test_tvmc_logger(caplog, tmpdir_factory, keras_simple):
pytest.importorskip("tensorflow")
tmpdir = tmpdir_factory.mktemp("out")
# TUNE
log_path = os.path.join(tmpdir, "records.json")
tune_cmd = f"tvmc tune --target llvm -vvvv --output {log_path} " f"--trials 2 {keras_simple}"
tuning_args = tune_cmd.split(" ")[1:]
_main(tuning_args)
# Check that we log during tvmc tune
for log_str in ("DEBUG", "INFO", "WARNING", "TVMC"):
assert log_str in caplog.text
caplog.clear()
# COMPILE
module_file = os.path.join(tmpdir, "m.tar")
compile_cmd = f"tvmc compile --target 'llvm' {keras_simple} -vvvv --output {module_file}"
compile_args = compile_cmd.split(" ")[1:]
_main(compile_args)
# Check that we log during tvmc compile
for log_str in ("DEBUG", "WARNING", "TVMC"):
assert log_str in caplog.text
caplog.clear()
# RUN
run_cmd = f"tvmc run -vvvv {module_file}"
run_args = run_cmd.split(" ")[1:]
_main(run_args)
# Check that we log during tvmc run
for log_str in ("DEBUG", "TVMC"):
assert log_str in caplog.text
# Unfortunately pytest seems to intercept the logging output, so we can't test whether it
# actually writes the logging output to sys.stdout, but we can test that we call
# logging.basicConfig with the correct arguments
def test_tvmc_logger_set_basicConfig(monkeypatch, tmpdir_factory, keras_simple):
pytest.importorskip("tensorflow")
mock_basicConfig = MagicMock()
monkeypatch.setattr(logging, "basicConfig", mock_basicConfig)
# Run a random tvmc command
tmpdir = tmpdir_factory.mktemp("out")
module_file = os.path.join(tmpdir, "m.tar")
compile_cmd = f"tvmc compile --target 'llvm' {keras_simple} -vvvv --output {module_file}"
compile_args = compile_cmd.split(" ")[1:]
_main(compile_args)
mock_basicConfig.assert_called_with(stream=sys.stdout)
def test_tvmc_print_pass_times(capsys, keras_simple, tmpdir_factory):
pytest.importorskip("tensorflow")
tmpdir = tmpdir_factory.mktemp("out")
print_cmd = "--print-pass-times"
# Compile model
module_file = os.path.join(tmpdir, "keras-tvm.tar")
compile_cmd = f"tvmc compile --target 'llvm' {keras_simple} --output {module_file} {print_cmd}"
compile_args = compile_cmd.split(" ")[1:]
_main(compile_args)
# Check for timing results output
captured_out = capsys.readouterr().out
for exp_str in ("Compilation time breakdown by pass:", "sequential:", "us]"):
assert exp_str in captured_out
@pytest.mark.parametrize(
"print_cmd, out_str",
[
(
"--print-ir-after=[tir.SplitHostDevice]",
(
"Print IR after: tir.SplitHostDevice\n# from tvm.script import ir as I\n",
"@I.ir_module",
),
),
(
"--print-ir-before=[tir.SplitHostDevice]",
("Print IR before: tir.SplitHostDevice\n# from tvm.script import ir as I\n"),
),
(
"--print-ir-after=[tir.ThreadSync,tir.SplitHostDevice]",
("tir.ThreadSync,tir.SplitHostDevice"),
),
(
"--print-ir-before=[tir.SplitHostDevice] --print-ir-after=[tir.SplitHostDevice]",
("Print IR before: tir.SplitHostDevice\n", "Print IR after: tir.SplitHostDevice\n"),
),
],
)
def test_tvmc_print_ir_before_after(capsys, keras_simple, tmpdir_factory, print_cmd, out_str):
pytest.importorskip("tensorflow")
tmpdir = tmpdir_factory.mktemp("out")
# Compile model
module_file = os.path.join(tmpdir, "keras-tvm.tar")
compile_cmd = f"tvmc compile --target 'llvm' {keras_simple} --output {module_file} {print_cmd}"
compile_args = compile_cmd.split(" ")[1:]
_main(compile_args)
# Check for printing IR before or IR after
captured_out = capsys.readouterr().out
for exp_str in out_str:
assert exp_str in captured_out