First commit with working display
This commit is contained in:
288
managed_components/lvgl__lvgl/tests/benchmark_emu.py
Normal file
288
managed_components/lvgl__lvgl/tests/benchmark_emu.py
Normal file
@@ -0,0 +1,288 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
benchmark_configs = {
|
||||
"perf32b": {
|
||||
"description": "Benchmark config ARM (so3) Emulated - 32 bit",
|
||||
"image_name": "ghcr.io/smartobjectoriented/so3-lvperf32b:main",
|
||||
"defaults_file": "lv_conf_perf32b.defaults",
|
||||
},
|
||||
"perf64b": {
|
||||
"description": "Benchmark config ARM (so3) Emulated - 64 bit",
|
||||
"image_name": "ghcr.io/smartobjectoriented/so3-lvperf64b:main",
|
||||
"defaults_file": "lv_conf_perf64b.defaults",
|
||||
},
|
||||
}
|
||||
|
||||
lvgl_test_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
lvgl_root_dir = os.path.dirname(lvgl_test_dir)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
epilog = """This program runs the LVGL demo benchmark
|
||||
In order to provide timing consistency between host computers,
|
||||
these runs are run in an ARM emulated environment inside QEMU.
|
||||
For the runtime environment, SO3 is used which is a lightweight, ARM-based
|
||||
operating system.
|
||||
Right now, this script requires a host linux computer as we depend on
|
||||
`losetup` which is used to set up and control loop devices.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Run LVGL benchmark tests.", epilog=epilog
|
||||
)
|
||||
parser.add_argument(
|
||||
"--config",
|
||||
nargs=1,
|
||||
choices=benchmark_configs.keys(),
|
||||
help="The benchmark config to use. Available configs: "
|
||||
+ ", ".join(benchmark_configs.keys()),
|
||||
required=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pull",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Pull latest images from registry before running tests",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--clean",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Clean existing build artifacts before operation",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--auto-clean",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Automatically clean build directories",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"actions",
|
||||
nargs="*",
|
||||
choices=["generate", "run"],
|
||||
help="generate: generate the lv_conf.h from the `configs/ci/perf` folder, run: run the benchmark with the provided config",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.actions:
|
||||
print("Error: At least one action must be specified")
|
||||
parser.print_help()
|
||||
return 1
|
||||
|
||||
configs = []
|
||||
if args.config:
|
||||
configs = args.config
|
||||
else:
|
||||
configs = benchmark_configs.keys()
|
||||
|
||||
for config in configs:
|
||||
if args.clean:
|
||||
clean(config)
|
||||
|
||||
if "generate" in args.actions:
|
||||
generate_benchmark_files(config)
|
||||
|
||||
if "run" in args.actions:
|
||||
run_benchmark(config, args.pull)
|
||||
|
||||
if args.auto_clean:
|
||||
clean(config)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def get_build_dir(config_name: str) -> str:
|
||||
"""Given the config name, return the build directory path."""
|
||||
build_dir_name = f"build_benchmark_{config_name}"
|
||||
return os.path.join(lvgl_test_dir, build_dir_name)
|
||||
|
||||
|
||||
def get_container_name(config_name: str) -> str:
|
||||
"""
|
||||
Returns the docker container name based on the config name
|
||||
"""
|
||||
return f"lv_benchmark_{config_name}"
|
||||
|
||||
|
||||
def get_docker_volumes(config_name: str) -> list[str]:
|
||||
"""
|
||||
Returns all docker volume names that should be created
|
||||
"""
|
||||
return [get_build_cache_volume(config_name), get_disk_cache_volume(config_name)]
|
||||
|
||||
|
||||
def get_build_cache_volume(config_name: str) -> str:
|
||||
"""
|
||||
Returns the docker volume name for storing cmake generated files
|
||||
"""
|
||||
return f"{get_container_name(config_name)}_build_cache"
|
||||
|
||||
|
||||
def get_disk_cache_volume(config_name: str) -> str:
|
||||
"""
|
||||
Returns the docker volume name for storing the virtual disks
|
||||
"""
|
||||
return f"{get_container_name(config_name)}_disk_cache"
|
||||
|
||||
|
||||
def create_dir_if_not_exists(build_dir: str) -> bool:
|
||||
"""Create directory if it doesn't exist"""
|
||||
created_build_dir = False
|
||||
|
||||
if os.path.exists(build_dir):
|
||||
if not os.path.isdir(build_dir):
|
||||
raise ValueError(f"{build_dir} exists but is not a directory")
|
||||
else:
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
created_build_dir = True
|
||||
|
||||
return created_build_dir
|
||||
|
||||
|
||||
def generate_config(config_name: str) -> None:
|
||||
"""Generate lv_conf.h from the defaults file for the given config"""
|
||||
print(f"Generating config for {config_name}")
|
||||
|
||||
build_dir = get_build_dir(config_name)
|
||||
create_dir_if_not_exists(build_dir)
|
||||
|
||||
defaults_file = benchmark_configs[config_name]["defaults_file"]
|
||||
defaults_path = os.path.join(lvgl_root_dir, "configs", "ci", "perf", defaults_file)
|
||||
template_path = os.path.join(lvgl_root_dir, "lv_conf_template.h")
|
||||
|
||||
assert os.path.exists(defaults_path)
|
||||
assert template_path
|
||||
|
||||
output_path = os.path.join(build_dir, "lv_conf.h")
|
||||
generate_script = os.path.join(lvgl_root_dir, "scripts", "generate_lv_conf.py")
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
generate_script,
|
||||
"--template",
|
||||
template_path,
|
||||
"--config",
|
||||
output_path,
|
||||
"--defaults",
|
||||
defaults_path,
|
||||
build_dir,
|
||||
]
|
||||
|
||||
subprocess.check_call(cmd)
|
||||
print(f"Generated lv_conf.h at {output_path}")
|
||||
|
||||
|
||||
def generate_benchmark_files(config_name: str) -> None:
|
||||
"""Generate the necessary files for running benchmark inside so3"""
|
||||
print()
|
||||
print()
|
||||
label = f"Generating benchmark files for: {config_name}: {benchmark_configs[config_name]['description']}"
|
||||
print("=" * len(label))
|
||||
print(label)
|
||||
print("=" * len(label))
|
||||
|
||||
build_dir = get_build_dir(config_name)
|
||||
create_dir_if_not_exists(build_dir)
|
||||
|
||||
generate_config(config_name)
|
||||
|
||||
|
||||
def clean(config_name: str) -> None:
|
||||
"""Clean build directory and docker resources"""
|
||||
print(f"Cleaning resources for {config_name}")
|
||||
|
||||
build_dir = get_build_dir(config_name)
|
||||
container_name = get_container_name(config_name)
|
||||
|
||||
if os.path.exists(build_dir):
|
||||
shutil.rmtree(build_dir)
|
||||
print(f"Removed build directory: {build_dir}")
|
||||
|
||||
subprocess.check_call(
|
||||
["docker", "rm", "-f", container_name],
|
||||
)
|
||||
|
||||
for volume in get_docker_volumes(config_name):
|
||||
subprocess.check_call(
|
||||
["docker", "volume", "remove", "-f", volume],
|
||||
)
|
||||
|
||||
|
||||
def run_benchmark(config_name: str, pull: bool) -> None:
|
||||
"""Run the benchmark using docker"""
|
||||
|
||||
def volume(src, dst):
|
||||
return ["-v", f"{src}:{dst}"]
|
||||
|
||||
def so3_usr_lib(path):
|
||||
return f"/so3/usr/lib/{path}"
|
||||
|
||||
so3_usr_build = "/so3/usr/build"
|
||||
persistence_dir = "/persistence"
|
||||
container_name = get_container_name(config_name)
|
||||
build_dir = get_build_dir(config_name)
|
||||
|
||||
for v in get_docker_volumes(config_name):
|
||||
subprocess.check_call(["docker", "volume", "create", v])
|
||||
|
||||
lvgl_src_path = os.path.join(lvgl_root_dir, "src")
|
||||
lvgl_h_path = os.path.join(lvgl_root_dir, "lvgl.h")
|
||||
lvgl_demos_path = os.path.join(lvgl_root_dir, "demos")
|
||||
lv_conf_path = os.path.join(build_dir, "lv_conf.h")
|
||||
if not os.path.exists(lv_conf_path):
|
||||
generate_config(config_name)
|
||||
assert os.path.exists(lv_conf_path)
|
||||
|
||||
docker_image_name = benchmark_configs[config_name]["image_name"]
|
||||
|
||||
volumes = [
|
||||
# This is necessary to create a loop device
|
||||
volume("/dev", "/dev"),
|
||||
# Replace container's lvgl source and lv_conf
|
||||
volume(lvgl_h_path, so3_usr_lib("lvgl/lvgl.h")),
|
||||
volume(lvgl_src_path, so3_usr_lib("lvgl/src")),
|
||||
volume(lvgl_demos_path, so3_usr_lib("lvgl/demos")),
|
||||
volume(lv_conf_path, so3_usr_lib("lv_conf.h")),
|
||||
# Cache build and disk folders so we don't regenerate everything in consecutive runs
|
||||
volume(get_build_cache_volume(config_name), so3_usr_build),
|
||||
volume(get_disk_cache_volume(config_name), persistence_dir),
|
||||
]
|
||||
|
||||
interactive = "-it" if sys.stdout.isatty() else "-t"
|
||||
command = [
|
||||
"docker",
|
||||
"run",
|
||||
"--rm",
|
||||
"--privileged",
|
||||
interactive,
|
||||
"--name",
|
||||
container_name,
|
||||
]
|
||||
|
||||
if pull:
|
||||
command.append("--pull=always")
|
||||
|
||||
for v in volumes:
|
||||
command.extend(v)
|
||||
|
||||
command.append(docker_image_name)
|
||||
|
||||
print()
|
||||
print()
|
||||
label = f"Running benchmark: {config_name}: {benchmark_configs[config_name]['description']}"
|
||||
print("=" * len(label))
|
||||
print(label)
|
||||
print("=" * len(label))
|
||||
|
||||
subprocess.check_call(command)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user