6540155c9d
* Implement AMDGPU driver info and GPU VRAM attributes in system info. section of analysis report. * Backward compatibility for rocprofiler-sdk avail module path migration * Fix roofline calculation where AI data points are N/A
917 строки
29 KiB
Python
917 строки
29 KiB
Python
##############################################################################
|
|
# MIT License
|
|
#
|
|
# Copyright (c) 2021 - 2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
|
|
##############################################################################
|
|
"""Get host/gpu specs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import importlib
|
|
import os
|
|
import re
|
|
import socket
|
|
import subprocess
|
|
from dataclasses import dataclass, field, fields
|
|
from datetime import datetime
|
|
from math import ceil
|
|
from pathlib import Path as path
|
|
from typing import Any, Optional, TypeVar
|
|
|
|
import pandas as pd
|
|
|
|
import config
|
|
from utils.amdsmi_interface import (
|
|
amdsmi_ctx,
|
|
get_amdgpu_driver_version,
|
|
get_gpu_compute_partition,
|
|
get_gpu_memory_partition,
|
|
get_gpu_vbios_part_number,
|
|
get_gpu_vram_size,
|
|
)
|
|
from utils.logger import (
|
|
console_debug,
|
|
console_error,
|
|
console_log,
|
|
console_warning,
|
|
demarcate,
|
|
)
|
|
from utils.mi_gpu_spec import mi_gpu_specs
|
|
from utils.tty import get_table_string
|
|
from utils.utils import get_version
|
|
|
|
T = TypeVar("T")
|
|
|
|
VERSION_LOC: list[str] = [
|
|
"version",
|
|
"version-dev",
|
|
"version-hip-libraries",
|
|
"version-hiprt",
|
|
"version-hiprt-devel",
|
|
"version-hip-sdk",
|
|
"version-libs",
|
|
"version-utils",
|
|
]
|
|
|
|
|
|
def detect_arch(rocminfo_lines: list[str]) -> Optional[tuple[str, int]]:
|
|
supported_gpu_arch = mi_gpu_specs.get_gpu_series_dict()
|
|
unsupported_gpu_arch: set[str] = set()
|
|
|
|
for idx1, line_text in enumerate(rocminfo_lines):
|
|
gpu_arch = search(
|
|
r"^\s*Name\s*:\s* ([Gg][Ff][Xx][a-zA-Z0-9]+).*\s*$", line_text
|
|
)
|
|
if not gpu_arch:
|
|
continue
|
|
|
|
if gpu_arch in supported_gpu_arch:
|
|
return (gpu_arch, idx1)
|
|
|
|
if gpu_arch not in unsupported_gpu_arch:
|
|
unsupported_gpu_arch.add(gpu_arch)
|
|
console_warning(
|
|
"Detected GPU architecture: "
|
|
f"{gpu_arch} is currently NOT supported by the profile mode."
|
|
)
|
|
|
|
if unsupported_gpu_arch:
|
|
console_log(f"Supported architectures: {list(supported_gpu_arch.keys())}")
|
|
|
|
console_error("Cannot find a supported arch in rocminfo.")
|
|
|
|
|
|
def detect_gpu_chip_id(rocminfo_lines: list[str]) -> Optional[str]:
|
|
chip_id_dict = mi_gpu_specs.get_chip_id_dict()
|
|
unknown_chips: list[str] = []
|
|
|
|
for idx, line_text in enumerate(rocminfo_lines):
|
|
chip_id = search(r"^\s*Chip ID\s*:\s* ([0-9]+).*\s*$", line_text)
|
|
if chip_id:
|
|
# Check if this chip ID is valid (known)
|
|
if chip_id in chip_id_dict or int(chip_id) in chip_id_dict:
|
|
return chip_id # Return first valid chip ID found
|
|
else:
|
|
unknown_chips.append(chip_id)
|
|
|
|
# Exhausted all lines - handle the cases where no valid chip was found
|
|
if unknown_chips:
|
|
for chip_id in unknown_chips:
|
|
console_warning(f"Unknown Chip ID(s) detected: {chip_id}")
|
|
else:
|
|
console_warning("No Chip ID detected")
|
|
|
|
return None
|
|
|
|
|
|
# Custom decorator to mimic the behavior of kw_only found in Python 3.10
|
|
def kw_only(cls: T) -> T:
|
|
def __init__(self: Any, *args: Any, **kwargs: Any) -> None: # noqa: ANN401
|
|
for name, value in kwargs.items():
|
|
setattr(self, name, value)
|
|
|
|
cls.__init__ = __init__ # type: ignore
|
|
return cls
|
|
|
|
|
|
def generate_machine_specs(
|
|
args: Optional[argparse.Namespace], sysinfo: Optional[dict[str, Any]] = None
|
|
) -> MachineSpecs:
|
|
if sysinfo is not None:
|
|
try:
|
|
sysinfo_ver = str(sysinfo["version"])
|
|
version = get_version(config.rocprof_compute_home)["version"]
|
|
curr_ver = version[: version.find(".")]
|
|
|
|
if sysinfo_ver != curr_ver:
|
|
console_warning(
|
|
"Detected mismatch in sysinfo versioning. "
|
|
"You need to reprofile to update data."
|
|
)
|
|
|
|
return MachineSpecs(**sysinfo)
|
|
except KeyError:
|
|
console_error(
|
|
"Detected mismatch in sysinfo versioning. You need to reprofile "
|
|
"to update data."
|
|
)
|
|
|
|
# read timestamp info
|
|
now = datetime.now()
|
|
local_now = now.astimezone()
|
|
local_tzname = local_now.tzinfo.tzname(local_now) # type: ignore
|
|
timestamp = f"{now.strftime('%c')} ({local_tzname})"
|
|
|
|
# set specs version
|
|
version = get_version(config.rocprof_compute_home)["version"]
|
|
# NB: Just taking major as specs version.
|
|
# May want to make this more specific in the future
|
|
# version will always follow 'major.minor.patch' format
|
|
specs_version = version[: version.find(".")]
|
|
|
|
##########################################
|
|
## A. Machine Specs
|
|
##########################################
|
|
machine_info = extract_machine_info()
|
|
|
|
# FIXME: use device
|
|
# Load amd-smi data
|
|
gpu_info = extract_gpu_info()
|
|
|
|
##########################################
|
|
## B. SoC Specs
|
|
##########################################
|
|
soc_info = extract_soc_info()
|
|
|
|
# Combine all specifications
|
|
with amdsmi_ctx():
|
|
specs = MachineSpecs(
|
|
version=specs_version,
|
|
timestamp=timestamp,
|
|
rocminfo_lines=soc_info["rocminfo_lines"],
|
|
hostname=socket.gethostname(),
|
|
cpu_model=machine_info["cpu_model"],
|
|
sbios=machine_info["sbios"],
|
|
linux_kernel_version=machine_info["linux_kernel_version"],
|
|
amd_gpu_kernel_version=get_amdgpu_driver_version(),
|
|
cpu_memory=machine_info["cpu_memory"],
|
|
gpu_memory=get_gpu_vram_size(),
|
|
linux_distro=machine_info["linux_distro"],
|
|
rocm_version=get_rocm_ver().strip(),
|
|
vbios=gpu_info["vbios"],
|
|
compute_partition=gpu_info["compute_partition"],
|
|
memory_partition=gpu_info["memory_partition"],
|
|
gpu_arch=soc_info["gpu_arch"],
|
|
gpu_chip_id=soc_info["gpu_chip_id"],
|
|
)
|
|
|
|
# Load above SoC specs via module import
|
|
try:
|
|
soc_module = importlib.import_module(
|
|
f"rocprof_compute_soc.soc_{specs.gpu_arch}"
|
|
)
|
|
soc_class = getattr(soc_module, f"{specs.gpu_arch}_soc")
|
|
soc_obj = soc_class(args, specs) # noqa: F841
|
|
except ModuleNotFoundError as e:
|
|
console_error(
|
|
f"Arch {specs.gpu_arch} marked as supported,"
|
|
f"but couldn't find class implementation {e}."
|
|
)
|
|
|
|
# Update arch specific specs
|
|
specs.gpu_model = (
|
|
mi_gpu_specs.get_gpu_model(specs.gpu_arch, specs.gpu_chip_id) or ""
|
|
)
|
|
specs.num_xcd = str(
|
|
mi_gpu_specs.get_num_xcds(
|
|
specs.gpu_arch, specs.gpu_model, specs.compute_partition
|
|
)
|
|
)
|
|
specs.total_l2_chan = totall2_banks(
|
|
specs.gpu_arch,
|
|
specs.gpu_model,
|
|
specs.l2_banks,
|
|
specs.compute_partition,
|
|
)
|
|
specs.num_hbm_channels = str(specs.get_hbm_channels())
|
|
|
|
return specs
|
|
|
|
|
|
@demarcate
|
|
def extract_machine_info() -> dict[str, Any]:
|
|
result: dict[str, Optional[str]] = {
|
|
"cpu_model": None,
|
|
"sbios": None,
|
|
"linux_kernel_version": None,
|
|
"cpu_memory": None,
|
|
"linux_distro": None,
|
|
}
|
|
|
|
try:
|
|
cpuinfo = path("/proc/cpuinfo").read_text()
|
|
meminfo = path("/proc/meminfo").read_text()
|
|
version = path("/proc/version").read_text()
|
|
os_release = path("/etc/os-release").read_text()
|
|
|
|
result["cpu_model"] = search(r"^model name\s*: (.*?)$", cpuinfo)
|
|
result["sbios"] = (
|
|
path("/sys/class/dmi/id/bios_vendor").read_text().strip()
|
|
+ path("/sys/class/dmi/id/bios_version").read_text().strip()
|
|
)
|
|
result["linux_kernel_version"] = search(r"version (\S*)", version)
|
|
result["cpu_memory"] = search(r"MemTotal:\s*(\S*)", meminfo)
|
|
result["linux_distro"] = search(r'PRETTY_NAME="(.*?)"', os_release) or ""
|
|
|
|
except OSError as e:
|
|
console_warning(f"Could not read system files: {e}")
|
|
return result
|
|
|
|
|
|
@demarcate
|
|
def extract_gpu_info() -> dict[str, Any]:
|
|
result: dict[str, Optional[str]] = {
|
|
"vbios": None,
|
|
"compute_partition": None,
|
|
"memory_partition": None,
|
|
}
|
|
|
|
with amdsmi_ctx():
|
|
result["vbios"] = get_gpu_vbios_part_number()
|
|
result["compute_partition"] = get_gpu_compute_partition()
|
|
result["memory_partition"] = get_gpu_memory_partition()
|
|
|
|
# Apply defaults and warnings
|
|
if result["compute_partition"] == "N/A" or not result["compute_partition"]:
|
|
console_warning("Cannot detect accelerator partition from amd-smi.")
|
|
console_warning("Applying default accelerator partition: SPX")
|
|
result["compute_partition"] = "SPX"
|
|
|
|
if result["memory_partition"] == "N/A" or not result["memory_partition"]:
|
|
console_warning("Cannot detect memory partition from amd-smi.")
|
|
|
|
console_debug(
|
|
f"vbios is {result['vbios']}, compute partition is "
|
|
f"{result['compute_partition']}, memory partition is "
|
|
f"{result['memory_partition']}"
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
@demarcate
|
|
def extract_soc_info() -> dict[str, Any]:
|
|
result: dict[str, Any] = {
|
|
"rocminfo_lines": None,
|
|
"gpu_arch": None,
|
|
"gpu_chip_id": None,
|
|
}
|
|
|
|
# Read rocminfo
|
|
rocminfo_full = run(["rocminfo"])
|
|
if rocminfo_full is None:
|
|
return result
|
|
|
|
rocminfo_lines = rocminfo_full.split("\n")
|
|
arch_result = detect_arch(rocminfo_lines)
|
|
|
|
if arch_result is None:
|
|
return result
|
|
|
|
result["gpu_arch"], arch_idx = arch_result
|
|
result["rocminfo_lines"] = rocminfo_lines[
|
|
arch_idx + 1 :
|
|
] # update rocminfo for target section
|
|
result["gpu_chip_id"] = detect_gpu_chip_id(rocminfo_lines)
|
|
|
|
return result
|
|
|
|
|
|
@kw_only
|
|
@dataclass
|
|
class MachineSpecs:
|
|
##########################################
|
|
## A. Workload / Spec info
|
|
##########################################
|
|
|
|
# these three fields are special in that they're not included
|
|
# when you use (e.g.,) --specs to view the machinespecs, but they
|
|
# _are_ included in profiling/analysis, so we mark them as 'optional'
|
|
# in the metadata to avoid erroring out on missing fields on
|
|
# serialization
|
|
workload_name: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The name of the workload data was collected for.",
|
|
"name": "Workload Name",
|
|
"optional": True,
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
command: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The command the workload was executed with.",
|
|
"name": "Command",
|
|
"optional": True,
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
ip_blocks: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The hardware blocks profiling information was collected for.",
|
|
"name": "IP Blocks",
|
|
"optional": True,
|
|
},
|
|
)
|
|
timestamp: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The time (in local system time) when data was collected",
|
|
"name": "Timestamp",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
version: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The version of the machine specification file format.",
|
|
"name": "MachineSpecs Version",
|
|
"intable": False,
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
timestamp: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The time (in local system time) when data was collected",
|
|
"name": "Timestamp",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
rocminfo_lines: Optional[list] = field(
|
|
default=None, metadata={"show_in_table": False}
|
|
)
|
|
##########################################
|
|
## A. Machine Specs
|
|
##########################################
|
|
hostname: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The hostname of the machine.",
|
|
"name": "Hostname",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
cpu_model: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The model name of the CPU used.",
|
|
"name": "CPU Model",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
sbios: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The system management bios version and vendor.",
|
|
"name": "SBIOS",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
linux_distro: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The Linux distribution installed on the machine.",
|
|
"name": "Linux Distribution",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
linux_kernel_version: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The Linux kernel version running on the machine.",
|
|
"name": "Linux Kernel Version",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
amd_gpu_kernel_version: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": ("The version of the AMDGPU driver installed on the machine."),
|
|
"name": "AMD GPU Kernel Version",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
cpu_memory: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The total amount of memory available to the CPU.",
|
|
"unit": "KB",
|
|
"name": "CPU Memory",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
gpu_memory: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The total amount of memory available to accelerators/GPUs "
|
|
"in the system."
|
|
),
|
|
"unit": "KB",
|
|
"name": "GPU Memory",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
rocm_version: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The ROCm version used during data-collection.",
|
|
"name": "ROCm Version",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
vbios: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The version of the accelerators/GPUs video bios in the system.",
|
|
"name": "VBIOS",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
compute_partition: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The compute partitioning mode active on the accelerators/GPUs in the "
|
|
"system (MI300 only)."
|
|
),
|
|
"name": "Compute Partition",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
memory_partition: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The memory partitioning mode active on the accelerators/GPUs in the "
|
|
"system (MI300 only)."
|
|
),
|
|
"name": "Memory Partition",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
|
|
##########################################
|
|
## B. SoC Specs
|
|
##########################################
|
|
gpu_series: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The series of the accelerators/GPUs in the system.",
|
|
"name": "GPU Series",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
gpu_model: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The product name of the accelerators/GPUs in the system.",
|
|
"name": "GPU Model",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
gpu_arch: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The architecture name of the accelerators/GPUs in the system,\n"
|
|
"as used by (e.g.,) the AMDGPU backed of LLVM.",
|
|
"name": "GPU Arch",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
gpu_chip_id: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "The Chip ID of the accelerators/GPUs in the system.",
|
|
"name": "Chip ID",
|
|
"optional": True,
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
gpu_l1: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The size of the vL1D cache (per compute-unit) on the "
|
|
"accelerators/GPUs."
|
|
),
|
|
"name": "GPU L1",
|
|
"unit": "KiB",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
gpu_l2: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The size of the vL1D cache (per compute-unit) on the "
|
|
"accelerators/GPUs."
|
|
),
|
|
"name": "GPU L2",
|
|
"unit": "KiB",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
cu_per_gpu: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The total number of compute units per accelerator/GPU in the system. "
|
|
"On systems with configurable partitioning, (e.g., MI300) this is "
|
|
"the total number of compute units in a partition."
|
|
),
|
|
"name": "CU per GPU",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
simd_per_cu: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The number of SIMD processors in a compute unit for the "
|
|
"accelerators/GPUs in the system."
|
|
),
|
|
"name": "SIMD per CU",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
se_per_gpu: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The number of shader engines on the accelerators/GPUs in the system. "
|
|
"On systems with configurable partitioning, (e.g., MI300) this is "
|
|
"the total number of shader engines in a partition."
|
|
),
|
|
"name": "SE per GPU",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
wave_size: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The number work-items in a wavefront on the accelerators/GPUs in "
|
|
"the system."
|
|
),
|
|
"name": "Wave Size",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
workgroup_max_size: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The maximum number of work-items in a workgroup on the "
|
|
"accelerators/GPUs in the system."
|
|
),
|
|
"name": "Workgroup Max Size",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
max_waves_per_cu: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The maximum number of wavefronts that can be resident on a "
|
|
"compute unit on the accelerators/GPUs in the system"
|
|
),
|
|
"name": "Max Waves per CU",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
max_sclk: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The maximum engine (compute-unit) clock rate of the "
|
|
"accelerators/GPUs in the system."
|
|
),
|
|
"name": "Max SCLK",
|
|
"unit": "MHz",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
max_mclk: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The maximum memory clock rate of the accelerators/GPUs in the system."
|
|
),
|
|
"name": "Max MCLK",
|
|
"unit": "MHz",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
cur_sclk: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"[RESERVED] The current engine (compute unit) clock rate of the "
|
|
"accelerators/GPUs in the system. Unused."
|
|
),
|
|
"name": "Cur SCLK",
|
|
"unit": "MHz",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
cur_mclk: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"[RESERVED] The current memory clock rate of the accelerators/GPUs "
|
|
"in the system. Unused."
|
|
),
|
|
"name": "Cur MCLK",
|
|
"unit": "MHz",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
l2_banks: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
total_l2_chan: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The maximum number of L2 cache channels on the accelerators/GPUs "
|
|
"in the system. On systems with configurable partitioning, "
|
|
"(e.g., MI300) this is the total number of L2 cache channels "
|
|
"in a partition."
|
|
),
|
|
"name": "Total L2 Channels",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
lds_banks_per_cu: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The number of banks in the LDS for a compute unit on the "
|
|
"accelerators/GPUs in the system."
|
|
),
|
|
"name": "LDS Banks per CU",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
sqc_per_gpu: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The number of L1I/sL1D caches on the accelerators/GPUs in the "
|
|
"system. On systems with configurable partitioning, (e.g., MI300) "
|
|
"this is the total number of L1I/sL1D caches in a partition."
|
|
),
|
|
"name": "SQC per GPU",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
pipes_per_gpu: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The number of scheduler-pipes on the accelerators/GPUs in the system."
|
|
),
|
|
"name": "Pipes per GPU",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
num_xcd: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": (
|
|
"The total number of accelerator complex dies in a compute partition "
|
|
"on the accelerators/GPUs in the system. For accelerators without "
|
|
"partitioning (i.e., pre-MI300), this is considered to be one."
|
|
),
|
|
"name": "Num XCDs",
|
|
"unit": "XCDs",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
num_hbm_channels: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"doc": "Number of HBM channels",
|
|
"name": "HBM channels",
|
|
"show_in_table": True,
|
|
},
|
|
)
|
|
|
|
def get_hbm_channels(self) -> Optional[str]:
|
|
if self.memory_partition and self.memory_partition.lower().startswith("nps"):
|
|
hbmchannels = 128
|
|
if self.memory_partition.lower() == "nps4":
|
|
hbmchannels //= 4
|
|
elif self.memory_partition.lower() == "nps8":
|
|
hbmchannels //= 8
|
|
return str(hbmchannels)
|
|
else:
|
|
return self.total_l2_chan
|
|
|
|
def get_class_members(self) -> pd.DataFrame:
|
|
data = {}
|
|
missing_required_fields = []
|
|
|
|
for class_field in fields(self):
|
|
if not class_field.metadata.get("show_in_table", True):
|
|
continue
|
|
|
|
name = class_field.name
|
|
value = getattr(self, name)
|
|
data[name] = value
|
|
|
|
# Check for missing required fields
|
|
if value is None and not class_field.metadata.get("optional", False):
|
|
missing_required_fields.append(name)
|
|
|
|
# Handle warnings after processing all fields
|
|
if missing_required_fields:
|
|
for field_name in missing_required_fields:
|
|
console_warning(
|
|
f"Incomplete class definition for {self.gpu_arch}. "
|
|
f"Expecting populated {field_name} but detected None."
|
|
)
|
|
console_warning(f"Missing specs fields for {self.gpu_arch}")
|
|
|
|
return pd.DataFrame(data, index=[0])
|
|
|
|
def __repr__(self) -> str:
|
|
topstr = (
|
|
"Machine Specifications: describing the state of the machine that "
|
|
"ROCm Compute Profiler data was collected on.\n"
|
|
)
|
|
data = []
|
|
for class_field in fields(self):
|
|
name = class_field.name
|
|
if class_field.metadata.get("show_in_table", True):
|
|
_data = {}
|
|
value = getattr(self, name)
|
|
if class_field.metadata:
|
|
# check out of table before any re-naming for pretty-printing
|
|
if (
|
|
"intable" in class_field.metadata
|
|
and not class_field.metadata["intable"]
|
|
):
|
|
if name == "version":
|
|
topstr += f"Output version: {value}\n"
|
|
else:
|
|
console_error(
|
|
f"Unknown out of table printing field: {name}"
|
|
)
|
|
continue
|
|
if "name" in class_field.metadata:
|
|
name = class_field.metadata["name"]
|
|
if "unit" in class_field.metadata:
|
|
_data["Unit"] = class_field.metadata["unit"]
|
|
if "doc" in class_field.metadata:
|
|
_data["Description"] = class_field.metadata["doc"]
|
|
_data["Spec"] = name
|
|
_data["Value"] = value
|
|
data.append(_data)
|
|
df = pd.DataFrame(data)
|
|
columns = ["Spec", "Value"]
|
|
if "Description" in df.columns:
|
|
columns += ["Description"]
|
|
if "Unit" in df.columns:
|
|
columns += ["Unit"]
|
|
df = df[columns]
|
|
df = df.fillna("")
|
|
return topstr + get_table_string(df, transpose=False, decimal=2)
|
|
|
|
|
|
def get_rocm_ver() -> str:
|
|
# Check for version files in ROCm installation
|
|
rocm_base_path = path(os.getenv("ROCM_PATH", "/opt/rocm"))
|
|
|
|
for version_file_name in VERSION_LOC:
|
|
version_file_path = rocm_base_path / ".info" / version_file_name
|
|
if version_file_path.exists():
|
|
return version_file_path.read_text().strip()
|
|
|
|
# Fallback to environment variable
|
|
ROCM_VER_USER = os.getenv("ROCM_VER")
|
|
if ROCM_VER_USER:
|
|
console_log(
|
|
"profiling",
|
|
"Overriding missing ROCm version detection with "
|
|
f"ROCM_VER = {ROCM_VER_USER}",
|
|
)
|
|
return ROCM_VER_USER
|
|
|
|
# No version found - log error and return empty string
|
|
console_warning("Unable to detect a complete local ROCm installation.")
|
|
console_warning(
|
|
f"The expected {rocm_base_path}/.info/ versioning directory is missing."
|
|
)
|
|
console_error("Ensure you have valid ROCm installation.", exit=False)
|
|
return ""
|
|
|
|
|
|
def run(cmd: list[str], exit_on_error: bool = False) -> str:
|
|
try:
|
|
p = subprocess.run(cmd, capture_output=True)
|
|
except FileNotFoundError as e:
|
|
console_error(
|
|
f"Unable to parse specs. Can't find ROCm asset: {e.filename}\n"
|
|
'Try passing a path to an existing workload results in "analyze" mode.'
|
|
)
|
|
|
|
if exit_on_error and p.returncode != 0: # type: ignore
|
|
console_error(f"Command {cmd} failed with non-zero exit code")
|
|
return p.stdout.decode("utf-8") # type: ignore
|
|
|
|
|
|
def search(pattern: str, string: str) -> Optional[str]:
|
|
m = re.search(pattern, string, re.MULTILINE)
|
|
if m is not None:
|
|
return m.group(1)
|
|
return None
|
|
|
|
|
|
def total_sqc(archname: str, num_compute_units: str, num_shader_engines: str) -> int:
|
|
cu_per_se = float(num_compute_units) / float(num_shader_engines)
|
|
sq_per_se = cu_per_se / 2.0
|
|
if archname.lower() in ["mi50", "mi100"]:
|
|
sq_per_se = cu_per_se / 3
|
|
sq_per_se = ceil(sq_per_se)
|
|
return int(sq_per_se) * int(num_shader_engines)
|
|
|
|
|
|
def totall2_banks(
|
|
gpu_arch: Optional[str] = None,
|
|
gpu_model: Optional[str] = None,
|
|
L2banks: Optional[str] = None,
|
|
compute_partition: Optional[str] = None,
|
|
) -> Optional[str]:
|
|
xcd_count = mi_gpu_specs.get_num_xcds(gpu_arch, gpu_model, compute_partition)
|
|
|
|
# TODO: MachineSpecs and OmniSoC mspec should converge...
|
|
if L2banks is not None and xcd_count is not None:
|
|
return str(int(L2banks) * int(xcd_count))
|
|
return None
|
|
|
|
|
|
if __name__ == "__main__":
|
|
specs = generate_machine_specs(None, None)
|
|
if specs:
|
|
print(specs)
|
|
else:
|
|
console_error("specs", "Failed to generate machine specifications", exit=False)
|