-
Notifications
You must be signed in to change notification settings - Fork 24
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
Feature/experiment scaling #375
base: develop
Are you sure you want to change the base?
Conversation
variant( | ||
"strong-scaling-factor", | ||
default="2", | ||
description="Strong-scaling factor (factor by which to increase resources)", | ||
) | ||
|
||
variant( | ||
"strong-scaling-num-exprs", | ||
default="4", | ||
description="Number of strong-scaling experiments", | ||
) | ||
|
||
variant( | ||
"weak-scaling-factor", | ||
default="2", | ||
description="Weak-scaling factor (factor by which to increase resources and problem sizes)", | ||
) | ||
|
||
variant( | ||
"weak-scaling-num-exprs", | ||
default="4", | ||
description="Number of weak-scaling experiments", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to implement conditional variants so we can make this clearer. This could be
variant("scaling", default="none", values=("none", "strong", "weak"), description="...")
variant("scaling-factor", default=2, values=lambda x: x >= 2, when="scaling=weak", description="...")
variant("scaling-factor", default=2, values=lambda x: x >= 2, when="scaling=strong", description="...")
variant("scaling-iterations", default=4, values=lambda x: x > 1, when="scaling=weak", description="...")
variant("scaling-iterations", default=4, values=lambda x: x > 1, when="scaling=strong", description="...")
And with a little additional syntax beyond that we could get rid of the duplicate lines.
from benchpark.directives import variant | ||
|
||
|
||
class Scaling(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would call this ScalingExperiment
instead of just Scaling
, I think it's a bit clearer that way
def generate_strong_scaling_parameters(self, initial_resource_list: list): | ||
scaling_factor = int(self.spec.variants["strong-scaling-factor"][0]) | ||
num_exprs = int(self.spec.variants["strong-scaling-num-exprs"][0]) - 1 | ||
round_robin_order = self.compute_round_robin_order(initial_resource_list) | ||
resource_list = [[x] for x in initial_resource_list] | ||
|
||
while num_exprs > 0: | ||
for idx in round_robin_order: | ||
for i, r in enumerate(resource_list): | ||
r.append(r[-1]*scaling_factor if i == idx else r[-1]) | ||
num_exprs=num_exprs-1 | ||
if not num_exprs: | ||
break | ||
return resource_list |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this method can be simplified slightly
def generate_strong_scaling_parameters(self, initial_resource_list: list): | |
scaling_factor = int(self.spec.variants["strong-scaling-factor"][0]) | |
num_exprs = int(self.spec.variants["strong-scaling-num-exprs"][0]) - 1 | |
round_robin_order = self.compute_round_robin_order(initial_resource_list) | |
resource_list = [[x] for x in initial_resource_list] | |
while num_exprs > 0: | |
for idx in round_robin_order: | |
for i, r in enumerate(resource_list): | |
r.append(r[-1]*scaling_factor if i == idx else r[-1]) | |
num_exprs=num_exprs-1 | |
if not num_exprs: | |
break | |
return resource_list | |
def generate_strong_scaling_parameters(self, initial_resource_list: list): | |
scaling_factor = int(self.spec.variants["strong-scaling-factor"][0]) | |
num_exprs = int(self.spec.variants["strong-scaling-num-exprs"][0]) | |
round_robin_order = self.compute_round_robin_order(initial_resource_list) | |
resource_list = [[x] for x in initial_resource_list] | |
for i in range(num_exprs - 1): | |
idx = (i + round_robin_order[0]) * len(initial_resource_list) | |
for r_idx, resource in enumerate(resource_list): | |
next_value = resource[-1] * scaling_value if r_idx == idx else resource[-1] | |
resource.append(next_value) | |
return resource_list |
break | ||
return resource_list | ||
|
||
def generate_weak_scaling_parameters(self, initial_resource_list: list, initial_problem_size_list: list): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's necessarily safe for us to assume that the resource requirements and the problem size are expressed in such neatly compatible ways. I think we should probably assume these could be different dimensions (e.g. resource list is a scalar, problem size is 3D) and round-robin them each separately.
The variant logic can be simplified once we merge #379 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any major issues, but have a few suggestions
from benchpark.directives import variant | ||
|
||
|
||
class ScalingExperiment(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(no change needed) I originally thought it would be problematic that this defines variant()
s but does not inherit Experiment
, but I think that as long as any package that inherits this also inherits Experiment
, there will be no issue with this.
That being said, IIRC one of your arguments about not inheriting Experiment
was a concern with overriding methods in Experiment
, which is technically still possible regardless of whether ScalingExperiment
inherits Experiment
.
) | ||
|
||
# input parameters: | ||
# 1. input_variables: dict[str, int | tuple(str), list[int]]. Input variables |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is another way to express:
input_variables key value pairs are either of type str: int or tuple(str): list(int)
I can say this mypy-like syntax of expression is confusing (e.g. if list(int)
values always have tuple(str)
keys and never str
keys, this isn't how it can be expressed in mypy
). I recommend replacing what you have on this line with the quoted explanation that you used elsewhere.
If that's not the intended meaning, can you explain how this input differs?
# the number of dimensions in an (ascending) round-robin order | ||
# | ||
# output: | ||
# scaling_order: list[int]. list of num_exprs values, one for each dimension, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
num_exprs
is not an input to this particular function, I think this is a list of indices.
# output: | ||
# output_variables: dict[str, int | list[int]]. num_exprs values for each | ||
# dimension of the input variable scaled by the scaling_factor according to the | ||
# scaling policy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think example inputs and outputs would be useful in the comments/doc for this function, which you could adapt from https://github.com/LLNL/benchpark/blob/develop/experiments/amg2023/openmp/ramble.yaml, for example:
input:
[[10], [10], [10]]
[[2], [2], [2]]
output:
nx: ['10', '20', '20']
ny: ['10', '10', '20']
nz: ['10', '10', '10']
px: ['2', '4', '4']
py: ['2', '2', '4']
pz: ['2', '2', '2']
if p_idx | ||
== scaling_order_index[exp_num % len(scaling_order_index)] | ||
else p_val[-1] | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO a comment like:
Take initial parameterized vector for experiment, for each experiment after the first, scale one dimension of that vector by the scaling factor; cycle through the dimensions in round-robin fashion.
would be useful here
nx = "nx" | ||
ny = "ny" | ||
nz = "nz" | ||
num_procs = f"{{{px}}} * {{{py}}} * {{{pz}}}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(minor) I'm assuming you are using px
as an abbreviation for "px"
, and not because it's value might change: in that case"{px} * {py} * {pz}"
is simpler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@scheibelp This will be removed once I add variants for all px
, py
and pz
val = input_variables[next(iter(input_variables))] | ||
min_dim = val.index(min(val)) if isinstance(val, list) else 0 | ||
|
||
return [(min_dim + i) % n_dims for i in range(n_dims)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(suggestion) Since this doesn't sort (e.g. doesn't make sure that the second-lowest dimension is handled second), IMO this would be simpler if it just returned min_dim
.
default="4", | ||
values=int, | ||
description="Number of experiments to be generated", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#379 was merged, but these are not conditioned on when=
(as mentioned in #375 (comment)). I think you can effectively turn scaling off by setting scaling-iterations=1
, although it still might be clearer to use when
.
variant( | ||
"scaling-factor", | ||
default="2", | ||
values=int, | ||
description="Factor by which to scale values of problem variables", | ||
) | ||
|
||
variant( | ||
"scaling-iterations", | ||
default="4", | ||
values=int, | ||
description="Number of experiments to be generated", | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@scheibelp I have incorporated your earlier suggestions into the scaling methods. I also moved the entire scaling implementation directly into the base Experiment class. Please review and comment this is a good approach. Especially the fact that the base Experiment class will have two variants scaling-factor
and scaling-iterations
defined in it
# dimension of the input variable scaled by the scaling_factor according to the | ||
# scaling policy | ||
def scale_experiment_variables( | ||
self, input_variables, scaling_factor, num_exprs, scaling_variable=None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @dyokelson, I've added the option to specify a scaling_variable to the experiment (all variables in input_variables
will be scaled according to the ordering defined on scaling_variable
input_params, | ||
int(self.spec.variants["scaling-factor"][0]), | ||
int(self.spec.variants["scaling-iterations"][0]), | ||
scaling_variable, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dyokelson Usage of the scaling_variable
@pearce8 @scheibelp If the changes in the PR are acceptable, we can take this to main. I can make further changes in a separate PR |
Draft implementation of the scaling functionality for benchmarks. Currently only strong scaling is implemented. Works for amg2023/openmp
bin/benchpark experiment init --dest ./experiments kripke programming_model=openmp scaling=strong strong-scaling-factor=2 strong-scaling-num-exprs=3
and kripke/openmp
bin/benchpark experiment init --dest ./experiments amg2023 programming_model=openmp experiment=strong strong-scaling-factor=2 strong-scaling-num-exprs=4