From 1953df4e29a26c704a907ac843d082ebacc9928a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kami=C5=84ski?= Date: Thu, 3 Jul 2025 11:13:14 +0000 Subject: [PATCH 1/3] [CI][Benchmarks] Refactor Compute Runtime builds - don't rebuild any of the Compute Runtime components if none of them has changed - rebuild Compute Runtime and its components if needed - refactor components directories' names to be more consistent - overall refactor the compute_runtime module --- .../benchmarks/utils/compute_runtime.py | 664 ++++++++++++++---- 1 file changed, 521 insertions(+), 143 deletions(-) diff --git a/devops/scripts/benchmarks/utils/compute_runtime.py b/devops/scripts/benchmarks/utils/compute_runtime.py index 24d78e86587dd..8e9bca211bda0 100644 --- a/devops/scripts/benchmarks/utils/compute_runtime.py +++ b/devops/scripts/benchmarks/utils/compute_runtime.py @@ -5,13 +5,19 @@ import os import re +import json import yaml +import shutil +from abc import ABC, abstractmethod -from .utils import * +from .utils import git_clone, run +from .logger import log from options import options -def replace_in_file(file_path, search_pattern, replacement): +def _replace_in_file(file_path: str, search_pattern: str, replacement: str) -> None: + """Replace a pattern in a file with a given replacement.""" + with open(file_path, "r") as file: content = file.read() @@ -21,216 +27,588 @@ def replace_in_file(file_path, search_pattern, replacement): file.write(modified_content) -class ComputeRuntime: - def __init__(self): - self.compute_runtime = self.build_compute_runtime() +def _remove_directory(path: str) -> None: + """Clean up a directory if it exists""" + if path and os.path.exists(path): + log.info(f"Cleaning directory: {path}") + try: + shutil.rmtree(path) + except OSError as e: + log.warning(f"Failed to remove directory {path}: {e}") + + +class Component(ABC): + """Base class for components of Compute Runtime.""" + + @property + @abstractmethod + def name(self) -> str: + """Returns the name of the component.""" + raise NotImplementedError("Subclasses must implement this method.") + + @property + @abstractmethod + def build_dir(self) -> str: + """Returns the build directory for the component.""" + raise NotImplementedError("Subclasses must implement this method.") + + @property + @abstractmethod + def install_dir(self) -> str: + """Returns the installation directory for the component.""" + raise NotImplementedError("Subclasses must implement this method.") + + @property + @abstractmethod + def src_dir(self) -> str: + """Returns the source directory for the component.""" + raise NotImplementedError("Subclasses must implement this method.") + + @property + def configure_cmd(self) -> list[str]: + """Returns the configure command for the component.""" + return [ + "cmake", + f"-B {self.build_dir}", + f"-S {self.src_dir}", + f"-DCMAKE_INSTALL_PREFIX={self.install_dir}", + f"-DCMAKE_BUILD_TYPE=Release", + ] - return + def clone_source(self, repo: str, commit: str) -> str: + """ + Clones the source repository for the component. - def ld_libraries(self) -> list[str]: - paths = [ - os.path.join(self.gmmlib, "lib"), - os.path.join(self.level_zero, "lib"), - os.path.join(self.compute_runtime, "bin"), - ] + Args: + repo: Repository URL + commit: Commit or tag to build - if options.build_igc: - paths.append(os.path.join(self.igc, "lib")) + Returns: + Path to the cloned source + """ + return git_clone(options.workdir, self.src_dir, repo, commit) - return paths + def run_build_cmd(self, **kwargs) -> None: + """Returns the build command for the component.""" + run(f"cmake --build {self.build_dir} -j {options.build_jobs}", **kwargs) - def env_vars(self) -> dict: - return { - "ZE_ENABLE_ALT_DRIVERS": os.path.join( - self.compute_runtime, "bin", "libze_intel_gpu.so" - ), - "OCL_ICD_FILENAMES": os.path.join( - self.compute_runtime, "bin", "libigdrcl.so" - ), - } - - def build_gmmlib(self, repo, commit): - log.info("Building GMMLib...") - self.gmmlib_repo = git_clone(options.workdir, "gmmlib-repo", repo, commit) - self.gmmlib_build = os.path.join(options.workdir, "gmmlib-build") - self.gmmlib_install = os.path.join(options.workdir, "gmmlib-install") - configure_command = [ - "cmake", - f"-B {self.gmmlib_build}", - f"-S {self.gmmlib_repo}", - f"-DCMAKE_INSTALL_PREFIX={self.gmmlib_install}", - f"-DCMAKE_BUILD_TYPE=Release", - ] - run(configure_command) - run(f"cmake --build {self.gmmlib_build} -j {options.build_jobs}") - run(f"cmake --install {self.gmmlib_build}") - log.info("GMMLib build complete.") - return self.gmmlib_install + def run_install_cmd(self) -> None: + """Returns the install command for the component.""" + run(f"cmake --install {self.build_dir}") + + def build(self, repo: str, commit: str): + """ + Builds the component. + + Args: + repo: Repository URL + commit: Commit or tag to build + """ + run(self.configure_cmd) + self.run_build_cmd() + self.run_install_cmd() + + def get_library_path(self) -> str: + """Returns the library path for LD_LIBRARY_PATH.""" + return os.path.join(self.install_dir, "lib") + + def clean(self) -> None: + """Cleans the component build and install directories.""" + _remove_directory(self.build_dir) + _remove_directory(self.install_dir) + + +class LevelZero(Component): + """Handles the build and setup of the Intel Level Zero runtime.""" + + @property + def name(self) -> str: + return "level_zero" - def build_level_zero(self, repo, commit): + @property + def build_dir(self) -> str: + return os.path.join(options.workdir, "level-zero-build") + + @property + def install_dir(self) -> str: + return os.path.join(options.workdir, "level-zero-install") + + @property + def src_dir(self) -> str: + return os.path.join(options.workdir, "level-zero-src") + + def build(self, repo: str, commit: str): log.info("Building Level Zero...") - self.level_zero_repo = git_clone( - options.workdir, "level-zero-repo", repo, commit - ) - self.level_zero_build = os.path.join(options.workdir, "level-zero-build") - self.level_zero_install = os.path.join(options.workdir, "level-zero-install") + level_zero_src = self.clone_source(repo, commit) - cmakelists_path = os.path.join(self.level_zero_repo, "CMakeLists.txt") # there's a bug in level-zero CMakeLists.txt that makes it install headers into incorrect location. - replace_in_file( + cmakelists_path = os.path.join(level_zero_src, "CMakeLists.txt") + _replace_in_file( cmakelists_path, r"DESTINATION \./include/", "DESTINATION include/" ) - configure_command = [ - "cmake", - f"-B {self.level_zero_build}", - f"-S {self.level_zero_repo}", - f"-DCMAKE_INSTALL_PREFIX={self.level_zero_install}", - f"-DCMAKE_BUILD_TYPE=Release", - ] - run(configure_command) - run(f"cmake --build {self.level_zero_build} -j {options.build_jobs}") - run(f"cmake --install {self.level_zero_build}") + super().build(repo, commit) log.info("Level Zero build complete.") - return self.level_zero_install - def build_igc(self, repo, commit): - log.info("Building IGC...") - self.igc_repo = git_clone(options.workdir, "igc", repo, commit) - self.vc_intr = git_clone( + +class Gmmlib(Component): + """Handles the build and setup of the Intel GMM library.""" + + @property + def name(self) -> str: + return "gmmlib" + + @property + def build_dir(self) -> str: + return os.path.join(options.workdir, "gmmlib-build") + + @property + def install_dir(self) -> str: + return os.path.join(options.workdir, "gmmlib-install") + + @property + def src_dir(self) -> str: + return os.path.join(options.workdir, "gmmlib-src") + + def build(self, repo: str, commit: str): + log.info("Building GMM library...") + self.clone_source(repo, commit) + super().build(repo, commit) + log.info("GMM library build complete.") + + +class Igc(Component): + """Handles the build and setup of the Intel IGC (Intel Graphics Compiler).""" + + @property + def name(self) -> str: + return "igc" + + @property + def build_dir(self) -> str: + return os.path.join(options.workdir, "igc-build") + + @property + def install_dir(self) -> str: + return os.path.join(options.workdir, "igc-install") + + @property + def src_dir(self) -> str: + return os.path.join(options.workdir, "igc-src") + + @property + def configure_cmd(self) -> list[str]: + return super().configure_cmd + [ + "-DCMAKE_C_FLAGS=-Wno-error", + "-DCMAKE_CXX_FLAGS=-Wno-error", + ] + + def run_build_cmd(self, **kwargs) -> None: + # set timeout to 2h. IGC takes A LONG time to build if building from scratch. + super().run_build_cmd(timeout=60 * 60 * 2) + + def run_install_cmd(self) -> None: + # cmake --install doesn't work... + run("make install", cwd=self.build_dir) + + def build(self, repo: str, commit: str): + """ + Builds the IGC component. + + Args: + repo: Repository URL + commit: Commit or tag to build + """ + log.info(f"Building IGC...") + self.clone_source(repo, commit) + # Clone all igc dependencies + git_clone( options.workdir, "vc-intrinsics", "https://github.com/intel/vc-intrinsics", "9d255266e1df8f1dc5d11e1fbb03213acfaa4fc7", ) - self.llvm_project = git_clone( + llvm_project_repo = git_clone( options.workdir, "llvm-project", "https://github.com/llvm/llvm-project", "llvmorg-15.0.7", ) - llvm_projects = os.path.join(self.llvm_project, "llvm", "projects") - self.ocl = git_clone( - llvm_projects, + llvm_projects_path = os.path.join(llvm_project_repo, "llvm", "projects") + git_clone( + llvm_projects_path, "opencl-clang", "https://github.com/intel/opencl-clang", "ocl-open-150", ) - self.translator = git_clone( - llvm_projects, + git_clone( + llvm_projects_path, "llvm-spirv", "https://github.com/KhronosGroup/SPIRV-LLVM-Translator", "llvm_release_150", ) - self.spirv_tools = git_clone( + git_clone( options.workdir, "SPIRV-Tools", "https://github.com/KhronosGroup/SPIRV-Tools.git", "f289d047f49fb60488301ec62bafab85573668cc", ) - self.spirv_headers = git_clone( + git_clone( options.workdir, "SPIRV-Headers", "https://github.com/KhronosGroup/SPIRV-Headers.git", "0e710677989b4326ac974fd80c5308191ed80965", ) - self.igc_build = os.path.join(options.workdir, "igc-build") - self.igc_install = os.path.join(options.workdir, "igc-install") - configure_command = [ - "cmake", - "-DCMAKE_C_FLAGS=-Wno-error", - "-DCMAKE_CXX_FLAGS=-Wno-error", - f"-B {self.igc_build}", - f"-S {self.igc_repo}", - f"-DCMAKE_INSTALL_PREFIX={self.igc_install}", - f"-DCMAKE_BUILD_TYPE=Release", - ] - run(configure_command) + super().build(repo, commit) - # set timeout to 2h. IGC takes A LONG time to build if building from scratch. - run( - f"cmake --build {self.igc_build} -j {options.build_jobs}", - timeout=60 * 60 * 2, + +class ManifestReader: + """Reads and parses manifest files.""" + + def __init__(self, manifest_path: str): + self._manifest: dict = self._load_manifest(manifest_path) + + def get_component_info(self, component_name: str) -> dict: + """ + Gets repository URL and commit from manifest. + + Args: + manifest: Dictionary containing manifest content + component_name: Name of the component to look for + + Returns: + Dictionary with 'repository' and 'revision' + """ + log.debug(f"Getting component info for {component_name} from manifest") + components_dict = self._manifest.get("components") + component = components_dict.get(component_name) if components_dict else None + if not component: + raise RuntimeError(f"Component {component_name} not found in manifest") + + repo_url = component.get("repository") + commit = component.get("revision") + if not repo_url or not commit: + raise RuntimeError(f"Repository or revision not found for {component_name}") + log.debug( + f"Found repository: {repo_url}, revision: {commit} for component {component_name}" ) - # cmake --install doesn't work... - run("make install", cwd=self.igc_build) - log.info("IGC build complete.") - return self.igc_install + return {"repository": repo_url, "revision": commit} - def read_manifest(self, manifest_path): + def _load_manifest(self, manifest_path: str): + """Loads the manifest file.""" + if not os.path.exists(manifest_path): + raise RuntimeError(f"Manifest file not found: {manifest_path}") with open(manifest_path, "r") as file: manifest = yaml.safe_load(file) + if not manifest: + raise RuntimeError(f"Failed to parse manifest file: {manifest_path}") return manifest - def get_repo_info(self, manifest, component_name): - component = manifest["components"].get(component_name) - if component: - repo = component.get("repository") - revision = component.get("revision") - return repo, revision - return None, None - def build_compute_runtime(self): - self.compute_runtime_repo = git_clone( +class BuildState: + """Tracks the state of component builds to avoid unnecessary rebuilds.""" + + def __init__(self): + self.state_file = os.path.join(options.workdir, "component_versions.json") + self.state = self._load_state() + + def _load_state(self) -> dict: + """Loads the build state from disk or creates an empty state.""" + if os.path.exists(self.state_file): + try: + with open(self.state_file, "r") as f: + return json.load(f) + except (json.JSONDecodeError, IOError) as e: + log.warning( + f"Failed to load build state file: {e}. Creating new state." + ) + return {} + return {} + + def _save_state(self) -> None: + """Saves the current build state to disk.""" + try: + with open(self.state_file, "w") as f: + json.dump(self.state, f, indent=2) + except IOError as e: + log.warning(f"Failed to save build state file: {e}") + + def get_components_to_rebuild( + self, components: dict[str, Component], manifest_reader: ManifestReader + ) -> list[Component]: + """ + Identifies which components have changed by comparing versions with stored state + and checking if install dirs exist. + + Args: + components: dict[str, Component]: Dictionary of components to check + manifest_reader: ManifestReader instance to get component info + + Returns: + list[str]: List of components that have changed + """ + to_rebuild = [] + for component in components.values(): + component_info = manifest_reader.get_component_info(component.name) + version = component_info["revision"] + install_dir = component.install_dir + + log.debug( + f"Checking component {component.name} with version {version} in {install_dir}" + ) + if component.name not in self.state: + log.info( + f"Component {component.name} not found in the Compute Runtime's components state file, will rebuild." + ) + to_rebuild.append(component) + continue + + if self.state[component.name] != version: + log.info( + f"Component {component.name} version changed from {self.state.get(component.name, 'unknown')} to {version}, will rebuild." + ) + to_rebuild.append(component) + continue + + if not os.path.exists(install_dir): + log.info( + f"Installation directory for {component.name} does not exist at {install_dir}, will rebuild." + ) + to_rebuild.append(component) + continue + log.debug(f"Components to rebuild: {[comp.name for comp in to_rebuild]}") + + return to_rebuild + + def set_component_state(self, component: str, version: str) -> None: + """Updates the state for a component after a successful build. + + Args: + component: Name of the component + version: Version or commit hash of the component + """ + self.state[component] = version + self._save_state() + + +class ComputeRuntime: + """Handles the build and setup of the Intel Compute Runtime and its dependencies.""" + + def __init__(self): + self._components = {"level_zero": LevelZero(), "gmmlib": Gmmlib()} + if options.build_igc: + self._components["igc"] = Igc() + self._build_dir = os.path.join(options.workdir, "compute-runtime-build") + self._setup() + + def ld_libraries(self) -> list[str]: + """ + Get the list of library paths needed for LD_LIBRARY_PATH. + + Returns: + list[str]: List of library paths + """ + paths = [] + + for component in self._components.values(): + if os.path.exists(component.install_dir): + paths.append(component.get_library_path()) + else: + raise RuntimeError( + f"Path to {component.name} libraries not found at {component.install_dir}" + ) + + compute_runtime_bin_path = os.path.join(self._build_dir, "bin") + if os.path.exists(compute_runtime_bin_path): + paths.append(compute_runtime_bin_path) + else: + raise RuntimeError( + f"Path to Compute Runtime binaries not found at {compute_runtime_bin_path}" + ) + + return paths + + def env_vars(self) -> dict: + """ + Get environment variables needed for runtime. + + Returns: + dict: Environment variables to set + """ + env_vars = {} + + if os.path.exists(self._build_dir): + libze_path = os.path.join(self._build_dir, "bin", "libze_intel_gpu.so") + libigdrcl_path = os.path.join(self._build_dir, "bin", "libigdrcl.so") + + if os.path.exists(libze_path): + env_vars["ZE_ENABLE_ALT_DRIVERS"] = libze_path + else: + raise RuntimeError(f"Level Zero driver not found at {libze_path}") + + if os.path.exists(libigdrcl_path): + env_vars["OCL_ICD_FILENAMES"] = libigdrcl_path + else: + raise RuntimeError(f"OpenCL driver not found at {libigdrcl_path}") + else: + raise RuntimeError( + f"Compute Runtime build directory not found at {self._build_dir}" + ) + + return env_vars + + def _setup(self): + """ + Sets up the Compute Runtime and its dependencies. + Uses build state to determine if a rebuild is needed. + Only rebuilds components that have changed and their dependents. + """ + build_state = BuildState() + + self._compute_runtime_src = git_clone( options.workdir, "compute-runtime-repo", "https://github.com/intel/compute-runtime.git", options.compute_runtime_tag, ) - self.compute_runtime_build = os.path.join( - options.workdir, "compute-runtime-build" - ) - manifest_path = os.path.join( - self.compute_runtime_repo, "manifests", "manifest.yml" + # Read the manifest to get component versions + manifest_reader = ManifestReader( + os.path.join(self._compute_runtime_src, "manifests", "manifest.yml") ) - manifest = self.read_manifest(manifest_path) - level_zero_repo, level_zero_commit = self.get_repo_info(manifest, "level_zero") - self.level_zero = self.build_level_zero(level_zero_repo, level_zero_commit) + # Determine which components need to be rebuilt + rebuild_components = build_state.get_components_to_rebuild( + self._components, manifest_reader + ) - gmmlib_repo, gmmlib_commit = self.get_repo_info(manifest, "gmmlib") - self.gmmlib = self.build_gmmlib(gmmlib_repo, gmmlib_commit) + # Check if ComputeRuntime itself needs rebuilding + compute_runtime_changed = self._check_compute_runtime_changed(build_state) - if options.build_igc: - igc_repo, igc_commit = self.get_repo_info(manifest, "igc") - self.igc = self.build_igc(igc_repo, igc_commit) + if not rebuild_components and not compute_runtime_changed: + log.info( + "No changes detected in components or ComputeRuntime. Using existing builds." + ) + return - cmakelists_path = os.path.join( - self.compute_runtime_repo, "level_zero", "cmake", "FindLevelZero.cmake" + components_changed_msg = ( + f"Detected changes in components: {', '.join(comp.name for comp in rebuild_components)}" + if rebuild_components + else "" ) - # specifying custom L0 is problematic... - replace_in_file( - cmakelists_path, r"(\$\{LEVEL_ZERO_ROOT\}\s*)", r"\1NO_DEFAULT_PATH\n" + runtime_changed_msg = ( + "Detected changes in ComputeRuntime" if compute_runtime_changed else "" ) + log.info(" ".join(filter(None, [components_changed_msg, runtime_changed_msg]))) - cmakelists_path = os.path.join(self.compute_runtime_repo, "CMakeLists.txt") - # Remove -Werror... - replace_in_file(cmakelists_path, r"\s-Werror(?:=[a-zA-Z]*)?", "") + self._rebuild( + rebuild_components, manifest_reader, build_state, compute_runtime_changed + ) + def _check_compute_runtime_changed(self, build_state: BuildState) -> bool: + """ + Checks if the ComputeRuntime itself needs rebuilding based on version changes + or if the build directory doesn't exist. + + Args: + build_state: BuildState instance with saved component states + + Returns: + bool: True if ComputeRuntime needs rebuilding, False otherwise + """ + # Check if build directory exists + if not os.path.exists(self._build_dir): + log.info( + f"ComputeRuntime build directory does not exist at {self._build_dir}, will rebuild." + ) + return True + + # Check if version has changed + current_version = options.compute_runtime_tag + previous_version = build_state.state.get("compute_runtime") + + if not previous_version: + log.info("ComputeRuntime not found in state file, will rebuild.") + return True + + if previous_version != current_version: + log.info( + f"ComputeRuntime version changed from {previous_version} to {current_version}, will rebuild." + ) + return True + + return False + + def _build(self): + """ + Builds the Compute Runtime. + """ log.info("Building Compute Runtime...") configure_command = [ - "cmake", - f"-B {self.compute_runtime_build}", - f"-S {self.compute_runtime_repo}", - "-DCMAKE_BUILD_TYPE=Release", - "-DNEO_ENABLE_i915_PRELIM_DETECTION=1", - "-DNEO_ENABLE_I915_PRELIM_DETECTION=1", - "-DNEO_SKIP_UNIT_TESTS=1", - f"-DGMM_DIR={self.gmmlib}", - f"-DLEVEL_ZERO_ROOT={self.level_zero}", + f"cmake", + f"-B{self._build_dir}", + f"-S{self._compute_runtime_src}", + f"-DCMAKE_BUILD_TYPE=Release", + f"-DNEO_ENABLE_i915_PRELIM_DETECTION=1", + f"-DNEO_SKIP_UNIT_TESTS=1", + f"-DGMM_DIR={self._components['gmmlib'].install_dir}", + f"-DLEVEL_ZERO_ROOT={self._components['level_zero'].install_dir}", ] - if options.build_igc: - configure_command.append(f"-DIGC_DIR={self.igc}") - + if self._components.get("igc"): + configure_command.append(f"-DIGC_DIR={self._components['igc'].install_dir}") run(configure_command) - run(f"cmake --build {self.compute_runtime_build} -j {options.build_jobs}") + run(f"cmake --build {self._build_dir} -j {options.build_jobs}") log.info("Compute Runtime build complete.") - return self.compute_runtime_build - -def get_compute_runtime() -> ComputeRuntime: # ComputeRuntime singleton - if not hasattr(get_compute_runtime, "instance"): - get_compute_runtime.instance = ComputeRuntime() - return get_compute_runtime.instance + def _rebuild( + self, + rebuild_components: list[Component], + manifest_reader: ManifestReader, + build_state: BuildState, + compute_runtime_changed: bool = False, + ): + """ + Rebuilds the specified components and optionally the ComputeRuntime. + + Args: + rebuild_components: List of components to rebuild + manifest_reader: ManifestReader instance to get component info + build_state: BuildState instance to update component states + compute_runtime_changed: Whether to force rebuild the ComputeRuntime even if no components changed + """ + if rebuild_components: + log.info( + f"Clean rebuild of components: {', '.join(comp.name for comp in rebuild_components)}" + ) + + for component in rebuild_components: + component.clean() + component.build( + manifest_reader.get_component_info(component.name)["repository"], + manifest_reader.get_component_info(component.name)["revision"], + ) + build_state.set_component_state( + component.name, + manifest_reader.get_component_info(component.name)["revision"], + ) + + # Rebuild compute_runtime if any dependency changed or if marked as changed + if rebuild_components or compute_runtime_changed: + _remove_directory(self._build_dir) + self._build() + build_state.set_component_state( + "compute_runtime", options.compute_runtime_tag + ) + + +# ComputeRuntime singleton instance +_compute_runtime_instance = None + + +def get_compute_runtime() -> ComputeRuntime: + """Returns a singleton instance of ComputeRuntime""" + global _compute_runtime_instance + if _compute_runtime_instance is None: + _compute_runtime_instance = ComputeRuntime() + return _compute_runtime_instance From 45ca3455d5d48ae6bbd900e3288a5c4356c275eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kami=C5=84ski?= Date: Fri, 11 Jul 2025 08:22:12 +0000 Subject: [PATCH 2/3] [CI][Benchmarks] Make setup() a required Suite method It is already implemented in all Suite subclasses. Add a type hint with a None return value. Fix the return value of setup() in a UMF suite. --- devops/scripts/benchmarks/benches/base.py | 3 ++- devops/scripts/benchmarks/benches/benchdnn.py | 2 +- devops/scripts/benchmarks/benches/compute.py | 2 +- devops/scripts/benchmarks/benches/gromacs.py | 2 +- devops/scripts/benchmarks/benches/llamacpp.py | 2 +- devops/scripts/benchmarks/benches/syclbench.py | 2 +- devops/scripts/benchmarks/benches/test.py | 2 +- devops/scripts/benchmarks/benches/umf.py | 4 ++-- devops/scripts/benchmarks/benches/velocity.py | 2 +- 9 files changed, 11 insertions(+), 10 deletions(-) diff --git a/devops/scripts/benchmarks/benches/base.py b/devops/scripts/benchmarks/benches/base.py index efbf7d77e003d..258d662af7bde 100644 --- a/devops/scripts/benchmarks/benches/base.py +++ b/devops/scripts/benchmarks/benches/base.py @@ -188,7 +188,8 @@ def benchmarks(self) -> list[Benchmark]: def name(self) -> str: pass - def setup(self): + @abstractmethod + def setup(self) -> None: return def additional_metadata(self) -> dict[str, BenchmarkMetadata]: diff --git a/devops/scripts/benchmarks/benches/benchdnn.py b/devops/scripts/benchmarks/benches/benchdnn.py index 0a2f0d67f6ba2..27202f20a0a90 100644 --- a/devops/scripts/benchmarks/benches/benchdnn.py +++ b/devops/scripts/benchmarks/benches/benchdnn.py @@ -58,7 +58,7 @@ def benchmarks(self) -> list: ) return benchmarks - def setup(self): + def setup(self) -> None: if options.sycl is None: return diff --git a/devops/scripts/benchmarks/benches/compute.py b/devops/scripts/benchmarks/benches/compute.py index 8ccfd929c9de9..0249b009cdbbb 100644 --- a/devops/scripts/benchmarks/benches/compute.py +++ b/devops/scripts/benchmarks/benches/compute.py @@ -52,7 +52,7 @@ def git_url(self) -> str: def git_hash(self) -> str: return "83b9ae3ebb3563552409f3a317cdc1cf3d3ca6bd" - def setup(self): + def setup(self) -> None: if options.sycl is None: return diff --git a/devops/scripts/benchmarks/benches/gromacs.py b/devops/scripts/benchmarks/benches/gromacs.py index 786699498d844..9981b41a22a92 100644 --- a/devops/scripts/benchmarks/benches/gromacs.py +++ b/devops/scripts/benchmarks/benches/gromacs.py @@ -50,7 +50,7 @@ def benchmarks(self) -> list[Benchmark]: # GromacsBenchmark(self, "0192", "rf", "eager"), ] - def setup(self): + def setup(self) -> None: self.gromacs_src = git_clone( self.directory, "gromacs-repo", diff --git a/devops/scripts/benchmarks/benches/llamacpp.py b/devops/scripts/benchmarks/benches/llamacpp.py index be2fe74c516a5..429e8eb787e4c 100644 --- a/devops/scripts/benchmarks/benches/llamacpp.py +++ b/devops/scripts/benchmarks/benches/llamacpp.py @@ -28,7 +28,7 @@ def git_url(self) -> str: def git_hash(self) -> str: return "916c83bfe7f8b08ada609c3b8e583cf5301e594b" - def setup(self): + def setup(self) -> None: if options.sycl is None: return diff --git a/devops/scripts/benchmarks/benches/syclbench.py b/devops/scripts/benchmarks/benches/syclbench.py index ffb164e2ce7cd..4ded06fa0fd74 100644 --- a/devops/scripts/benchmarks/benches/syclbench.py +++ b/devops/scripts/benchmarks/benches/syclbench.py @@ -26,7 +26,7 @@ def git_url(self) -> str: def git_hash(self) -> str: return "31fc70be6266193c4ba60eb1fe3ce26edee4ca5b" - def setup(self): + def setup(self) -> None: if options.sycl is None: return diff --git a/devops/scripts/benchmarks/benches/test.py b/devops/scripts/benchmarks/benches/test.py index bfc1cfcd55323..6c7a7626ed815 100644 --- a/devops/scripts/benchmarks/benches/test.py +++ b/devops/scripts/benchmarks/benches/test.py @@ -16,7 +16,7 @@ class TestSuite(Suite): def __init__(self): return - def setup(self): + def setup(self) -> None: return def name(self) -> str: diff --git a/devops/scripts/benchmarks/benches/umf.py b/devops/scripts/benchmarks/benches/umf.py index 0010c5f9faac6..f575f23b8595a 100644 --- a/devops/scripts/benchmarks/benches/umf.py +++ b/devops/scripts/benchmarks/benches/umf.py @@ -26,9 +26,9 @@ def __init__(self, directory): def name(self) -> str: return "UMF" - def setup(self): + def setup(self) -> None: if not isUMFAvailable(): - return [] + return self.built = True def benchmarks(self) -> list[Benchmark]: diff --git a/devops/scripts/benchmarks/benches/velocity.py b/devops/scripts/benchmarks/benches/velocity.py index d4ceae393144b..0a8071bd946c5 100644 --- a/devops/scripts/benchmarks/benches/velocity.py +++ b/devops/scripts/benchmarks/benches/velocity.py @@ -29,7 +29,7 @@ def git_url(self) -> str: def git_hash(self) -> str: return "b22215c16f789100449c34bf4eaa3fb178983d69" - def setup(self): + def setup(self) -> None: if options.sycl is None: return From df36dc5e32e66e19225829ecd27d5ec12f517163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kami=C5=84ski?= Date: Fri, 11 Jul 2025 10:53:31 +0000 Subject: [PATCH 3/3] GitProject class --- devops/scripts/benchmarks/git_project.py | 52 ++++++++++++++++++++++++ devops/scripts/benchmarks/utils/utils.py | 26 ++++++++---- 2 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 devops/scripts/benchmarks/git_project.py diff --git a/devops/scripts/benchmarks/git_project.py b/devops/scripts/benchmarks/git_project.py new file mode 100644 index 0000000000000..cf50ca9a8fa55 --- /dev/null +++ b/devops/scripts/benchmarks/git_project.py @@ -0,0 +1,52 @@ +# Copyright (C) 2025 Intel Corporation +# Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions. +# See LICENSE.TXT +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +from abc import ABC, abstractmethod +from pathlib import Path + +from utils.utils import git_clone, run + +class GitProject(ABC): + def __init__(self, directory: Path, src_dir: Path, build_dir: Path, install_dir: Path): + self.directory = directory + self.src_dir = src_dir + self.build_dir = build_dir + self.install_dir = install_dir + self.git_url = git_url + self.git_hash = git_hash + + @abstractmethod + def setup(self) -> None: + """Sets up the project environment: checks if the project needs to be cloned + or updated, and prepares the build. + """ + repo_updated = git_clone(self.src_dir, self.git_url, self.git_hash) + repo_installed = self.check_install() + if repo_updated or not repo_installed: + self.rebuild() + + def rebuild(self) -> None: + """Rebuilds the project.""" + self.configure() + self.build() + self.install() + + def configure(self) -> None: + """Configures the project.""" + run(f"cmake -S {self.src_dir} -B build", cwd=self.src_dir) + + def build(self) -> None: + """Builds the project.""" + run(f"cmake --build {self.build_dir}", cwd=self.src_dir) + + def install(self) -> None: + """Installs the project.""" + run(f"cmake --install {self.install_dir}", cwd=self.src_dir) + + def check_install(self) -> bool: + """Checks if the project is already installed + by searching for files in the install directory. + """ + return self.install_dir.exists() and any(item.is_file() for item in self.install_dir.iterdir()) diff --git a/devops/scripts/benchmarks/utils/utils.py b/devops/scripts/benchmarks/utils/utils.py index 6849032e03b6e..8da93918cb413 100644 --- a/devops/scripts/benchmarks/utils/utils.py +++ b/devops/scripts/benchmarks/utils/utils.py @@ -79,23 +79,35 @@ def run( raise -def git_clone(dir, name, repo, commit): - repo_path = os.path.join(dir, name) - log.debug(f"Cloning {repo} into {repo_path} at commit {commit}") +def git_clone(repo_path, repo_url, commit) -> bool: + """Clone a git repository into a specified directory at a specific commit. + Args: + repo_path (str): The directory where the repository should be cloned. + repo (str): The URL of the git repository. + commit (str): The commit hash to checkout. + Returns: + bool: True if the repository was cloned or updated, False if it was already up-to-date. + """ + log.debug(f"Cloning {repo_url} into {repo_path} at commit {commit}") if os.path.isdir(repo_path) and os.path.isdir(os.path.join(repo_path, ".git")): run("git fetch", cwd=repo_path) run("git reset --hard", cwd=repo_path) - run(f"git checkout {commit}", cwd=repo_path) + target_commit = run(f"git rev-parse {commit}", cwd=repo_path).stdout.decode().strip() + current_commit = run("git rev-parse HEAD", cwd=repo_path).stdout.decode().strip() + if current_commit != target_commit: + log.debug(f"Current commit {current_commit} does not match target {target_commit}, checking out {commit}.") + run(f"git checkout {commit}", cwd=repo_path) + return False elif not os.path.exists(repo_path): - run(f"git clone --recursive {repo} {repo_path}") + run(f"git clone --recursive {repo_url} {repo_path}") run(f"git checkout {commit}", cwd=repo_path) else: raise Exception( f"The directory {repo_path} exists but is not a git repository." ) - log.debug(f"Cloned {repo} into {repo_path} at commit {commit}") - return repo_path + log.debug(f"Cloned {repo_url} into {repo_path} at commit {commit}") + return True def prepare_bench_cwd(dir):