dag: add DagAlgorithm.first_ancestors

Summary:
This will be useful to optimize `_firstancestors` revset, which is useful to
calculate a linear branch to draw growth graphs. Without a fast path, the pure
Python `_firstancestors` implementation would have id <-> name translation
overhead that makes Rust changelog O(20) slower than the Python revlog.

Reviewed By: sfilipco

Differential Revision: D26182240

fbshipit-source-id: d44f5ca5dc8c38df74281832931d87868791209e
This commit is contained in:
Jun Wu 2021-02-05 11:50:59 -08:00 committed by Facebook GitHub Bot
parent 01b0122b0b
commit fb187ef86e
3 changed files with 38 additions and 0 deletions

View File

@ -203,6 +203,32 @@ pub(crate) async fn first_ancestor_nth(
Ok(Some(vertex))
}
pub(crate) async fn first_ancestors(
this: &(impl DagAlgorithm + ?Sized),
set: NameSet,
) -> Result<NameSet> {
let mut to_visit: Vec<VertexName> = {
let mut list = Vec::with_capacity(set.count().await?);
let mut iter = set.iter().await?;
while let Some(next) = iter.next().await {
let vertex = next?;
list.push(vertex);
}
list
};
let mut visited: HashSet<VertexName> = to_visit.clone().into_iter().collect();
while let Some(v) = to_visit.pop() {
#[allow(clippy::never_loop)]
if let Some(parent) = this.parent_names(v).await?.into_iter().next() {
if visited.insert(parent.clone()) {
to_visit.push(parent);
}
}
}
let set = NameSet::from_iter(visited.into_iter().map(Ok));
this.sort(&set).await
}
pub(crate) async fn heads(this: &(impl DagAlgorithm + ?Sized), set: NameSet) -> Result<NameSet> {
Ok(set.clone() - this.parents(set).await?)
}

View File

@ -117,6 +117,13 @@ macro_rules! delegate {
{
self.$($t)*.ancestors(set)
}
fn first_ancestors<'a: 's, 's>(&'a self, set: $crate::Set)
-> std::pin::Pin<Box<dyn std::future::Future<Output=
$crate::Result<$crate::Set>
> + Send + 's>> where Self: 's
{
self.$($t)*.first_ancestors(set)
}
fn parents<'a: 's, 's>(&'a self, set: $crate::Set)
-> std::pin::Pin<Box<dyn std::future::Future<Output=
$crate::Result<$crate::Set>

View File

@ -57,6 +57,11 @@ pub trait DagAlgorithm: Send + Sync {
default_impl::first_ancestor_nth(self, name, n).await
}
/// Calculates ancestors but only follows the first parent.
async fn first_ancestors(&self, set: NameSet) -> Result<NameSet> {
default_impl::first_ancestors(self, set).await
}
/// Calculates heads of the given set.
async fn heads(&self, set: NameSet) -> Result<NameSet> {
default_impl::heads(self, set).await