diff --git a/cpp/include/cuopt/utilities/timestamp_utils.hpp b/cpp/include/cuopt/utilities/timestamp_utils.hpp new file mode 100644 index 000000000..e539ca999 --- /dev/null +++ b/cpp/include/cuopt/utilities/timestamp_utils.hpp @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace cuopt { +namespace utilities { + +/** + * @brief Check if extra timestamps should be printed based on environment variable + * + * Checks the CUOPT_EXTRA_TIMESTAMPS environment variable once and caches the result. + * Returns true if the environment variable is set to "True", "true", or "1". + * + * @return true if extra timestamps are enabled, false otherwise + */ +bool extraTimestamps(); + +/** + * @brief Get current timestamp as seconds since epoch + * + * @return Current timestamp as a double representing seconds since epoch + */ +double getCurrentTimestamp(); + +/** + * @brief Print a timestamp with label if extra timestamps are enabled + * + * @param label The label to print with the timestamp + */ +void printTimestamp(const std::string& label); + +} // namespace utilities +} // namespace cuopt diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 76a742617..d6868b7d3 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -15,7 +15,8 @@ set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logger_helper.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/version_info.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/version_info.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp) add_subdirectory(linear_programming) add_subdirectory(math_optimization) diff --git a/cpp/src/linear_programming/cuopt_c.cpp b/cpp/src/linear_programming/cuopt_c.cpp index fc77e2323..df1fa1a4b 100644 --- a/cpp/src/linear_programming/cuopt_c.cpp +++ b/cpp/src/linear_programming/cuopt_c.cpp @@ -21,11 +21,13 @@ #include #include #include +#include #include #include +#include #include #include @@ -115,6 +117,8 @@ cuopt_int_t cuOptCreateProblem(cuopt_int_t num_constraints, const char* variable_types, cuOptOptimizationProblem* problem_ptr) { + cuopt::utilities::printTimestamp("CUOPT_CREATE_PROBLEM"); + if (problem_ptr == nullptr || objective_coefficients == nullptr || constraint_matrix_row_offsets == nullptr || constraint_matrix_column_indices == nullptr || constraint_matrix_coefficent_values == nullptr || constraint_sense == nullptr || @@ -170,6 +174,8 @@ cuopt_int_t cuOptCreateRangedProblem(cuopt_int_t num_constraints, const char* variable_types, cuOptOptimizationProblem* problem_ptr) { + cuopt::utilities::printTimestamp("CUOPT_CREATE_PROBLEM"); + if (problem_ptr == nullptr || objective_coefficients == nullptr || constraint_matrix_row_offsets == nullptr || constraint_matrix_column_indices == nullptr || constraint_matrix_coefficent_values == nullptr || constraint_lower_bounds == nullptr || @@ -596,6 +602,8 @@ cuopt_int_t cuOptSolve(cuOptOptimizationProblem problem, cuOptSolverSettings settings, cuOptSolution* solution_ptr) { + cuopt::utilities::printTimestamp("CUOPT_SOLVE_START"); + if (problem == nullptr) { return CUOPT_INVALID_ARGUMENT; } if (settings == nullptr) { return CUOPT_INVALID_ARGUMENT; } if (solution_ptr == nullptr) { return CUOPT_INVALID_ARGUMENT; } @@ -614,6 +622,9 @@ cuopt_int_t cuOptSolve(cuOptOptimizationProblem problem, solution_and_stream_view->mip_solution_ptr = new mip_solution_t( solve_mip(*op_problem, mip_settings)); *solution_ptr = static_cast(solution_and_stream_view); + + cuopt::utilities::printTimestamp("CUOPT_SOLVE_RETURN"); + return static_cast( solution_and_stream_view->mip_solution_ptr->get_error_status().get_error_type()); } else { @@ -629,6 +640,9 @@ cuopt_int_t cuOptSolve(cuOptOptimizationProblem problem, new optimization_problem_solution_t( solve_lp(*op_problem, pdlp_settings)); *solution_ptr = static_cast(solution_and_stream_view); + + cuopt::utilities::printTimestamp("CUOPT_SOLVE_RETURN"); + return static_cast( solution_and_stream_view->lp_solution_ptr->get_error_status().get_error_type()); } diff --git a/cpp/src/utilities/timestamp_utils.cpp b/cpp/src/utilities/timestamp_utils.cpp new file mode 100644 index 000000000..e3dd1abee --- /dev/null +++ b/cpp/src/utilities/timestamp_utils.cpp @@ -0,0 +1,61 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +namespace cuopt { +namespace utilities { + +bool extraTimestamps() +{ + static bool initialized = false; + static bool enableTimestamps = false; + + if (!initialized) { + const char* envValue = std::getenv("CUOPT_EXTRA_TIMESTAMPS"); + if (envValue != nullptr) { + std::string value(envValue); + enableTimestamps = (value == "True" || value == "true" || value == "1"); + } + initialized = true; + } + + return enableTimestamps; +} + +double getCurrentTimestamp() +{ + auto now = std::chrono::system_clock::now(); + return std::chrono::duration(now.time_since_epoch()).count(); +} + +void printTimestamp(const std::string& label) +{ + if (extraTimestamps()) { + std::cout << std::fixed << std::setprecision(6); + std::cout << std::endl << label << ": " << getCurrentTimestamp() << std::endl; + } +} + +} // namespace utilities +} // namespace cuopt diff --git a/python/cuopt/cuopt/linear_programming/data_model/data_model.py b/python/cuopt/cuopt/linear_programming/data_model/data_model.py index d5e43302d..6279c3167 100644 --- a/python/cuopt/cuopt/linear_programming/data_model/data_model.py +++ b/python/cuopt/cuopt/linear_programming/data_model/data_model.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os +import time + from . import data_model_wrapper from .utilities import catch_cuopt_exception @@ -151,6 +154,12 @@ class DataModel(data_model_wrapper.DataModel): """ def __init__(self): + if os.environ.get("CUOPT_EXTRA_TIMESTAMPS", False) in ( + True, + "True", + "true", + ): + print(f"CUOPT_CREATE_PROBLEM: {time.time()}") super().__init__() @catch_cuopt_exception diff --git a/python/cuopt/cuopt/linear_programming/problem.py b/python/cuopt/cuopt/linear_programming/problem.py index 9ad77ca58..9baa965de 100644 --- a/python/cuopt/cuopt/linear_programming/problem.py +++ b/python/cuopt/cuopt/linear_programming/problem.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. + import copy from enum import Enum @@ -767,6 +768,8 @@ def _from_data_model(self, dm): raise Exception("Couldn't initialize constraints") def _to_data_model(self): + dm = data_model.DataModel() + # iterate through the constraints and construct the constraint matrix n = len(self.vars) self.rhs = [] @@ -812,7 +815,6 @@ def _to_data_model(self): self.var_names.append(self.vars[j].VariableName) # Initialize datamodel - dm = data_model.DataModel() dm.set_csr_constraint_matrix( np.array(self.constraint_csr_matrix["values"]), np.array(self.constraint_csr_matrix["column_indices"]), diff --git a/python/cuopt/cuopt/linear_programming/solver/solver.py b/python/cuopt/cuopt/linear_programming/solver/solver.py index 12921ae7c..61491c96f 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver.py +++ b/python/cuopt/cuopt/linear_programming/solver/solver.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os +import time + from cuopt.linear_programming.solver import solver_wrapper from cuopt.linear_programming.solver_settings import SolverSettings from cuopt.utilities import catch_cuopt_exception @@ -78,6 +81,15 @@ def Solve(data_model, solver_settings=None): >>> # Print the value of one specific variable >>> print(solution.get_vars()["var_name"]) """ + + emit_stamps = os.environ.get("CUOPT_EXTRA_TIMESTAMPS", False) in ( + True, + "True", + "true", + ) + if emit_stamps: + print(f"CUOPT_SOLVE_START: {time.time()}") + if solver_settings is None: solver_settings = SolverSettings() @@ -95,11 +107,14 @@ def is_mip(var_types): # Mixed types - fallback to comprehensive check return any(vt == "I" or vt == b"I" for vt in var_types) - return solver_wrapper.Solve( + s = solver_wrapper.Solve( data_model, solver_settings, mip=is_mip(data_model.get_variable_types()), ) + if emit_stamps: + print(f"CUOPT_SOLVE_RETURN: {time.time()}") + return s @catch_cuopt_exception