-
Notifications
You must be signed in to change notification settings - Fork 1
Block redistribution #584
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
Merged
Merged
Block redistribution #584
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
71f01ea
Dumbest possible redistribution working.
0b53ccd
Generalize local redistribution to handle slices.
6abb248
Generalize plan creation for block redistribution.
04656ec
Cleanup imports.
3857d02
Use tuple_intersection().
5120cab
Fix after rebase.
fb74fe9
Adds redistribution test for many-to-one.
0f40f84
Multidimensional block redistribution working.
868d2bb
More tests and debugging of n-dimensional redistribution.
a027b77
Test for local-only operation in redistribution.
e846519
More general block redistribution working.
e318fb1
Fix API changes after rebase.
d193354
Merge branch 'master' into feature/block-redistribution
244031f
Add default value back to `Context.apply()`.
4f341b7
WIP: on our way towards redistribution working.
14af63a
Uncomment test, skip it instead.
6470fd4
Fix `reduce` imports for Py3.
d3ef7ae
More general redistribution.
a67c843
Cleanup a bit.
57d5d05
Merge branch 'master' into feature/block-redistribution
e7b1a23
Fix bug with NoDist dimensions and redistribution.
f6f4a5c
Put in TODOs for cleanup / refactoring.
d3d9162
Merge branch 'master' into feature/block-redistribution
3b2d540
Merge branch 'master' into feature/block-redistribution
d164ed8
Redistribution cleanup and refactoring.
7d5e431
Refactor redistribution tests.
98ad6ea
Merge branch 'master' into feature/block-redistribution
8c8bc3e
Remove `default` argument to apply.
75921e1
Adds docstrings.
1579b1b
Raise `ValueError` if attempting to redistribute to incompatible size.
4d3c805
Raise `NotImplementedError` in some redistribution cases.
f61e439
Fix target renaming in Makefile.
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,7 +41,9 @@ | |
| sanitize_indices, | ||
| _start_stop_block, | ||
| tuple_intersection, | ||
| shapes_from_dim_data_per_rank) | ||
| shapes_from_dim_data_per_rank, | ||
| condense, | ||
| strides_from_shape) | ||
|
|
||
|
|
||
| def _dedup_dim_dicts(dim_dicts): | ||
|
|
@@ -551,7 +553,7 @@ def from_maps(cls, context, maps, targets=None): | |
| self = super(Distribution, cls).__new__(cls) | ||
| self.context = context | ||
| self.targets = sorted(targets or context.targets) | ||
| self.comm = self.context.make_subcomm(self.targets) | ||
| self._comm = None | ||
| self.maps = maps | ||
| self.shape = tuple(m.size for m in self.maps) | ||
| self.ndim = len(self.maps) | ||
|
|
@@ -758,6 +760,12 @@ def __getitem__(self, idx): | |
| def __len__(self): | ||
| return len(self.maps) | ||
|
|
||
| @property | ||
| def comm(self): | ||
| if self._comm is None: | ||
| self._comm = self.context.make_subcomm(self.targets) | ||
| return self._comm | ||
|
|
||
| @property | ||
| def has_precise_index(self): | ||
| """ | ||
|
|
@@ -869,3 +877,140 @@ def view(self, new_dimsize=None): | |
|
|
||
| def localshapes(self): | ||
| return shapes_from_dim_data_per_rank(self.get_dim_data_per_rank()) | ||
|
|
||
| def comm_union(self, *dists): | ||
| """ | ||
| Make a communicator that includes the union of all targets in `dists`. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| dists: sequence of distribution objects. | ||
|
|
||
| Returns | ||
| ------- | ||
| tuple | ||
| First element is encompassing communicator proxy; second is a | ||
| sequence of all targets in `dists`. | ||
|
|
||
| """ | ||
| dist_targets = [d.targets for d in dists] | ||
| all_targets = sorted(reduce(set.union, dist_targets, set(self.targets))) | ||
| return self.context.make_subcomm(all_targets), all_targets | ||
|
|
||
| # ------------------------------------------------------------------------ | ||
| # Redistribution | ||
| # ------------------------------------------------------------------------ | ||
|
|
||
| @staticmethod | ||
| def _redist_intersection_same_shape(source_dimdata, dest_dimdata): | ||
|
|
||
| intersections = [] | ||
| for source_dimdict, dest_dimdict in zip(source_dimdata, dest_dimdata): | ||
|
|
||
| if not (source_dimdict['dist_type'] == | ||
| dest_dimdict['dist_type'] == 'b'): | ||
| raise ValueError("Only 'b' dist_type supported") | ||
|
|
||
| source_idxs = source_dimdict['start'], source_dimdict['stop'] | ||
| dest_idxs = dest_dimdict['start'], dest_dimdict['stop'] | ||
|
|
||
| intersections.append(tuple_intersection(source_idxs, dest_idxs)) | ||
|
|
||
| return intersections | ||
|
|
||
| @staticmethod | ||
| def _redist_intersection_reshape(source_dimdata, dest_dimdata): | ||
| source_flat = global_flat_indices(source_dimdata) | ||
| dest_flat = global_flat_indices(dest_dimdata) | ||
| return _global_flat_indices_intersection(source_flat, dest_flat) | ||
|
|
||
| def get_redist_plan(self, other_dist): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Docstring |
||
| # Get all targets | ||
| all_targets = sorted(set(self.targets + other_dist.targets)) | ||
| union_rank_from_target = {t: r for (r, t) in enumerate(all_targets)} | ||
|
|
||
| source_ranks = range(len(self.targets)) | ||
| source_targets = self.targets | ||
| union_rank_from_source_rank = {sr: union_rank_from_target[st] | ||
| for (sr, st) in | ||
| zip(source_ranks, source_targets)} | ||
|
|
||
| dest_ranks = range(len(other_dist.targets)) | ||
| dest_targets = other_dist.targets | ||
| union_rank_from_dest_rank = {sr: union_rank_from_target[st] | ||
| for (sr, st) in | ||
| zip(dest_ranks, dest_targets)} | ||
|
|
||
| source_ddpr = self.get_dim_data_per_rank() | ||
| dest_ddpr = other_dist.get_dim_data_per_rank() | ||
| source_dest_pairs = product(source_ddpr, dest_ddpr) | ||
|
|
||
| if self.shape == other_dist.shape: | ||
| _intersection = Distribution._redist_intersection_same_shape | ||
| else: | ||
| _intersection = Distribution._redist_intersection_reshape | ||
|
|
||
| plan = [] | ||
| for source_dd, dest_dd in source_dest_pairs: | ||
| intersections = _intersection(source_dd, dest_dd) | ||
| if intersections and all(i for i in intersections): | ||
| source_coords = tuple(dd['proc_grid_rank'] for dd in source_dd) | ||
| source_rank = self.rank_from_coords[source_coords] | ||
| dest_coords = tuple(dd['proc_grid_rank'] for dd in dest_dd) | ||
| dest_rank = other_dist.rank_from_coords[dest_coords] | ||
| plan.append({ | ||
| 'source_rank': union_rank_from_source_rank[source_rank], | ||
| 'dest_rank': union_rank_from_dest_rank[dest_rank], | ||
| 'indices': intersections, | ||
| } | ||
| ) | ||
|
|
||
| return plan | ||
|
|
||
|
|
||
| # ---------------------------------------------------------------------------- | ||
| # Redistribution helper functions. | ||
| # ---------------------------------------------------------------------------- | ||
|
|
||
| def global_flat_indices(dim_data): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Docstring |
||
| """ | ||
| Return a list of tuples of indices into the flattened global array. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| dim_data: dimension dictionary. | ||
|
|
||
| Returns | ||
| ------- | ||
| list of 2-tuples of ints. | ||
| Each tuple is a (start, stop) interval into the flattened global array. | ||
| All selected ranges comprise the indices for this dim_data's sub-array. | ||
|
|
||
| """ | ||
| # TODO: FIXME: can be optimized when the last dimension is 'n'. | ||
|
|
||
| for dd in dim_data: | ||
| if dd['dist_type'] == 'n': | ||
| dd['start'] = 0 | ||
| dd['stop'] = dd['size'] | ||
|
|
||
| glb_shape = tuple(dd['size'] for dd in dim_data) | ||
| glb_strides = strides_from_shape(glb_shape) | ||
|
|
||
| ranges = [range(dd['start'], dd['stop']) for dd in dim_data[:-1]] | ||
| start_ranges = ranges + [[dim_data[-1]['start']]] | ||
| stop_ranges = ranges + [[dim_data[-1]['stop']]] | ||
|
|
||
| def flatten(idx): | ||
| return sum(a * b for (a, b) in zip(idx, glb_strides)) | ||
|
|
||
| starts = map(flatten, product(*start_ranges)) | ||
| stops = map(flatten, product(*stop_ranges)) | ||
|
|
||
| intervals = zip(starts, stops) | ||
| return condense(intervals) | ||
|
|
||
| def _global_flat_indices_intersection(gfis0, gfis1): | ||
| intersections = filter(None, [tuple_intersection(a, b) | ||
| for (a, b) in product(gfis0, gfis1)]) | ||
| return [i[:2] for i in intersections] | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Docstring might be nice, though maybe this should be
_comm_unioninstead.