Skip to content

Commit a9dec5c

Browse files
Refactor chain to use a vec
It was discovered during profiling that filter optimisation spends lots of time rearranging chains. Using a vec will make operations like finding the last element in a chain much faster.
1 parent 4081b12 commit a9dec5c

File tree

18 files changed

+876
-829
lines changed

18 files changed

+876
-829
lines changed

josh-core/src/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub fn squash(ids: Option<&[(git2::Oid, Filter)]>) -> Filter {
4444

4545
/// Create a filter that is the result of feeding the output of `first` into `second`
4646
pub fn chain(first: Filter, second: Filter) -> Filter {
47-
opt::optimize(to_filter(Op::Chain(first, second)))
47+
opt::optimize(to_filter(Op::Chain(vec![first, second])))
4848
}
4949

5050
/// Create a filter that is the result of overlaying the output of `first` onto `second`

josh-core/src/filter/mod.rs

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,11 @@ fn lazy_refs2(op: &Op) -> Vec<String> {
222222
})
223223
}
224224
Op::Exclude(filter) | Op::Pin(filter) => lazy_refs(*filter),
225-
Op::Chain(a, b) => {
226-
let mut av = lazy_refs(*a);
227-
av.append(&mut lazy_refs(*b));
225+
Op::Chain(filters) => {
226+
let mut av = vec![];
227+
for filter in filters {
228+
av.append(&mut lazy_refs(*filter));
229+
}
228230
av
229231
}
230232
Op::Subtract(a, b) => {
@@ -282,7 +284,7 @@ fn resolve_refs2(refs: &std::collections::HashMap<String, git2::Oid>, op: &Op) -
282284
}
283285
Op::Exclude(filter) => Op::Exclude(resolve_refs(refs, *filter)),
284286
Op::Pin(filter) => Op::Pin(resolve_refs(refs, *filter)),
285-
Op::Chain(a, b) => Op::Chain(resolve_refs(refs, *a), resolve_refs(refs, *b)),
287+
Op::Chain(filters) => Op::Chain(filters.iter().map(|f| resolve_refs(refs, *f)).collect()),
286288
Op::Subtract(a, b) => Op::Subtract(resolve_refs(refs, *a), resolve_refs(refs, *b)),
287289
Op::Rev(filters) => {
288290
let lr = filters
@@ -344,7 +346,9 @@ fn src_path2(op: &Op) -> std::path::PathBuf {
344346
normalize_path(&match op {
345347
Op::Subdir(path) => path.to_owned(),
346348
Op::File(_, source_path) => source_path.to_owned(),
347-
Op::Chain(a, b) => src_path(*a).join(src_path(*b)),
349+
Op::Chain(filters) => filters
350+
.iter()
351+
.fold(std::path::PathBuf::new(), |acc, f| acc.join(src_path(*f))),
348352
_ => std::path::PathBuf::new(),
349353
})
350354
}
@@ -357,7 +361,10 @@ fn dst_path2(op: &Op) -> std::path::PathBuf {
357361
normalize_path(&match op {
358362
Op::Prefix(path) => path.to_owned(),
359363
Op::File(dest_path, _) => dest_path.to_owned(),
360-
Op::Chain(a, b) => dst_path(*b).join(dst_path(*a)),
364+
Op::Chain(filters) => filters
365+
.iter()
366+
.rev()
367+
.fold(std::path::PathBuf::new(), |acc, f| acc.join(dst_path(*f))),
361368
_ => std::path::PathBuf::new(),
362369
})
363370
}
@@ -497,15 +504,22 @@ fn apply_to_commit2(
497504
Op::Nop => return Ok(Some(commit.id())),
498505
Op::Empty => return Ok(Some(git2::Oid::zero())),
499506

500-
Op::Chain(a, b) => {
501-
let r = some_or!(apply_to_commit2(&to_op(*a), commit, transaction)?, {
502-
return Ok(None);
503-
});
504-
return if let Ok(r) = repo.find_commit(r) {
505-
apply_to_commit2(&to_op(*b), &r, transaction)
506-
} else {
507-
Ok(Some(git2::Oid::zero()))
508-
};
507+
Op::Chain(filters) => {
508+
let mut current_oid = commit.id();
509+
for filter in filters {
510+
if current_oid == git2::Oid::zero() {
511+
break;
512+
}
513+
let current_commit = repo.find_commit(current_oid)?;
514+
let r = some_or!(
515+
apply_to_commit2(&to_op(*filter), &current_commit, transaction)?,
516+
{
517+
return Ok(None);
518+
}
519+
);
520+
current_oid = r;
521+
}
522+
return Ok(Some(current_oid));
509523
}
510524
Op::Squash(None) => {
511525
return Some(history::rewrite_commit(
@@ -521,6 +535,7 @@ fn apply_to_commit2(
521535
if let Some(oid) = transaction.get(filter, commit.id()) {
522536
return Ok(Some(oid));
523537
}
538+
// Continue to process the filter if not cached
524539
}
525540
};
526541

@@ -637,9 +652,11 @@ fn apply_to_commit2(
637652
}
638653
Op::Squash(Some(ids)) => {
639654
if let Some(sq) = ids.get(&LazyRef::Resolved(commit.id())) {
640-
let oid = if let Some(oid) =
641-
apply_to_commit2(&Op::Chain(filter::squash(None), *sq), commit, transaction)?
642-
{
655+
let oid = if let Some(oid) = apply_to_commit2(
656+
&Op::Chain(vec![filter::squash(None), *sq]),
657+
commit,
658+
transaction,
659+
)? {
643660
oid
644661
} else {
645662
return Ok(None);
@@ -1424,8 +1441,12 @@ fn apply2<'a>(
14241441
Ok(x.with_tree(tree::compose(transaction, filtered)?))
14251442
}
14261443

1427-
Op::Chain(a, b) => {
1428-
return apply(transaction, *b, apply(transaction, *a, x.clone())?);
1444+
Op::Chain(filters) => {
1445+
let mut result = x;
1446+
for filter in filters {
1447+
result = apply(transaction, *filter, result)?;
1448+
}
1449+
return Ok(result);
14291450
}
14301451
Op::Hook(_) => Err(josh_error("not applicable to tree")),
14311452

@@ -1488,24 +1509,35 @@ pub fn unapply<'a>(
14881509
return Ok(ws);
14891510
}
14901511

1491-
if let Op::Chain(a, b) = to_op(filter) {
1492-
// If filter "a" is invertable, use "invert(invert(a))" version of it, otherwise use as is
1493-
let a_normalized = if let Ok(a_inverted) = invert(a) {
1494-
invert(a_inverted)?
1512+
if let Op::Chain(filters) = to_op(filter) {
1513+
if filters.is_empty() {
1514+
return Ok(tree);
1515+
}
1516+
if filters.len() == 1 {
1517+
return unapply(transaction, filters[0], tree, parent_tree);
1518+
}
1519+
// Split into first and rest, unapply recursively
1520+
let (first, rest) = filters.split_first().unwrap();
1521+
let rest_chain = to_filter(Op::Chain(rest.to_vec()));
1522+
1523+
// Compute filtered_parent_tree for the first filter
1524+
let first_normalized = if let Ok(first_inverted) = invert(*first) {
1525+
invert(first_inverted)?
14951526
} else {
1496-
a
1527+
*first
14971528
};
14981529
let filtered_parent_tree = apply(
14991530
transaction,
1500-
a_normalized,
1531+
first_normalized,
15011532
Rewrite::from_tree(parent_tree.clone()),
15021533
)?
15031534
.into_tree();
15041535

1536+
// Recursively unapply: first unapply the rest, then unapply first
15051537
return unapply(
15061538
transaction,
1507-
a,
1508-
unapply(transaction, b, tree, filtered_parent_tree)?,
1539+
*first,
1540+
unapply(transaction, rest_chain, tree, filtered_parent_tree)?,
15091541
parent_tree,
15101542
);
15111543
}
@@ -1721,7 +1753,7 @@ fn is_ancestor_of(
17211753
pub fn is_linear(filter: Filter) -> bool {
17221754
match to_op(filter) {
17231755
Op::Linear => true,
1724-
Op::Chain(a, b) => is_linear(a) || is_linear(b),
1756+
Op::Chain(filters) => filters.iter().any(|f| is_linear(*f)),
17251757
_ => false,
17261758
}
17271759
}
@@ -1735,7 +1767,9 @@ where
17351767
let f = f.into_iter().map(|f| legalize_pin(f, c)).collect();
17361768
to_filter(Op::Compose(f))
17371769
}
1738-
Op::Chain(a, b) => to_filter(Op::Chain(legalize_pin(a, c), legalize_pin(b, c))),
1770+
Op::Chain(filters) => to_filter(Op::Chain(
1771+
filters.iter().map(|f| legalize_pin(*f, c)).collect(),
1772+
)),
17391773
Op::Subtract(a, b) => to_filter(Op::Subtract(legalize_pin(a, c), legalize_pin(b, c))),
17401774
Op::Exclude(f) => to_filter(Op::Exclude(legalize_pin(f, c))),
17411775
Op::Pin(f) => c(f),
@@ -1761,14 +1795,15 @@ fn legalize_stored(t: &cache::Transaction, f: Filter, tree: &git2::Tree) -> Josh
17611795
.collect::<JoshResult<Vec<_>>>()?;
17621796
to_filter(Op::Compose(f))
17631797
}
1764-
Op::Chain(a, b) => {
1765-
let first = legalize_stored(t, a, tree)?;
1766-
let second = legalize_stored(
1767-
t,
1768-
b,
1769-
&apply(t, first, Rewrite::from_tree(tree.clone()))?.tree,
1770-
)?;
1771-
to_filter(Op::Chain(first, second))
1798+
Op::Chain(filters) => {
1799+
let mut result = vec![];
1800+
let mut current_tree = tree.clone();
1801+
for filter in filters {
1802+
let legalized = legalize_stored(t, filter, &current_tree)?;
1803+
current_tree = apply(t, legalized, Rewrite::from_tree(current_tree.clone()))?.tree;
1804+
result.push(legalized);
1805+
}
1806+
to_filter(Op::Chain(result))
17721807
}
17731808
Op::Subtract(a, b) => to_filter(Op::Subtract(
17741809
legalize_stored(t, a, tree)?,

josh-core/src/filter/op.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub enum Op {
8383
Unapply(LazyRef, Filter),
8484

8585
Compose(Vec<Filter>),
86-
Chain(Filter, Filter),
86+
Chain(Vec<Filter>),
8787
Subtract(Filter, Filter),
8888
Exclude(Filter),
8989
Pin(Filter),

0 commit comments

Comments
 (0)