Merge pull request #228 from zed-industries/faster-clock-global

Max Brunsfeld created

Switch to a dense representation for `clock::Global`

Change summary

crates/buffer/src/lib.rs   |  41 ++++++----
crates/clock/src/lib.rs    | 139 ++++++++++++++++++++++++++-------------
crates/language/src/lib.rs |  10 +-
3 files changed, 120 insertions(+), 70 deletions(-)

Detailed changes

crates/buffer/src/lib.rs 🔗

@@ -438,11 +438,22 @@ impl Buffer {
     pub fn new(replica_id: u16, remote_id: u64, history: History) -> Buffer {
         let mut fragments = SumTree::new();
 
+        let mut local_clock = clock::Local::new(replica_id);
+        let mut lamport_clock = clock::Lamport::new(replica_id);
+        let mut version = clock::Global::new();
         let visible_text = Rope::from(history.base_text.as_ref());
         if visible_text.len() > 0 {
+            let timestamp = InsertionTimestamp {
+                replica_id: 0,
+                local: 1,
+                lamport: 1,
+            };
+            local_clock.observe(timestamp.local());
+            lamport_clock.observe(timestamp.lamport());
+            version.observe(timestamp.local());
             fragments.push(
                 Fragment {
-                    timestamp: Default::default(),
+                    timestamp,
                     len: visible_text.len(),
                     visible: true,
                     deletions: Default::default(),
@@ -456,7 +467,7 @@ impl Buffer {
             visible_text,
             deleted_text: Rope::new(),
             fragments,
-            version: clock::Global::new(),
+            version,
             last_edit: clock::Local::default(),
             undo_map: Default::default(),
             history,
@@ -465,8 +476,8 @@ impl Buffer {
             deferred_replicas: HashSet::default(),
             replica_id,
             remote_id,
-            local_clock: clock::Local::new(replica_id),
-            lamport_clock: clock::Lamport::new(replica_id),
+            local_clock,
+            lamport_clock,
         }
     }
 
@@ -1093,10 +1104,10 @@ impl Buffer {
             false
         } else {
             match op {
-                Operation::Edit(edit) => self.version >= edit.version,
-                Operation::Undo { undo, .. } => self.version >= undo.version,
+                Operation::Edit(edit) => self.version.ge(&edit.version),
+                Operation::Undo { undo, .. } => self.version.ge(&undo.version),
                 Operation::UpdateSelections { selections, .. } => {
-                    self.version >= *selections.version()
+                    self.version.ge(selections.version())
                 }
                 Operation::RemoveSelections { .. } => true,
                 Operation::SetActiveSelections { set_id, .. } => {
@@ -1947,10 +1958,10 @@ impl<'a> Content<'a> {
         let fragments_cursor = if since == self.version {
             None
         } else {
-            Some(self.fragments.filter(
-                move |summary| summary.max_version.changed_since(since),
-                &None,
-            ))
+            Some(
+                self.fragments
+                    .filter(move |summary| !since.ge(&summary.max_version), &None),
+            )
         };
 
         Edits {
@@ -2233,13 +2244,9 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for VersionedFullOffset {
     fn add_summary(&mut self, summary: &'a FragmentSummary, cx: &Option<clock::Global>) {
         if let Self::Offset(offset) = self {
             let version = cx.as_ref().unwrap();
-            if *version >= summary.max_insertion_version {
+            if version.ge(&summary.max_insertion_version) {
                 *offset += summary.text.visible + summary.text.deleted;
-            } else if !summary
-                .min_insertion_version
-                .iter()
-                .all(|t| !version.observed(*t))
-            {
+            } else if version.observed_any(&summary.min_insertion_version) {
                 *self = Self::Invalid;
             }
         }

crates/clock/src/lib.rs 🔗

@@ -1,9 +1,8 @@
 use smallvec::SmallVec;
 use std::{
     cmp::{self, Ordering},
-    fmt,
+    fmt, iter,
     ops::{Add, AddAssign},
-    slice,
 };
 
 pub type ReplicaId = u16;
@@ -59,7 +58,7 @@ impl<'a> AddAssign<&'a Local> for Local {
 }
 
 #[derive(Clone, Default, Hash, Eq, PartialEq)]
-pub struct Global(SmallVec<[Local; 3]>);
+pub struct Global(SmallVec<[u32; 8]>);
 
 impl From<Vec<rpc::proto::VectorClockEntry>> for Global {
     fn from(message: Vec<rpc::proto::VectorClockEntry>) -> Self {
@@ -98,75 +97,119 @@ impl Global {
     }
 
     pub fn get(&self, replica_id: ReplicaId) -> Seq {
-        self.0
-            .iter()
-            .find(|t| t.replica_id == replica_id)
-            .map_or(0, |t| t.value)
+        self.0.get(replica_id as usize).copied().unwrap_or(0) as Seq
     }
 
     pub fn observe(&mut self, timestamp: Local) {
-        if let Some(entry) = self
-            .0
-            .iter_mut()
-            .find(|t| t.replica_id == timestamp.replica_id)
-        {
-            entry.value = cmp::max(entry.value, timestamp.value);
-        } else {
-            self.0.push(timestamp);
+        if timestamp.value > 0 {
+            let new_len = timestamp.replica_id as usize + 1;
+            if new_len > self.0.len() {
+                self.0.resize(new_len, 0);
+            }
+
+            let entry = &mut self.0[timestamp.replica_id as usize];
+            *entry = cmp::max(*entry, timestamp.value);
         }
     }
 
     pub fn join(&mut self, other: &Self) {
-        for timestamp in other.0.iter() {
-            self.observe(*timestamp);
+        if other.0.len() > self.0.len() {
+            self.0.resize(other.0.len(), 0);
+        }
+
+        for (left, right) in self.0.iter_mut().zip(&other.0) {
+            *left = cmp::max(*left, *right);
         }
     }
 
     pub fn meet(&mut self, other: &Self) {
-        for timestamp in other.0.iter() {
-            if let Some(entry) = self
-                .0
-                .iter_mut()
-                .find(|t| t.replica_id == timestamp.replica_id)
-            {
-                entry.value = cmp::min(entry.value, timestamp.value);
-            } else {
-                self.0.push(*timestamp);
+        if other.0.len() > self.0.len() {
+            self.0.resize(other.0.len(), 0);
+        }
+
+        let mut new_len = 0;
+        for (ix, (left, right)) in self
+            .0
+            .iter_mut()
+            .zip(other.0.iter().chain(iter::repeat(&0)))
+            .enumerate()
+        {
+            if *left == 0 {
+                *left = *right;
+            } else if *right > 0 {
+                *left = cmp::min(*left, *right);
+            }
+
+            if *left != 0 {
+                new_len = ix + 1;
             }
         }
+        self.0.resize(new_len, 0);
     }
 
     pub fn observed(&self, timestamp: Local) -> bool {
         self.get(timestamp.replica_id) >= timestamp.value
     }
 
-    pub fn changed_since(&self, other: &Self) -> bool {
-        self.0.iter().any(|t| t.value > other.get(t.replica_id))
+    pub fn observed_any(&self, other: &Self) -> bool {
+        let mut lhs = self.0.iter();
+        let mut rhs = other.0.iter();
+        loop {
+            if let Some(left) = lhs.next() {
+                if let Some(right) = rhs.next() {
+                    if *right > 0 && left >= right {
+                        return true;
+                    }
+                } else {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        }
     }
 
-    pub fn iter(&self) -> slice::Iter<Local> {
-        self.0.iter()
+    pub fn ge(&self, other: &Self) -> bool {
+        let mut lhs = self.0.iter();
+        let mut rhs = other.0.iter();
+        loop {
+            if let Some(left) = lhs.next() {
+                if let Some(right) = rhs.next() {
+                    if left < right {
+                        return false;
+                    }
+                } else {
+                    return true;
+                }
+            } else {
+                return rhs.next().is_none();
+            }
+        }
     }
-}
 
-impl PartialOrd for Global {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        let mut global_ordering = Ordering::Equal;
-
-        for timestamp in self.0.iter().chain(other.0.iter()) {
-            let ordering = self
-                .get(timestamp.replica_id)
-                .cmp(&other.get(timestamp.replica_id));
-            if ordering != Ordering::Equal {
-                if global_ordering == Ordering::Equal {
-                    global_ordering = ordering;
-                } else if ordering != global_ordering {
-                    return None;
+    pub fn gt(&self, other: &Self) -> bool {
+        let mut lhs = self.0.iter();
+        let mut rhs = other.0.iter();
+        loop {
+            if let Some(left) = lhs.next() {
+                if let Some(right) = rhs.next() {
+                    if left <= right {
+                        return false;
+                    }
+                } else {
+                    return true;
                 }
+            } else {
+                return rhs.next().is_none();
             }
         }
+    }
 
-        Some(global_ordering)
+    pub fn iter<'a>(&'a self) -> impl 'a + Iterator<Item = Local> {
+        self.0.iter().enumerate().map(|(replica_id, seq)| Local {
+            replica_id: replica_id as ReplicaId,
+            value: *seq,
+        })
     }
 }
 
@@ -219,11 +262,11 @@ impl fmt::Debug for Lamport {
 impl fmt::Debug for Global {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "Global {{")?;
-        for (i, element) in self.0.iter().enumerate() {
-            if i > 0 {
+        for timestamp in self.iter() {
+            if timestamp.replica_id > 0 {
                 write!(f, ", ")?;
             }
-            write!(f, "{}: {}", element.replica_id, element.value)?;
+            write!(f, "{}: {}", timestamp.replica_id, timestamp.value)?;
         }
         write!(f, "}}")
     }

crates/language/src/lib.rs 🔗

@@ -319,9 +319,9 @@ impl Buffer {
         }
 
         Self {
-            text: buffer,
             saved_mtime,
-            saved_version: clock::Global::new(),
+            saved_version: buffer.version(),
+            text: buffer,
             file,
             syntax_tree: Mutex::new(None),
             parsing_in_background: false,
@@ -620,7 +620,7 @@ impl Buffer {
                                 this.language.as_ref().map_or(true, |curr_language| {
                                     !Arc::ptr_eq(curr_language, &language)
                                 });
-                            let parse_again = this.version > parsed_version || language_changed;
+                            let parse_again = this.version.gt(&parsed_version) || language_changed;
                             this.parsing_in_background = false;
                             this.did_finish_parsing(new_tree, parsed_version, cx);
 
@@ -1132,12 +1132,12 @@ impl Buffer {
     }
 
     pub fn is_dirty(&self) -> bool {
-        self.version > self.saved_version
+        !self.saved_version.ge(&self.version)
             || self.file.as_ref().map_or(false, |file| file.is_deleted())
     }
 
     pub fn has_conflict(&self) -> bool {
-        self.version > self.saved_version
+        !self.saved_version.ge(&self.version)
             && self
                 .file
                 .as_ref()