[PPL-devel] [GIT] ppl/ppl(master): Sparse_Row: optimize linear_combine(), avoiding the insertion of too many elements.

Marco Poletti poletti.marco at gmail.com
Sun Oct 3 17:12:59 CEST 2010


Module: ppl/ppl
Branch: master
Commit: 27a720c897ec87c9854374630e9846d882e86e7b
URL:    http://www.cs.unipr.it/git/gitweb.cgi?p=ppl/ppl.git;a=commit;h=27a720c897ec87c9854374630e9846d882e86e7b

Author: Marco Poletti <poletti.marco at gmail.com>
Date:   Sun Oct  3 17:13:33 2010 +0200

Sparse_Row: optimize linear_combine(), avoiding the insertion of too many elements.

---

 src/Sparse_Row.cc |  164 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 163 insertions(+), 1 deletions(-)

diff --git a/src/Sparse_Row.cc b/src/Sparse_Row.cc
index c3c0f3f..1dec76d 100644
--- a/src/Sparse_Row.cc
+++ b/src/Sparse_Row.cc
@@ -232,6 +232,107 @@ PPL::Sparse_Row::normalize() {
   PPL_ASSERT(OK());
 }
 
+namespace {
+
+class sparse_row_linear_combine_helper_iterator {
+public:
+  sparse_row_linear_combine_helper_iterator(
+    const PPL::Sparse_Row& x, const PPL::Sparse_Row& y,
+    PPL::Coefficient_traits::const_reference coeff1_1,
+    PPL::Coefficient_traits::const_reference coeff2_1)
+    : coeff1(coeff1_1), coeff2(coeff2_1) {
+    i = x.begin();
+    i_end = x.end();
+    j = y.begin();
+    j_end = y.end();
+    update_current_data();
+  }
+
+  void operator++() {
+    if (from_i)
+      ++i;
+    if (from_j)
+      ++j;
+    update_current_data();
+  }
+
+  PPL::Coefficient_traits::const_reference operator*() {
+    return current_value;
+  }
+
+  PPL::dimension_type index() {
+    return current_index;
+  }
+
+private:
+  void update_current_data() {
+    if (i == i_end) {
+      if (j == j_end) {
+        return;
+      } else {
+        // i == i_end, j != j_end, so use j.
+        current_index = j.index();
+        current_value = *j;
+        current_value *= coeff2;
+        from_i = false;
+        from_j = true;
+      }
+    } else {
+      if (j == j_end) {
+        // i != i_end, j == j_end, so use i.
+        current_index = i.index();
+        current_value = *i;
+        current_value *= coeff1;
+        from_i = true;
+        from_j = false;
+      } else {
+        // i != i_end and j != j_end.
+        if (i.index() < j.index()) {
+          // i.index() < j.index(), so use i.
+          current_index = i.index();
+          current_value = *i;
+          current_value *= coeff1;
+          from_i = true;
+          from_j = false;
+        } else {
+          if (i.index() != j.index()) {
+            PPL_ASSERT(i.index() > j.index());
+            // i.index() > j.index(), so use j.
+            current_index = j.index();
+            current_value = *j;
+            current_value *= coeff2;
+            from_i = false;
+            from_j = true;
+          } else {
+            // i.index() == j.index(), so use both i and j.
+            current_index = i.index();
+            current_value = *i;
+            current_value *= coeff1;
+            PPL::add_mul_assign(current_value, *j, coeff2);
+            from_i = true;
+            from_j = true;
+          }
+        }
+      }
+    }
+    PPL_ASSERT(!from_i || i != i_end);
+    PPL_ASSERT(!from_j || j != j_end);
+  }
+
+  PPL::Coefficient_traits::const_reference coeff1;
+  PPL::Coefficient_traits::const_reference coeff2;
+  PPL::Sparse_Row::const_iterator i;
+  PPL::Sparse_Row::const_iterator i_end;
+  PPL::Sparse_Row::const_iterator j;
+  PPL::Sparse_Row::const_iterator j_end;
+  PPL::dimension_type current_index;
+  PPL::Coefficient current_value;
+  bool from_i;
+  bool from_j;
+};
+
+} // namespace
+
 void
 PPL::Sparse_Row::linear_combine(const Sparse_Row& y,
                                 Coefficient_traits::const_reference coeff1,
@@ -239,7 +340,9 @@ PPL::Sparse_Row::linear_combine(const Sparse_Row& y,
   PPL_ASSERT(coeff1 != 0);
   PPL_ASSERT(coeff2 != 0);
   PPL_ASSERT(this != &y);
+
   if (coeff1 == 1) {
+    // Optimize for this special case.
     iterator i = end();
     for (const_iterator j = y.begin(), j_end = y.end(); j != j_end; ++j) {
       i = insert(i, j.index());
@@ -247,7 +350,45 @@ PPL::Sparse_Row::linear_combine(const Sparse_Row& y,
       if (*i == 0)
         i = reset(i);
     }
-  } else {
+    return;
+  }
+
+  dimension_type counter = 0;
+  // Count the number of elements that are stored in y but not in *this.
+  {
+    iterator i = begin();
+    iterator i_end = end();
+    const_iterator j = y.begin();
+    const_iterator j_end = y.end();
+    if (i != i_end) {
+      while (j != j_end) {
+        PPL_ASSERT(i != i_end);
+        if (i.index() == j.index()) {
+          ++i;
+          ++j;
+          if (i == i_end)
+            break;
+        } else
+          if (i.index() < j.index()) {
+            i = lower_bound(i, j.index());
+            if (i == i_end)
+              break;
+          } else {
+            PPL_ASSERT(i.index() > j.index());
+            ++counter;
+            ++j;
+          }
+      }
+    }
+    PPL_ASSERT(i == i_end || j == j_end);
+    for ( ; j != j_end; ++j)
+      ++counter;
+  }
+  // This condition is arbitrary. Changing it affects performance but not
+  // correctness. The values have been tuned using some ppl_lpsol benchmarks
+  // on 2 October 2010.
+  if (counter == 0 || counter < 7 * size() / 64) {
+    // Few insertions needed, do them directly.
     iterator i = begin();
     // This is a const reference to an internal iterator, that is kept valid.
     // If we just stored a copy, that would be invalidated by the calls to
@@ -283,6 +424,27 @@ PPL::Sparse_Row::linear_combine(const Sparse_Row& y,
       i = insert(i, j.index(), *j);
       (*i) *= coeff2;
     }
+  } else {
+    // Too many insertions needed, a full copy is probably faster than
+    // inserting all those new elements into *this.
+    CO_Tree new_tree(sparse_row_linear_combine_helper_iterator(*this, y,
+                                                                coeff1,
+                                                                coeff2),
+                     counter + tree.size());
+    std::swap(tree, new_tree);
+
+    // Now remove stored zeroes.
+    iterator i = begin();
+    // Note that end() can not be called only once, because reset()
+    // invalidates all iterators.
+    while (i != end()) {
+      if (*i == 0) {
+        const dimension_type old_index = i.index();
+        i = reset(i);
+        PPL_ASSERT(find(old_index) == end());
+      } else
+        ++i;
+    }
   }
 }
 




More information about the PPL-devel mailing list