Skip to content

Commit e46028c

Browse files
committed
remote execution implementation
cuopt uses embedded grpc client to solve problems on a remote server
1 parent 0aa1b31 commit e46028c

39 files changed

Lines changed: 16840 additions & 125 deletions

GRPC_ARCHITECTURE.md

Lines changed: 392 additions & 0 deletions
Large diffs are not rendered by default.

SERVER_ARCHITECTURE.md

Lines changed: 308 additions & 0 deletions
Large diffs are not rendered by default.

build.sh

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ REPODIR=$(cd "$(dirname "$0")"; pwd)
1515
LIBCUOPT_BUILD_DIR=${LIBCUOPT_BUILD_DIR:=${REPODIR}/cpp/build}
1616
LIBMPS_PARSER_BUILD_DIR=${LIBMPS_PARSER_BUILD_DIR:=${REPODIR}/cpp/libmps_parser/build}
1717

18-
VALIDARGS="clean libcuopt libmps_parser cuopt_mps_parser 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-fatbin-write --host-lineinfo [--cmake-args=\\\"<args>\\\"] [--cache-tool=<tool>] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help"
18+
VALIDARGS="clean libcuopt cuopt_grpc_server libmps_parser cuopt_mps_parser 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-fatbin-write --host-lineinfo [--cmake-args=\\\"<args>\\\"] [--cache-tool=<tool>] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help"
1919
HELP="$0 [<target> ...] [<flag> ...]
2020
where <target> is:
2121
clean - remove all existing build artifacts and configuration (start over)
2222
libcuopt - build the cuopt C++ code
23+
cuopt_grpc_server - build only the gRPC server binary (configures + builds libcuopt as needed)
2324
libmps_parser - build the libmps_parser C++ code
2425
cuopt_mps_parser - build the cuopt_mps_parser python package
2526
cuopt - build the cuopt Python package
@@ -358,8 +359,8 @@ if buildAll || hasArg libmps_parser; then
358359
fi
359360

360361
################################################################################
361-
# Configure, build, and install libcuopt
362-
if buildAll || hasArg libcuopt; then
362+
# Configure and build libcuopt (and optionally just the gRPC server)
363+
if buildAll || hasArg libcuopt || hasArg cuopt_grpc_server; then
363364
mkdir -p "${LIBCUOPT_BUILD_DIR}"
364365
cd "${LIBCUOPT_BUILD_DIR}"
365366
cmake -DDEFINE_ASSERT=${DEFINE_ASSERT} \
@@ -386,7 +387,10 @@ if buildAll || hasArg libcuopt; then
386387
"${EXTRA_CMAKE_ARGS[@]}" \
387388
"${REPODIR}"/cpp
388389
JFLAG="${PARALLEL_LEVEL:+-j${PARALLEL_LEVEL}}"
389-
if hasArg -n; then
390+
if hasArg cuopt_grpc_server && ! hasArg libcuopt && ! buildAll; then
391+
# Build only the gRPC server (ninja resolves libcuopt as a dependency)
392+
cmake --build "${LIBCUOPT_BUILD_DIR}" --target cuopt_grpc_server ${VERBOSE_FLAG} ${JFLAG}
393+
elif hasArg -n; then
390394
# Manual make invocation to start its jobserver
391395
make ${JFLAG} -C "${REPODIR}/cpp" LIBCUOPT_BUILD_DIR="${LIBCUOPT_BUILD_DIR}" VERBOSE_FLAG="${VERBOSE_FLAG}" PARALLEL_LEVEL="${PARALLEL_LEVEL}" ninja-build
392396
else

conda/recipes/libcuopt/recipe.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ cache:
7676
- tbb-devel
7777
- zlib
7878
- bzip2
79+
- grpc-cpp
80+
- libprotobuf
81+
- libuuid
7982
host:
8083
- cpp-argparse
8184
- cuda-version =${{ cuda_version }}
@@ -90,6 +93,8 @@ cache:
9093
- tbb-devel
9194
- zlib
9295
- bzip2
96+
- grpc-cpp
97+
- libprotobuf
9398

9499
outputs:
95100
- package:
@@ -164,13 +169,17 @@ outputs:
164169
- libcublas
165170
- libcudss-dev >=0.7
166171
- libcusparse-dev
172+
- grpc-cpp
173+
- libprotobuf
167174
run:
168175
- ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="x") }}
169176
- ${{ pin_subpackage("libmps-parser", exact=True) }}
170177
- libboost-devel
171178
- librmm =${{ minor_version }}
172179
- cuda-nvrtc
173180
- libcudss
181+
- grpc-cpp
182+
- libprotobuf
174183
ignore_run_exports:
175184
by_name:
176185
- cuda-nvtx
@@ -212,6 +221,8 @@ outputs:
212221
- libcublas
213222
- libcudss-dev >=0.7
214223
- libcusparse-dev
224+
- grpc-cpp
225+
- libprotobuf
215226
run:
216227
- ${{ pin_subpackage("libcuopt", exact=True) }}
217228
- ${{ pin_subpackage("libmps-parser", exact=True) }}

cpp/CMakeLists.txt

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,92 @@ create_logger_macros(CUOPT "cuopt::default_logger()" include/cuopt)
255255

256256
find_package(CUDSS REQUIRED)
257257

258+
# ##################################################################################################
259+
# - gRPC and Protobuf setup (REQUIRED) ------------------------------------------------------------
260+
261+
# gRPC is required for this branch - it provides remote execution features
262+
# gRPC can come from either:
263+
# - an installed CMake package (gRPCConfig.cmake), or
264+
# - an in-tree build (e.g. python/libcuopt uses FetchContent(grpc), which defines gRPC::grpc++).
265+
if(NOT TARGET gRPC::grpc++)
266+
find_package(gRPC CONFIG REQUIRED)
267+
endif()
268+
269+
# Find Protobuf (should come with gRPC, but verify)
270+
if(NOT TARGET protobuf::libprotobuf)
271+
find_package(protobuf CONFIG REQUIRED)
272+
endif()
273+
274+
set(CUOPT_ENABLE_GRPC ON)
275+
add_compile_definitions(CUOPT_ENABLE_GRPC)
276+
message(STATUS "gRPC enabled (target gRPC::grpc++ is available)")
277+
278+
# Find protoc compiler (provided by config package or target)
279+
if(TARGET protobuf::protoc)
280+
get_target_property(_PROTOBUF_PROTOC protobuf::protoc IMPORTED_LOCATION_RELEASE)
281+
if(NOT _PROTOBUF_PROTOC)
282+
get_target_property(_PROTOBUF_PROTOC protobuf::protoc IMPORTED_LOCATION)
283+
endif()
284+
else()
285+
find_package(protobuf CONFIG REQUIRED)
286+
get_target_property(_PROTOBUF_PROTOC protobuf::protoc IMPORTED_LOCATION_RELEASE)
287+
if(NOT _PROTOBUF_PROTOC)
288+
get_target_property(_PROTOBUF_PROTOC protobuf::protoc IMPORTED_LOCATION)
289+
endif()
290+
endif()
291+
292+
if(NOT _PROTOBUF_PROTOC)
293+
message(FATAL_ERROR "protoc not found (Protobuf_PROTOC_EXECUTABLE is empty)")
294+
endif()
295+
296+
# Find grpc_cpp_plugin
297+
if(TARGET grpc_cpp_plugin)
298+
set(_GRPC_CPP_PLUGIN_EXECUTABLE "$<TARGET_FILE:grpc_cpp_plugin>")
299+
else()
300+
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
301+
if(NOT _GRPC_CPP_PLUGIN_EXECUTABLE)
302+
message(FATAL_ERROR "grpc_cpp_plugin not found")
303+
endif()
304+
endif()
305+
306+
# Generate C++ code from cuopt_remote.proto (base message definitions)
307+
set(PROTO_FILE "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/cuopt_remote.proto")
308+
set(PROTO_SRCS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_remote.pb.cc")
309+
set(PROTO_HDRS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_remote.pb.h")
310+
311+
add_custom_command(
312+
OUTPUT "${PROTO_SRCS}" "${PROTO_HDRS}"
313+
COMMAND ${_PROTOBUF_PROTOC}
314+
ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR}
315+
--proto_path ${CMAKE_CURRENT_SOURCE_DIR}/src/grpc
316+
${PROTO_FILE}
317+
DEPENDS ${PROTO_FILE}
318+
COMMENT "Generating C++ code from cuopt_remote.proto"
319+
VERBATIM
320+
)
321+
322+
# Generate gRPC service code from cuopt_remote_service.proto
323+
set(GRPC_PROTO_FILE "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/cuopt_remote_service.proto")
324+
set(GRPC_PROTO_SRCS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_remote_service.pb.cc")
325+
set(GRPC_PROTO_HDRS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_remote_service.pb.h")
326+
set(GRPC_SERVICE_SRCS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_remote_service.grpc.pb.cc")
327+
set(GRPC_SERVICE_HDRS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_remote_service.grpc.pb.h")
328+
329+
add_custom_command(
330+
OUTPUT "${GRPC_PROTO_SRCS}" "${GRPC_PROTO_HDRS}" "${GRPC_SERVICE_SRCS}" "${GRPC_SERVICE_HDRS}"
331+
COMMAND ${_PROTOBUF_PROTOC}
332+
ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR}
333+
--grpc_out ${CMAKE_CURRENT_BINARY_DIR}
334+
--plugin=protoc-gen-grpc=${_GRPC_CPP_PLUGIN_EXECUTABLE}
335+
--proto_path ${CMAKE_CURRENT_SOURCE_DIR}/src/grpc
336+
${GRPC_PROTO_FILE}
337+
DEPENDS ${GRPC_PROTO_FILE} ${PROTO_FILE}
338+
COMMENT "Generating gRPC C++ code from cuopt_remote_service.proto"
339+
VERBATIM
340+
)
341+
342+
message(STATUS "gRPC protobuf code generation configured")
343+
258344
if(BUILD_TESTS)
259345
include(cmake/thirdparty/get_gtest.cmake)
260346
endif()
@@ -264,6 +350,20 @@ add_subdirectory(src)
264350
if (HOST_LINEINFO)
265351
set_source_files_properties(${CUOPT_SRC_FILES} DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTIES COMPILE_OPTIONS "-g1")
266352
endif()
353+
354+
# Add gRPC mapper files and generated protobuf sources
355+
list(APPEND CUOPT_SRC_FILES
356+
${PROTO_SRCS}
357+
${GRPC_PROTO_SRCS}
358+
${GRPC_SERVICE_SRCS}
359+
src/grpc/grpc_problem_mapper.cu
360+
src/grpc/grpc_solution_mapper.cu
361+
src/grpc/grpc_settings_mapper.cu
362+
src/grpc/grpc_service_mapper.cu
363+
src/grpc/client/grpc_client.cu
364+
src/grpc/client/solve_remote.cu
365+
)
366+
267367
add_library(cuopt SHARED
268368
${CUOPT_SRC_FILES}
269369
)
@@ -315,6 +415,9 @@ target_include_directories(cuopt
315415
PRIVATE
316416
"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty"
317417
"${CMAKE_CURRENT_SOURCE_DIR}/src"
418+
"${CMAKE_CURRENT_SOURCE_DIR}/src/grpc"
419+
"${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/client"
420+
"${CMAKE_CURRENT_BINARY_DIR}"
318421
"${CUDSS_INCLUDE}"
319422
PUBLIC
320423
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
@@ -384,6 +487,8 @@ target_link_libraries(cuopt
384487
${CUDSS_LIB_FILE}
385488
PRIVATE
386489
${CUOPT_PRIVATE_CUDA_LIBS}
490+
protobuf::libprotobuf
491+
gRPC::grpc++
387492
)
388493

389494

@@ -606,6 +711,109 @@ if(BUILD_LP_BENCHMARKS)
606711
endif()
607712
endif()
608713

714+
# ##################################################################################################
715+
# - cuopt_grpc_server - gRPC-based remote server --------------------------------------------------
716+
717+
add_executable(cuopt_grpc_server
718+
src/grpc/server/grpc_server_main.cpp
719+
src/grpc/server/grpc_worker.cpp
720+
src/grpc/server/grpc_worker_infra.cpp
721+
src/grpc/server/grpc_server_threads.cpp
722+
src/grpc/server/grpc_job_management.cpp
723+
src/grpc/server/grpc_service_impl.cpp
724+
)
725+
726+
set_target_properties(cuopt_grpc_server
727+
PROPERTIES
728+
CXX_STANDARD 20
729+
CXX_STANDARD_REQUIRED ON
730+
CXX_SCAN_FOR_MODULES OFF
731+
)
732+
733+
target_compile_options(cuopt_grpc_server
734+
PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CUOPT_CXX_FLAGS}>"
735+
)
736+
737+
target_include_directories(cuopt_grpc_server
738+
PRIVATE
739+
"${CMAKE_CURRENT_SOURCE_DIR}/src"
740+
"${CMAKE_CURRENT_SOURCE_DIR}/src/grpc"
741+
"${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/server"
742+
"${CMAKE_CURRENT_SOURCE_DIR}/include"
743+
"${CMAKE_CURRENT_SOURCE_DIR}/libmps_parser/include"
744+
"${CMAKE_CURRENT_BINARY_DIR}"
745+
PUBLIC
746+
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
747+
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
748+
)
749+
750+
find_library(UUID_LIBRARY uuid REQUIRED)
751+
752+
target_link_libraries(cuopt_grpc_server
753+
PUBLIC
754+
cuopt
755+
OpenMP::OpenMP_CXX
756+
PRIVATE
757+
protobuf::libprotobuf
758+
gRPC::grpc++
759+
${UUID_LIBRARY}
760+
)
761+
762+
# Use RUNPATH when building locally
763+
target_link_options(cuopt_grpc_server PRIVATE -Wl,--enable-new-dtags)
764+
set_property(TARGET cuopt_grpc_server PROPERTY INSTALL_RPATH "$ORIGIN/../${lib_dir}")
765+
766+
# Install the grpc server executable
767+
install(TARGETS cuopt_grpc_server
768+
COMPONENT runtime
769+
RUNTIME DESTINATION ${_BIN_DEST}
770+
)
771+
772+
message(STATUS "Building cuopt_grpc_server (gRPC-based remote solve server)")
773+
774+
# ##################################################################################################
775+
# - test_grpc_client - Simple test client for cuopt_grpc_server -----------------------------------
776+
777+
add_executable(test_grpc_client src/grpc/client/test_grpc_client.cpp)
778+
779+
set_target_properties(test_grpc_client
780+
PROPERTIES
781+
CXX_STANDARD 20
782+
CXX_STANDARD_REQUIRED ON
783+
CXX_SCAN_FOR_MODULES OFF
784+
)
785+
786+
target_compile_options(test_grpc_client
787+
PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CUOPT_CXX_FLAGS}>"
788+
)
789+
790+
target_include_directories(test_grpc_client
791+
PRIVATE
792+
"${CMAKE_CURRENT_SOURCE_DIR}/src"
793+
"${CMAKE_CURRENT_SOURCE_DIR}/src/grpc"
794+
"${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/client"
795+
"${CMAKE_CURRENT_SOURCE_DIR}/include"
796+
"${CMAKE_CURRENT_SOURCE_DIR}/libmps_parser/include"
797+
"${CMAKE_CURRENT_BINARY_DIR}"
798+
PUBLIC
799+
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
800+
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
801+
)
802+
803+
target_link_libraries(test_grpc_client
804+
PUBLIC
805+
cuopt
806+
PRIVATE
807+
protobuf::libprotobuf
808+
gRPC::grpc++
809+
)
810+
811+
# Use RUNPATH when building locally
812+
target_link_options(test_grpc_client PRIVATE -Wl,--enable-new-dtags)
813+
set_property(TARGET test_grpc_client PROPERTY INSTALL_RPATH "$ORIGIN/../${lib_dir}")
814+
815+
message(STATUS "Building test_grpc_client (simple gRPC client for testing)")
816+
609817

610818
# ##################################################################################################
611819
# - CPack has to be the last item in the cmake file-------------------------------------------------

0 commit comments

Comments
 (0)