瀏覽代碼

Remove another critical memcpy.

This memcpy occurs in `insert` which makes use of `replace_with` which
will implicitly memcpy a value to the stack and then back again to the
pointer. This value is relatively large and thus the memcpy is
expensive.
ticki 8 年之前
父節點
當前提交
940fd5ecb5
共有 3 個文件被更改,包括 111 次插入113 次删除
  1. 1 0
      src/arena.rs
  2. 1 0
      src/bk/node.rs
  3. 109 113
      src/bk/seek.rs

+ 1 - 0
src/arena.rs

@@ -54,6 +54,7 @@ impl PointerList {
     /// This is unsafe due to holding the invariant that it is valid.
     #[inline]
     unsafe fn push(&mut self, ptr: Pointer<PointerList>) {
+        // TODO: Eliminate this memcpy.
         take::replace_with(self, |x| {
             // Set the head to the pointer.
             x.head = Some(ptr.cast());

+ 1 - 0
src/bk/node.rs

@@ -44,6 +44,7 @@ struct Node {
 impl Node {
     /// Insert a new node after this node.
     fn insert(&mut self, new_node: Jar<Node>) {
+        // TODO: Eliminate this (critical) memcpy.
         take::replace_with(self, |node| {
             new_node.next = Some(node);
             new_node

+ 109 - 113
src/bk/seek.rs

@@ -94,7 +94,7 @@ impl<'a> Seek<'a> {
             // If our configuration now looks like:
             //     [==self=============][==rest==]
             // We need to maximize the former level, so we merge right.
-            if self.try_merge_right(arena).is_ok() { return; }
+            self.try_merge_right(arena);
 
         // Note that we do not merge our block to the seeked block from the left side. This is due
         // to the seeked block being strictly less than our target, and thus we go one forward to
@@ -185,126 +185,122 @@ impl<'a> Seek<'a> {
     fn insert_no_merge(&mut self, block: Block, arena: &mut Arena<Node>) {
         log!(DEBUG, "Inserting (without merge) block {:?} before block {:?}...", block, self.node.block);
 
-        take::replace_with(self, |mut seek| {
-            // Make sure that there are no adjacent blocks.
-            debug_assert!(!self.node.left_to(&block), "Inserting left-adjacent block without \
-                          merge.");
-            debug_assert!(!self.node.next.map_or(false, |x| x.left_to(&block)), "Inserting \
-                          right-adjacent block without merge.");
-            // Check that we're inserting at a fitting position, such that the list is kept sorted.
-            debug_assert!(self.node.block < block, "Inserting at a wrong position.");
-
-            // Put the old node behind the new node holding block.
-            seek.node.insert(arena.alloc(Node {
-                block: block,
-                // Place-holder.
-                shortcuts: Default::default(),
-                next: None,
-            }));
-
-            // Generate the maximum level of the new node's shortcuts.
-            if let Some(max_lv) = lv::Level::generate() {
-                // If we actually have a bottom layer (i.e. the generated level is higher than zero),
-                // we obviously need to update its fat values based on the main list.
-                // FIXME: This code is copied from the loop below. Find a way to avoid repeating.
-                // Place the node into the bottom shortcut.
-                self.insert_shortcut(lv::Level::min());
-
-                // For details how this works, see the loop below. This is really just taken from
-                // the iteration from that to reflect the special structure of the block list.
-
-                // Calculate the fat value of the bottom layer.
-                let new_fat = seek.node.calculate_fat_value_bottom();
-
-                let skip = &mut seek.get_skip(lv::Level::min());
-                if new_fat == skip.fat {
-                    if let Some(next) = skip.next {
-                        next.fat = next.calculate_fat_value_bottom();
-                    }
-                } else {
-                    skip.node.shortcuts[0].increase_fat(mem::replace(&mut skip.fat, new_fat));
+        // Make sure that there are no adjacent blocks.
+        debug_assert!(!self.node.left_to(&block), "Inserting left-adjacent block without \
+                      merge.");
+        debug_assert!(!self.node.next.map_or(false, |x| x.left_to(&block)), "Inserting \
+                      right-adjacent block without merge.");
+        // Check that we're inserting at a fitting position, such that the list is kept sorted.
+        debug_assert!(self.node.block < block, "Inserting at a wrong position.");
+
+        // Put the old node behind the new node holding block.
+        self.node.insert(arena.alloc(Node {
+            block: block,
+            // Place-holder.
+            shortcuts: Default::default(),
+            next: None,
+        }));
+
+        // Generate the maximum level of the new node's shortcuts.
+        if let Some(max_lv) = lv::Level::generate() {
+            // If we actually have a bottom layer (i.e. the generated level is higher than zero),
+            // we obviously need to update its fat values based on the main list.
+            // FIXME: This code is copied from the loop below. Find a way to avoid repeating.
+            // Place the node into the bottom shortcut.
+            self.insert_shortcut(lv::Level::min());
+
+            // For details how this works, see the loop below. This is really just taken from
+            // the iteration from that to reflect the special structure of the block list.
+
+            // Calculate the fat value of the bottom layer.
+            let new_fat = self.node.calculate_fat_value_bottom();
+
+            let skip = &mut self.get_skip(lv::Level::min());
+            if new_fat == skip.fat {
+                if let Some(next) = skip.next {
+                    next.fat = next.calculate_fat_value_bottom();
                 }
+            } else {
+                skip.node.shortcuts[0].increase_fat(mem::replace(&mut skip.fat, new_fat));
+            }
 
-                // Place new shortcuts up to this level.
-                for lv in lv::Iter::non_bottom().to(max_lv) {
-                    // Place the node inbetween the shortcuts.
-                    seek.insert_shortcut(lv);
-
-                    // The configuration (at level `lv`) should now look like:
-                    //     [seek.node] --> [old self.node] --> [old shortcut]
-
-                    // Since we inserted a new shortcut here, we might have invalidated the old fat
-                    // value, so we need to recompute the fat value. Fortunately, since we go from
-                    // densest to opaquer layer, we can base the fat value on the values of the
-                    // previous layer.
-
-                    // An example state could look like this: Assume we go from this configuration:
-                    //     [a] -y----------> [b] ---> ...
-                    //     [a] -x----------> [b] ---> ...
-                    //     ...
-                    // To this:
-                    //     [a] -y'---------> [b] ---> ...
-                    //     [a] -p-> [n] -q-> [b] ---> ...
-                    //     ...
-                    // Here, you cannot express _p_ and _q_ in terms  of _x_ and _y_. Instead, we need
-                    // to compute them from the layer below.
-                    let new_fat = seek.node.calculate_fat_value_non_bottom(lv);
-
-                    // You have been visitied by the silly ferris.
-                    //      ()    ()
-                    //       \/\/\/
-                    //       &_^^_&
-                    //       \    /
-                    // Fix all bugs in 0.9345 seconds, or you will be stuck doing premature
-                    // optimizations forever.
-
-                    // Avoid multiple bound checks.
-                    let skip = &mut seek.get_skip(lv);
-                    // The shortcut behind the updated one might be invalidated as well. We use a nifty
-                    // trick: If the fat node is not present on one part of the split (defined by the
-                    // newly inserted node), it must fall on the other. So, we can shortcut and safely
-                    // skip computation of the second part half of the time.
-                    if new_fat == skip.fat {
-                        if let Some(next) = skip.next {
-                            // The fat value was left unchanged. This means that we need to recompute the
-                            // next segment's fat value.
-                            next.fat = next.calculate_fat_value_non_bottom(lv);
-                        }
-
-                        // Since it was unchanged, there is no need for setting the value to what it
-                        // already is!
-                    } else {
-                        // The first segment did not contain the fat node! This means that we know a
-                        // priori that the second segment contains the old fat node, i.e. has either
-                        // the same fat value or the updated node is itself the fat node. So, we
-                        // replace the new fat value and update the next segment with the old one.  As
-                        // an example, suppose the configuration looked like:
-                        //     [a] -f----------> [b]
-                        // And we inserted a new node $x$:
-                        //     [a] -g-> [x] -h-> [b]
-                        // Since the fat node (size $f$) couldn't be found on the left side ($g$) of
-                        // $x$, it must be on the right side ($h ≥ f$). It might not be equal to it,
-                        // because the size of $x$ could be greater than $f$, so we take the maximal of
-                        // $x$ and $f$ and assigns that to $h$. This way we avoid having to iterate
-                        // over all the nodes between $x$ and $b$.
-                        skip.next.unwrap().shortcuts[lv]
-                            .increase_fat(cmp::max(mem::replace(&mut skip.fat, new_fat), seek.node.block.size()));
+            // Place new shortcuts up to this level.
+            for lv in lv::Iter::non_bottom().to(max_lv) {
+                // Place the node inbetween the shortcuts.
+                self.insert_shortcut(lv);
+
+                // The configuration (at level `lv`) should now look like:
+                //     [self.node] --> [old self.node] --> [old shortcut]
+
+                // Since we inserted a new shortcut here, we might have invalidated the old fat
+                // value, so we need to recompute the fat value. Fortunately, since we go from
+                // densest to opaquer layer, we can base the fat value on the values of the
+                // previous layer.
+
+                // An example state could look like this: Assume we go from this configuration:
+                //     [a] -y----------> [b] ---> ...
+                //     [a] -x----------> [b] ---> ...
+                //     ...
+                // To this:
+                //     [a] -y'---------> [b] ---> ...
+                //     [a] -p-> [n] -q-> [b] ---> ...
+                //     ...
+                // Here, you cannot express _p_ and _q_ in terms  of _x_ and _y_. Instead, we need
+                // to compute them from the layer below.
+                let new_fat = self.node.calculate_fat_value_non_bottom(lv);
+
+                // You have been visitied by the silly ferris.
+                //      ()    ()
+                //       \/\/\/
+                //       &_^^_&
+                //       \    /
+                // Fix all bugs in 0.9345 seconds, or you will be stuck doing premature
+                // optimizations forever.
+
+                // Avoid multiple bound checks.
+                let skip = &mut self.get_skip(lv);
+                // The shortcut behind the updated one might be invalidated as well. We use a nifty
+                // trick: If the fat node is not present on one part of the split (defined by the
+                // newly inserted node), it must fall on the other. So, we can shortcut and safely
+                // skip computation of the second part half of the time.
+                if new_fat == skip.fat {
+                    if let Some(next) = skip.next {
+                        // The fat value was left unchanged. This means that we need to recompute the
+                        // next segment's fat value.
+                        next.fat = next.calculate_fat_value_non_bottom(lv);
                     }
-                }
 
-                // The levels above the inserted shortcuts need to get there fat value updated, since a
-                // new node was inserted below. Since we only inserted (i.e. added a potentially new
-                // fat node), we simply need to max the old (unupdated) fat values with the new block's
-                // size.
-                if let Some(above) = max_lv.above() {
-                    seek.increase_fat(seek.node.block.size(), above);
+                    // Since it was unchanged, there is no need for setting the value to what it
+                    // already is!
+                } else {
+                    // The first segment did not contain the fat node! This means that we know a
+                    // priori that the second segment contains the old fat node, i.e. has either
+                    // the same fat value or the updated node is itself the fat node. So, we
+                    // replace the new fat value and update the next segment with the old one.  As
+                    // an example, suppose the configuration looked like:
+                    //     [a] -f----------> [b]
+                    // And we inserted a new node $x$:
+                    //     [a] -g-> [x] -h-> [b]
+                    // Since the fat node (size $f$) couldn't be found on the left side ($g$) of
+                    // $x$, it must be on the right side ($h ≥ f$). It might not be equal to it,
+                    // because the size of $x$ could be greater than $f$, so we take the maximal of
+                    // $x$ and $f$ and assigns that to $h$. This way we avoid having to iterate
+                    // over all the nodes between $x$ and $b$.
+                    skip.next.unwrap().shortcuts[lv]
+                        .increase_fat(cmp::max(mem::replace(&mut skip.fat, new_fat), self.node.block.size()));
                 }
             }
 
-            seek.node.check();
+            // The levels above the inserted shortcuts need to get there fat value updated, since a
+            // new node was inserted below. Since we only inserted (i.e. added a potentially new
+            // fat node), we simply need to max the old (unupdated) fat values with the new block's
+            // size.
+            if let Some(above) = max_lv.above() {
+                self.increase_fat(self.node.block.size(), above);
+            }
+        }
 
-            seek
-        });
+        self.node.check();
 
         self.check();
     }