Skip to content

Commit 9535c2a

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 9535c2a

File tree

12 files changed

+454
-292
lines changed

12 files changed

+454
-292
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: 76 additions & 39 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);
@@ -866,7 +883,7 @@ fn apply_to_commit2(
866883
for (root, _link_file) in v {
867884
let embeding = some_or!(
868885
apply_to_commit2(
869-
&Op::Chain(message("{@}"), file(root.join(".josh-link.toml"))),
886+
&Op::Chain(vec![message("{@}"), file(root.join(".josh-link.toml"))]),
870887
&commit,
871888
transaction
872889
)?,
@@ -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,37 @@ 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+
// Split into first and rest, unapply recursively
1514+
let (first, rest) = match filters.split_first() {
1515+
Some((first, rest)) => (first, rest),
1516+
None => return Ok(tree),
1517+
};
1518+
1519+
if rest.is_empty() {
1520+
return unapply(transaction, *first, tree, parent_tree);
1521+
}
1522+
1523+
let rest_chain = to_filter(Op::Chain(rest.to_vec()));
1524+
1525+
// Compute filtered_parent_tree for the first filter
1526+
let first_normalized = if let Ok(first_inverted) = invert(*first) {
1527+
invert(first_inverted)?
14951528
} else {
1496-
a
1529+
*first
14971530
};
14981531
let filtered_parent_tree = apply(
14991532
transaction,
1500-
a_normalized,
1533+
first_normalized,
15011534
Rewrite::from_tree(parent_tree.clone()),
15021535
)?
15031536
.into_tree();
15041537

1538+
// Recursively unapply: first unapply the rest, then unapply first
15051539
return unapply(
15061540
transaction,
1507-
a,
1508-
unapply(transaction, b, tree, filtered_parent_tree)?,
1541+
*first,
1542+
unapply(transaction, rest_chain, tree, filtered_parent_tree)?,
15091543
parent_tree,
15101544
);
15111545
}
@@ -1721,7 +1755,7 @@ fn is_ancestor_of(
17211755
pub fn is_linear(filter: Filter) -> bool {
17221756
match to_op(filter) {
17231757
Op::Linear => true,
1724-
Op::Chain(a, b) => is_linear(a) || is_linear(b),
1758+
Op::Chain(filters) => filters.iter().any(|f| is_linear(*f)),
17251759
_ => false,
17261760
}
17271761
}
@@ -1735,7 +1769,9 @@ where
17351769
let f = f.into_iter().map(|f| legalize_pin(f, c)).collect();
17361770
to_filter(Op::Compose(f))
17371771
}
1738-
Op::Chain(a, b) => to_filter(Op::Chain(legalize_pin(a, c), legalize_pin(b, c))),
1772+
Op::Chain(filters) => to_filter(Op::Chain(
1773+
filters.iter().map(|f| legalize_pin(*f, c)).collect(),
1774+
)),
17391775
Op::Subtract(a, b) => to_filter(Op::Subtract(legalize_pin(a, c), legalize_pin(b, c))),
17401776
Op::Exclude(f) => to_filter(Op::Exclude(legalize_pin(f, c))),
17411777
Op::Pin(f) => c(f),
@@ -1761,14 +1797,15 @@ fn legalize_stored(t: &cache::Transaction, f: Filter, tree: &git2::Tree) -> Josh
17611797
.collect::<JoshResult<Vec<_>>>()?;
17621798
to_filter(Op::Compose(f))
17631799
}
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))
1800+
Op::Chain(filters) => {
1801+
let mut result = Vec::with_capacity(filters.len());
1802+
let mut current_tree = tree.clone();
1803+
for filter in filters {
1804+
let legalized = legalize_stored(t, filter, &current_tree)?;
1805+
current_tree = apply(t, legalized, Rewrite::from_tree(current_tree.clone()))?.tree;
1806+
result.push(legalized);
1807+
}
1808+
to_filter(Op::Chain(result))
17721809
}
17731810
Op::Subtract(a, b) => to_filter(Op::Subtract(
17741811
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)