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

Use Ramble modifier to fill in allocation variables #195

Merged
merged 70 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
fe09c79
initial modifier
scheibelp Apr 2, 2024
4b1ad5e
partial work
scheibelp Apr 3, 2024
98eba09
dont mess with locals()
scheibelp Apr 3, 2024
068281e
changed variable name
scheibelp Apr 3, 2024
d33bc90
Able to proceed with Ramble#452; that uncovered a str-to-int conversi…
scheibelp Apr 3, 2024
a5941a0
remove debugging statements
scheibelp Apr 3, 2024
aa0001d
remove filled-in variable from experiment name
scheibelp Apr 3, 2024
12f092f
intermediate work on also getting modifier to generate batch submissions
scheibelp Apr 3, 2024
9642b6f
finished up work that allows the modifier to define allocations as well
scheibelp Apr 4, 2024
4bed118
style fix
scheibelp Apr 4, 2024
c25ad66
refactor away from context manager
scheibelp Apr 4, 2024
10f7d77
handle flux directives and timeout
scheibelp Apr 4, 2024
e63a142
remove unused import
scheibelp Apr 4, 2024
a39dbce
add references for clarification
scheibelp Apr 4, 2024
b8ff318
n_threads is not special; also rename it to n_omp_threads_per_task
scheibelp Apr 4, 2024
67c0c15
Merge branch 'develop' into feature/allocation-modifier
scheibelp Apr 10, 2024
3cdb92d
intermediate work
scheibelp Apr 11, 2024
f43c15e
done with doing placeholder inference based on exceeding max-request …
scheibelp Apr 11, 2024
85c1534
env_var_modification needs mode; not sure 100 percent what that shoul…
scheibelp Apr 12, 2024
18caf43
add n_cores_per_node (different than n_cores_per_rank)
scheibelp Apr 12, 2024
8d4a16d
style edits
scheibelp Apr 12, 2024
327f3f0
there can now be one execute_experiment.tpl
scheibelp Apr 12, 2024
6742b81
removal of all individual execute_experiment.tpl files
scheibelp Apr 12, 2024
1f94ff7
update all system configs except Fugaku and Sierra
scheibelp Apr 12, 2024
2742c4d
update all experiments based on (a) new names and (b) logic that fill…
scheibelp Apr 13, 2024
215ec65
style edit
scheibelp Apr 13, 2024
3534ec5
sierra batch/run cmd options implemented
scheibelp Apr 15, 2024
8e50491
add fugaku batch opt generation logic
scheibelp Apr 15, 2024
2738222
replace variables for Sierra and Fugaku
scheibelp Apr 15, 2024
8d3bd24
consolidate variable accessor logic into single class; add explanator…
scheibelp Apr 15, 2024
f7684c5
syntax error
scheibelp Apr 15, 2024
479b5e2
testing+fixing some issues for fugaku
scheibelp Apr 15, 2024
6fb2e32
typos for sierra
scheibelp Apr 15, 2024
224287d
fix sierra reference errors etc. and recognition of 'queue' as variable
scheibelp Apr 15, 2024
0021951
style fix
scheibelp Apr 15, 2024
1d579f5
apply real values to sys_cpus_per_node/sys_gpus_per_node for LLNL sys…
scheibelp Apr 19, 2024
05dd482
the scheduler used for Sierra is called 'lsf', so use that name
scheibelp Apr 19, 2024
51db624
add basic alias substitution logic (omp_num_threads can be used inste…
scheibelp Apr 19, 2024
cca0288
fix alias issue and add comments
scheibelp Apr 20, 2024
3f60df0
style fix
scheibelp Apr 20, 2024
b4a0f05
set appropriate schedulers
scheibelp Apr 20, 2024
3bac54e
scheduler on Fugaku is called 'pjm'
scheibelp Apr 20, 2024
ba68b83
all experiments need to use the allocation modifier
scheibelp Apr 23, 2024
ed025e7
amg2023 benchmark should not be doing requesting any number of ranks/…
scheibelp Apr 23, 2024
92c614c
logic to set n_ranks based on n_gpus (if the latter is set and the fo…
scheibelp Apr 23, 2024
747e473
handle the most common case of gpu specification for Flux, not using …
scheibelp Apr 23, 2024
0d72a13
add docstring
scheibelp Apr 23, 2024
a63fa5a
syntax error
scheibelp Apr 23, 2024
cc46e80
style fix
scheibelp Apr 23, 2024
d9f0b1b
Fugaku system description
scheibelp Apr 23, 2024
e1ff889
LUMI system description
scheibelp Apr 23, 2024
1b9ccb0
add reference link
scheibelp Apr 23, 2024
6122960
Piz Daint system description
scheibelp Apr 23, 2024
53a081c
add reference link
scheibelp Apr 23, 2024
301ba79
partial description of Eiger/Alps
scheibelp Apr 23, 2024
44fdb95
proper detection of unset vars; fixed error w/ calculation of n_nodes…
scheibelp Apr 23, 2024
7a71927
Both flux and lsf want gpus_per_rank
scheibelp Apr 23, 2024
564aba8
style fix
scheibelp Apr 23, 2024
59ec13d
more style fixes
scheibelp Apr 23, 2024
a7950b7
restore default nosite config
scheibelp Apr 23, 2024
7f4e1c5
missed converting input param name
scheibelp May 14, 2024
ca86837
saxpy/raja-perf cuda/rocm experiments should just specify the number …
scheibelp May 14, 2024
efb2f3c
add CI checks to exercise the allocation modifier logic (use --dry-ru…
scheibelp May 14, 2024
3f4113b
sys_cpus_per_node -> sys_cores_per_node
scheibelp May 15, 2024
30d7aa0
intercept divide-by-zero error
scheibelp May 15, 2024
5b7a521
clarify we currently only support lrun and not jsrun
scheibelp May 15, 2024
14681da
style fix
scheibelp May 15, 2024
13a0afa
Merge branch 'develop' into feature/allocation-modifier
pearce8 May 26, 2024
b4ac027
Merge branch 'develop' into feature/allocation-modifier
pearce8 Jun 5, 2024
ae8ec2d
Merge branch 'develop' into feature/allocation-modifier
pearce8 Jun 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions configs/nosite-x86_64/variables.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
# SPDX-License-Identifier: Apache-2.0

variables:
batch_time: ''
mpi_command: 'mpirun -n {n_nodes} -c {n_ranks} --oversubscribe'
batch_submit: '{execute_experiment}'
batch_nodes: ''
batch_ranks: ''
batch_timeout: ''
mpi_command: 'mpirun -n {n_ranks} --oversubscribe'
pearce8 marked this conversation as resolved.
Show resolved Hide resolved
n_ranks: '7'
n_nodes: '7'
scheibelp marked this conversation as resolved.
Show resolved Hide resolved
n_threads: '7'
scheibelp marked this conversation as resolved.
Show resolved Hide resolved
scheduler: "slurm"
batch_submit: "placeholder"
mpi_command: "placeholder"
pearce8 marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 1 addition & 3 deletions experiments/saxpy/openmp/execute_experiment.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
#
# SPDX-License-Identifier: Apache-2.0

{batch_nodes}
{batch_ranks}
{batch_timeout}
{allocation_directives}
pearce8 marked this conversation as resolved.
Show resolved Hide resolved

cd {experiment_run_dir}

Expand Down
9 changes: 6 additions & 3 deletions experiments/saxpy/openmp/ramble.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ ramble:
install: '--add --keep-stage'
concretize: '-U -f'

modifiers:
- name: allocation

applications:
saxpy:
workloads:
Expand All @@ -23,11 +26,11 @@ ramble:
set:
OMP_NUM_THREADS: '{n_threads}'
variables:
n_ranks: '8'
n_cores_per_task: '1'
experiments:
saxpy_{n}_{n_nodes}_{n_ranks}_{n_threads}:
saxpy_{n}_{n_nodes}_{n_threads}:
variables:
processes_per_node: ['8', '4']
n_ranks_per_node: ['8', '4']
n_nodes: ['1', '2']
n_threads: ['2', '4']
n: ['512', '1024']
Expand Down
204 changes: 204 additions & 0 deletions modifiers/allocation/modifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# Copyright 2023 Lawrence Livermore National Security, LLC and other
# Benchpark Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: Apache-2.0

import math
from enum import Enum
from ramble.modkit import *


class AllocOpt(Enum):
# Experiment resource requests
N_RANKS = 1
N_NODES = 2
N_CORES_PER_TASK = 3
scheibelp marked this conversation as resolved.
Show resolved Hide resolved
N_THREADS = 4 # number of OMP threads per task
N_RANKS_PER_NODE = 5
N_GPUS = 6

# Descriptions of resources available on systems
GPUS_PER_NODE = 100
scheibelp marked this conversation as resolved.
Show resolved Hide resolved
CPUS_PER_NODE = 101
scheibelp marked this conversation as resolved.
Show resolved Hide resolved

# Scheduler identification and other high-level instructions
SCHEDULER = 200
scheibelp marked this conversation as resolved.
Show resolved Hide resolved
TIMEOUT = 201 # This is assumed to be in minutes
pearce8 marked this conversation as resolved.
Show resolved Hide resolved

@staticmethod
def as_type(enumval, input):
if enumval == AllocOpt.SCHEDULER:
parsed_str = str(input)
if parsed_str == SENTINEL_UNDEFINED_VALUE_STR:
return None
else:
return parsed_str
else:
parsed_int = int(input)
if parsed_int == SENTINEL_UNDEFINED_VALUE_INT:
return None
else:
return parsed_int


# If we see this value, we assume the user wants us to substitute it.
# Ramble expects n_ranks and n_nodes to be set to *something* so even
# if we want to fill those in ourselves, we have to supply something.
SENTINEL_UNDEFINED_VALUE_INT = 7
SENTINEL_UNDEFINED_VALUE_STR = "placeholder"


def defined_allocation_options(expander):
"""For each possible allocation option, check if it was filled in by
Ramble.
"""
defined = {}
for alloc_opt in AllocOpt:
var_def = expander.expand_var(f"{{{alloc_opt.name.lower()}}}")
scheibelp marked this conversation as resolved.
Show resolved Hide resolved
try:
val = AllocOpt.as_type(alloc_opt, var_def)
except ValueError:
continue

if val is not None:
defined[alloc_opt] = val

return defined


class AttrDict(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self["_attributes"] = set()

def __getattr__(self, *args, **kwargs):
return self.__getitem__(*args, **kwargs)

def __setattr__(self, *args, **kwargs):
self.__setitem__(*args, **kwargs)

def __delattr__(self, *args, **kwargs):
self.__delitem__(*args, **kwargs)

def __setitem__(self, key, value):
super().__setitem__(key, value)
if key != "_attributes":
self["_attributes"].add(key)

def defined(self):
return list((k, self[k]) for k in self["_attributes"])

@staticmethod
def from_predefined_variables(var_defs):
v = AttrDict()
for alloc_opt in AllocOpt:
setattr(v, alloc_opt.name.lower(), var_defs.get(alloc_opt, None))

return v


class Allocation(BasicModifier):

name = "allocation"

tags("infrastructure")

# Currently there is only one mode. The only behavior supported right
# now is to attempt to request "enough" resources for a given
# request (e.g. to make sure we request enough nodes, assuming we
# know how many CPUs we want)"
mode("standard", description="Standard execution mode for allocation")
default_mode("standard")

def inherit_from_application(self, app):
super().inherit_from_application(app)

var_defs = defined_allocation_options(app.expander)
v = AttrDict.from_predefined_variables(var_defs)

# Calculate unset values (e.g. determine n_nodes if not set)
self.determine_allocation(v)

self.determine_scheduler_instructions(v)

# Definitions
for var, val in v.defined():
app.define_variable(var, str(val))

def determine_allocation(self, v):
if not v.n_ranks:
if v.n_ranks_per_node and v.n_nodes:
v.n_ranks = v.n_nodes * v.n_ranks_per_node

if not v.n_nodes:
if v.n_ranks:
cpus_request_per_task = 1
multi_cpus_per_task = v.n_cores_per_task or v.n_threads or 0
cpus_request_per_task = max(multi_cpus_per_task, 1)
scheibelp marked this conversation as resolved.
Show resolved Hide resolved
tasks_per_node = math.floor(cpus_per_node / cpus_request_per_task)
v.n_nodes = math.ceil(v.n_ranks / tasks_per_node)
if v.n_gpus and v.gpus_per_node:
v.n_nodes = math.ceil(v.n_gpus / float(v.gpus_per_node))

if not v.n_threads:
v.n_threads = 1

def slurm_instructions(self, v):
srun_opts = []
sbatch_opts = [] # opts just for the sbatch script

if v.n_ranks:
srun_opts.append(f"-n {v.n_ranks}")
if v.n_gpus:
srun_opts.append(f"--gpus {v.n_gpus}")
if v.n_nodes:
srun_opts.append(f"-N {v.n_nodes}")

if v.timeout:
sbatch_opts.append(f"--time {v.timeout}")

sbatch_directives = list(f"#SBATCH {x}" for x in (srun_opts + sbatch_opts))

v.mpi_command = f"srun {' '.join(srun_opts)}"
v.batch_submit = "sbatch {execute_experiment}"
v.allocation_directives = "\n".join(sbatch_directives)

def flux_instructions(self, v):
cmd_opts = []
batch_opts = []

if v.n_ranks:
cmd_opts.append(f"-n {v.n_ranks}")
if v.n_nodes:
cmd_opts.append(f"-N {v.n_nodes}")

if v.timeout:
batch_opts.append("-t {v.timeout}m")

batch_directives = list(f"# flux: {x}" for x in (cmd_opts + batch_opts))

v.mpi_command = f"flux run {' '.join(cmd_opts)}"
v.batch_submit = "flux batch {execute_experiment}"
v.allocation_directives = "\n".join(batch_directives)

def mpi_instructions(self, v):
v.mpi_command = f"mpirun -n {v.n_ranks} --oversubscribe"
v.batch_submit = "{execute_experiment}"
v.allocation_directives = ""

def determine_scheduler_instructions(self, v):
handler = {
"slurm": self.slurm_instructions,
"flux": self.flux_instructions,
"mpi": self.mpi_instructions,
}
if v.scheduler not in handler:
raise ValueError(
f"scheduler ({v.scheduler}) must be one of : "
+ " ".join(handler.keys())
)

if not v.timeout:
v.timeout = 120

handler[v.scheduler](v)
Loading