From 16687e6575c0c4345749634f5de4a9d3dd783785 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Wed, 5 Feb 2025 18:11:16 +0100 Subject: [PATCH] speedup glop; better respect the time limit --- ortools/glop/BUILD.bazel | 1 + ortools/glop/basis_representation.h | 4 +++ ortools/glop/dual_edge_norms.cc | 5 +-- ortools/glop/lu_factorization.cc | 12 ++++--- ortools/glop/markowitz.cc | 19 +++++++---- ortools/glop/markowitz.h | 16 +++++---- ortools/glop/primal_edge_norms.cc | 13 +++++++- ortools/glop/primal_edge_norms.h | 4 +++ ortools/glop/rank_one_update.h | 4 +-- ortools/glop/revised_simplex.cc | 7 ++-- ortools/glop/update_row.cc | 9 ++--- ortools/lp_data/lp_utils.h | 15 ++++++--- ortools/lp_data/permutation.h | 52 ++++++++++++++++++----------- ortools/lp_data/scattered_vector.h | 18 ++++++---- ortools/lp_data/sparse.cc | 3 +- ortools/util/bitset.h | 11 ++++-- ortools/util/time_limit.h | 4 +-- 17 files changed, 131 insertions(+), 66 deletions(-) diff --git a/ortools/glop/BUILD.bazel b/ortools/glop/BUILD.bazel index 1b8d779ac4..da89eef6ff 100644 --- a/ortools/glop/BUILD.bazel +++ b/ortools/glop/BUILD.bazel @@ -286,6 +286,7 @@ cc_library( "//ortools/lp_data:lp_utils", "//ortools/lp_data:scattered_vector", "//ortools/util:stats", + "//ortools/util:time_limit", ], ) diff --git a/ortools/glop/basis_representation.h b/ortools/glop/basis_representation.h index 140db214df..00c87b06a7 100644 --- a/ortools/glop/basis_representation.h +++ b/ortools/glop/basis_representation.h @@ -310,6 +310,10 @@ class BasisFactorization { // Returns the number of updates since last refactorization. int NumUpdates() const { return num_updates_; } + EntryIndex NumberOfEntriesInLU() const { + return lu_factorization_.NumberOfEntries(); + } + private: // Called by ForceRefactorization() or Refactorize() or Initialize(). Status ComputeFactorization(); diff --git a/ortools/glop/dual_edge_norms.cc b/ortools/glop/dual_edge_norms.cc index 4b6ae95254..4e81385ddf 100644 --- a/ortools/glop/dual_edge_norms.cc +++ b/ortools/glop/dual_edge_norms.cc @@ -16,6 +16,7 @@ #include #include "ortools/lp_data/lp_utils.h" +#include "ortools/lp_data/permutation.h" namespace operations_research { namespace glop { @@ -42,8 +43,8 @@ DenseColumn::ConstView DualEdgeNorms::GetEdgeSquaredNorms() { void DualEdgeNorms::UpdateDataOnBasisPermutation( const ColumnPermutation& col_perm) { if (recompute_edge_squared_norms_) return; - ApplyColumnPermutationToRowIndexedVector(col_perm, &edge_squared_norms_, - &tmp_edge_squared_norms_); + ApplyColumnPermutationToRowIndexedVector( + col_perm.const_view(), &edge_squared_norms_, &tmp_edge_squared_norms_); } bool DualEdgeNorms::TestPrecision(RowIndex leaving_row, diff --git a/ortools/glop/lu_factorization.cc b/ortools/glop/lu_factorization.cc index 3a26b3d4bc..e34fa6c5d5 100644 --- a/ortools/glop/lu_factorization.cc +++ b/ortools/glop/lu_factorization.cc @@ -404,12 +404,14 @@ bool LuFactorization::LeftSolveLWithNonZeros( // use a different algorithm. ClearAndResizeVectorWithNonZeros(x->size(), result_before_permutation); x->swap(result_before_permutation->values); + const auto input = result_before_permutation->values.const_view(); + const auto inverse_row_perm = inverse_row_perm_.const_view(); if (nz->empty()) { - const RowIndex num_rows = inverse_row_perm_.size(); + const RowIndex num_rows = inverse_row_perm.size(); for (RowIndex row(0); row < num_rows; ++row) { - const Fractional value = (*result_before_permutation)[row]; + const Fractional value = input[row]; if (value != 0.0) { - const RowIndex permuted_row = inverse_row_perm_[row]; + const RowIndex permuted_row = inverse_row_perm[row]; (*x)[permuted_row] = value; } } @@ -417,8 +419,8 @@ bool LuFactorization::LeftSolveLWithNonZeros( nz->swap(result_before_permutation->non_zeros); nz->reserve(result_before_permutation->non_zeros.size()); for (const RowIndex row : result_before_permutation->non_zeros) { - const Fractional value = (*result_before_permutation)[row]; - const RowIndex permuted_row = inverse_row_perm_[row]; + const Fractional value = input[row]; + const RowIndex permuted_row = inverse_row_perm[row]; (*x)[permuted_row] = value; nz->push_back(permuted_row); } diff --git a/ortools/glop/markowitz.cc b/ortools/glop/markowitz.cc index 74d7f1f025..a292f137ca 100644 --- a/ortools/glop/markowitz.cc +++ b/ortools/glop/markowitz.cc @@ -63,7 +63,8 @@ Status Markowitz::ComputeRowAndColumnPermutation( // Initialize residual_matrix_non_zero_ with the submatrix left after we // removed the singleton and residual singleton columns. residual_matrix_non_zero_.InitializeFromMatrixSubset( - basis_matrix, *row_perm, *col_perm, &singleton_column_, &singleton_row_); + basis_matrix, row_perm->const_view(), col_perm->const_view(), + &singleton_column_, &singleton_row_); // Perform Gaussian elimination. const int end_index = std::min(num_rows.value(), num_cols.value()); @@ -216,9 +217,9 @@ void Markowitz::ExtractSingletonColumns( basis_matrix.num_rows().value()); } -bool Markowitz::IsResidualSingletonColumn(const ColumnView& column, - const RowPermutation& row_perm, - RowIndex* row) { +bool Markowitz::IsResidualSingletonColumn( + const ColumnView& column, StrictITISpan row_perm, + RowIndex* row) { int residual_degree = 0; for (const auto e : column) { if (row_perm[e.row()] != kInvalidRow) continue; @@ -238,7 +239,9 @@ void Markowitz::ExtractResidualSingletonColumns( for (ColIndex col(0); col < num_cols; ++col) { if ((*col_perm)[col] != kInvalidCol) continue; const ColumnView column = basis_matrix.column(col); - if (!IsResidualSingletonColumn(column, *row_perm, &row)) continue; + if (!IsResidualSingletonColumn(column, row_perm->const_view(), &row)) { + continue; + } (*col_perm)[col] = ColIndex(*index); (*row_perm)[row] = RowIndex(*index); lower_.AddDiagonalOnlyColumn(1.0); @@ -569,8 +572,10 @@ void MatrixNonZeroPattern::Reset(RowIndex num_rows, ColIndex num_cols) { } void MatrixNonZeroPattern::InitializeFromMatrixSubset( - const CompactSparseMatrixView& basis_matrix, const RowPermutation& row_perm, - const ColumnPermutation& col_perm, std::vector* singleton_columns, + const CompactSparseMatrixView& basis_matrix, + StrictITISpan row_perm, + StrictITISpan col_perm, + std::vector* singleton_columns, std::vector* singleton_rows) { const ColIndex num_cols = basis_matrix.num_cols(); const RowIndex num_rows = basis_matrix.num_rows(); diff --git a/ortools/glop/markowitz.h b/ortools/glop/markowitz.h index 9740d2d137..6e043f946a 100644 --- a/ortools/glop/markowitz.h +++ b/ortools/glop/markowitz.h @@ -116,11 +116,12 @@ class MatrixNonZeroPattern { // Resets the pattern to the one of the given matrix but only for the // rows/columns whose given permutation is kInvalidRow or kInvalidCol. // This also fills the singleton columns/rows with the corresponding entries. - void InitializeFromMatrixSubset(const CompactSparseMatrixView& basis_matrix, - const RowPermutation& row_perm, - const ColumnPermutation& col_perm, - std::vector* singleton_columns, - std::vector* singleton_rows); + void InitializeFromMatrixSubset( + const CompactSparseMatrixView& basis_matrix, + StrictITISpan row_perm, + StrictITISpan col_perm, + std::vector* singleton_columns, + std::vector* singleton_rows); // Adds a non-zero entry to the matrix. There should be no duplicates. void AddEntry(RowIndex row, ColIndex col); @@ -377,8 +378,9 @@ class Markowitz { // Helper function for determining if a column is a residual singleton column. // If it is, RowIndex* row contains the index of the single residual edge. - bool IsResidualSingletonColumn(const ColumnView& column, - const RowPermutation& row_perm, RowIndex* row); + bool IsResidualSingletonColumn( + const ColumnView& column, + StrictITISpan row_perm, RowIndex* row); // Returns the column of the current residual matrix with an index 'col' in // the initial matrix. We compute it by solving a linear system with the diff --git a/ortools/glop/primal_edge_norms.cc b/ortools/glop/primal_edge_norms.cc index b65ef5b06f..9840b83dc6 100644 --- a/ortools/glop/primal_edge_norms.cc +++ b/ortools/glop/primal_edge_norms.cc @@ -156,16 +156,27 @@ void PrimalEdgeNorms::ComputeMatrixColumnNorms() { void PrimalEdgeNorms::ComputeEdgeSquaredNorms() { SCOPED_TIME_STAT(&stats_); + // time_limit_->LimitReached() can be costly sometimes, so we only do that + // if we feel this will be slow anyway. + const bool test_limit = (time_limit_ != nullptr) && + basis_factorization_.NumberOfEntriesInLU() > 10'000; + // Since we will do a lot of inversions, it is better to be as efficient and // precise as possible by refactorizing the basis. DCHECK(basis_factorization_.IsRefactorized()); - edge_squared_norms_.resize(compact_matrix_.num_cols(), 0.0); + edge_squared_norms_.resize(compact_matrix_.num_cols(), 1.0); for (const ColIndex col : variables_info_.GetIsRelevantBitRow()) { // Note the +1.0 in the squared norm for the component of the edge on the // 'entering_col'. edge_squared_norms_[col] = 1.0 + basis_factorization_.RightSolveSquaredNorm( compact_matrix_.column(col)); + + // This operation can be costly, and we abort if we are stuck here. + // Note that we still mark edges as "recomputed" otherwise we can runs into + // some DCHECK before we actually abort the solve. + if (test_limit && time_limit_->LimitReached()) break; } + recompute_edge_squared_norms_ = false; } diff --git a/ortools/glop/primal_edge_norms.h b/ortools/glop/primal_edge_norms.h index 78ae54d7f7..4f5c6ecf9b 100644 --- a/ortools/glop/primal_edge_norms.h +++ b/ortools/glop/primal_edge_norms.h @@ -27,6 +27,7 @@ #include "ortools/lp_data/scattered_vector.h" #include "ortools/lp_data/sparse.h" #include "ortools/util/stats.h" +#include "ortools/util/time_limit.h" namespace operations_research { namespace glop { @@ -146,6 +147,8 @@ class PrimalEdgeNorms { return DeterministicTimeForFpOperations(num_operations_); } + void SetTimeLimit(TimeLimit* time_limit) { time_limit_ = time_limit; } + private: // Statistics about this class. struct Stats : public StatsGroup { @@ -192,6 +195,7 @@ class PrimalEdgeNorms { const CompactSparseMatrix& compact_matrix_; const VariablesInfo& variables_info_; const BasisFactorization& basis_factorization_; + TimeLimit* time_limit_ = nullptr; // Internal data. GlopParameters parameters_; diff --git a/ortools/glop/rank_one_update.h b/ortools/glop/rank_one_update.h index c6d3046a87..700ee69542 100644 --- a/ortools/glop/rank_one_update.h +++ b/ortools/glop/rank_one_update.h @@ -177,7 +177,7 @@ class RankOneUpdateFactorization { } // y->is_non_zero is always all false before and after this code. - DCHECK(IsAllFalse(y->is_non_zero)); + DCHECK(y->is_non_zero.IsAllFalse()); y->RepopulateSparseMask(); bool use_dense = y->ShouldUseDenseIteration(hypersparse_ratio_); for (int i = elementary_matrices_.size() - 1; i >= 0; --i) { @@ -213,7 +213,7 @@ class RankOneUpdateFactorization { } // d->is_non_zero is always all false before and after this code. - DCHECK(IsAllFalse(d->is_non_zero)); + DCHECK(d->is_non_zero.IsAllFalse()); d->RepopulateSparseMask(); bool use_dense = d->ShouldUseDenseIteration(hypersparse_ratio_); const size_t end = elementary_matrices_.size(); diff --git a/ortools/glop/revised_simplex.cc b/ortools/glop/revised_simplex.cc index a012221b9c..9961a5e474 100644 --- a/ortools/glop/revised_simplex.cc +++ b/ortools/glop/revised_simplex.cc @@ -220,6 +220,7 @@ ABSL_MUST_USE_RESULT Status RevisedSimplex::SolveInternal( [this, time_limit]() { AdvanceDeterministicTime(time_limit); }); SOLVER_LOG(logger_, ""); + primal_edge_norms_.SetTimeLimit(time_limit); if (logger_->LoggingIsEnabled()) { DisplayBasicVariableStatistics(); } @@ -2563,13 +2564,15 @@ void RevisedSimplex::PermuteBasis() { if (col_perm.empty()) return; // Permute basis_. - ApplyColumnPermutationToRowIndexedVector(col_perm, &basis_, &tmp_basis_); + ApplyColumnPermutationToRowIndexedVector(col_perm.const_view(), &basis_, + &tmp_basis_); // Permute dual_pricing_vector_ if needed. if (!dual_pricing_vector_.empty()) { // TODO(user): We need to permute dual_prices_ too now, we recompute // everything one each basis factorization, so this don't matter. - ApplyColumnPermutationToRowIndexedVector(col_perm, &dual_pricing_vector_, + ApplyColumnPermutationToRowIndexedVector(col_perm.const_view(), + &dual_pricing_vector_, &tmp_dual_pricing_vector_); } diff --git a/ortools/glop/update_row.cc b/ortools/glop/update_row.cc index 2db77b8ed6..f73da6b1bc 100644 --- a/ortools/glop/update_row.cc +++ b/ortools/glop/update_row.cc @@ -106,20 +106,21 @@ void UpdateRow::ComputeUpdateRow(RowIndex leaving_row) { // small entries (no complexity changes). const Fractional drop_tolerance = parameters_.drop_tolerance(); unit_row_left_inverse_filtered_non_zeros_.clear(); - const auto view = transposed_matrix_.view(); + const auto matrix_view = transposed_matrix_.view(); if (unit_row_left_inverse_.non_zeros.empty()) { const ColIndex size = unit_row_left_inverse_.values.size(); + const auto values = unit_row_left_inverse_.values.view(); for (ColIndex col(0); col < size; ++col) { - if (std::abs(unit_row_left_inverse_.values[col]) > drop_tolerance) { + if (std::abs(values[col]) > drop_tolerance) { unit_row_left_inverse_filtered_non_zeros_.push_back(col); - num_row_wise_entries += view.ColumnNumEntries(col); + num_row_wise_entries += matrix_view.ColumnNumEntries(col); } } } else { for (const auto e : unit_row_left_inverse_) { if (std::abs(e.coefficient()) > drop_tolerance) { unit_row_left_inverse_filtered_non_zeros_.push_back(e.column()); - num_row_wise_entries += view.ColumnNumEntries(e.column()); + num_row_wise_entries += matrix_view.ColumnNumEntries(e.column()); } } } diff --git a/ortools/lp_data/lp_utils.h b/ortools/lp_data/lp_utils.h index deb104050f..f4b3dc7a65 100644 --- a/ortools/lp_data/lp_utils.h +++ b/ortools/lp_data/lp_utils.h @@ -249,12 +249,17 @@ inline void PermuteWithScratchpad( const IndexType size = input_output->size(); zero_scratchpad->swap(*input_output); input_output->resize(size, 0.0); - for (IndexType index(0); index < size; ++index) { - const Fractional value = (*zero_scratchpad)[index]; + + // Caching the pointers help. + const Fractional* const input = zero_scratchpad->data(); + const IndexType* const perm = permutation.data(); + Fractional* const output = input_output->data(); + for (int i = 0; i < size; ++i) { + DCHECK_GE(perm[i], 0); + DCHECK_LT(perm[i], size); + const Fractional value = input[i]; if (value != 0.0) { - const IndexType permuted_index( - permutation[PermutationIndexType(index.value())].value()); - (*input_output)[permuted_index] = value; + output[perm[i].value()] = value; } } zero_scratchpad->assign(size, 0.0); diff --git a/ortools/lp_data/permutation.h b/ortools/lp_data/permutation.h index ee32a38350..04eb15d029 100644 --- a/ortools/lp_data/permutation.h +++ b/ortools/lp_data/permutation.h @@ -14,6 +14,9 @@ #ifndef OR_TOOLS_LP_DATA_PERMUTATION_H_ #define OR_TOOLS_LP_DATA_PERMUTATION_H_ +#include + +#include "absl/log/check.h" #include "absl/random/random.h" #include "ortools/base/strong_vector.h" #include "ortools/lp_data/lp_types.h" @@ -45,13 +48,13 @@ class Permutation { public: Permutation() : perm_() {} - explicit Permutation(IndexType size) : perm_(size.value(), IndexType(0)) {} + explicit Permutation(IndexType size) : perm_(size, IndexType(0)) {} // This type is neither copyable nor movable. Permutation(const Permutation&) = delete; Permutation& operator=(const Permutation&) = delete; - IndexType size() const { return IndexType(perm_.size()); } + IndexType size() const { return perm_.size(); } bool empty() const { return perm_.empty(); } void clear() { perm_.clear(); } @@ -60,9 +63,7 @@ class Permutation { perm_.resize(size.value(), value); } - void assign(IndexType size, IndexType value) { - perm_.assign(size.value(), value); - } + void assign(IndexType size, IndexType value) { perm_.assign(size, value); } IndexType& operator[](IndexType i) { return perm_[i]; } @@ -89,8 +90,16 @@ class Permutation { // is -1. (Remembering hint: the signature of a swap (a 2-cycle) is -1.) int ComputeSignature() const; + // For hot-loops it might be slighlty faster to cache the pointer and avoid + // bound checking on each [] access. + const IndexType* data() const { return perm_.data(); } + + StrictITISpan const_view() const { + return perm_.view(); + } + private: - util_intops::StrongVector perm_; + StrictITIVector perm_; }; typedef Permutation RowPermutation; @@ -116,16 +125,21 @@ void ApplyInversePermutation(const Permutation& perm, // row-indexed vector v. template void ApplyColumnPermutationToRowIndexedVector( - const Permutation& col_perm, RowIndexedVector* v) { - RowIndexedVector temp_v = *v; - ApplyPermutation(col_perm, temp_v, v); -} - -template -void ApplyColumnPermutationToRowIndexedVector( - const Permutation& col_perm, RowIndexedVector* v, + StrictITISpan col_perm, RowIndexedVector* v, RowIndexedVector* tmp) { - ApplyPermutation(col_perm, *v, tmp); + // Empty size means identity. + const int size = col_perm.size().value(); + if (size == 0) return; + + // Permute into tmp and swap. + DCHECK_EQ(size, v->size()); + + tmp->resize(RowIndex(size)); + const auto* from = v->data(); + auto* to = tmp->data(); + for (int i = 0; i < size; ++i) { + to[col_perm[ColIndex(i)].value()] = from[i]; + } std::swap(*tmp, *v); } @@ -135,7 +149,7 @@ void ApplyColumnPermutationToRowIndexedVector( template void Permutation::PopulateFromInverse(const Permutation& inverse) { - const size_t size = inverse.perm_.size(); + const IndexType size = inverse.perm_.size(); perm_.resize(size); for (IndexType i(0); i < size; ++i) { perm_[inverse[i]] = i; @@ -144,7 +158,7 @@ void Permutation::PopulateFromInverse(const Permutation& inverse) { template void Permutation::PopulateFromIdentity() { - const size_t size = perm_.size(); + const IndexType size = perm_.size(); perm_.resize(size, IndexType(0)); for (IndexType i(0); i < size; ++i) { perm_[i] = i; @@ -159,7 +173,7 @@ void Permutation::PopulateRandomly() { template bool Permutation::Check() const { - const size_t size = perm_.size(); + const size_t size = perm_.size().value(); util_intops::StrongVector visited(size, false); for (IndexType i(0); i < size; ++i) { if (perm_[i] < 0 || perm_[i] >= size) { @@ -177,7 +191,7 @@ bool Permutation::Check() const { template int Permutation::ComputeSignature() const { - const size_t size = perm_.size(); + const size_t size = perm_.size().value(); util_intops::StrongVector visited(size); DCHECK(Check()); int signature = 1; diff --git a/ortools/lp_data/scattered_vector.h b/ortools/lp_data/scattered_vector.h index 4572ba59d3..083109d3f7 100644 --- a/ortools/lp_data/scattered_vector.h +++ b/ortools/lp_data/scattered_vector.h @@ -19,6 +19,7 @@ #include "absl/log/check.h" #include "ortools/lp_data/lp_types.h" +#include "ortools/util/bitset.h" namespace operations_research { namespace glop { @@ -61,7 +62,7 @@ struct ScatteredVector { // Temporary vector used in some sparse computation on the ScatteredVector. // True indicates a possible non-zero value. Note that its state is not always // consistent. - StrictITIVector is_non_zero; + Bitset64 is_non_zero; // In many cases there is a choice between treating the ScatteredVector as // dense or as sparse. By default, dense algorithms are used when the @@ -95,7 +96,7 @@ struct ScatteredVector { void Add(Index index, Fractional value) { values[index] += value; if (!is_non_zero[index] && value != 0.0) { - is_non_zero[index] = true; + is_non_zero.Set(index); non_zeros.push_back(index); non_zeros_are_sorted = false; } @@ -128,20 +129,23 @@ struct ScatteredVector { // Efficiently clears the is_non_zero vector. void ClearSparseMask() { if (ShouldUseDenseIteration()) { - is_non_zero.assign(values.size(), false); + is_non_zero.ClearAndResize(values.size()); } else { - is_non_zero.resize(values.size(), false); + is_non_zero.Resize(values.size()); for (const Index index : non_zeros) { - is_non_zero[index] = false; + is_non_zero.ClearBucket(index); } - DCHECK(IsAllFalse(is_non_zero)); + DCHECK(is_non_zero.IsAllFalse()); } } // Update the is_non_zero vector to be consistent with the non_zeros vector. void RepopulateSparseMask() { ClearSparseMask(); - for (const Index index : non_zeros) is_non_zero[index] = true; + for (const Index index : non_zeros) { + // is_non_zero[index] = true; + is_non_zero.Set(index); + } } // If the proportion of non-zero entries is too large, clears the vector of diff --git a/ortools/lp_data/sparse.cc b/ortools/lp_data/sparse.cc index 3eba029f88..415fa875df 100644 --- a/ortools/lp_data/sparse.cc +++ b/ortools/lp_data/sparse.cc @@ -1481,10 +1481,11 @@ void TriangularMatrix::ComputeRowsToConsiderInSortedOrder( stored.Set(row); } + const auto matrix_view = view(); const auto entry_rows = rows_.view(); for (int i = 0; i < non_zero_rows->size(); ++i) { const RowIndex row = (*non_zero_rows)[i]; - for (const EntryIndex index : Column(RowToColIndex(row))) { + for (const EntryIndex index : matrix_view.Column(RowToColIndex(row))) { ++num_ops; const RowIndex entry_row = entry_rows[index]; if (!stored[entry_row]) { diff --git a/ortools/util/bitset.h b/ortools/util/bitset.h index 5b1706a649..9e2e47b00f 100644 --- a/ortools/util/bitset.h +++ b/ortools/util/bitset.h @@ -592,11 +592,13 @@ class Bitset64 { // the higher order bits are assumed to be 0. void Intersection(const Bitset64& other) { const int min_size = std::min(data_.size(), other.data_.size()); + uint64_t* const d = data_.data(); + const uint64_t* const o = other.data_.data(); for (int i = 0; i < min_size; ++i) { - data_[i] &= other.data_[i]; + d[i] &= o[i]; } for (int i = min_size; i < data_.size(); ++i) { - data_[i] = 0; + d[i] = 0; } } @@ -725,6 +727,11 @@ class Bitset64 { return output; } + bool IsAllFalse() const { + return std::all_of(data_.begin(), data_.end(), + [](uint64_t v) { return v == 0; }); + } + private: // Returns the value of the index type. // This function is specialized below to work with IntType and int64_t. diff --git a/ortools/util/time_limit.h b/ortools/util/time_limit.h index 4599724723..0d25a597ee 100644 --- a/ortools/util/time_limit.h +++ b/ortools/util/time_limit.h @@ -38,7 +38,7 @@ * Enables changing the behavior of the TimeLimit class to use -b usertime * instead of \b walltime. This is mainly useful for benchmarks. */ -OR_DLL ABSL_DECLARE_FLAG(bool, time_limit_use_usertime); +ABSL_DECLARE_FLAG(bool, time_limit_use_usertime); namespace operations_research { @@ -91,7 +91,7 @@ namespace operations_research { */ // TODO(user): The expression "deterministic time" should be replaced with // "number of operations" to avoid confusion with "real" time. -class OR_DLL TimeLimit { +class TimeLimit { public: static const double kSafetyBufferSeconds; // See the .cc for the value. static const int kHistorySize;