Этот коммит содержится в:
xuchen-amd
2025-09-12 16:24:44 -04:00
коммит произвёл GitHub
родитель 4daf25944d
Коммит eb46160a8f
11 изменённых файлов: 146 добавлений и 103 удалений
+67 -11
Просмотреть файл
@@ -58,7 +58,7 @@ To ensure code quality and consistency, we use **Ruff**, a fast Python linter an
-----
## Installing and Running Ruff
### Installing and Running Ruff
Ruff is available on PyPI and can be installed using `pip`:
@@ -82,18 +82,17 @@ ruff format .
-----
```markdown
## Type Annotations
### Type Annotation Guidelines
This project enforces type annotations using Ruff's `flake8-annotations` rules. All new code must include proper type annotations.
This project enforces type annotations using Ruff's `flake8-annotations` rules (ANN). All new code in `src/` must include proper type annotations.
### Requirements
#### Requirements
- All function arguments must have type annotations (except `self` and `cls`)
- All function return types must be annotated
- Class attributes should have type annotations where applicable
### Examples
#### Examples
```python
# Good - properly annotated
@@ -106,7 +105,7 @@ def process_kernel_data(kernel_name, metrics):
return {"kernel": kernel_name, "avg": sum(metrics) / len(metrics)}
```
### Checking Type Annotations
#### Checking Type Annotations
To specifically check for missing type annotations:
@@ -115,9 +114,66 @@ ruff check --select ANN .
```
For existing code, we're gradually adding type annotations. When modifying existing functions, please add type annotations to any code you touch.
-----
### String Formatting Guidelines
This project enforces modern Python string formatting practices using Ruff's `pyupgrade` rules (UP). All new code in `src/` should use f-strings where applicable instead of older formatting methods.
#### Requirements
- Use f-strings for string formatting when variables or expressions need to be embedded
- Replace `.format()` method calls and `%` formatting with f-strings where possible
- F-strings are preferred for readability and performance
#### Examples
```python
# Good - using f-strings
name = "kernel_analysis"
count = 42
message = f"Processing {name} with {count} metrics"
path = f"{base_dir}/results/{filename}.csv"
# Bad - will be caught by Ruff (UP045)
message = "Processing {} with {} metrics".format(name, count)
message = "Processing %s with %s metrics" % (name, count)
path = "{}/results/{}.csv".format(base_dir, filename)
```
## Disabling Formatting for Specific Sections
-----
### Path Handling Guidelines
This project enforces modern Python path handling practices using Ruff's `flake8-use-pathlib` rules (PTH). All new code in `src/` should use `pathlib.Path` methods instead of legacy `os.path` functions for directory operations.
#### Requirements
- Use `pathlib.Path` methods for all path operations instead of `os.path` functions
- Use `Path.cwd()` instead of `os.getcwd()`
- Use `Path.exists()` instead of `os.path.exists()`
- Use `Path.is_file()` and `Path.is_dir()` instead of `os.path.isfile()` and `os.path.isdir()`
- Use the `/` operator for path joining instead of `os.path.join()`
#### Examples
```python
# Good - using pathlib methods
current_dir = Path.cwd()
config_path = current_dir / "config" / "settings.yaml"
if config_path.exists() and config_path.is_file():
# Process file
# Bad - will be caught by Ruff (PTH rules)
import os
current_dir = Path(os.getcwd()) # PTH109
config_path = os.path.join(current_dir, "config", "settings.yaml") # PTH118
if os.path.exists(config_path) and os.path.isfile(config_path): # PTH110, PTH113
# Process file
```
-----
### Disabling Formatting for Specific Sections
There may be instances where you need to disable Ruff's formatting on a specific block of code. You can do this using special comments:
@@ -126,12 +182,12 @@ There may be instances where you need to disable Ruff's formatting on a specific
You can also disable specific linting rules for a line by using `# noqa: <rule_code>`.
## Coding guidelines
### Coding guidelines
Below are some repository specific guidelines which are followed througout the repository.
Any future contributions should adhere to these guidelines:
* Use the `pathlib` library functions instead of `os.path` for manipulating the file paths.
## Build and test documentation changes
### Build and test documentation changes
For instructions on how to build and test documentation changes (files under docs folder), please see https://rocm.docs.amd.com/en/latest/contribute/contributing.html
For instructions on how to build and test documentation changes (files under docs folder), please see https://rocm.docs.amd.com/en/latest/contribute/contributing.html
+6 -2
Просмотреть файл
@@ -33,11 +33,15 @@ extend-exclude = [
[tool.ruff.lint]
# Enable Pyflakes (F), pycodestyle (E, W for PEP8), aisort (I),
# type annotation (ANN), and f-string (UP) rules.
select = ["E", "W", "F", "I", "ANN","UP"]
ignore = ["E713", "E711", "ANN001", "ANN401", "UP045"]
select = ["E", "W", "F", "I", "ANN", "UP", "PTH"]
ignore = ["E713", "E711", "UP045", "PTH123", "PTH207"]
fixable = ["ALL"]
unfixable = []
[tool.ruff.lint.per-file-ignores]
# Ignore ANN and UP rules for all files except those in src/
"!src/**" = ["ANN", "UP", "PTH"]
[tool.ruff.lint.flake8-annotations]
allow-star-arg-any = true #allow Any for *args
ignore-fully-untyped = false #require type annotations
+2 -3
Просмотреть файл
@@ -24,7 +24,6 @@
##############################################################################
import argparse
import os
import re
from pathlib import Path
from typing import Optional
@@ -153,10 +152,10 @@ Examples:
metavar="",
type=str,
dest="path",
default=str(Path(os.getcwd()) / "workloads"),
default=str(Path.cwd() / "workloads"),
required=False,
help=(
f"\t\t\tSpecify path to save workload.\n\t\t\t(DEFAULT: {os.getcwd()}/workloads/<name>)" # noqa: E501
f"\t\t\tSpecify path to save workload.\n\t\t\t(DEFAULT: {Path.cwd()}/workloads/<name>)" # noqa: E501
),
)
profile_group.add_argument(
+1 -2
Просмотреть файл
@@ -25,7 +25,6 @@
import argparse
import importlib
import os
import socket
import sys
import time
@@ -216,7 +215,7 @@ class RocProfCompute:
def handle_profile_args(self) -> None:
# Add --name to workload path if --path is not given
if self.__args.path == str(Path(os.getcwd()) / "workloads"):
if self.__args.path == str(Path.cwd() / "workloads"):
if not hasattr(self.__args, "name") or not self.__args.name:
console_error("-n/--name is required")
self.__args.path = str(Path(self.__args.path) / self.__args.name)
+14 -28
Просмотреть файл
@@ -25,9 +25,6 @@
import argparse
import csv
import glob
import os
import re
import shlex
import shutil
import time
@@ -129,7 +126,7 @@ class RocProfCompute_Base:
# handle rocpd format
if args.format_rocprof_output == "rocpd":
# Vertically concat (by rows) results_*.csv into pmc_perf.csv
result_files = glob.glob(f"{args.path}/results_*.csv")
result_files = list(Path(args.path).glob("results_*.csv"))
with open(output_file, "w", newline="") as outfile:
writer = None
@@ -148,7 +145,7 @@ class RocProfCompute_Base:
# Delete results_*.csv files
for file in result_files:
os.remove(file)
Path(file).unlink()
console_debug(f"Deleted file: {file}")
return
@@ -158,27 +155,17 @@ class RocProfCompute_Base:
files = [
file
for pattern in csv_patterns
for file in glob.glob(f"{args.path}/{pattern}")
for file in Path(args.path).glob(pattern)
]
if args.hip_trace:
# remove hip api trace ouputs from this list
files = [
f
for f in files
if not re.compile(r"^.*_hip_api_trace\.csv$").match(
os.path.basename(f)
)
]
# remove hip api trace outputs from this list
files = [f for f in files if not f.name.endswith("_hip_api_trace.csv")]
if args.kokkos_trace:
# remove marker api trace ouputs from this list
# remove marker api trace outputs from this list
files = [
f
for f in files
if not re.compile(r"^.*_marker_api_trace\.csv$").match(
os.path.basename(f)
)
f for f in files if not f.name.endswith("_marker_api_trace.csv")
]
elif isinstance(args.path, list):
files = args.path
@@ -344,7 +331,7 @@ class RocProfCompute_Base:
for file in files:
# Do not remove accumulate counter files
if "SQ_" not in file or "SQC_" not in file:
os.remove(file)
Path(file).unlink()
return None
else:
return df
@@ -410,7 +397,7 @@ class RocProfCompute_Base:
print_status(status_msg)
# Run profiling on each input file
input_files = sorted(glob.glob(f"{args.path}/perfmon/*.txt"))
input_files = sorted(Path(args.path).glob("perfmon/*.txt"))
total_runs = len(input_files)
total_profiling_time = 0.0
@@ -440,7 +427,7 @@ class RocProfCompute_Base:
"-i",
"-r",
f"s%^(kernel:).*%kernel: {','.join(self.__args.kernel)}%g",
fname,
str(fname),
])
# log output from profile filtering
if not success:
@@ -455,7 +442,7 @@ class RocProfCompute_Base:
"-i",
"-r",
f"s%^(range:).*%range: {' '.join(self.__args.dispatch)}%g",
fname,
str(fname),
])
# log output from profile filtering
if not success:
@@ -471,10 +458,10 @@ class RocProfCompute_Base:
"rocprofv3",
"rocprofiler-sdk",
):
options = self.get_profiler_options(fname, self._soc)
options = self.get_profiler_options(str(fname), self._soc)
start_time = time.time()
run_prof(
fname=fname,
fname=str(fname),
profiler_options=options,
workload_dir=args.path,
mspec=self._soc._mspec,
@@ -500,8 +487,7 @@ class RocProfCompute_Base:
):
return
input_files = glob.glob(f"{args.path}/perfmon/*.txt")
total_runs = len(input_files)
total_runs = len(list(Path(args.path).glob("perfmon/*.txt")))
console_log(f"[Run {total_runs + 1}/{total_runs + 1}][PC sampling profile run]")
+2 -3
Просмотреть файл
@@ -24,7 +24,6 @@
##############################################################################
import argparse
import glob
import json
import math
import os
@@ -293,8 +292,8 @@ class OmniSoC_Base:
# File id dict
config_root_dir = f"{args.config_dir}/{self.__arch}"
config_filename_dict = {
Path(filename).name.split("_")[0]: filename
for filename in glob.glob(f"{config_root_dir}/*.yaml")
filename.name.split("_")[0]: str(filename)
for filename in Path(config_root_dir).glob("*.yaml")
}
filter_blocks = args.filter_blocks
+15 -10
Просмотреть файл
@@ -26,6 +26,7 @@
import os
import platform
import subprocess
from pathlib import Path
from typing import Optional
from rich.text import Text
@@ -43,7 +44,7 @@ class Terimnal(Container):
classes: Optional[str] = None,
) -> None:
super().__init__(name=name, id=id, classes=classes)
self.current_directory = os.getcwd()
self.current_directory = Path.cwd()
self.output_text: str = ""
self.input_text: str = ""
self.input_prompt: str = ""
@@ -80,9 +81,7 @@ class Terimnal(Container):
def update_prompt(self) -> None:
"""Update the command prompt in the input field."""
input_widget = self.query_one("#terminal-input")
current_path = (
os.path.basename(self.current_directory) or self.current_directory
)
current_path = Path(self.current_directory).name or self.current_directory
if platform.system() != "Windows":
prompt = f"{current_path} $ "
@@ -156,15 +155,21 @@ class Terimnal(Container):
path = command[3:].strip()
if not path:
# Just "cd" usually goes to home directory
path = os.path.expanduser("~")
new_path = Path.home()
else:
new_path = Path(path)
# Handle relative paths
if not os.path.isabs(path):
path = os.path.join(self.current_directory, path)
# Handle relative paths
if not new_path.is_absolute():
new_path = Path(self.current_directory) / new_path
# Resolve any symlinks and normalize the path
new_path = new_path.resolve()
# Change to the new directory
os.chdir(path)
self.current_directory = os.getcwd()
new_path_str = str(new_path)
os.chdir(new_path_str)
self.current_directory = new_path_str
self.add_output(f"Changed directory to {self.current_directory}\n")
self.update_prompt()
except Exception as e:
+1 -2
Просмотреть файл
@@ -24,7 +24,6 @@
##############################################################################
import argparse
import os
import textwrap
import time
from abc import abstractmethod
@@ -164,7 +163,7 @@ class Roofline:
base_path = Path(base_dir)
if base_path.name == "workloads" and base_path.parent == Path(os.getcwd()):
if base_path.name == "workloads" and base_path.parent == Path.cwd():
app_name = getattr(self.__args, "name", "default_app_name")
gpu_model_name = getattr(self.__mspec, "gpu_model", "default_gpu_model")
+33 -38
Просмотреть файл
@@ -22,8 +22,6 @@
# THE SOFTWARE.
##############################################################################
import os
import re
from collections import OrderedDict
from pathlib import Path
@@ -56,20 +54,18 @@ def load_panel_configs(
"""
configs: dict[int, dict[str, Any]] = {}
for dir_path in dirs:
for root, _, files in os.walk(dir_path):
for file_name in files:
if file_name.endswith(".yaml"):
with open(Path(root) / file_name) as file:
config_yml = yaml.safe_load(file)
# metric key can be None due to some metric-
# tables not having any metrics
# metric key should be empty dict instead of None
panel_config = config_yml["Panel Config"]
for data_source in panel_config["data source"]:
metric_table = data_source.get("metric_table")
if metric_table and metric_table["metric"] is None:
metric_table["metric"] = {}
configs[panel_config["id"]] = panel_config
for yaml_file in Path(dir_path).rglob("*.yaml"):
with open(yaml_file) as file:
config_yml = yaml.safe_load(file)
# metric key can be None due to some metric-
# tables not having any metrics
# metric key should be empty dict instead of None
panel_config = config_yml["Panel Config"]
for data_source in panel_config["data source"]:
metric_table = data_source.get("metric_table")
if metric_table and metric_table["metric"] is None:
metric_table["metric"] = {}
configs[panel_config["id"]] = panel_config
# TODO: sort metrics as the header order in case they-
# are not defined in the same order
@@ -207,33 +203,32 @@ def create_df_pmc(
dfs: list[pd.DataFrame] = []
coll_levels: list[str] = []
for root, _, files in os.walk(raw_data_dir):
for file_name in files:
# Process SQ*.csv or pmc_perf.csv files
is_sq_file = file_name.endswith(".csv") and file_name.startswith("SQ")
is_pmc_perf = file_name == f"{schema.PMC_PERF_FILE_PREFIX}.csv"
for csv_file in Path(raw_data_dir).rglob("*.csv"):
file_name = csv_file.name
if is_sq_file or is_pmc_perf:
file_path = Path(root) / file_name
tmp_df = pd.read_csv(file_path)
is_sq_file = file_name.startswith("SQ")
is_pmc_perf = file_name == f"{schema.PMC_PERF_FILE_PREFIX}.csv"
if config_dict.get("format_rocprof_output") == "rocpd":
tmp_df = rocpd_data.process_rocpd_csv(tmp_df)
if is_sq_file or is_pmc_perf:
tmp_df = pd.read_csv(csv_file)
# Demangle original KernelNames
kernel_name_shortener(tmp_df, kernel_verbose)
if config_dict.get("format_rocprof_output") == "rocpd":
tmp_df = rocpd_data.process_rocpd_csv(tmp_df)
# NB:
# Idealy, the Node column should be added out of
# multiindexing level. Here, we add it into pmc_perf
# as it is the main sub-df which can be handled easily
# later.
if file_name == "pmc_perf.csv" and node_name is not None:
tmp_df.insert(0, "Node", node_name)
# Demangle original KernelNames
kernel_name_shortener(tmp_df, kernel_verbose)
dfs.append(tmp_df)
# Remove .csv extension for collection level
coll_levels.append(file_name[:-4])
# NB:
# Idealy, the Node column should be added out of
# multiindexing level. Here, we add it into pmc_perf
# as it is the main sub-df which can be handled easily
# later.
if file_name == "pmc_perf.csv" and node_name is not None:
tmp_df.insert(0, "Node", node_name)
dfs.append(tmp_df)
# Remove .csv extension for collection level
coll_levels.append(csv_file.stem)
if not dfs:
return pd.DataFrame()
+3 -3
Просмотреть файл
@@ -23,7 +23,7 @@
##############################################################################
import os
from pathlib import Path
from typing import Any, Optional
import yaml
@@ -103,8 +103,8 @@ class MIGPUSpecs:
| -- memory partition mode
"""
current_dir = os.path.dirname(__file__)
yaml_file_path = os.path.join(current_dir, "mi_gpu_spec.yaml")
current_dir = Path(__file__).parent
yaml_file_path = current_dir / "mi_gpu_spec.yaml"
# Load the YAML data
yaml_data = cls._load_yaml(yaml_file_path)
+2 -1
Просмотреть файл
@@ -798,7 +798,7 @@ def run_prof(
if retain_rocpd_output:
shutil.copyfile(
glob.glob(f"{workload_dir}/out/pmc_1/*/*.db")[0],
"f{workload_dir}/{fbase}.db",
f"{workload_dir}/{fbase}.db",
)
console_warning(
f"Retaining large raw rocpd database: {workload_dir}/{fbase}.db"
@@ -853,6 +853,7 @@ def run_prof(
f"Cannot write results for {fbase}.csv due to no counter "
"csv files generated."
)
return
# Combine results into single CSV file
combined_results = pd.concat(