Remove unnecessary memory allocations when using SuiteSparse. 1. Add SuiteSparse::CreateDenseVectorView 2. Replace calls to SuiteSparse::CreateDenseVector with SuiteSparse::CreateDenseVectorView. 2. Replace NULL with nullptr in suitesparse.cc and dynamic_sparse_normal_cholesky_solver.cc Change-Id: I94355c1dc27789e5b987a7b2850e9db6176a0914
diff --git a/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc b/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc index a46c85e..f966083 100644 --- a/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc +++ b/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc
@@ -66,7 +66,7 @@ VectorRef(x, num_cols).setZero(); A->LeftMultiply(b, x); - if (per_solve_options.D != NULL) { + if (per_solve_options.D != nullptr) { // Temporarily append a diagonal block to the A matrix, but undo // it before returning the matrix to the user. std::unique_ptr<CompressedRowSparseMatrix> regularizer; @@ -96,7 +96,7 @@ << options_.sparse_linear_algebra_library_type; } - if (per_solve_options.D != NULL) { + if (per_solve_options.D != nullptr) { A->DeleteRows(num_cols); } @@ -253,19 +253,18 @@ cholmod_factor* factor = ss.AnalyzeCholesky(&lhs, &summary.message); event_logger.AddEvent("Analysis"); - if (factor == NULL) { + if (factor == nullptr) { summary.termination_type = LINEAR_SOLVER_FATAL_ERROR; return summary; } summary.termination_type = ss.Cholesky(&lhs, factor, &summary.message); if (summary.termination_type == LINEAR_SOLVER_SUCCESS) { - cholmod_dense* rhs = - ss.CreateDenseVector(rhs_and_solution, num_cols, num_cols); - cholmod_dense* solution = ss.Solve(factor, rhs, &summary.message); + cholmod_dense cholmod_rhs = + ss.CreateDenseVectorView(rhs_and_solution, num_cols); + cholmod_dense* solution = ss.Solve(factor, &cholmod_rhs, &summary.message); event_logger.AddEvent("Solve"); - ss.Free(rhs); - if (solution != NULL) { + if (solution != nullptr) { memcpy( rhs_and_solution, solution->x, num_cols * sizeof(*rhs_and_solution)); ss.Free(solution);
diff --git a/internal/ceres/suitesparse.cc b/internal/ceres/suitesparse.cc index 5e93d2e..cc07a01 100644 --- a/internal/ceres/suitesparse.cc +++ b/internal/ceres/suitesparse.cc
@@ -97,11 +97,11 @@ m.nrow = A->num_cols(); m.ncol = A->num_rows(); m.nzmax = A->num_nonzeros(); - m.nz = NULL; + m.nz = nullptr; m.p = reinterpret_cast<void*>(A->mutable_rows()); m.i = reinterpret_cast<void*>(A->mutable_cols()); m.x = reinterpret_cast<void*>(A->mutable_values()); - m.z = NULL; + m.z = nullptr; if (A->storage_type() == CompressedRowSparseMatrix::LOWER_TRIANGULAR) { m.stype = 1; @@ -120,12 +120,24 @@ return m; } +cholmod_dense SuiteSparse::CreateDenseVectorView(const double* x, int size) { + cholmod_dense v; + v.nrow = size; + v.ncol = 1; + v.nzmax = size; + v.d = size; + v.x = const_cast<void*>(reinterpret_cast<const void*>(x)); + v.xtype = CHOLMOD_REAL; + v.dtype = CHOLMOD_DOUBLE; + return v; +} + cholmod_dense* SuiteSparse::CreateDenseVector(const double* x, int in_size, int out_size) { CHECK_LE(in_size, out_size); cholmod_dense* v = cholmod_zeros(out_size, 1, CHOLMOD_REAL, &cc_); - if (x != NULL) { + if (x != nullptr) { memcpy(v->x, x, in_size * sizeof(*x)); } return v; @@ -149,7 +161,7 @@ if (cc_.status != CHOLMOD_OK) { *message = StringPrintf("cholmod_analyze failed. error code: %d", cc_.status); - return NULL; + return nullptr; } return CHECK_NOTNULL(factor); @@ -161,7 +173,7 @@ string* message) { vector<int> ordering; if (!BlockAMDOrdering(A, row_blocks, col_blocks, &ordering)) { - return NULL; + return nullptr; } return AnalyzeCholeskyWithUserOrdering(A, ordering, message); } @@ -174,14 +186,14 @@ cc_.method[0].ordering = CHOLMOD_GIVEN; cholmod_factor* factor = - cholmod_analyze_p(A, const_cast<int*>(&ordering[0]), NULL, 0, &cc_); + cholmod_analyze_p(A, const_cast<int*>(&ordering[0]), nullptr, 0, &cc_); if (VLOG_IS_ON(2)) { cholmod_print_common(const_cast<char*>("Symbolic Analysis"), &cc_); } if (cc_.status != CHOLMOD_OK) { *message = StringPrintf("cholmod_analyze failed. error code: %d", cc_.status); - return NULL; + return nullptr; } return CHECK_NOTNULL(factor); @@ -200,7 +212,7 @@ if (cc_.status != CHOLMOD_OK) { *message = StringPrintf("cholmod_analyze failed. error code: %d", cc_.status); - return NULL; + return nullptr; } return CHECK_NOTNULL(factor); @@ -230,7 +242,7 @@ block_matrix.nzmax = block_rows.size(); block_matrix.p = reinterpret_cast<void*>(&block_cols[0]); block_matrix.i = reinterpret_cast<void*>(&block_rows[0]); - block_matrix.x = NULL; + block_matrix.x = nullptr; block_matrix.stype = A->stype; block_matrix.itype = CHOLMOD_INT; block_matrix.xtype = CHOLMOD_PATTERN; @@ -239,7 +251,7 @@ block_matrix.packed = 1; vector<int> block_ordering(num_row_blocks); - if (!cholmod_amd(&block_matrix, NULL, 0, &block_ordering[0], &cc_)) { + if (!cholmod_amd(&block_matrix, nullptr, 0, &block_ordering[0], &cc_)) { return false; } @@ -311,7 +323,7 @@ string* message) { if (cc_.status != CHOLMOD_OK) { *message = "cholmod_solve failed. CHOLMOD status is not CHOLMOD_OK"; - return NULL; + return nullptr; } return cholmod_solve(CHOLMOD_A, L, b, &cc_); @@ -319,13 +331,13 @@ bool SuiteSparse::ApproximateMinimumDegreeOrdering(cholmod_sparse* matrix, int* ordering) { - return cholmod_amd(matrix, NULL, 0, ordering, &cc_); + return cholmod_amd(matrix, nullptr, 0, ordering, &cc_); } bool SuiteSparse::ConstrainedApproximateMinimumDegreeOrdering( cholmod_sparse* matrix, int* constraints, int* ordering) { #ifndef CERES_NO_CAMD - return cholmod_camd(matrix, NULL, 0, constraints, ordering, &cc_); + return cholmod_camd(matrix, nullptr, 0, constraints, ordering, &cc_); #else LOG(FATAL) << "Congratulations you have found a bug in Ceres." << "Ceres Solver was compiled with SuiteSparse " @@ -342,24 +354,24 @@ } SuiteSparseCholesky::SuiteSparseCholesky(const OrderingType ordering_type) - : ordering_type_(ordering_type), factor_(NULL) {} + : ordering_type_(ordering_type), factor_(nullptr) {} SuiteSparseCholesky::~SuiteSparseCholesky() { - if (factor_ != NULL) { + if (factor_ != nullptr) { ss_.Free(factor_); } } LinearSolverTerminationType SuiteSparseCholesky::Factorize( CompressedRowSparseMatrix* lhs, string* message) { - if (lhs == NULL) { + if (lhs == nullptr) { *message = "Failure: Input lhs is NULL."; return LINEAR_SOLVER_FATAL_ERROR; } cholmod_sparse cholmod_lhs = ss_.CreateSparseMatrixTransposeView(lhs); - if (factor_ == NULL) { + if (factor_ == nullptr) { if (ordering_type_ == NATURAL) { factor_ = ss_.AnalyzeCholeskyWithNaturalOrdering(&cholmod_lhs, message); } else { @@ -371,7 +383,7 @@ } } - if (factor_ == NULL) { + if (factor_ == nullptr) { return LINEAR_SOLVER_FATAL_ERROR; } } @@ -390,18 +402,17 @@ double* solution, string* message) { // Error checking - if (factor_ == NULL) { + if (factor_ == nullptr) { *message = "Solve called without a call to Factorize first."; return LINEAR_SOLVER_FATAL_ERROR; } const int num_cols = factor_->n; - cholmod_dense* cholmod_dense_rhs = - ss_.CreateDenseVector(rhs, num_cols, num_cols); + cholmod_dense cholmod_rhs = ss_.CreateDenseVectorView(rhs, num_cols); cholmod_dense* cholmod_dense_solution = - ss_.Solve(factor_, cholmod_dense_rhs, message); - ss_.Free(cholmod_dense_rhs); - if (cholmod_dense_solution == NULL) { + ss_.Solve(factor_, &cholmod_rhs, message); + + if (cholmod_dense_solution == nullptr) { return LINEAR_SOLVER_FAILURE; }
diff --git a/internal/ceres/suitesparse.h b/internal/ceres/suitesparse.h index 0646602..91cd24b 100644 --- a/internal/ceres/suitesparse.h +++ b/internal/ceres/suitesparse.h
@@ -99,6 +99,11 @@ // use the SuiteSparse machinery to allocate memory. cholmod_sparse CreateSparseMatrixTransposeView(CompressedRowSparseMatrix* A); + // Create a cholmod_dense vector around the contents of the array x. + // This is a shallow object, which refers to the contents of x and + // does not use the SuiteSparse machinery to allocate memory. + cholmod_dense CreateDenseVectorView(const double* x, int size); + // Given a vector x, build a cholmod_dense vector of size out_size // with the first in_size entries copied from x. If x is NULL, then // an all zeros vector is returned. Caller owns the result.