Mercurial > hg
comparison rust/hg-core/src/revlog/index.rs @ 51397:b01e7d97e167
revlog: add a Rust implementation of `headrevsdiff`
Python implementation of `headrevsdiff` can be very slow in the worst
case compared with the `heads` computation it replaces, since the
latter is done in Rust.
Even the average case of this Python implementation is still
noticeable in the profiles.
This patch makes the computation much much faster by doing it in Rust.
author | Arseniy Alekseyev <aalekseyev@janestreet.com> |
---|---|
date | Mon, 12 Feb 2024 20:01:27 +0000 |
parents | c4cbb515b006 |
children | 7c6d0b9dde37 |
comparison
equal
deleted
inserted
replaced
51396:3f37d80d3ab4 | 51397:b01e7d97e167 |
---|---|
552 /// Python-specific shortcut to save on PyList creation | 552 /// Python-specific shortcut to save on PyList creation |
553 pub fn head_revs_shortcut( | 553 pub fn head_revs_shortcut( |
554 &self, | 554 &self, |
555 ) -> Result<Option<Vec<Revision>>, GraphError> { | 555 ) -> Result<Option<Vec<Revision>>, GraphError> { |
556 self.head_revs_filtered(&HashSet::new(), true) | 556 self.head_revs_filtered(&HashSet::new(), true) |
557 } | |
558 | |
559 /// Return the heads removed and added by advancing from `begin` to `end`. | |
560 /// In revset language, we compute: | |
561 /// - `heads(:begin)-heads(:end)` | |
562 /// - `heads(:end)-heads(:begin)` | |
563 pub fn head_revs_diff( | |
564 &self, | |
565 begin: Revision, | |
566 end: Revision, | |
567 ) -> Result<(Vec<Revision>, Vec<Revision>), GraphError> { | |
568 let mut heads_added = vec![]; | |
569 let mut heads_removed = vec![]; | |
570 | |
571 let mut acc = HashSet::new(); | |
572 let Revision(begin) = begin; | |
573 let Revision(end) = end; | |
574 let mut i = end; | |
575 | |
576 while i > begin { | |
577 // acc invariant: | |
578 // `j` is in the set iff `j <= i` and it has children | |
579 // among `i+1..end` (inclusive) | |
580 if !acc.remove(&i) { | |
581 heads_added.push(Revision(i)); | |
582 } | |
583 for Revision(parent) in self.parents(Revision(i))? { | |
584 acc.insert(parent); | |
585 } | |
586 i -= 1; | |
587 } | |
588 | |
589 // At this point `acc` contains old revisions that gained new children. | |
590 // We need to check if they had any children before. If not, those | |
591 // revisions are the removed heads. | |
592 while !acc.is_empty() { | |
593 // acc invariant: | |
594 // `j` is in the set iff `j <= i` and it has children | |
595 // among `begin+1..end`, but not among `i+1..begin` (inclusive) | |
596 | |
597 assert!(i >= -1); // yes, `-1` can also be a head if the repo is empty | |
598 if acc.remove(&i) { | |
599 heads_removed.push(Revision(i)); | |
600 } | |
601 for Revision(parent) in self.parents(Revision(i))? { | |
602 acc.remove(&parent); | |
603 } | |
604 i -= 1; | |
605 } | |
606 | |
607 Ok((heads_removed, heads_added)) | |
557 } | 608 } |
558 | 609 |
559 /// Return the head revisions of this index | 610 /// Return the head revisions of this index |
560 pub fn head_revs_filtered( | 611 pub fn head_revs_filtered( |
561 &self, | 612 &self, |