changeset 433:9d8aae30be98

cmp: Make memtable key comparisons ~4x faster. Similar to the internal key comparison improvement: Only parse expensive FixedInts when required. This change brings rusty-leveldb within 10% of the original for write-heavy workloads and 25% for iterating workloads.
author Lewin Bormann <lbo@spheniscida.de>
date Sun, 22 Oct 2017 09:42:19 +0200
parents e0e746a0ddce
children b09db8d3d60c
files src/cmp.rs src/key_types.rs
diffstat 2 files changed, 38 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/src/cmp.rs	Sun Oct 22 09:24:50 2017 +0200
+++ b/src/cmp.rs	Sun Oct 22 09:42:19 2017 +0200
@@ -132,23 +132,7 @@
 
 impl Cmp for MemtableKeyCmp {
     fn cmp(&self, a: &[u8], b: &[u8]) -> Ordering {
-        let (akeylen, akeyoff, atag, _, _) = key_types::parse_memtable_key(a);
-        let (bkeylen, bkeyoff, btag, _, _) = key_types::parse_memtable_key(b);
-
-        let userkey_a = &a[akeyoff..akeyoff + akeylen];
-        let userkey_b = &b[bkeyoff..bkeyoff + bkeylen];
-
-        match self.0.cmp(userkey_a, userkey_b) {
-            Ordering::Less => Ordering::Less,
-            Ordering::Greater => Ordering::Greater,
-            Ordering::Equal => {
-                let (_, aseq) = key_types::parse_tag(atag);
-                let (_, bseq) = key_types::parse_tag(btag);
-
-                // reverse!
-                bseq.cmp(&aseq)
-            }
-        }
+        key_types::cmp_memtable_key(self.0.as_ref().as_ref(), a, b)
     }
 
     fn id(&self) -> &'static str {
--- a/src/key_types.rs	Sun Oct 22 09:24:50 2017 +0200
+++ b/src/key_types.rs	Sun Oct 22 09:42:19 2017 +0200
@@ -96,20 +96,6 @@
     }
 }
 
-/// cmp_internal_key efficiently compares to keys in InternalKey format.
-pub fn cmp_internal_key<'a, 'b>(ucmp: &Cmp, a: InternalKey<'a>, b: InternalKey<'b>) -> Ordering {
-    match ucmp.cmp(&a[0..a.len() - 8], &b[0..b.len() - 8]) {
-        Ordering::Less => Ordering::Less,
-        Ordering::Greater => Ordering::Greater,
-        Ordering::Equal => {
-            let seqa = parse_tag(FixedInt::decode_fixed(&a[a.len() - 8..])).1;
-            let seqb = parse_tag(FixedInt::decode_fixed(&b[b.len() - 8..])).1;
-            // reverse comparison!
-            seqb.cmp(&seqa)
-        }
-    }
-}
-
 /// A memtable key is a bytestring containing (keylen, key, tag, vallen, val). This function
 /// builds such a key. It's called key because the underlying Map implementation will only be
 /// concerned with keys; the value field is not used (instead, the value is encoded in the key,
@@ -144,26 +130,43 @@
 /// meaningless.
 pub fn parse_memtable_key<'a>(mkey: MemtableKey<'a>) -> (usize, usize, u64, usize, usize) {
     let (keylen, mut i): (usize, usize) = VarInt::decode_var(&mkey);
-
     let keyoff = i;
     i += keylen - 8;
 
     if mkey.len() > i {
         let tag = FixedInt::decode_fixed(&mkey[i..i + 8]);
         i += 8;
-
         let (vallen, j): (usize, usize) = VarInt::decode_var(&mkey[i..]);
-
         i += j;
-
         let valoff = i;
-
         return (keylen - 8, keyoff, tag, vallen, valoff);
     } else {
         return (keylen - 8, keyoff, 0, 0, 0);
     }
 }
 
+/// cmp_memtable_key efficiently compares two memtable keys by only parsing what's actually needed.
+pub fn cmp_memtable_key<'a, 'b>(ucmp: &Cmp, a: MemtableKey<'a>, b: MemtableKey<'b>) -> Ordering {
+        let (alen, aoff): (usize, usize) = VarInt::decode_var(&a);
+        let (blen, boff): (usize, usize) = VarInt::decode_var(&b);
+        let userkey_a = &a[aoff..aoff + alen - 8];
+        let userkey_b = &b[boff..boff + blen - 8];
+
+        match ucmp.cmp(userkey_a, userkey_b) {
+            Ordering::Less => Ordering::Less,
+            Ordering::Greater => Ordering::Greater,
+            Ordering::Equal => {
+                let atag = FixedInt::decode_fixed(&a[aoff+alen-8..aoff+alen]);
+                let btag = FixedInt::decode_fixed(&b[boff+blen-8..boff+blen]);
+                let (_, aseq) = parse_tag(atag);
+                let (_, bseq) = parse_tag(btag);
+
+                // reverse!
+                bseq.cmp(&aseq)
+            }
+        }
+}
+
 /// Parse a key in InternalKey format.
 pub fn parse_internal_key<'a>(ikey: InternalKey<'a>) -> (ValueType, SequenceNumber, UserKey<'a>) {
     if ikey.is_empty() {
@@ -174,6 +177,21 @@
     return (typ, seq, &ikey[0..ikey.len() - 8]);
 }
 
+/// cmp_internal_key efficiently compares keys in InternalKey format by only parsing the parts that
+/// are actually needed for a comparison.
+pub fn cmp_internal_key<'a, 'b>(ucmp: &Cmp, a: InternalKey<'a>, b: InternalKey<'b>) -> Ordering {
+    match ucmp.cmp(&a[0..a.len() - 8], &b[0..b.len() - 8]) {
+        Ordering::Less => Ordering::Less,
+        Ordering::Greater => Ordering::Greater,
+        Ordering::Equal => {
+            let seqa = parse_tag(FixedInt::decode_fixed(&a[a.len() - 8..])).1;
+            let seqb = parse_tag(FixedInt::decode_fixed(&b[b.len() - 8..])).1;
+            // reverse comparison!
+            seqb.cmp(&seqa)
+        }
+    }
+}
+
 /// truncate_to_userkey performs an in-place conversion from InternalKey to UserKey format.
 pub fn truncate_to_userkey(ikey: &mut Vec<u8>) {
     let len = ikey.len();