dag: add new range algorithm

Summary:
Similar to descendants, the new range algorithm avoids potentially expensive
checks about whether high-level segments can be used or not. Practically this
is overall an improvement.

`cargo bench --bench dag_ops -- range`:

Before:

  range (2 ids)                                     115.380 ms
  range (spans)                                     243.666 ms

After:

  range (2 ids)                                     123.274 ms
  range (spans)                                      23.101 ms

It is 100x faster with the range x::y benchmark added later on `git.git`.

Reviewed By: sfilipco

Differential Revision: D23106175

fbshipit-source-id: 691e0418ba2b7ad9f52ac15b5cd6088ec28d5f48
This commit is contained in:
Jun Wu 2020-08-26 15:26:49 -07:00 committed by Facebook GitHub Bot
parent c2e03b9129
commit 4d798c39d9

View File

@ -1012,7 +1012,34 @@ impl<Store: IdDagStore> IdDag<Store> {
/// ```plain,ignore
/// intersect(ancestors(heads), descendants(roots))
/// ```
///
/// This is O(flat segments), or O(merges).
pub fn range(&self, roots: impl Into<SpanSet>, heads: impl Into<SpanSet>) -> Result<SpanSet> {
let roots = roots.into();
if roots.is_empty() {
return Ok(SpanSet::empty());
}
let heads = heads.into();
#[cfg(test)]
let result_old = self.range_old(roots.clone(), heads.clone())?;
let max_head_id = match heads.max() {
Some(id) => id,
None => return Ok(SpanSet::empty()),
};
let ancestors_of_heads = self.ancestors(heads)?;
let descendants_of_roots = self.descendants_up_to(&roots, max_head_id)?;
let result = ancestors_of_heads.intersection(&descendants_of_roots);
#[cfg(test)]
{
assert_eq!(result.as_spans(), result_old.as_spans());
}
Ok(result)
}
#[cfg(test)]
fn range_old(&self, roots: impl Into<SpanSet>, heads: impl Into<SpanSet>) -> Result<SpanSet> {
// Pre-calculate ancestors.
let heads = heads.into();
let roots = roots.into();