Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ ARGS=$*
REPODIR=$(cd "$(dirname "$0")"; pwd)
LIBCUOPT_BUILD_DIR=${LIBCUOPT_BUILD_DIR:=${REPODIR}/cpp/build}

VALIDARGS="clean codegen libcuopt cuopt_grpc_server cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-grpc-build --skip-fatbin-write --host-lineinfo --split-compile [--cmake-args=\\\"<args>\\\"] [--cache-tool=<tool>] --install --allgpuarch --ci-only-arch --show_depr_warn -h --help"
VALIDARGS="clean codegen libcuopt cuopt_grpc_server cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-grpc-build --skip-distributed-pdlp-build --skip-fatbin-write --host-lineinfo --split-compile [--cmake-args=\\\"<args>\\\"] [--cache-tool=<tool>] --install --allgpuarch --ci-only-arch --show_depr_warn -h --help"
HELP="$0 [<target> ...] [<flag> ...]
where <target> is:
clean - remove all existing build artifacts and configuration (start over)
Expand Down Expand Up @@ -43,6 +43,7 @@ HELP="$0 [<target> ...] [<flag> ...]
--skip-tests-build - disable building of all tests
--skip-routing-build - skip building routing components
--skip-grpc-build - skip building gRPC and protobuf components (auto-enabled with -tsan)
--skip-distributed-pdlp-build - skip distributed PDLP, NCCL, and KaMinPar
--skip-fatbin-write - skip the fatbin write
--host-lineinfo - build with debug line information for host code
--split-compile - opt in to nvcc split compilation; builds may be nondeterministic
Expand Down Expand Up @@ -84,6 +85,7 @@ SKIP_C_PYTHON_ADAPTERS=0
SKIP_TESTS_BUILD=0
SKIP_ROUTING_BUILD=0
SKIP_GRPC_BUILD=0
SKIP_DISTRIBUTED_PDLP_BUILD=0
WRITE_FATBIN=1
HOST_LINEINFO=0
CACHE_ARGS=()
Expand Down Expand Up @@ -255,6 +257,9 @@ fi
if hasArg --skip-grpc-build; then
SKIP_GRPC_BUILD=1
fi
if hasArg --skip-distributed-pdlp-build; then
SKIP_DISTRIBUTED_PDLP_BUILD=1
fi
if hasArg --skip-fatbin-write; then
WRITE_FATBIN=0
fi
Expand Down Expand Up @@ -399,6 +404,7 @@ if buildAll || hasArg libcuopt || hasArg cuopt_grpc_server; then
-DBUILD_TESTS=$((1 - ${SKIP_TESTS_BUILD})) \
-DSKIP_ROUTING_BUILD=${SKIP_ROUTING_BUILD} \
-DSKIP_GRPC_BUILD=${SKIP_GRPC_BUILD} \
-DSKIP_DISTRIBUTED_PDLP_BUILD=${SKIP_DISTRIBUTED_PDLP_BUILD} \
-DWRITE_FATBIN=${WRITE_FATBIN} \
-DHOST_LINEINFO=${HOST_LINEINFO} \
-DPARALLEL_LEVEL="${PARALLEL_LEVEL}" \
Expand Down
98 changes: 55 additions & 43 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ option(BUILD_LP_ONLY "Build only linear programming components, exclude routing
option(SKIP_C_PYTHON_ADAPTERS "Skip building C and Python adapter files (cython_solve.cu and cuopt_c.cpp)" OFF)
option(SKIP_ROUTING_BUILD "Skip building routing components" OFF)
option(SKIP_GRPC_BUILD "Skip building gRPC and protobuf components" OFF)
option(SKIP_DISTRIBUTED_PDLP_BUILD "Skip distributed PDLP, NCCL, and KaMinPar" OFF)
option(WRITE_FATBIN "Enable fatbin writing" ON)
option(HOST_LINEINFO "Build with debug line information for host code" OFF)

Expand All @@ -62,9 +63,14 @@ message(VERBOSE "cuOpt: Disable OpenMP: ${DISABLE_OPENMP}")
message(VERBOSE "cuOpt: Build LP-only mode: ${BUILD_LP_ONLY}")
message(VERBOSE "cuOpt: Skip C/Python adapters: ${SKIP_C_PYTHON_ADAPTERS}")
message(VERBOSE "cuOpt: Skip routing build: ${SKIP_ROUTING_BUILD}")
message(VERBOSE "cuOpt: Skip distributed PDLP build: ${SKIP_DISTRIBUTED_PDLP_BUILD}")
message(VERBOSE "cuOpt: Build with debug line information for host code: ${HOST_LINEINFO}")
message(VERBOSE "cuOpt: fatbin: ${WRITE_FATBIN}")

if (NOT SKIP_DISTRIBUTED_PDLP_BUILD)
add_compile_definitions(CUOPT_ENABLE_DISTRIBUTED_PDLP)
endif ()

# ##################################################################################################
# - compiler options ------------------------------------------------------------------------------

Expand Down Expand Up @@ -333,39 +339,41 @@ find_package(CUDSS REQUIRED)
# - NCCL (multi-GPU distributed PDLP) -------------------------------------------------------------
# NCCL is shipped via the conda env; no canonical CMake config target, so look it
# up by name in the standard lib paths (plus CONDA_PREFIX as a hint).
set(NCCL_HINT_PREFIXES "")
if (DEFINED ENV{CONDA_PREFIX} AND NOT "$ENV{CONDA_PREFIX}" STREQUAL "")
list(APPEND NCCL_HINT_PREFIXES "$ENV{CONDA_PREFIX}")
endif ()
find_path(NCCL_INCLUDE_DIR
NAMES nccl.h
HINTS ${NCCL_HINT_PREFIXES}
PATH_SUFFIXES include
)
find_library(NCCL_LIBRARY
NAMES nccl
HINTS ${NCCL_HINT_PREFIXES}
PATH_SUFFIXES lib lib64
)
if (NOT NCCL_INCLUDE_DIR OR NOT NCCL_LIBRARY)
message(FATAL_ERROR "NCCL not found. Looked in ${NCCL_HINT_PREFIXES}. Install nccl-dev / libnccl-dev in the active env.")
endif ()
add_library(nccl_external UNKNOWN IMPORTED GLOBAL)
set_target_properties(nccl_external PROPERTIES
IMPORTED_LOCATION "${NCCL_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${NCCL_INCLUDE_DIR}"
)
message(STATUS "Using NCCL: ${NCCL_LIBRARY}")

# ##################################################################################################
# - KaMinPar (multi-threaded partitioning for distributed PDLP) ------------------------------------
# Brought in the RAPIDS way (rapids_cpm_find): uses an installed KaMinPar (deb/rpm/conda,
# discovered via its CMake config) if present, otherwise builds the pinned source via CPM.
include(cmake/thirdparty/get_kaminpar.cmake)
if (NOT TARGET KaMinPar::KaMinPar)
message(FATAL_ERROR "KaMinPar::KaMinPar was not made available by get_kaminpar.cmake")
if (NOT SKIP_DISTRIBUTED_PDLP_BUILD)
set(NCCL_HINT_PREFIXES "")
if (DEFINED ENV{CONDA_PREFIX} AND NOT "$ENV{CONDA_PREFIX}" STREQUAL "")
list(APPEND NCCL_HINT_PREFIXES "$ENV{CONDA_PREFIX}")
endif ()
find_path(NCCL_INCLUDE_DIR
NAMES nccl.h
HINTS ${NCCL_HINT_PREFIXES}
PATH_SUFFIXES include
)
find_library(NCCL_LIBRARY
NAMES nccl
HINTS ${NCCL_HINT_PREFIXES}
PATH_SUFFIXES lib lib64
)
if (NOT NCCL_INCLUDE_DIR OR NOT NCCL_LIBRARY)
message(FATAL_ERROR "NCCL not found. Looked in ${NCCL_HINT_PREFIXES}. Install nccl-dev / libnccl-dev in the active env.")
endif ()
add_library(nccl_external UNKNOWN IMPORTED GLOBAL)
set_target_properties(nccl_external PROPERTIES
IMPORTED_LOCATION "${NCCL_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${NCCL_INCLUDE_DIR}"
)
message(STATUS "Using NCCL: ${NCCL_LIBRARY}")

# ##################################################################################################
# - KaMinPar (multi-threaded partitioning for distributed PDLP) ------------------------------------
# Brought in the RAPIDS way (rapids_cpm_find): uses an installed KaMinPar (deb/rpm/conda,
# discovered via its CMake config) if present, otherwise builds the pinned source via CPM.
include(cmake/thirdparty/get_kaminpar.cmake)
if (NOT TARGET KaMinPar::KaMinPar)
message(FATAL_ERROR "KaMinPar::KaMinPar was not made available by get_kaminpar.cmake")
endif ()
message(STATUS "Using KaMinPar (distributed PDLP graph partitioner)")
endif ()
message(STATUS "Using KaMinPar (distributed PDLP graph partitioner)")

# ##################################################################################################
# - gRPC and Protobuf setup -----------------------------------------------------------------------
Expand Down Expand Up @@ -698,8 +706,6 @@ add_dependencies(cuopt_objs PSLP)
# "target KaMinPar is not in any export set"). libKaMinPar.a is self-contained (the
# kaminpar-common OBJECT lib is archived into it); we only need its public headers
# (<kaminpar.h>, which pulls in stdlib + TBB) at compile time.
target_include_directories(cuopt_objs SYSTEM PRIVATE
$<TARGET_PROPERTY:KaMinPar::KaMinPar,INTERFACE_INCLUDE_DIRECTORIES>)
# partitioner.cpp includes <kaminpar.h>. Because KaMinPar is linked by file, cuopt
# does NOT inherit KaMinPar's PUBLIC compile definitions, so we must replicate the ones that
# affect what <kaminpar.h> compiles to:
Expand All @@ -709,11 +715,15 @@ target_include_directories(cuopt_objs SYSTEM PRIVATE
# with the KAMINPAR_64BIT_* options in get_kaminpar.cmake;
# otherwise the public API types (e.g. copy_graph's span<>
# widths) mismatch the prebuilt archive => undefined refs.
target_compile_definitions(cuopt_objs PRIVATE TBB_PREVIEW_GLOBAL_CONTROL KAMINPAR_64BIT_EDGE_IDS)
# Real (non-imported) target only exists when KaMinPar is built from source via CPM;
# force it to build before the objects that include its headers since it is EXCLUDE_FROM_ALL.
if (TARGET KaMinPar)
if (NOT SKIP_DISTRIBUTED_PDLP_BUILD)
target_include_directories(cuopt_objs SYSTEM PRIVATE
$<TARGET_PROPERTY:KaMinPar::KaMinPar,INTERFACE_INCLUDE_DIRECTORIES>)
target_compile_definitions(cuopt_objs PRIVATE TBB_PREVIEW_GLOBAL_CONTROL KAMINPAR_64BIT_EDGE_IDS)
if (TARGET KaMinPar)
add_dependencies(cuopt_objs KaMinPar)
endif ()
endif ()

# ##################################################################################################
Expand Down Expand Up @@ -790,7 +800,6 @@ if (BUILD_TESTS)
OpenMP::OpenMP_CXX
PRIVATE
${CUOPT_PRIVATE_CUDA_LIBS}
nccl_external
$<$<BOOL:${CUOPT_ENABLE_GRPC}>:protobuf::libprotobuf>
$<$<BOOL:${CUOPT_ENABLE_GRPC}>:gRPC::grpc++>
)
Expand All @@ -813,9 +822,11 @@ if (BUILD_TESTS)
)
target_link_libraries(cuopt_static PRIVATE $<TARGET_FILE:PSLP>)
add_dependencies(cuopt_static PSLP)
target_link_libraries(cuopt_static PRIVATE $<TARGET_FILE:KaMinPar::KaMinPar>)
if (TARGET KaMinPar)
add_dependencies(cuopt_static KaMinPar)
if (NOT SKIP_DISTRIBUTED_PDLP_BUILD)
target_link_libraries(cuopt_static PRIVATE nccl_external $<TARGET_FILE:KaMinPar::KaMinPar>)
if (TARGET KaMinPar)
add_dependencies(cuopt_static KaMinPar)
endif ()
endif ()
add_subdirectory(tests)
endif (BUILD_TESTS)
Expand Down Expand Up @@ -848,7 +859,6 @@ target_link_libraries(cuopt
${CUDSS_LIB_FILE}
PRIVATE
${CUOPT_PRIVATE_CUDA_LIBS}
nccl_external
simde::simde
OpenMP::OpenMP_CXX
OpenMP::OpenMP_CUDA
Expand All @@ -857,9 +867,11 @@ target_link_libraries(cuopt
)
target_link_libraries(cuopt PRIVATE $<TARGET_FILE:PSLP>)
add_dependencies(cuopt PSLP)
target_link_libraries(cuopt PRIVATE $<TARGET_FILE:KaMinPar::KaMinPar>)
if (TARGET KaMinPar)
if (NOT SKIP_DISTRIBUTED_PDLP_BUILD)
target_link_libraries(cuopt PRIVATE nccl_external $<TARGET_FILE:KaMinPar::KaMinPar>)
if (TARGET KaMinPar)
add_dependencies(cuopt KaMinPar)
endif ()
endif ()
# Propagate compile definitions that consumers need when including cuopt headers.
# These were on cuopt directly before the cuopt_objs refactor; $<TARGET_OBJECTS:...>
Expand Down
15 changes: 10 additions & 5 deletions cpp/src/pdlp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,18 @@ set(LP_CORE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/termination_strategy/convergence_information.cu
${CMAKE_CURRENT_SOURCE_DIR}/optimal_batch_size_handler/optimal_batch_size_handler.cu
${CMAKE_CURRENT_SOURCE_DIR}/utilities/ping_pong_graph.cu
${CMAKE_CURRENT_SOURCE_DIR}/distributed_pdlp/shard.cu
${CMAKE_CURRENT_SOURCE_DIR}/distributed_pdlp/multi_gpu_engine.cu
${CMAKE_CURRENT_SOURCE_DIR}/distributed_pdlp/distributed_algorithms.cu
${CMAKE_CURRENT_SOURCE_DIR}/distributed_pdlp/distributed_utils.cu
${CMAKE_CURRENT_SOURCE_DIR}/distributed_pdlp/partitioner.cpp
)

if(NOT SKIP_DISTRIBUTED_PDLP_BUILD)
list(APPEND LP_CORE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/distributed_pdlp/shard.cu
${CMAKE_CURRENT_SOURCE_DIR}/distributed_pdlp/multi_gpu_engine.cu
${CMAKE_CURRENT_SOURCE_DIR}/distributed_pdlp/distributed_algorithms.cu
${CMAKE_CURRENT_SOURCE_DIR}/distributed_pdlp/distributed_utils.cu
${CMAKE_CURRENT_SOURCE_DIR}/distributed_pdlp/partitioner.cpp
)
endif()

# C and Python adapter files
set(LP_ADAPTER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/utilities/cython_solve.cu
Expand Down
26 changes: 26 additions & 0 deletions cpp/src/pdlp/pdhg.cu
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
#include <pdlp/distributed_pdlp/multi_gpu_engine.hpp>
#endif
#include <pdlp/pdhg.hpp>
#include <pdlp/pdlp.cuh>
#include <pdlp/pdlp_climber_strategy.hpp>
Expand Down Expand Up @@ -510,10 +512,12 @@ void pdhg_solver_t<i_t, f_t>::compute_At_y()

// Multi-GPU dispatch: when the master pdhg has an engine, drive halo
// exchange + per-shard SpMV via the engine.
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
if (is_distributed_master()) {
mgpu_engine_->distributed_compute_At_y();
return;
}
#endif

if (!batch_mode_) {
if constexpr (std::is_same_v<f_t, double>) {
Expand Down Expand Up @@ -568,10 +572,12 @@ void pdhg_solver_t<i_t, f_t>::compute_A_x()
// Multi-GPU dispatch: see compute_At_y. The engine halo-updates the
// reflected_primal vector (the buffer this SpMV reads) and then drives
// per-shard local cusparse SpMV.
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
if (is_distributed_master()) {
mgpu_engine_->distributed_compute_A_x();
return;
}
#endif

if (!batch_mode_) {
if constexpr (std::is_same_v<f_t, double>) {
Expand Down Expand Up @@ -1229,23 +1235,29 @@ void pdhg_solver_t<i_t, f_t>::compute_next_primal_dual_solution_reflected(

using f_t2 = typename type_2<f_t>::type;

#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
if (is_distributed_master()) { mgpu_engine_->sync_await_shards(stream_view_); }
#endif

// Compute next primal solution reflected.

if (should_major) {
graph_all.run(should_major, [&]() {
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
// Adds all the shards streams into the graph capture
if (is_distributed_master()) { mgpu_engine_->graph_capture_fork_to_shards(stream_view_); }
#endif

compute_At_y();

if (is_distributed_master()) {
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
mgpu_engine_->for_each_shard([](auto& shard) {
auto& sub_pdlp = *shard.sub_pdlp;
sub_pdlp.pdhg_solver_.primal_reflected_major_projection_transform(
sub_pdlp.get_primal_step_size());
});
#endif
} else if (!batch_mode_) {
primal_reflected_major_projection_transform(primal_step_size);
} else {
Expand Down Expand Up @@ -1307,11 +1319,13 @@ void pdhg_solver_t<i_t, f_t>::compute_next_primal_dual_solution_reflected(
compute_A_x();

if (is_distributed_master()) {
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
mgpu_engine_->for_each_shard([](auto& shard) {
auto& sub_pdlp = *shard.sub_pdlp;
sub_pdlp.pdhg_solver_.dual_reflected_major_projection_transform(
sub_pdlp.get_dual_step_size());
});
#endif
} else if (!batch_mode_) {
dual_reflected_major_projection_transform(dual_step_size);
} else {
Expand All @@ -1338,13 +1352,17 @@ void pdhg_solver_t<i_t, f_t>::compute_next_primal_dual_solution_reflected(
// Multi-GPU: close the fork by joining every shard stream back into
// the master stream so cudaStreamEndCapture sees a single graph
// spanning all streams.
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
if (is_distributed_master()) { mgpu_engine_->graph_capture_join_from_shards(stream_view_); }
#endif
});

} else {
graph_all.run(should_major, [&]() {
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
// Same reason as above, adds all the shards streams into the graph capture
if (is_distributed_master()) { mgpu_engine_->graph_capture_fork_to_shards(stream_view_); }
#endif

// Compute next primal
compute_At_y();
Expand All @@ -1358,11 +1376,13 @@ void pdhg_solver_t<i_t, f_t>::compute_next_primal_dual_solution_reflected(
#endif

if (is_distributed_master()) {
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
mgpu_engine_->for_each_shard([](auto& shard) {
auto& sub_pdlp = *shard.sub_pdlp;
sub_pdlp.pdhg_solver_.primal_reflected_projection_transform(
sub_pdlp.get_primal_step_size());
});
#endif
} else if (!batch_mode_) {
primal_reflected_projection_transform(primal_step_size);
} else {
Expand Down Expand Up @@ -1425,10 +1445,12 @@ void pdhg_solver_t<i_t, f_t>::compute_next_primal_dual_solution_reflected(
compute_A_x();

if (is_distributed_master()) {
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
mgpu_engine_->for_each_shard([](auto& shard) {
auto& sub_pdlp = *shard.sub_pdlp;
sub_pdlp.pdhg_solver_.dual_reflected_projection_transform(sub_pdlp.get_dual_step_size());
});
#endif
} else if (!batch_mode_) {
dual_reflected_projection_transform(dual_step_size);
} else {
Expand All @@ -1449,12 +1471,16 @@ void pdhg_solver_t<i_t, f_t>::compute_next_primal_dual_solution_reflected(
print("reflected_dual_", reflected_dual_);
#endif

#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
if (is_distributed_master()) { mgpu_engine_->graph_capture_join_from_shards(stream_view_); }
#endif
});
}

// sync to master stream after the graph is captured
#ifdef CUOPT_ENABLE_DISTRIBUTED_PDLP
if (is_distributed_master()) { mgpu_engine_->sync_await_master(stream_view_); }
#endif
}

template <typename i_t, typename f_t>
Expand Down
Loading
Loading