diff --git a/pyproject.toml b/pyproject.toml index 99bc8197..b7339b98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,19 @@ readme = "README.md" requires-python = ">=3.9" # Python dependencies -dependencies = ["tomli", "tabulate", "ml_dtypes", "dspy==2.6.27", "pandas", "duckdb", "rich", "pytest", "litellm[proxy]", "rpds-py"] +dependencies = [ + "tomli", + "tabulate", + "ml_dtypes", + "dspy==2.6.27", + "pandas", + "duckdb", + "rich", + "pytest", + "litellm[proxy]", + "rpds-py", + "nexus @ git+https://github.com/AMDResearch/nexus.git@main", +] [tool.setuptools] package-dir = {"" = "src"} @@ -39,19 +51,6 @@ python3 -m pip install --ignore-installed blinker && python3 -m pip install -r requirements.txt """ - -[tool.nexus] -git = "https://github.com/AMDResearch/nexus.git" -branch = "main" -build_command = """ -export CC=${ROCM_PATH}/bin/hipcc -export CXX=${ROCM_PATH}/bin/hipcc -cmake -B build -DCMAKE_PREFIX_PATH=/opt/rocm\ - -DLLVM_INSTALL_DIR=/opt/rocm/llvm\ - -DCMAKE_BUILD_TYPE=Debug -cmake --build build --parallel 16 -""" - [project.optional-dependencies] dev = [ "ruff==0.3.0", diff --git a/src/accordo/validator.py b/src/accordo/validator.py index 3d68b00d..bb77f085 100644 --- a/src/accordo/validator.py +++ b/src/accordo/validator.py @@ -139,15 +139,15 @@ def __init__( # Auto-detect accordo_path if not provided if accordo_path is None: - # Try to find it relative to this file + # Find it relative to this file (accordo package directory) accordo_dir = Path(__file__).parent if (accordo_dir / "build").exists() or (accordo_dir / "CMakeLists.txt").exists(): accordo_path = accordo_dir else: - # Try environment variable - from intelliperf.utils.env import get_accordo_path - - accordo_path = Path(get_accordo_path()) + raise RuntimeError( + f"Could not find Accordo build directory. Expected at {accordo_dir}. " + "Please build Accordo first or specify accordo_path explicitly." + ) self.accordo_path = Path(accordo_path) logging.debug(f"Accordo path: {self.accordo_path}") diff --git a/src/intelliperf/core/application.py b/src/intelliperf/core/application.py index 7accae92..203d04d2 100644 --- a/src/intelliperf/core/application.py +++ b/src/intelliperf/core/application.py @@ -34,7 +34,6 @@ from intelliperf.utils import process from intelliperf.utils.env import ( get_guided_tuning_path, - get_nexus_path, get_rocprofiler_path, ) from intelliperf.utils.process import capture_subprocess_output, exit_on_fail @@ -224,26 +223,65 @@ def clone(self): ) def collect_source_code(self): - nexus_directory = get_nexus_path() - lib = os.path.join(nexus_directory, "build", "lib", "libnexus.so") - env = os.environ.copy() - - with tempfile.TemporaryDirectory() as tmp: - json_result_file = os.path.join(tmp, "nexus_output.json") - - env["HSA_TOOLS_LIB"] = lib - env["NEXUS_LOG_LEVEL"] = "2" - env["NEXUS_OUTPUT_FILE"] = json_result_file - env["TRITON_ALWAYS_COMPILE"] = "1" - env["TRITON_DISABLE_LINE_INFO"] = "0" - capture_subprocess_output(self.get_app_cmd(), new_env=env, working_directory=self.get_project_directory()) - - if os.path.exists(json_result_file): - df_results = json.loads(open(json_result_file).read()) + """ + Collect source code for GPU kernels using Nexus. + + Returns: + dict: Dictionary containing kernel information with assembly, HIP source, files, and line numbers + """ + try: + from nexus import Nexus + except ImportError: + logging.error("Nexus Python API not found. Please install it: pip install git+https://github.com/AMDResearch/nexus.git@main") + return {"kernels": {}} + + try: + # Map Python logging level to Nexus log level + # Python: NOTSET=0, DEBUG=10, INFO=20, WARNING=30, ERROR=40, CRITICAL=50 + # Nexus: 0=none, 1=info, 2=warning, 3=error, 4=detail + current_level = logging.getLogger().getEffectiveLevel() + if current_level <= logging.DEBUG: + nexus_log_level = 4 # detail (most verbose) + elif current_level <= logging.INFO: + nexus_log_level = 1 # info + elif current_level <= logging.WARNING: + nexus_log_level = 2 # warning else: - df_results = {"kernels": {}} + nexus_log_level = 0 # none + + # Create Nexus tracer with inherited log level + nexus = Nexus(log_level=nexus_log_level) + + # Additional environment for Triton kernels + triton_env = { + "TRITON_ALWAYS_COMPILE": "1", + "TRITON_DISABLE_LINE_INFO": "0", + } + + # Run the application and capture kernel trace + trace = nexus.run( + command=self.get_app_cmd(), + env=triton_env, + cwd=self.get_project_directory(), + ) + + # Convert trace to the expected format + df_results = {"kernels": {}} + for kernel in trace: + df_results["kernels"][kernel.name] = { + "assembly": kernel.assembly, + "hip": kernel.hip, + "files": kernel.files, + "lines": kernel.lines, + "signature": kernel.signature, + } + return df_results + except Exception as e: + logging.error(f"Failed to collect source code with Nexus: {e}") + return {"kernels": {}} + def get_binary_absolute_path(self): if self.get_project_directory() != "": binary = self.get_app_cmd_without_args() diff --git a/src/intelliperf/utils/env.py b/src/intelliperf/utils/env.py index 996d21ed..7a8a8a8f 100644 --- a/src/intelliperf/utils/env.py +++ b/src/intelliperf/utils/env.py @@ -34,18 +34,10 @@ def get_guided_tuning_path(): return (Path(__file__).resolve().parent / "../../../external/guided-tuning").resolve() -def get_accordo_path(): - return (Path(__file__).resolve().parent / "../../accordo").resolve() - - def get_rocprofiler_path(): return (Path(__file__).resolve().parent / "../../../external/rocprofiler-compute/src").resolve() -def get_nexus_path(): - return (Path(__file__).resolve().parent / "../../../external/nexus").resolve() - - def get_llm_api_key(): llm_key = os.environ.get("LLM_GATEWAY_KEY") if not llm_key: