SparseCore: Consume a descending reach back to front instead of reversing it libeigen/eigen!2994
diff --git a/Eigen/src/SparseCore/TriangularSolver.h b/Eigen/src/SparseCore/TriangularSolver.h index d886e15..0a58bc9 100644 --- a/Eigen/src/SparseCore/TriangularSolver.h +++ b/Eigen/src/SparseCore/TriangularSolver.h
@@ -192,24 +192,20 @@ std::is_same<typename traits<Rhs>::StorageIndex, typename traits<Lhs>::StorageIndex>::value>; // The reach for a column arrives in one of three compile-time-known orders, but the -// output column must store ascending inner index. reach_reorder encapsulates the -// per-order fix-up via partial specialization: an unordered reach (Ordered == false, -// the pointer/DFS path -- Upper irrelevant) is sorted; the iterator reach is already -// in solve order -- ascending for lower (no-op), descending for upper (reverse). +// output column must store ascending inner index. An unordered reach (Ordered == false, +// the pointer/DFS path -- Upper irrelevant) is sorted ascending here; the iterator reach +// already arrives in solve order, ascending for lower and descending for upper, so it +// needs no pass of its own -- the descending one is read back to front at insertion. template <bool Ordered, bool Upper, typename StorageIndex> struct reach_reorder { // Ordered == false: unordered pointer/DFS reach static void run(StorageIndex* first, StorageIndex* last) { std::sort(first, last); } }; -template <typename StorageIndex> -struct reach_reorder<true, false, StorageIndex> { // iterator reach, lower: already ascending +template <bool Upper, typename StorageIndex> +struct reach_reorder<true, Upper, StorageIndex> { // iterator reach: already in solve order static void run(StorageIndex* /*first*/, StorageIndex* /*last*/) {} }; -template <typename StorageIndex> -struct reach_reorder<true, true, StorageIndex> { // iterator reach, upper: descending - static void run(StorageIndex* first, StorageIndex* last) { std::reverse(first, last); } -}; -// Common per-column finish: reorder the reach xi[top..n) to ascending inner index, +// Common per-column finish: read the reach xi[top..n) in ascending inner index order, // insert reading values from xwork, and clear xwork and mark for the next column. // The reach is a structural bound, not a numeric one: a reached coefficient can be // exactly zero (a zero rhs entry, or numerical cancellation), so skip exact zeros at @@ -218,8 +214,11 @@ template <bool Ordered, bool Upper, typename Res, typename StorageIndex, typename Scalar> void reach_insert_column(Res& res, Index col, StorageIndex* xi, Index top, Index n, Scalar* xwork, uint8_t* mark) { reach_reorder<Ordered, Upper, StorageIndex>::run(xi + top, xi + n); + // Only the iterator upper reach is descending; it is read back to front, so k walks + // up while the load walks down. Every other order is ascending by this point. + constexpr bool Descending = Ordered && Upper; for (Index k = top; k < n; ++k) { - StorageIndex j = xi[k]; + StorageIndex j = xi[Descending ? top + n - 1 - k : k]; if (!numext::is_exactly_zero(xwork[j])) res.insert(j, col) = xwork[j]; xwork[j] = Scalar(0); mark[j] = 0;