Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Autotuner] Plotting utility + test #2394

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
13 changes: 13 additions & 0 deletions docs/user/InstructionsForAutoTuner.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,19 @@ python3 distributed.py --design gcd --platform sky130hd \
sweep
```

#### Plot images

After running an AutoTuner experiment, you can generate a graph to understand the results better.
The graph will show the progression of one metric (see list below) over the execution of the experiment.

- QoR
- Runtime per trial
- Clock Period
- Worst slack

```shell
python3 utils/plot.py --results_dir <your-autotuner-result-path>
```

### Google Cloud Platform (GCP) distribution with Ray

Expand Down
44 changes: 25 additions & 19 deletions flow/test/test_autotuner.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/usr/bin/env bash
DESIGN_NAME=${1:-gcd}
PLATFORM=${2:-nangate45}

# run the commands in ORFS root dir
echo "[INFO FLW-0029] Installing dependencies in virtual environment."
Expand All @@ -20,28 +22,32 @@ python3 -m unittest tools.AutoTuner.test.smoke_test_sweep.${PLATFORM}SweepSmokeT
echo "Running Autotuner smoke tests for --sample and --iteration."
python3 -m unittest tools.AutoTuner.test.smoke_test_sample_iteration.${PLATFORM}SampleIterationSmokeTest.test_sample_iteration

if [ "$PLATFORM" == "asap7" ] && [ "$DESIGN" == "gcd" ]; then
if [ "$PLATFORM" == "asap7" ] && [ "$DESIGN_NAME" == "gcd" ]; then
echo "Running Autotuner ref file test (only once)"
python3 -m unittest tools.AutoTuner.test.ref_file_check.RefFileCheck.test_files
fi

echo "Running Autotuner smoke algorithm & evaluation test"
python3 -m unittest tools.AutoTuner.test.smoke_test_algo_eval.${PLATFORM}AlgoEvalSmokeTest.test_algo_eval

# run this test last (because it modifies current path)
echo "Running Autotuner remote test"
if [ "$PLATFORM" == "asap7" ] && [ "$DESIGN" == "gcd" ]; then
# Get the directory of the current script
script_dir="$(dirname "${BASH_SOURCE[0]}")"
cd "$script_dir"/../../
latest_image=$(./etc/DockerTag.sh -dev)
echo "ORFS_VERSION=$latest_image" > ./tools/AutoTuner/.env
cd ./tools/AutoTuner
docker compose up --wait
docker compose exec ray-worker bash -c "cd /OpenROAD-flow-scripts/tools/AutoTuner/src/autotuner && \
python3 distributed.py --design gcd --platform asap7 --server 127.0.0.1 --port 10001 \
--config ../../../../flow/designs/asap7/gcd/autotuner.json tune --samples 1"
docker compose down -v --remove-orphans
echo "Running Autotuner plotting smoke test (only once)"
latest_folder=$(ls -dt ./flow/logs/asap7/gcd/test-tune*/ | tail -n 1)
python3 tools/AutoTuner/src/autotuner/utils/plot.py --results_dir $latest_folder
fi

# echo "Running Autotuner smoke algorithm & evaluation test"
# python3 -m unittest tools.AutoTuner.test.smoke_test_algo_eval.${PLATFORM}AlgoEvalSmokeTest.test_algo_eval

# # run this test last (because it modifies current path)
# echo "Running Autotuner remote test"
# if [ "$PLATFORM" == "asap7" ] && [ "$DESIGN_NAME" == "gcd" ]; then
# # Get the directory of the current script
# script_dir="$(dirname "${BASH_SOURCE[0]}")"
# cd "$script_dir"/../../
# latest_image=$(./etc/DockerTag.sh -dev)
# echo "ORFS_VERSION=$latest_image" > ./tools/AutoTuner/.env
# cd ./tools/AutoTuner
# docker compose up --wait
# docker compose exec ray-worker bash -c "cd /OpenROAD-flow-scripts/tools/AutoTuner/src/autotuner && \
# python3 distributed.py --design gcd --platform asap7 --server 127.0.0.1 --port 10001 \
# --config ../../../../flow/designs/asap7/gcd/autotuner.json tune --samples 1"
# docker compose down -v --remove-orphans
# fi
luarss marked this conversation as resolved.
Show resolved Hide resolved

exit $ret
2 changes: 1 addition & 1 deletion flow/test/test_helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ fi
if [ "${RUN_AUTOTUNER}" == "true" ]; then
set +x
echo "Start AutoTuner test."
./test/test_autotuner.sh
./test/test_autotuner.sh $DESIGN_NAME $PLATFORM
set -x
fi

Expand Down
1 change: 1 addition & 0 deletions tools/AutoTuner/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ tensorboard>=2.14.0,<=2.16.2
protobuf==3.20.3
SQLAlchemy==1.4.17
urllib3<=1.26.15
matplotlib==3.10.0
106 changes: 106 additions & 0 deletions tools/AutoTuner/src/autotuner/utils/plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import glob
import json
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import re
import os
import argparse

AT_REGEX = r"variant-AutoTunerBase-([\w-]+)-\w+"
IMG_DIR = "images"


def load_dir(dir: str) -> pd.DataFrame:
# Concatenate progress DFs
df = pd.concat([pd.read_csv(fname) for fname in glob.glob(f"{dir}/*/progress.csv")])

# Concatenate params.json & metrics.json file
params = []
for fname in glob.glob(f"{dir}/*/params.json"):
try:
with open(fname, "r") as f:
_dict = json.load(f)
_dict["trial_id"] = re.search(AT_REGEX, fname).group(1)
with open(fname.replace("params.json", "metrics.json"), "r") as f:
metrics = json.load(f)
ws = metrics["finish"]["timing__setup__ws"]
metrics["worst_slack"] = ws
_dict.update(metrics)
params.append(_dict)
except Exception as e:
print(f"Error in {fname}: {e}")
continue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep a list of the files that were not processed so we can print them at the very end in order for the error not to be buried in the logs? And should the script fail silently in this case?

Copy link
Contributor Author

@luarss luarss Jan 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And should the script fail silently in this case?

Just to clarify, this exception happens when any Autotuner run does not proceed to the "finish" stage, it does not fail silently

Should we keep a list of the files that were not processed so we can print them at the very end in order for the error not to be buried in the logs?

Sure I can add that.

tmp_df = pd.DataFrame(params)

# Merge all dataframe
df = df.merge(tmp_df, on="trial_id")
return df


def preprocess(df: pd.DataFrame) -> pd.DataFrame:
cols_to_remove = [
"done",
"training_iteration",
"date",
"pid",
"hostname",
"node_ip",
"time_since_restore",
"time_total_s",
"iterations_since_restore",
]
rename_dict = {
"time_this_iter_s": "runtime",
"_SDC_CLK_PERIOD": "clk_period",
"minimum": "qor",
}
df = df.rename(columns=rename_dict)
df = df.drop(columns=cols_to_remove)
df = df[df["qor"] != 9e99]
df["timestamp"] -= df["timestamp"].min()
return df


def plot(df: pd.DataFrame, key: str):
# Plot box plot and time series plot for key
fig, ax = plt.subplots(1, figsize=(15, 10))
ax.scatter(df["timestamp"], df[key])
ax.set_xlabel("Time (s)")
ax.set_ylabel(key)
ax.set_title(f"{key} vs Time")
z = np.polyfit(df["timestamp"], df[key], 1)
p = np.poly1d(z)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

z I assume is the z-axis; what is p?

Copy link
Contributor Author

@luarss luarss Jan 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

z is the np array of polynomial (ax+b) coefficients, and p is the actual polynomial function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then let's use more descriptive names, maybe:
z --> coeff
p --> poly_func

ax.plot(
df["timestamp"], p(df["timestamp"]), "r--", label=f"y={z[0]:.2f}x+{z[1]:.2f}"
)
ax.legend()
fig.savefig(f"images/{key}.png")

plt.figure(figsize=(15, 10))
plt.boxplot(df[key])
plt.ylabel(key)
plt.title(f"{key} Boxplot")
plt.savefig(f"images/{key}-boxplot.png")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also parametrize the output folder to save images from CI into the reports folder for the design. This way it will be automatically saved to the build artifacts.



def main(results_dir: str):
# Default: saves to <REPO_ROOT>/images. Change the IMG_DIR above.
os.makedirs(IMG_DIR, exist_ok=True)
df = load_dir(results_dir)
df = preprocess(df)
keys = ["qor", "runtime", "clk_period", "worst_slack"]
for key in keys:
plot(df, key)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Plot AutoTuner results.")
parser.add_argument(
"--results_dir",
type=str,
help="Directory containing the results.",
required=True,
)
args = parser.parse_args()
main(args.results_dir)
Loading