Skip to content

Commit 17d621b

Browse files
authored
Only sort parallel-moves list once (#157)
* Only sort parallel-moves list once Checking whether sources and dests overlap can use the same sort order that's needed later, as long as the check is updated to match. * Review comments, and defer overlap check Now that we're sorting by `dst` from the start, the overlap check can wait until after we remove self-moves. If the only overlaps are self-moves we don't need the full cycle-resolution machinery.
1 parent 783ba47 commit 17d621b

File tree

1 file changed

+21
-15
lines changed

1 file changed

+21
-15
lines changed

src/moves.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@ impl<T: Clone + Copy + Default + PartialEq> ParallelMoves<T> {
4444
}
4545

4646
fn sources_overlap_dests(&self) -> bool {
47-
// Assumes `parallel_moves` has already been sorted in `resolve()` below.
48-
for &(_, dst, _) in &self.parallel_moves {
47+
// Assumes `parallel_moves` has already been sorted by `dst`
48+
// in `resolve()` below. The O(n log n) cost of this loop is no
49+
// worse than the sort we already did.
50+
for &(src, _, _) in &self.parallel_moves {
4951
if self
5052
.parallel_moves
51-
.binary_search_by_key(&dst, |&(src, _, _)| src)
53+
.binary_search_by_key(&src, |&(_, dst, _)| dst)
5254
.is_ok()
5355
{
5456
return true;
@@ -75,16 +77,17 @@ impl<T: Clone + Copy + Default + PartialEq> ParallelMoves<T> {
7577
return MoveVecWithScratch::NoScratch(self.parallel_moves);
7678
}
7779

78-
// Sort moves by source so that we can efficiently test for
79-
// presence.
80+
// Sort moves so that we can efficiently test for presence.
81+
// For that purpose it doesn't matter whether we sort by
82+
// source or destination, but later we'll want them sorted
83+
// by destination.
8084
self.parallel_moves
81-
.sort_by_key(|&(src, dst, _)| u64_key(src.bits(), dst.bits()));
85+
.sort_by_key(|&(src, dst, _)| u64_key(dst.bits(), src.bits()));
8286

83-
// Do any dests overlap sources? If not, we can also just
84-
// return the list.
85-
if !self.sources_overlap_dests() {
86-
return MoveVecWithScratch::NoScratch(self.parallel_moves);
87-
}
87+
// Duplicate moves cannot change the semantics of this
88+
// parallel move set, so remove them. This is cheap since we
89+
// just sorted the list.
90+
self.parallel_moves.dedup();
8891

8992
// General case: some moves overwrite dests that other moves
9093
// read as sources. We'll use a general algorithm.
@@ -102,10 +105,7 @@ impl<T: Clone + Copy + Default + PartialEq> ParallelMoves<T> {
102105
// know we have the full cycle and we can do a cyclic move
103106
// sequence and continue.
104107

105-
// Sort moves by destination and check that each destination
106-
// has only one writer.
107-
self.parallel_moves.sort_by_key(|&(_, dst, _)| dst);
108-
self.parallel_moves.dedup();
108+
// Check that each destination has only one writer.
109109
if cfg!(debug_assertions) {
110110
let mut last_dst = None;
111111
for &(_, dst, _) in &self.parallel_moves {
@@ -121,6 +121,12 @@ impl<T: Clone + Copy + Default + PartialEq> ParallelMoves<T> {
121121
// into that destination.
122122
self.parallel_moves.retain(|&mut (src, dst, _)| src != dst);
123123

124+
// Do any dests overlap sources? If not, we can also just
125+
// return the list.
126+
if !self.sources_overlap_dests() {
127+
return MoveVecWithScratch::NoScratch(self.parallel_moves);
128+
}
129+
124130
// Construct a mapping from move indices to moves they must
125131
// come before. Any given move must come before a move that
126132
// overwrites its destination; we have moves sorted by dest

0 commit comments

Comments
 (0)