Fix some tests and refactor simulation script

This commit is contained in:
2024-03-04 12:15:28 +01:00
committed by Derek Christ
parent be1807e9b0
commit ecf9127faa
8 changed files with 104 additions and 137 deletions

View File

@@ -111,7 +111,7 @@
"PRPDEN": 1, "PRPDEN": 1,
"REFPDEN": 1, "REFPDEN": 1,
"RTRS": 1, "RTRS": 1,
"clkMhz": 533 "tCK": 1877
} }
}, },
"simconfig": { "simconfig": {

View File

@@ -1,22 +1,22 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os from pathlib import Path
import matplotlib.pyplot as plt
from dataclasses import dataclass from dataclasses import dataclass
from simulation import ( from simulation import (
Simulation, Simulation,
Configuration, ConfigTokens,
get_options_from_args, get_argparser,
get_options,
simulation_results, simulation_results,
) )
script_directory = os.path.dirname(os.path.abspath(__file__)) import matplotlib.pyplot as plt
base_config = os.path.join(script_directory, "example_ddr3_simulations.json")
SCRIPT_DIRECTORY = Path(__file__).parent.resolve()
DEFAULT_BASE_CONFIG = SCRIPT_DIRECTORY / "example_ddr3_simulations.json"
simulations: list[Simulation] = [] simulations: list[Simulation] = []
@dataclass(frozen=True) @dataclass(frozen=True)
class SchedulerBuffer: class SchedulerBuffer:
buffer: str buffer: str
@@ -35,7 +35,7 @@ for refresh_policy in refresh_policies:
for pattern in patterns: for pattern in patterns:
PAGE_POLICY = "Open" if pattern == "sequential" else "Closed" PAGE_POLICY = "Open" if pattern == "sequential" else "Closed"
config = Configuration( config_tokens = ConfigTokens(
f"{refresh_policy}_{scheduler_buffer.buffer}_{scheduler_buffer.size}_{pattern}", f"{refresh_policy}_{scheduler_buffer.buffer}_{scheduler_buffer.size}_{pattern}",
{ {
"refresh_policy": refresh_policy, "refresh_policy": refresh_policy,
@@ -46,69 +46,39 @@ for refresh_policy in refresh_policies:
}, },
) )
simulations.append(Simulation(config)) simulations.append(Simulation(config_tokens))
options = get_options_from_args() parser = get_argparser()
options = get_options(parser.parse_args())
if options.base_config is None: if options.base_config is None:
options.base_config = base_config options.base_config = DEFAULT_BASE_CONFIG
dataframe = simulation_results(options, simulations) df = simulation_results(options, simulations)
# Generate interesting plots! # Generate interesting plots!
for refresh_policy in refresh_policies: for name, data in df.group_by("refresh_policy", "scheduler_buffer_size", maintain_order=True):
refresh_policy_filter = dataframe["refresh_policy"] == refresh_policy plt.figure(figsize=(8.0, 12.0))
for scheduler_buffer in scheduler_buffers: plt.bar(
scheduler_buffer_filter = ( "pattern",
dataframe["scheduler_buffer"] == scheduler_buffer.buffer "bandwidth",
) data=data,
scheduler_buffer_size_filter = ( )
dataframe["scheduler_buffer_size"] == scheduler_buffer.size
)
filters = ( # Plot MAX bar
refresh_policy_filter plt.bar(
& scheduler_buffer_filter "MAX",
& scheduler_buffer_size_filter "max_bandwidth",
) data=data,
color="darkgray",
)
TEMP_DATAFRAME = dataframe[filters] plt.title(name)
plt.axis(ymin=0, ymax=120)
plt.xlabel("Access Pattern")
plt.ylabel("Bandwidth [Gb/s]")
for pattern in patterns: plt.savefig(options.out_dir / f"{name}.png")
pattern_filter = dataframe["pattern"] == pattern plt.close()
plt.figure(figsize=(8.0, 12.0))
filters = (
refresh_policy_filter
& scheduler_buffer_filter
& scheduler_buffer_size_filter
)
TEMP_DATAFRAME = dataframe[filters]
plt.bar(
"pattern",
"bandwidth",
data=TEMP_DATAFRAME,
)
# Plot MAX line
plt.bar(
"MAX",
"max_bandwidth",
data=TEMP_DATAFRAME,
color="darkgray",
)
title = f"{refresh_policy} {scheduler_buffer.buffer} {scheduler_buffer.size}"
plt.title(title)
plt.axis(ymin=0, ymax=120)
plt.xlabel("Access Pattern")
plt.ylabel("Bandwidth [Gb/s]")
plt.savefig(f"{options.out_dir}/{title}.png")
plt.close()

View File

@@ -1,3 +1,3 @@
tqdm tqdm
pandas polars
matplotlib matplotlib

View File

@@ -1,45 +1,34 @@
import argparse import argparse
import subprocess
import sys
import sqlite3
import json import json
import os import os
import re import re
import sqlite3
from pathlib import Path import subprocess
from dataclasses import dataclass, fields import sys
from typing import Optional from dataclasses import dataclass, field, fields
from multiprocessing.pool import ThreadPool from multiprocessing.pool import ThreadPool
from tqdm import tqdm from pathlib import Path
import pandas as pd import polars as pl
from tqdm import tqdm
sys.path.append("extensions/apps/traceAnalyzer/scripts") sys.path.append("extensions/apps/traceAnalyzer/scripts")
from metrics import ( from metrics import (average_response_latency_in_ns,
average_response_latency_in_ns, max_response_latency_in_ns, maximum_data_rate,
max_response_latency_in_ns, memory_active_in_percent)
memory_active_in_percent,
maximum_data_rate,
)
@dataclass @dataclass
class Options: class Options:
dramsys: Path dramsys: Path | None
override: bool override: bool
out_dir: Path out_dir: Path
simulate: bool simulate: bool
metrics: bool metrics: bool
base_config: Path | None base_config: Path | None
resource_dir: Path | None = None resource_dir: Path | None
jobs: int | None = None jobs: int | None
@dataclass(frozen=True)
class SubConfig:
name: str
parameters: dict[str, str]
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -53,26 +42,32 @@ class Statistics:
@dataclass(frozen=True) @dataclass(frozen=True)
class Configuration: class ConfigTokens:
name: str name: str
tokens: dict[str, str | int] tokens: dict[str, str | int]
@dataclass @dataclass
class Simulation: class Simulation:
config: Configuration config_tokens: ConfigTokens
directory: Optional[str] = None directory: Path | None = None
statistics: Optional[list[Statistics]] = None statistics: list[Statistics] = field(default_factory=list)
def run_dramsys(dramsys: Path, simulation_dir: Path, resource_dir: Path | None): def run_dramsys(dramsys: Path, simulation_dir: Path, resource_dir: Path | None):
with open(f"{simulation_dir}/out.txt", "w", encoding="utf-8") as output_file: with open(simulation_dir / "stdout.txt", "w", encoding="utf-8") as output_file:
command = [dramsys.absolute(), "config.json"] command = [dramsys.absolute().as_posix(), "config.json"]
if resource_dir is not None: if resource_dir is not None:
command.append(resource_dir) command.append(resource_dir.absolute().as_posix())
subprocess.run(command, cwd=simulation_dir, stdout=output_file, check=True) subprocess.run(
command,
cwd=simulation_dir,
stdout=output_file,
stderr=output_file,
check=True,
)
def calculate_simulation_metrics(simulation: Simulation): def calculate_simulation_metrics(simulation: Simulation):
@@ -131,12 +126,13 @@ def simulate(
work_item: WorkItem, work_item: WorkItem,
): ):
simulation_dir = work_item.simulation.directory simulation_dir = work_item.simulation.directory
assert simulation_dir
json_config = None json_config = None
with open(work_item.base_config, encoding="utf-8") as config_file: with open(work_item.base_config, encoding="utf-8") as config_file:
config_string = config_file.read() config_string = config_file.read()
config_string = replace_placeholders( config_string = replace_placeholders(
config_string, work_item.simulation.config.tokens config_string, work_item.simulation.config_tokens.tokens
) )
json_config = json.loads(config_string) json_config = json.loads(config_string)
@@ -151,13 +147,13 @@ def simulate(
calculate_simulation_metrics(work_item.simulation) calculate_simulation_metrics(work_item.simulation)
def generate_dataframe(simulations: list[Simulation], out_dir: str) -> pd.DataFrame: def generate_dataframe(simulations: list[Simulation], out_dir: Path) -> pl.DataFrame:
# Pack results in a panda dataframe # Pack results in dataframe
labels = ["name", "channel"] labels = ["name", "channel"]
statistic_labels = list(map(lambda field: field.name, fields(Statistics))) statistic_labels = [field.name for field in fields(Statistics)]
# Get one simulation... # Get one simulation...
config_keys, _ = zip(*simulations[0].config.tokens.items()) config_keys = simulations[0].config_tokens.tokens.keys()
labels.extend(config_keys) labels.extend(config_keys)
labels.extend(statistic_labels) labels.extend(statistic_labels)
@@ -165,15 +161,15 @@ def generate_dataframe(simulations: list[Simulation], out_dir: str) -> pd.DataFr
entries = [] entries = []
for simulation in simulations: for simulation in simulations:
_, config_values = zip(*simulation.config.tokens.items()) config_values = simulation.config_tokens.tokens.values()
for stat in simulation.statistics: for stat in simulation.statistics:
channel_pattern = re.compile("(?<=ch)[0-9]+") m = re.search("(?<=ch)[0-9]+", stat.filename)
channel = int(channel_pattern.search(stat.filename)[0]) channel = m.group(0) if m else -1
entries.append( entries.append(
[ [
simulation.config.name, simulation.config_tokens.name,
channel, channel,
*config_values, *config_values,
stat.filename, stat.filename,
@@ -185,17 +181,17 @@ def generate_dataframe(simulations: list[Simulation], out_dir: str) -> pd.DataFr
] ]
) )
dataframe = pd.DataFrame(data=entries, columns=labels) df = pl.DataFrame(data=entries, schema=labels)
dataframe.to_csv(f"{out_dir}/statistics.csv", sep=";") df.write_csv(out_dir / "statistics.csv")
return dataframe return df
def populate_simulation_directories( def populate_simulation_directories(
simulations: list[Simulation], out_dir: str, override: bool simulations: list[Simulation], out_dir: Path, override: bool
): ):
for simulation in simulations: for simulation in simulations:
simulation_dir = Path(f"{out_dir}/simulations/{simulation.config.name}") simulation_dir = out_dir / "simulations" / simulation.config_tokens.name
try: try:
simulation_dir.mkdir(parents=True, exist_ok=override) simulation_dir.mkdir(parents=True, exist_ok=override)
@@ -209,8 +205,8 @@ def populate_simulation_directories(
def calculate_metrics( def calculate_metrics(
simulations: list[Simulation], out_dir: str, jobs: int | None simulations: list[Simulation], out_dir: Path, jobs: int | None
) -> pd.DataFrame: ) -> pl.DataFrame:
populate_simulation_directories(simulations, out_dir, override=True) populate_simulation_directories(simulations, out_dir, override=True)
with ThreadPool(jobs) as thread_pool: with ThreadPool(jobs) as thread_pool:
@@ -223,13 +219,17 @@ def calculate_metrics(
return generate_dataframe(simulations, out_dir) return generate_dataframe(simulations, out_dir)
def run_simulations(simulations: list[Simulation], options: Options) -> pd.DataFrame: def run_simulations(simulations: list[Simulation], options: Options) -> pl.DataFrame:
if len(simulations) == 0: if len(simulations) == 0:
print("Must specify at least one simulation configuration!") print("Must specify at least one simulation configuration!")
sys.exit(-1) sys.exit(-1)
if options.dramsys is None:
print("Must specify DRAMSys executable!")
sys.exit(-1)
if options.base_config is None: if options.base_config is None:
print("Must specify a base config") print("Must specify a base config!")
sys.exit(-1) sys.exit(-1)
print("Create simulation directories...") print("Create simulation directories...")
@@ -251,9 +251,11 @@ def run_simulations(simulations: list[Simulation], options: Options) -> pd.DataF
return calculate_metrics(simulations, options.out_dir, options.jobs) return calculate_metrics(simulations, options.out_dir, options.jobs)
def get_options_from_args() -> Options: def get_argparser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="DRAMSys simulation utility") parser = argparse.ArgumentParser(description="DRAMSys simulation utility")
parser.add_argument("dramsys", type=Path, help="path to the DRAMSys executable") parser.add_argument(
"dramsys", type=Path, nargs="?", help="path to the DRAMSys executable"
)
parser.add_argument( parser.add_argument(
"--simulate", "--simulate",
default=False, default=False,
@@ -269,6 +271,7 @@ def get_options_from_args() -> Options:
parser.add_argument( parser.add_argument(
"-f", "-f",
"--force", "--force",
dest="override",
default=False, default=False,
action="store_true", action="store_true",
help="force override existing simulation artifacts", help="force override existing simulation artifacts",
@@ -298,23 +301,16 @@ def get_options_from_args() -> Options:
help="run N jobs in parallel", help="run N jobs in parallel",
) )
arguments = parser.parse_args() return parser
return Options(
arguments.dramsys, def get_options(args: argparse.Namespace) -> Options:
arguments.force, return Options(**vars(args))
arguments.out_dir,
arguments.simulate,
arguments.metrics,
arguments.base_config,
arguments.resource_dir,
arguments.jobs,
)
def simulation_results( def simulation_results(
options: Options, options: Options,
simulations: list[Simulation], simulations: list[Simulation],
) -> pd.DataFrame: ) -> pl.DataFrame:
if options.simulate: if options.simulate:
return run_simulations(simulations, options) return run_simulations(simulations, options)
@@ -323,9 +319,10 @@ def simulation_results(
print("Summarizing simulation results in statistics.csv...") print("Summarizing simulation results in statistics.csv...")
statistics_file = f"{options.out_dir}/statistics.csv" statistics_file = options.out_dir / "statistics.csv"
if not os.path.isfile(statistics_file): if not os.path.isfile(statistics_file):
print("Run the simulations first to generate simulation artifacts") print("Run the simulations first to generate simulation artifacts")
sys.exit(-1) sys.exit(-1)
return pd.read_csv(f"{options.out_dir}/statistics.csv", sep=";") return pl.read_csv(statistics_file)

View File

@@ -136,7 +136,7 @@
"WTR_L": 16, "WTR_L": 16,
"WTR_S": 4, "WTR_S": 4,
"XP": 12, "XP": 12,
"clkMhz": 1600 "tCK": 625
} }
}, },
"simconfig": { "simconfig": {

View File

@@ -71,7 +71,7 @@
"ACTPDEN": 2, "ACTPDEN": 2,
"PRPDEN": 2, "PRPDEN": 2,
"REFPDEN": 2, "REFPDEN": 2,
"clkMhz": 1600 "tCK": 625
} }
} }
} }

View File

@@ -164,7 +164,7 @@ DRAMSys::Config::MemSpec ConfigurationTest::createMemSpec()
{"REFI1", 6240}, {"REFI2", 3120}, {"REFISB", 1560}, {"REFSBRD_slr", 48}, {"REFI1", 6240}, {"REFI2", 3120}, {"REFISB", 1560}, {"REFSBRD_slr", 48},
{"REFSBRD_dlr", 0}, {"RTRS", 2}, {"CPDED", 8}, {"PD", 12}, {"REFSBRD_dlr", 0}, {"RTRS", 2}, {"CPDED", 8}, {"PD", 12},
{"XP", 12}, {"ACTPDEN", 2}, {"PRPDEN", 2}, {"REFPDEN", 2}, {"XP", 12}, {"ACTPDEN", 2}, {"PRPDEN", 2}, {"REFPDEN", 2},
{"clkMhz", 1600}, {"tCK", 625},
}}}; }}};
return {memArchitectureSpec, return {memArchitectureSpec,
@@ -446,7 +446,7 @@ TEST_F(ConfigurationTest, MemSpec)
"WTR_L": 16, "WTR_L": 16,
"WTR_S": 4, "WTR_S": 4,
"XP": 12, "XP": 12,
"clkMhz": 1600 "tCK": 625
} }
} }
} }