From b7102065793131c299e71ab1fcc7f5936a8af047 Mon Sep 17 00:00:00 2001 From: Derek Schafer Date: Fri, 8 Aug 2025 17:05:30 -0600 Subject: [PATCH 01/15] Add CMake module for library + general cleanup of CMake code (#41) * Reworked CMake to remove duplicate and stray cmake code paths, as well as adding a CMake Module * Removed a few compile flag lines * Removed duplicate code setting language on files * Added packages to cmake configure file * Removed extra cmake line including directories already included --- CMakeLists.txt | 80 ++++++++++--------- ...g.cmake => locality_aware-config.cmake.in} | 3 + src/CMakeLists.txt | 22 ----- src/collective/CMakeLists.txt | 9 +-- src/communicator/CMakeLists.txt | 6 +- src/heterogeneous/CMakeLists.txt | 6 +- src/neighborhood/CMakeLists.txt | 6 +- src/persistent/CMakeLists.txt | 6 +- src/utils/CMakeLists.txt | 11 +-- 9 files changed, 58 insertions(+), 91 deletions(-) rename cmake/{mpiadvanceConfig.cmake => locality_aware-config.cmake.in} (61%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d9c2103c8..a6412f782 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,12 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_C_STANDARD 99) # Add warning flags to help with debugging -set(CMAKE_C_FLAGS "-Wall -Wextra -Wpedantic -Wshadow") -set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wpedantic -Wshadow") +string(TOLOWER ${CMAKE_BUILD_TYPE} build_type) +message(STATUS "Build type: ${CMAKE_BUILD_TYPE} -> ${build_type}") +if(build_type STREQUAL "debug") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -Wshadow") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Wshadow") +endif() # Build Options option(USE_CUDA "Enable NVIDIA CUDA support" OFF) @@ -22,26 +26,15 @@ set(MPICXX "mpicxx" CACHE STRING "MPICXX command") set(MPIRUN "mpirun" CACHE STRING "MPIRUN command") set(CUDA_ARCH "75" CACHE STRING "CUDA Architecture") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src") - # Find MPI find_package(MPI REQUIRED) -if (MPI_COMPILER_FLAGS) - add_compile_options(${MPI_COMPILER_FLAGS}) -endif() -include_directories(${MPI_INCLUDE_PATH}) -set(EXTERNAL_LIBS ${MPI_LIBRARIES}) +set(EXTERNAL_LIBS "MPI::MPI_CXX") # Find OpenMP (not required) find_package(OpenMP) if (${OpenMP_CXX_FOUND}) - # Add OpenMP flags to compile options - add_compile_options(${OpenMP_CXX_FLAGS}) - - set(EXTERNAL_LIBS ${EXTERNAL_LIBS} ${OpenMP_CXX_LIBRARIES}) + list(APPEND EXTERNAL_LIBS "OpenMP::OpenMP_CXX") add_definitions(-DOPENMP) - message(STATUS "Found OpenMP ${OpenMP_CXX_LIBRARIES}") endif() # Set default language of .cpp files @@ -71,14 +64,9 @@ elseif (USE_HIP) endif() endif() -add_subdirectory(src/utils) -add_subdirectory(src/communicator) -add_subdirectory(src/persistent) -add_subdirectory(src/collective) -add_subdirectory(src/neighborhood) -if(USE_CUDA OR USE_HIP) - add_subdirectory(src/heterogeneous) -endif() +message(STATUS "Compiling all files as: ${locality_aware_LANG}") + +add_subdirectory(src) set_source_files_properties( ${utils_SOURCES} @@ -97,14 +85,19 @@ add_library(mpi_advance ${neighborhood_SOURCES} ${neighborhood_HEADERS} ${heterogeneous_SOURCES} ${heterogeneous_HEADERS} ) + +set_target_properties(mpi_advance PROPERTIES PUBLIC_HEADER src/mpi_advance.h) + +target_include_directories(mpi_advance PUBLIC + $ + $ + ) + target_link_libraries(mpi_advance PUBLIC ${EXTERNAL_LIBS}) -install(TARGETS mpi_advance - ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +install(TARGETS mpi_advance) -install(FILES src/mpi_advance.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) +### Install all headers that locality_aware exports install(FILES ${utils_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/utils) install(FILES ${communicator_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/communicator) install(FILES ${collective_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/collective) @@ -112,15 +105,28 @@ install(FILES ${persistent_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ install(FILES ${neighborhood_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/neighborhood) install(FILES ${heterogeneous_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/heterogeneous) -if (ENABLE_UNIT_TESTS) - enable_testing() - add_subdirectory(src/collective/tests) - add_subdirectory(src/neighborhood/tests) - if (USE_GPU) - add_subdirectory(src/heterogeneous/tests) - endif(USE_GPU) -endif() - add_subdirectory(benchmarks) +### Installation Requirements for Spack Package ### +include(CMakePackageConfigHelpers) + +# Install the actual target(s) and register them for export +install(TARGETS mpi_advance + EXPORT mpiadvanceTargets + DESTINATION lib) + +# Export the targets into a CMake package +install(EXPORT mpiadvanceTargets + NAMESPACE MPIAdvance:: + DESTINATION share/mpiadvance) + +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/locality_aware-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/mpiadvance-config.cmake" + INSTALL_DESTINATION share/mpiadvance) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/mpiadvance-config.cmake" + DESTINATION share/mpiadvance) +############## End of Spack Section ################ diff --git a/cmake/mpiadvanceConfig.cmake b/cmake/locality_aware-config.cmake.in similarity index 61% rename from cmake/mpiadvanceConfig.cmake rename to cmake/locality_aware-config.cmake.in index 4957b958a..b489d0198 100644 --- a/cmake/mpiadvanceConfig.cmake +++ b/cmake/locality_aware-config.cmake.in @@ -1,3 +1,6 @@ @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/mpiadvanceTargets.cmake") + +find_package(OpenMP) +find_package(MPI REQUIRED) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cd168e63b..9fd6dd3fc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,3 @@ -include_directories(".") - add_subdirectory(utils) add_subdirectory(communicator) add_subdirectory(persistent) @@ -9,26 +7,6 @@ if(USE_CUDA OR USE_HIP) add_subdirectory(heterogeneous) endif() -set_source_files_properties( - ${utils_SOURCES} - ${communicator_SOURCES} - ${collective_SOURCES} - ${persistent_SOURCES} - ${neighborhood_SOURCES} - ${heterogeneous_SOURCES} - PROPERTIES LANGUAGE ${locality_aware_LANG}) - -add_library(mpi_advance - ${utils_SOURCES} ${utils_HEADERS} - ${communicator_SOURCES} ${communicator_HEADERS} - ${collective_SOURCES} ${collective_HEADERS} - ${persistent_SOURCES} ${persistent_HEADERS} - ${neighborhood_SOURCES} ${neighborhood_HEADERS} - ${heterogeneous_SOURCES} ${heterogeneous_HEADERS} -) - -target_link_libraries(mpi_advance ${MPI_LIBRARIES}) - if (ENABLE_UNIT_TESTS) add_subdirectory(collective/tests) add_subdirectory(neighborhood/tests) diff --git a/src/collective/CMakeLists.txt b/src/collective/CMakeLists.txt index a96875c1e..34878cb6c 100644 --- a/src/collective/CMakeLists.txt +++ b/src/collective/CMakeLists.txt @@ -1,11 +1,6 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) file(GLOB collective_HEADERS CONFIGURE_DEPENDS "*.h") file(GLOB collective_SOURCES CONFIGURE_DEPENDS "*.c") -set_source_files_properties( - ${collective_HEADERS} ${collective_SOURCES} - PROPERTIES LANGUAGE ${locality_aware_LANG}) - -set(collective_HEADERS ${collective_HEADERS} PARENT_SCOPE) -set(collective_SOURCES ${collective_SOURCES} PARENT_SCOPE) +set(collective_HEADERS ${collective_HEADERS} CACHE INTERNAL "All headers for collective files.") +set(collective_SOURCES ${collective_SOURCES} CACHE INTERNAL "All source files for collective directory.") diff --git a/src/communicator/CMakeLists.txt b/src/communicator/CMakeLists.txt index 80892a66c..17e5f970e 100644 --- a/src/communicator/CMakeLists.txt +++ b/src/communicator/CMakeLists.txt @@ -1,8 +1,6 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) - file(GLOB communicator_HEADERS CONFIGURE_DEPENDS "*.h") file(GLOB communicator_SOURCES CONFIGURE_DEPENDS "*.c") -set(communicator_HEADERS ${communicator_HEADERS} PARENT_SCOPE) -set(communicator_SOURCES ${communicator_SOURCES} PARENT_SCOPE) +set(communicator_HEADERS ${communicator_HEADERS} CACHE INTERNAL "All headers for communicator files.") +set(communicator_SOURCES ${communicator_SOURCES} CACHE INTERNAL "All source files for communicator directory.") diff --git a/src/heterogeneous/CMakeLists.txt b/src/heterogeneous/CMakeLists.txt index 3f90eb2ec..0f0c764e9 100644 --- a/src/heterogeneous/CMakeLists.txt +++ b/src/heterogeneous/CMakeLists.txt @@ -1,9 +1,7 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) - file(GLOB heterogeneous_HEADERS CONFIGURE_DEPENDS "*.h") file(GLOB heterogeneous_SOURCES CONFIGURE_DEPENDS "*.c") -set(heterogeneous_HEADERS ${heterogeneous_HEADERS} PARENT_SCOPE) -set(heterogeneous_SOURCES ${heterogeneous_SOURCES} PARENT_SCOPE) +set(heterogeneous_HEADERS ${heterogeneous_HEADERS} CACHE INTERNAL "All headers for heterogeneous files.") +set(heterogeneous_SOURCES ${heterogeneous_SOURCES} CACHE INTERNAL "All source files for heterogeneous directory.") message(STATUS ${heterogeneous_HEADERS} ${heterogeneous_SOURCES}) diff --git a/src/neighborhood/CMakeLists.txt b/src/neighborhood/CMakeLists.txt index 21b1e596f..35bdb6792 100644 --- a/src/neighborhood/CMakeLists.txt +++ b/src/neighborhood/CMakeLists.txt @@ -1,8 +1,6 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) - file(GLOB neighborhood_HEADERS CONFIGURE_DEPENDS "*.h") file(GLOB neighborhood_SOURCES CONFIGURE_DEPENDS "*.c" "*.cpp") -set(neighborhood_HEADERS ${neighborhood_HEADERS} PARENT_SCOPE) -set(neighborhood_SOURCES ${neighborhood_SOURCES} PARENT_SCOPE) +set(neighborhood_HEADERS ${neighborhood_HEADERS} CACHE INTERNAL "All headers for neighborhood files.") +set(neighborhood_SOURCES ${neighborhood_SOURCES} CACHE INTERNAL "All source files for neighborhood directory.") diff --git a/src/persistent/CMakeLists.txt b/src/persistent/CMakeLists.txt index 18fca06ad..6e3fa98f5 100644 --- a/src/persistent/CMakeLists.txt +++ b/src/persistent/CMakeLists.txt @@ -1,8 +1,6 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) - file(GLOB persistent_HEADERS CONFIGURE_DEPENDS "*.h") file(GLOB persistent_SOURCES CONFIGURE_DEPENDS "*.c") -set(persistent_HEADERS ${persistent_HEADERS} PARENT_SCOPE) -set(persistent_SOURCES ${persistent_SOURCES} PARENT_SCOPE) +set(persistent_HEADERS ${persistent_HEADERS} CACHE INTERNAL "All headers for persistent files.") +set(persistent_SOURCES ${persistent_SOURCES} CACHE INTERNAL "All source files for persistent directory.") diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index ccf470c70..215ddaaf7 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -1,5 +1,3 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) - if (USE_CUDA) set(gpu_util_HEADERS src/utils/utils_cuda.h @@ -11,17 +9,12 @@ elseif (USE_HIP) endif() file(GLOB utils_SOURCES CONFIGURE_DEPENDS "*.cpp") -set(utils_SOURCES ${utils_SOURCES} PARENT_SCOPE) +set(utils_SOURCES ${utils_SOURCES} CACHE INTERNAL "All source files for utils directory.") set(utils_HEADERS src/utils/utils.h ${gpu_util_HEADERS} - PARENT_SCOPE + CACHE INTERNAL "All headers for utils files." ) -#set(utils_SOURCES -# src/utils/utils.cpp -# PARENT_SCOPE -# ) - From 042f6290f6a45339b45cd23b406a2ab621b57b3e Mon Sep 17 00:00:00 2001 From: Derek Schafer Date: Thu, 14 Aug 2025 08:06:26 -0600 Subject: [PATCH 02/15] Initial style guide for formatting; applied formatting to all non-benchmark files --- .clang-format | 29 + CMakeLists.txt | 10 +- src/collective/alltoall.c | 809 ++++++++++++---------- src/collective/alltoall.h | 244 +++---- src/collective/alltoallv.c | 308 ++++++--- src/collective/alltoallv.h | 110 +-- src/collective/collective.h | 56 +- src/communicator/comm_data.c | 47 +- src/communicator/comm_data.h | 8 +- src/communicator/comm_pkg.c | 6 +- src/communicator/comm_pkg.h | 6 +- src/communicator/locality_comm.c | 23 +- src/communicator/locality_comm.h | 26 +- src/communicator/mpix_comm.c | 200 +++--- src/communicator/mpix_comm.h | 14 +- src/heterogeneous/gpu_alltoall.c | 390 +++++------ src/heterogeneous/gpu_alltoall.h | 112 ++-- src/heterogeneous/gpu_alltoallv.c | 590 ++++++++-------- src/heterogeneous/gpu_alltoallv.h | 225 +++---- src/mpi_advance.h | 20 +- src/neighborhood/alltoall_crs.cpp | 495 ++++++++++---- src/neighborhood/alltoallv_crs.cpp | 577 +++++++++++----- src/neighborhood/dist_graph.c | 51 +- src/neighborhood/dist_graph.h | 26 +- src/neighborhood/dist_topo.c | 86 ++- src/neighborhood/dist_topo.h | 29 +- src/neighborhood/neighbor.c | 199 +++--- src/neighborhood/neighbor.h | 109 +-- src/neighborhood/neighbor_init.c | 621 +++++++++-------- src/neighborhood/neighbor_init.h | 259 ++++--- src/neighborhood/neighbor_locality.cpp | 500 ++++++++------ src/neighborhood/sparse_coll.c | 93 ++- src/neighborhood/sparse_coll.h | 257 ++++--- src/neighborhood/sparse_coll_utils.cpp | 893 +++++++++++++++++++------ src/persistent/neighbor_persistent.c | 72 +- src/persistent/neighbor_persistent.h | 6 +- src/persistent/persistent.c | 46 +- src/persistent/persistent.h | 17 +- src/utils/utils.cpp | 175 +++-- src/utils/utils.h | 33 +- src/utils/utils_cuda.h | 2 +- src/utils/utils_hip.h | 4 +- 42 files changed, 4715 insertions(+), 3068 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..0a2ea47d5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,29 @@ +AccessModifierOffset: -2 +BasedOnStyle: Google +IndentWidth: 4 +DerivePointerAlignment: false +PointerAlignment: Left +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + SplitEmptyRecord: false +NamespaceIndentation: All +AlignConsecutiveAssignments: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false + +BinPackArguments: false +BinPackParameters: false + +ColumnLimit: 90 + +InsertBraces: true \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index a6412f782..7d6f92d5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,9 +8,17 @@ project(locality_aware LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_C_STANDARD 99) -# Add warning flags to help with debugging +# Set default build type +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "No CMAKE_BUILD_TYPE specified, setting build type to RELEASE.") + set(CMAKE_BUILD_TYPE "RELEASE") +endif() + +# Convert to lower case string to ignore case string(TOLOWER ${CMAKE_BUILD_TYPE} build_type) message(STATUS "Build type: ${CMAKE_BUILD_TYPE} -> ${build_type}") + +# Add warning flags to help with debugging if(build_type STREQUAL "debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -Wshadow") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Wshadow") diff --git a/src/collective/alltoall.c b/src/collective/alltoall.c index 635fa0c8b..51afdb9a7 100644 --- a/src/collective/alltoall.c +++ b/src/collective/alltoall.c @@ -1,19 +1,19 @@ #include "alltoall.h" -#include + #include +#include #ifdef GPU #include "heterogeneous/gpu_alltoall.h" #endif - // Default alltoall is pairwise AlltoallMethod mpix_alltoall_implementation = ALLTOALL_PAIRWISE; /************************************************** * Locality-Aware Point-to-Point Alltoall - * - Aggregates messages locally to reduce - * non-local communciation + * - Aggregates messages locally to reduce + * non-local communication * - First redistributes on-node so that each * process holds all data for a subset * of other nodes @@ -25,24 +25,24 @@ AlltoallMethod mpix_alltoall_implementation = ALLTOALL_PAIRWISE; * the correct final data *************************************************/ int MPIX_Alltoall(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* mpi_comm) -{ + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* mpi_comm) +{ #ifdef GPU #ifdef GPU_AWARE return gpu_aware_alltoall(alltoall_pairwise, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - mpi_comm); -#endif + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + mpi_comm); +#endif #endif alltoall_ftn method; @@ -61,7 +61,7 @@ int MPIX_Alltoall(const void* sendbuf, method = alltoall_hierarchical_nonblocking; break; case ALLTOALL_MULTILEADER_PAIRWISE: - method = alltoall_multileader_pairwise; + method = alltoall_multileader_pairwise; break; case ALLTOALL_MULTILEADER_NONBLOCKING: method = alltoall_multileader_nonblocking; @@ -92,26 +92,17 @@ int MPIX_Alltoall(const void* sendbuf, break; } - - return method(sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - mpi_comm); - + return method(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, mpi_comm); } - int pairwise_helper(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPI_Comm comm, - int tag) + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPI_Comm comm, + int tag) { int rank, num_procs; MPI_Comm_rank(comm, &rank); @@ -134,52 +125,62 @@ int pairwise_helper(const void* sendbuf, { send_proc = rank + i; if (send_proc >= num_procs) + { send_proc -= num_procs; + } recv_proc = rank - i; if (recv_proc < 0) + { recv_proc += num_procs; + } send_pos = send_proc * sendcount * send_size; recv_pos = recv_proc * recvcount * recv_size; - MPI_Sendrecv(send_buffer + send_pos, - sendcount, - sendtype, - send_proc, - tag, - recv_buffer + recv_pos, - recvcount, - recvtype, - recv_proc, - tag, - comm, - &status); + MPI_Sendrecv(send_buffer + send_pos, + sendcount, + sendtype, + send_proc, + tag, + recv_buffer + recv_pos, + recvcount, + recvtype, + recv_proc, + tag, + comm, + &status); } return MPI_SUCCESS; } int alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { int tag; MPIX_Comm_tag(comm, &tag); - return pairwise_helper(sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm->global_comm, tag); + return pairwise_helper(sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm->global_comm, + tag); } int nonblocking_helper(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPI_Comm comm, - int tag) + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPI_Comm comm, + int tag) { int rank, num_procs; MPI_Comm_rank(comm, &rank); @@ -195,7 +196,7 @@ int nonblocking_helper(const void* sendbuf, MPI_Type_size(sendtype, &send_size); MPI_Type_size(recvtype, &recv_size); - MPI_Request* requests = (MPI_Request*)malloc(2*num_procs*sizeof(MPI_Request)); + MPI_Request* requests = (MPI_Request*)malloc(2 * num_procs * sizeof(MPI_Request)); // Send to rank + i // Recv from rank - i @@ -203,62 +204,69 @@ int nonblocking_helper(const void* sendbuf, { send_proc = rank + i; if (send_proc >= num_procs) + { send_proc -= num_procs; + } recv_proc = rank - i; if (recv_proc < 0) + { recv_proc += num_procs; + } send_pos = send_proc * sendcount * send_size; recv_pos = recv_proc * recvcount * recv_size; MPI_Isend(send_buffer + send_pos, - sendcount, - sendtype, - send_proc, - tag, - comm, - &(requests[i])); + sendcount, + sendtype, + send_proc, + tag, + comm, + &(requests[i])); MPI_Irecv(recv_buffer + recv_pos, - recvcount, - recvtype, - recv_proc, - tag, - comm, - &(requests[num_procs + i])); + recvcount, + recvtype, + recv_proc, + tag, + comm, + &(requests[num_procs + i])); } - MPI_Waitall(2*num_procs, requests, MPI_STATUSES_IGNORE); + MPI_Waitall(2 * num_procs, requests, MPI_STATUSES_IGNORE); free(requests); return MPI_SUCCESS; } - int alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { int tag; MPIX_Comm_tag(comm, &tag); - return nonblocking_helper(sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm->global_comm, tag); + return nonblocking_helper(sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm->global_comm, + tag); } - -int alltoall_multileader( - alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm, - int n_leaders) +int alltoall_multileader(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm, + int n_leaders) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -268,7 +276,9 @@ int alltoall_multileader( MPIX_Comm_tag(comm, &tag); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } int ppn; MPI_Comm_size(comm->local_comm, &ppn); @@ -276,7 +286,6 @@ int alltoall_multileader( MPI_Comm local_comm = comm->local_comm; MPI_Comm group_comm = comm->group_comm; - if (n_leaders > 1) { if (ppn < n_leaders) @@ -292,12 +301,16 @@ int alltoall_multileader( int ppl; MPI_Comm_size(comm->leader_comm, &ppl); if (ppl != procs_per_leader) + { MPI_Comm_free(&comm->leader_comm); + } } // If leader comm does not exist, create it if (comm->leader_comm == MPI_COMM_NULL) + { MPIX_Comm_leader_init(comm, procs_per_leader); + } local_comm = comm->leader_comm; group_comm = comm->leader_group_comm; @@ -324,8 +337,8 @@ int alltoall_multileader( if (local_rank == 0) { - local_send_buffer = (char*)malloc(ppl*num_procs*sendcount*send_size); - local_recv_buffer = (char*)malloc(ppl*num_procs*recvcount*recv_size); + local_send_buffer = (char*)malloc(ppl * num_procs * sendcount * send_size); + local_recv_buffer = (char*)malloc(ppl * num_procs * recvcount * recv_size); } else { @@ -334,11 +347,17 @@ int alltoall_multileader( } // 1. Gather locally - MPI_Gather(send_buffer, sendcount*num_procs, sendtype, local_recv_buffer, sendcount*num_procs, sendtype, - 0, local_comm); + MPI_Gather(send_buffer, + sendcount * num_procs, + sendtype, + local_recv_buffer, + sendcount * num_procs, + sendtype, + 0, + local_comm); // 2. Re-pack for sends - // Assumes SMP ordering + // Assumes SMP ordering // TODO: allow for other orderings int ctr; @@ -351,15 +370,22 @@ int alltoall_multileader( for (int origin_proc = 0; origin_proc < ppl; origin_proc++) { int origin_proc_start = origin_proc * num_procs * sendcount * send_size; - memcpy(&(local_send_buffer[ctr]), &(local_recv_buffer[origin_proc_start + dest_node_start]), - ppl * sendcount * send_size); + memcpy(&(local_send_buffer[ctr]), + &(local_recv_buffer[origin_proc_start + dest_node_start]), + ppl * sendcount * send_size); ctr += ppl * sendcount * send_size; } } // 3. MPI_Alltoall between leaders - f(local_send_buffer, ppl * ppl * sendcount, sendtype, - local_recv_buffer, ppl * ppl * recvcount, recvtype, group_comm, tag); + f(local_send_buffer, + ppl * ppl * sendcount, + sendtype, + local_recv_buffer, + ppl * ppl * recvcount, + recvtype, + group_comm, + tag); // 4. Re-pack for local scatter ctr = 0; @@ -369,18 +395,23 @@ int alltoall_multileader( for (int orig_proc = 0; orig_proc < num_procs; orig_proc++) { int orig_proc_start = orig_proc * ppl * recvcount * recv_size; - memcpy(&(local_send_buffer[ctr]), &(local_recv_buffer[orig_proc_start + dest_proc_start]), - recvcount * recv_size); + memcpy(&(local_send_buffer[ctr]), + &(local_recv_buffer[orig_proc_start + dest_proc_start]), + recvcount * recv_size); ctr += recvcount * recv_size; - } } } - // 5. Scatter - MPI_Scatter(local_send_buffer, recvcount * num_procs, recvtype, recv_buffer, recvcount*num_procs, recvtype, - 0, local_comm); - + // 5. Scatter + MPI_Scatter(local_send_buffer, + recvcount * num_procs, + recvtype, + recv_buffer, + recvcount * num_procs, + recvtype, + 0, + local_comm); free(local_send_buffer); free(local_recv_buffer); @@ -388,88 +419,106 @@ int alltoall_multileader( return MPI_SUCCESS; } -int alltoall_hierarchical( - alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_hierarchical(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return alltoall_multileader(f, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm, 1); + return alltoall_multileader( + f, sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm, 1); } -int alltoall_hierarchical_pairwise( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_hierarchical_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return alltoall_hierarchical(pairwise_helper, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm); + return alltoall_hierarchical(pairwise_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); } -int alltoall_hierarchical_nonblocking( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_hierarchical_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return alltoall_hierarchical(nonblocking_helper, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm); + return alltoall_hierarchical(nonblocking_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); } -int alltoall_multileader_pairwise( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_multileader_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return alltoall_multileader(pairwise_helper, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm, 4); + return alltoall_multileader(pairwise_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm, + 4); } -int alltoall_multileader_nonblocking( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_multileader_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return alltoall_multileader(nonblocking_helper, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm, 4); + return alltoall_multileader(nonblocking_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm, + 4); } - - -int alltoall_locality_aware_helper( - alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm, - int groups_per_node, - MPI_Comm local_comm, - MPI_Comm group_comm, - int tag) -{ +int alltoall_locality_aware_helper(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm, + int groups_per_node, + MPI_Comm local_comm, + MPI_Comm group_comm, + int tag) +{ int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); @@ -485,11 +534,17 @@ int alltoall_locality_aware_helper( int n_groups = num_procs / ppg; - char* tmpbuf = (char*)malloc(num_procs*sendcount*send_size); + char* tmpbuf = (char*)malloc(num_procs * sendcount * send_size); // 1. Alltoall between group_comms (all data for any process on node) - f(sendbuf, ppg*sendcount, sendtype, tmpbuf, ppg*recvcount, recvtype, - group_comm, tag); + f(sendbuf, + ppg * sendcount, + sendtype, + tmpbuf, + ppg * recvcount, + recvtype, + group_comm, + tag); // 2. Re-pack int ctr = 0; @@ -499,14 +554,22 @@ int alltoall_locality_aware_helper( for (int origin = 0; origin < n_groups; origin++) { int node_offset = origin * ppg * recvcount * recv_size; - memcpy(&(recv_buffer[ctr]), &(tmpbuf[node_offset + offset]), recvcount*recv_size); + memcpy(&(recv_buffer[ctr]), + &(tmpbuf[node_offset + offset]), + recvcount * recv_size); ctr += recvcount * recv_size; } } // 3. Local alltoall - f(recvbuf, n_groups*recvcount, recvtype, - tmpbuf, n_groups*recvcount, recvtype, local_comm, tag); + f(recvbuf, + n_groups * recvcount, + recvtype, + tmpbuf, + n_groups * recvcount, + recvtype, + local_comm, + tag); // 4. Re-order ctr = 0; @@ -516,8 +579,9 @@ int alltoall_locality_aware_helper( for (int dest = 0; dest < ppg; dest++) { int dest_offset = dest * n_groups * recvcount * recv_size; - memcpy(&(recv_buffer[ctr]), &(tmpbuf[node_offset + dest_offset]), - recvcount * recv_size); + memcpy(&(recv_buffer[ctr]), + &(tmpbuf[node_offset + dest_offset]), + recvcount * recv_size); ctr += recvcount * recv_size; } } @@ -526,16 +590,15 @@ int alltoall_locality_aware_helper( return MPI_SUCCESS; } -int alltoall_locality_aware( - alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm, - int groups_per_node) +int alltoall_locality_aware(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm, + int groups_per_node) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -545,7 +608,9 @@ int alltoall_locality_aware( MPIX_Comm_tag(comm, &tag); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } int ppn; MPI_Comm_size(comm->local_comm, &ppn); @@ -566,98 +631,129 @@ int alltoall_locality_aware( int ppg; MPI_Comm_size(comm->leader_comm, &ppg); if (ppg != procs_per_group) + { MPI_Comm_free(&(comm->leader_comm)); + } } if (comm->leader_comm == MPI_COMM_NULL) + { MPIX_Comm_leader_init(comm, procs_per_group); + } local_comm = comm->leader_comm; group_comm = comm->leader_group_comm; } - return alltoall_locality_aware_helper(f, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm, groups_per_node, local_comm, group_comm, tag); + return alltoall_locality_aware_helper(f, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm, + groups_per_node, + local_comm, + group_comm, + tag); } -int alltoall_node_aware( - alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_node_aware(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return alltoall_locality_aware(f, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm, 1); + return alltoall_locality_aware( + f, sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm, 1); } -int alltoall_node_aware_pairwise( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_node_aware_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return alltoall_node_aware(pairwise_helper, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm); + return alltoall_node_aware(pairwise_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); } -int alltoall_node_aware_nonblocking( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_node_aware_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return alltoall_node_aware(nonblocking_helper, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm); + return alltoall_node_aware(nonblocking_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); } -int alltoall_locality_aware_pairwise( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_locality_aware_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return alltoall_locality_aware(pairwise_helper, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm, 4); + return alltoall_locality_aware(pairwise_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm, + 4); } -int alltoall_locality_aware_nonblocking( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_locality_aware_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return alltoall_locality_aware(nonblocking_helper, sendbuf, sendcount, sendtype, - recvbuf, recvcount, recvtype, comm, 4); + return alltoall_locality_aware(nonblocking_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm, + 4); } - - - -int alltoall_multileader_locality( - alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_multileader_locality(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -667,7 +763,9 @@ int alltoall_multileader_locality( MPIX_Comm_tag(comm, &tag); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } int local_rank, ppn; MPI_Comm_rank(comm->local_comm, &local_rank); @@ -677,10 +775,12 @@ int alltoall_multileader_locality( { int num_leaders_per_node = 4; if (ppn < num_leaders_per_node) + { num_leaders_per_node = ppn; + } MPIX_Comm_leader_init(comm, ppn / num_leaders_per_node); } - + int procs_per_leader, leader_rank; MPI_Comm_rank(comm->leader_comm, &leader_rank); MPI_Comm_size(comm->leader_comm, &procs_per_leader); @@ -695,19 +795,20 @@ int alltoall_multileader_locality( // TODO: currently assuming full nodes, even procs_per_leader per node // this is common, so fair assumption for now // likely need to fix before using in something like Trilinos - int n_nodes = num_procs / ppn; + int n_nodes = num_procs / ppn; int n_leaders = num_procs / procs_per_leader; - + int leaders_per_node; MPI_Comm_size(comm->leader_local_comm, &leaders_per_node); - char* local_send_buffer = NULL; char* local_recv_buffer = NULL; if (leader_rank == 0) { - local_send_buffer = (char*)malloc(procs_per_leader*num_procs*sendcount*send_size); - local_recv_buffer = (char*)malloc(procs_per_leader*num_procs*recvcount*recv_size); + local_send_buffer = + (char*)malloc(procs_per_leader * num_procs * sendcount * send_size); + local_recv_buffer = + (char*)malloc(procs_per_leader * num_procs * recvcount * recv_size); } else { @@ -715,22 +816,27 @@ int alltoall_multileader_locality( local_recv_buffer = (char*)malloc(sizeof(char)); } // 1. Gather locally - MPI_Gather(send_buffer, sendcount*num_procs, sendtype, local_recv_buffer, sendcount*num_procs, sendtype, - 0, comm->leader_comm); - + MPI_Gather(send_buffer, + sendcount * num_procs, + sendtype, + local_recv_buffer, + sendcount * num_procs, + sendtype, + 0, + comm->leader_comm); // 2. Re-pack for sends - // Assumes SMP ordering + // Assumes SMP ordering // TODO: allow for other orderings int ctr; if (leader_rank == 0) { - /* - alltoall_locality_aware_helper(f, sendbuf, procs_per_leader*sendcount, sendtype, - recvbuf, procs_per_leader*recvcount, recvtype, comm, groups_per_node, - comm->leader_local_comm, comm->group_comm); -*/ + /* + alltoall_locality_aware_helper(f, sendbuf, procs_per_leader*sendcount, + sendtype, recvbuf, procs_per_leader*recvcount, recvtype, comm, groups_per_node, + comm->leader_local_comm, comm->group_comm); + */ ctr = 0; for (int dest_node = 0; dest_node < n_leaders; dest_node++) @@ -739,111 +845,140 @@ int alltoall_multileader_locality( for (int origin_proc = 0; origin_proc < procs_per_leader; origin_proc++) { int origin_proc_start = origin_proc * num_procs * sendcount * send_size; - memcpy(&(local_send_buffer[ctr]), &(local_recv_buffer[origin_proc_start + dest_node_start]), - procs_per_leader * sendcount * send_size); + memcpy(&(local_send_buffer[ctr]), + &(local_recv_buffer[origin_proc_start + dest_node_start]), + procs_per_leader * sendcount * send_size); ctr += procs_per_leader * sendcount * send_size; } } - // 3. MPI_Alltoall between nodes - f(local_send_buffer, ppn*procs_per_leader*sendcount, sendtype, - local_recv_buffer, ppn*procs_per_leader*recvcount, recvtype, comm->group_comm, tag); + // 3. MPI_Alltoall between nodes + f(local_send_buffer, + ppn * procs_per_leader * sendcount, + sendtype, + local_recv_buffer, + ppn * procs_per_leader * recvcount, + recvtype, + comm->group_comm, + tag); // Re-Pack for exchange between local leaders ctr = 0; for (int local_leader = 0; local_leader < leaders_per_node; local_leader++) { - int leader_start = local_leader*procs_per_leader*procs_per_leader*sendcount*send_size; + int leader_start = local_leader * procs_per_leader * procs_per_leader * + sendcount * send_size; for (int dest_node = 0; dest_node < n_nodes; dest_node++) { - int dest_node_start = dest_node*ppn*procs_per_leader*sendcount*send_size; - memcpy(&(local_send_buffer[ctr]), &(local_recv_buffer[dest_node_start+leader_start]), - procs_per_leader*procs_per_leader*sendcount*send_size); - ctr += procs_per_leader*procs_per_leader*sendcount*send_size; + int dest_node_start = + dest_node * ppn * procs_per_leader * sendcount * send_size; + memcpy(&(local_send_buffer[ctr]), + &(local_recv_buffer[dest_node_start + leader_start]), + procs_per_leader * procs_per_leader * sendcount * send_size); + ctr += procs_per_leader * procs_per_leader * sendcount * send_size; } } - f(local_send_buffer, n_nodes*procs_per_leader*procs_per_leader*sendcount, sendtype, - local_recv_buffer, n_nodes*procs_per_leader*procs_per_leader*recvcount, recvtype, - comm->leader_local_comm, tag); + f(local_send_buffer, + n_nodes * procs_per_leader * procs_per_leader * sendcount, + sendtype, + local_recv_buffer, + n_nodes * procs_per_leader * procs_per_leader * recvcount, + recvtype, + comm->leader_local_comm, + tag); ctr = 0; for (int dest_proc = 0; dest_proc < procs_per_leader; dest_proc++) { int dest_proc_start = dest_proc * recvcount * recv_size; - + for (int orig_node = 0; orig_node < n_nodes; orig_node++) { - int orig_node_start = orig_node*procs_per_leader*procs_per_leader*recvcount*recv_size; + int orig_node_start = orig_node * procs_per_leader * procs_per_leader * + recvcount * recv_size; for (int orig_leader = 0; orig_leader < leaders_per_node; orig_leader++) { - int orig_leader_start = orig_leader*n_nodes*procs_per_leader*procs_per_leader*recvcount*recv_size; + int orig_leader_start = orig_leader * n_nodes * procs_per_leader * + procs_per_leader * recvcount * recv_size; for (int orig_proc = 0; orig_proc < procs_per_leader; orig_proc++) { - int orig_proc_start = orig_proc*procs_per_leader*recvcount*recv_size; - int idx = orig_node_start + orig_leader_start + orig_proc_start + dest_proc_start; - memcpy(&(local_send_buffer[ctr]), &(local_recv_buffer[idx]), recvcount*recv_size); + int orig_proc_start = + orig_proc * procs_per_leader * recvcount * recv_size; + int idx = orig_node_start + orig_leader_start + orig_proc_start + + dest_proc_start; + memcpy(&(local_send_buffer[ctr]), + &(local_recv_buffer[idx]), + recvcount * recv_size); ctr += recvcount * recv_size; - } + } } } } } - // 5. Scatter - MPI_Scatter(local_send_buffer, recvcount * num_procs, recvtype, recv_buffer, recvcount*num_procs, recvtype, - 0, comm->leader_comm); + // 5. Scatter + MPI_Scatter(local_send_buffer, + recvcount * num_procs, + recvtype, + recv_buffer, + recvcount * num_procs, + recvtype, + 0, + comm->leader_comm); free(local_send_buffer); free(local_recv_buffer); - return MPI_SUCCESS; } - -int alltoall_multileader_locality_pairwise( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_multileader_locality_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { return alltoall_multileader_locality(pairwise_helper, - sendbuf, sendcount, sendtype, recvbuf, recvcount, - recvtype, comm); + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); } -int alltoall_multileader_locality_nonblocking( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int alltoall_multileader_locality_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { return alltoall_multileader_locality(nonblocking_helper, - sendbuf, sendcount, sendtype, recvbuf, recvcount, - recvtype, comm); + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); } - - // Calls underlying MPI implementation int alltoall_pmpi(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return PMPI_Alltoall(sendbuf, sendcount, sendtype, recvbuf, recvcount, - recvtype, comm->global_comm); + return PMPI_Alltoall( + sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm->global_comm); } - - diff --git a/src/collective/alltoall.h b/src/collective/alltoall.h index 50e1c6cd8..5c04c1fd7 100644 --- a/src/collective/alltoall.h +++ b/src/collective/alltoall.h @@ -1,144 +1,144 @@ #ifndef MPI_ADVANCE_ALLTOALL_H #define MPI_ADVANCE_ALLTOALL_H -#include -#include #include -#include "utils/utils.h" +#include +#include + #include "collective.h" #include "communicator/mpix_comm.h" +#include "utils/utils.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif // TODO : need to add batch/batch asynch as underlying options for Alltoall -enum AlltoallMethod { ALLTOALL_PAIRWISE, ALLTOALL_NONBLOCKING, ALLTOALL_HIERARCHICAL_PAIRWISE, - ALLTOALL_HIERARCHICAL_NONBLOCKING, ALLTOALL_MULTILEADER_PAIRWISE, ALLTOALL_MULTILEADER_NONBLOCKING, - ALLTOALL_NODE_AWARE_PAIRWISE, ALLTOALL_NODE_AWARE_NONBLOCKING, ALLTOALL_LOCALITY_AWARE_PAIRWISE, - ALLTOALL_LOCALITY_AWARE_NONBLOCKING, ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE, - ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING, ALLTOALL_PMPI }; +enum AlltoallMethod +{ + ALLTOALL_PAIRWISE, + ALLTOALL_NONBLOCKING, + ALLTOALL_HIERARCHICAL_PAIRWISE, + ALLTOALL_HIERARCHICAL_NONBLOCKING, + ALLTOALL_MULTILEADER_PAIRWISE, + ALLTOALL_MULTILEADER_NONBLOCKING, + ALLTOALL_NODE_AWARE_PAIRWISE, + ALLTOALL_NODE_AWARE_NONBLOCKING, + ALLTOALL_LOCALITY_AWARE_PAIRWISE, + ALLTOALL_LOCALITY_AWARE_NONBLOCKING, + ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE, + ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING, + ALLTOALL_PMPI +}; extern AlltoallMethod mpix_alltoall_implementation; - -typedef int (*alltoall_ftn)(const void*, const int, MPI_Datatype, void*, const int, MPI_Datatype, MPIX_Comm*); -typedef int (*alltoall_helper_ftn)(const void*, const int, MPI_Datatype, void*, const int, MPI_Datatype, MPI_Comm, int tag); +typedef int (*alltoall_ftn)( + const void*, const int, MPI_Datatype, void*, const int, MPI_Datatype, MPIX_Comm*); +typedef int (*alltoall_helper_ftn)(const void*, + const int, + MPI_Datatype, + void*, + const int, + MPI_Datatype, + MPI_Comm, + int tag); int alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); int alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); - - -int alltoall_hierarchical_pairwise( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); -int alltoall_hierarchical_nonblocking( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); -int alltoall_multileader_pairwise( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); -int alltoall_multileader_nonblocking( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); - - - -int alltoall_node_aware_pairwise( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); -int alltoall_node_aware_nonblocking( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); -int alltoall_locality_aware_pairwise( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); -int alltoall_locality_aware_nonblocking( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); - - - -int alltoall_multileader_locality_pairwise( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); -int alltoall_multileader_locality_nonblocking( - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); - - + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); + +int alltoall_hierarchical_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); +int alltoall_hierarchical_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); +int alltoall_multileader_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); +int alltoall_multileader_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); + +int alltoall_node_aware_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); +int alltoall_node_aware_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); +int alltoall_locality_aware_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); +int alltoall_locality_aware_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); + +int alltoall_multileader_locality_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); +int alltoall_multileader_locality_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); // Calls underlying MPI implementation int alltoall_pmpi(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); - + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); #ifdef __cplusplus } diff --git a/src/collective/alltoallv.c b/src/collective/alltoallv.c index fe02f582f..0dbe47858 100644 --- a/src/collective/alltoallv.c +++ b/src/collective/alltoallv.c @@ -1,6 +1,7 @@ #include "alltoallv.h" -#include + #include +#include #ifdef GPU #include "heterogeneous/gpu_alltoallv.h" @@ -12,7 +13,7 @@ AlltoallvMethod mpix_alltoallv_implementation = ALLTOALLV_PAIRWISE; /************************************************** * Locality-Aware Point-to-Point Alltoallv * Same as PMPI_Alltoall (no load balancing) - * - Aggregates messages locally to reduce + * - Aggregates messages locally to reduce * non-local communciation * - First redistributes on-node so that each * process holds all data for a subset @@ -25,30 +26,30 @@ AlltoallvMethod mpix_alltoallv_implementation = ALLTOALLV_PAIRWISE; * the correct final data * - To be used when sizes are relatively balanced * - For load balacing, use persistent version - * - Load balacing is too expensive for + * - Load balacing is too expensive for * non-persistent Alltoallv *************************************************/ int MPIX_Alltoallv(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* mpi_comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* mpi_comm) { #ifdef GPU #ifdef GPU_AWARE return gpu_aware_alltoallv_pairwise(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - mpi_comm); + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + mpi_comm); #endif #endif alltoallv_ftn method; @@ -75,28 +76,26 @@ int MPIX_Alltoallv(const void* sendbuf, break; } - return method( - sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - mpi_comm); + return method(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + mpi_comm); } - int alltoallv_pairwise(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -112,7 +111,7 @@ int alltoallv_pairwise(const void* sendbuf, int send_size, recv_size; MPI_Type_size(sendtype, &send_size); MPI_Type_size(recvtype, &recv_size); - + char* send_buffer = (char*)sendbuf; char* recv_buffer = (char*)recvbuf; @@ -122,39 +121,61 @@ int alltoallv_pairwise(const void* sendbuf, { send_proc = rank + i; if (send_proc >= num_procs) + { send_proc -= num_procs; + } recv_proc = rank - i; if (recv_proc < 0) + { recv_proc += num_procs; + } send_pos = sdispls[send_proc] * send_size; recv_pos = rdispls[recv_proc] * recv_size; - MPI_Sendrecv(send_buffer + send_pos, sendcounts[send_proc], sendtype, send_proc, tag, - recv_buffer + recv_pos, recvcounts[recv_proc], recvtype, recv_proc, tag, - comm->global_comm, &status); + MPI_Sendrecv(send_buffer + send_pos, + sendcounts[send_proc], + sendtype, + send_proc, + tag, + recv_buffer + recv_pos, + recvcounts[recv_proc], + recvtype, + recv_proc, + tag, + comm->global_comm, + &status); } return 0; } int alltoallv_nonblocking(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); if (num_procs <= 1) - alltoallv_pairwise(sendbuf, sendcounts, sdispls, sendtype, - recvbuf, recvcounts, rdispls, recvtype, comm); + { + alltoallv_pairwise(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); + } int tag; MPIX_Comm_tag(comm, &tag); @@ -166,7 +187,7 @@ int alltoallv_nonblocking(const void* sendbuf, MPI_Type_size(sendtype, &send_size); MPI_Type_size(recvtype, &recv_size); - MPI_Request* requests = (MPI_Request*)malloc(2*num_procs*sizeof(MPI_Request)); + MPI_Request* requests = (MPI_Request*)malloc(2 * num_procs * sizeof(MPI_Request)); char* send_buffer = (char*)sendbuf; char* recv_buffer = (char*)recvbuf; @@ -177,21 +198,35 @@ int alltoallv_nonblocking(const void* sendbuf, { send_proc = rank + i; if (send_proc >= num_procs) + { send_proc -= num_procs; + } recv_proc = rank - i; if (recv_proc < 0) + { recv_proc += num_procs; + } send_pos = sdispls[send_proc] * send_size; recv_pos = rdispls[recv_proc] * recv_size; - MPI_Isend(send_buffer + send_pos, sendcounts[send_proc], sendtype, send_proc, tag, - comm->global_comm, &(requests[i])); - MPI_Irecv(recv_buffer + recv_pos, recvcounts[recv_proc], recvtype, recv_proc, tag, - comm->global_comm, &(requests[num_procs+i])); + MPI_Isend(send_buffer + send_pos, + sendcounts[send_proc], + sendtype, + send_proc, + tag, + comm->global_comm, + &(requests[i])); + MPI_Irecv(recv_buffer + recv_pos, + recvcounts[recv_proc], + recvtype, + recv_proc, + tag, + comm->global_comm, + &(requests[num_procs + i])); } - MPI_Waitall(2*num_procs, requests, MPI_STATUSES_IGNORE); + MPI_Waitall(2 * num_procs, requests, MPI_STATUSES_IGNORE); free(requests); @@ -199,24 +234,33 @@ int alltoallv_nonblocking(const void* sendbuf, } int alltoallv_batch(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); - // Tuning Parameter : number of non-blocking messages between waits + // Tuning Parameter : number of non-blocking messages between waits int nb_stride = 5; if (nb_stride >= num_procs) - alltoallv_nonblocking(sendbuf, sendcounts, sdispls, sendtype, - recvbuf, recvcounts, rdispls, recvtype, comm); + { + alltoallv_nonblocking(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); + } int tag; MPIX_Comm_tag(comm, &tag); @@ -229,7 +273,7 @@ int alltoallv_batch(const void* sendbuf, MPI_Type_size(sendtype, &send_size); MPI_Type_size(recvtype, &recv_size); - MPI_Request* requests = (MPI_Request*)malloc(2*nb_stride*sizeof(MPI_Request)); + MPI_Request* requests = (MPI_Request*)malloc(2 * nb_stride * sizeof(MPI_Request)); char* send_buffer = (char*)sendbuf; char* recv_buffer = (char*)recvbuf; @@ -241,28 +285,44 @@ int alltoallv_batch(const void* sendbuf, { send_proc = rank + i; if (send_proc >= num_procs) + { send_proc -= num_procs; + } recv_proc = rank - i; if (recv_proc < 0) + { recv_proc += num_procs; + } send_pos = sdispls[send_proc] * send_size; recv_pos = rdispls[recv_proc] * recv_size; - MPI_Isend(send_buffer + send_pos, sendcounts[send_proc], sendtype, send_proc, tag, - comm->global_comm, &(requests[ctr++])); - MPI_Irecv(recv_buffer + recv_pos, recvcounts[recv_proc], recvtype, recv_proc, tag, - comm->global_comm, &(requests[ctr++])); - - if ((i+1) % nb_stride == 0) + MPI_Isend(send_buffer + send_pos, + sendcounts[send_proc], + sendtype, + send_proc, + tag, + comm->global_comm, + &(requests[ctr++])); + MPI_Irecv(recv_buffer + recv_pos, + recvcounts[recv_proc], + recvtype, + recv_proc, + tag, + comm->global_comm, + &(requests[ctr++])); + + if ((i + 1) % nb_stride == 0) { - MPI_Waitall(2*nb_stride, requests, MPI_STATUSES_IGNORE); + MPI_Waitall(2 * nb_stride, requests, MPI_STATUSES_IGNORE); ctr = 0; } } - + if (ctr) + { MPI_Waitall(ctr, requests, MPI_STATUSES_IGNORE); + } free(requests); @@ -270,24 +330,33 @@ int alltoallv_batch(const void* sendbuf, } int alltoallv_batch_async(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); - // Tuning Parameter : number of non-blocking messages between waits + // Tuning Parameter : number of non-blocking messages between waits int nb_stride = 5; if (nb_stride >= num_procs) - return alltoallv_nonblocking(sendbuf, sendcounts, sdispls, sendtype, - recvbuf, recvcounts, rdispls, recvtype, comm); + { + return alltoallv_nonblocking(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); + } int tag; MPIX_Comm_tag(comm, &tag); @@ -299,7 +368,7 @@ int alltoallv_batch_async(const void* sendbuf, MPI_Type_size(sendtype, &send_size); MPI_Type_size(recvtype, &recv_size); - MPI_Request* requests = (MPI_Request*)malloc(2*nb_stride*sizeof(MPI_Request)); + MPI_Request* requests = (MPI_Request*)malloc(2 * nb_stride * sizeof(MPI_Request)); char* send_buffer = (char*)sendbuf; char* recv_buffer = (char*)recvbuf; @@ -312,45 +381,66 @@ int alltoallv_batch_async(const void* sendbuf, { send_proc = rank + i; if (send_proc >= num_procs) + { send_proc -= num_procs; + } recv_proc = rank - i; if (recv_proc < 0) + { recv_proc += num_procs; + } send_pos = sdispls[send_proc] * send_size; recv_pos = rdispls[recv_proc] * recv_size; - MPI_Isend(send_buffer + send_pos, sendcounts[send_proc], sendtype, send_proc, tag, - comm->global_comm, &(requests[send_idx++])); - MPI_Irecv(recv_buffer + recv_pos, recvcounts[recv_proc], recvtype, recv_proc, tag, - comm->global_comm, &(requests[nb_stride + recv_idx++])); - - if ((i+1) >= nb_stride) + MPI_Isend(send_buffer + send_pos, + sendcounts[send_proc], + sendtype, + send_proc, + tag, + comm->global_comm, + &(requests[send_idx++])); + MPI_Irecv(recv_buffer + recv_pos, + recvcounts[recv_proc], + recvtype, + recv_proc, + tag, + comm->global_comm, + &(requests[nb_stride + recv_idx++])); + + if ((i + 1) >= nb_stride) { MPI_Waitany(nb_stride, requests, &send_idx, MPI_STATUSES_IGNORE); - MPI_Waitany(nb_stride, &(requests[nb_stride]), &recv_idx, MPI_STATUSES_IGNORE); + MPI_Waitany( + nb_stride, &(requests[nb_stride]), &recv_idx, MPI_STATUSES_IGNORE); } } - MPI_Waitall(2*nb_stride, requests, MPI_STATUSES_IGNORE); + MPI_Waitall(2 * nb_stride, requests, MPI_STATUSES_IGNORE); free(requests); return 0; } - // Calls underlying MPI implementation int alltoallv_pmpi(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return PMPI_Alltoallv(sendbuf, sendcounts, sdispls, sendtype, - recvbuf, recvcounts, rdispls, recvtype, comm->global_comm); + return PMPI_Alltoallv(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm->global_comm); } diff --git a/src/collective/alltoallv.h b/src/collective/alltoallv.h index 77f5ec106..ce3826a19 100644 --- a/src/collective/alltoallv.h +++ b/src/collective/alltoallv.h @@ -1,73 +1,85 @@ #ifndef MPI_ADVANCE_ALLTOALLV_H #define MPI_ADVANCE_ALLTOALLV_H -#include -#include #include +#include +#include + #include "collective.h" #include "communicator/mpix_comm.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif // TODO : need to add hierarchical/locality-aware methods for alltoallv -enum AlltoallvMethod { ALLTOALLV_PAIRWISE, ALLTOALLV_NONBLOCKING, ALLTOALLV_BATCH, - ALLTOALLV_BATCH_ASYNC, ALLTOALLV_PMPI }; +enum AlltoallvMethod +{ + ALLTOALLV_PAIRWISE, + ALLTOALLV_NONBLOCKING, + ALLTOALLV_BATCH, + ALLTOALLV_BATCH_ASYNC, + ALLTOALLV_PMPI +}; extern AlltoallvMethod mpix_alltoallv_implementation; -typedef int (*alltoallv_ftn)(const void*, const int*, const int*, MPI_Datatype, -void*, const int*, const int*, MPI_Datatype, MPIX_Comm*); +typedef int (*alltoallv_ftn)(const void*, + const int*, + const int*, + MPI_Datatype, + void*, + const int*, + const int*, + MPI_Datatype, + MPIX_Comm*); // Helper Functions int alltoallv_pairwise(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); int alltoallv_nonblocking(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); int alltoallv_batch(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); int alltoallv_batch_async(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); - + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); int alltoallv_pmpi(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); #ifdef __cplusplus } diff --git a/src/collective/collective.h b/src/collective/collective.h index ccabce8f3..cc5356d1d 100644 --- a/src/collective/collective.h +++ b/src/collective/collective.h @@ -1,49 +1,47 @@ #ifndef MPI_ADVANCE_COLLECTIVES_H #define MPI_ADVANCE_COLLECTIVES_H -#include -#include #include -//#include -#include "utils/utils.h" +#include +#include +// #include #include "alltoall.h" #include "alltoallv.h" +#include "utils/utils.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif int MPIX_Alltoall(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); int MPI_Alltoallv(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPI_Comm comm); + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPI_Comm comm); int MPIX_Alltoallv(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); #ifdef __cplusplus } #endif - #endif diff --git a/src/communicator/comm_data.c b/src/communicator/comm_data.c index 27c5a6be5..08e122895 100644 --- a/src/communicator/comm_data.c +++ b/src/communicator/comm_data.c @@ -4,34 +4,47 @@ void init_comm_data(CommData** comm_data_ptr, MPI_Datatype datatype) { CommData* data = (CommData*)malloc(sizeof(CommData)); - data->num_msgs = 0; + data->num_msgs = 0; data->size_msgs = 0; MPI_Type_size(datatype, &(data->datatype_size)); - data->procs = NULL; - data->indptr = NULL; + data->procs = NULL; + data->indptr = NULL; data->indices = NULL; - data->buffer = NULL; + data->buffer = NULL; *comm_data_ptr = data; } void destroy_comm_data(CommData* data) { - if (data->procs) free(data->procs); - if (data->indptr) free(data->indptr); - if (data->indices) free(data->indices); - if (data->buffer) free(data->buffer); + if (data->procs) + { + free(data->procs); + } + if (data->indptr) + { + free(data->indptr); + } + if (data->indices) + { + free(data->indices); + } + if (data->buffer) + { + free(data->buffer); + } free(data); } - void init_num_msgs(CommData* data, int num_msgs) { data->num_msgs = num_msgs; if (data->num_msgs) - data->procs = (int*)malloc(sizeof(int)*data->num_msgs); - data->indptr = (int*)malloc(sizeof(int)*(data->num_msgs+1)); + { + data->procs = (int*)malloc(sizeof(int) * data->num_msgs); + } + data->indptr = (int*)malloc(sizeof(int) * (data->num_msgs + 1)); data->indptr[0] = 0; } @@ -39,14 +52,16 @@ void init_size_msgs(CommData* data, int size_msgs) { data->size_msgs = size_msgs; if (data->size_msgs) - data->indices = (int*)malloc(data->size_msgs*sizeof(int)); + { + data->indices = (int*)malloc(data->size_msgs * sizeof(int)); + } } - void finalize_comm_data(CommData* data) { if (data->size_msgs) - data->buffer = (char*)malloc(data->size_msgs*data->datatype_size*sizeof(char)); + { + data->buffer = + (char*)malloc(data->size_msgs * data->datatype_size * sizeof(char)); + } } - - diff --git a/src/communicator/comm_data.h b/src/communicator/comm_data.h index a0ec5a9d4..1b2cfb3ff 100644 --- a/src/communicator/comm_data.h +++ b/src/communicator/comm_data.h @@ -1,13 +1,12 @@ #ifndef MPI_ADVANCE_COMM_DATA_H #define MPI_ADVANCE_COMM_DATA_H -#include -#include #include +#include +#include #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif typedef struct _CommData @@ -31,5 +30,4 @@ void finalize_comm_data(CommData* data); } #endif - #endif diff --git a/src/communicator/comm_pkg.c b/src/communicator/comm_pkg.c index c49723e4c..144f930fc 100644 --- a/src/communicator/comm_pkg.c +++ b/src/communicator/comm_pkg.c @@ -1,6 +1,9 @@ #include "comm_pkg.h" -void init_comm_pkg(CommPkg** comm_ptr, MPI_Datatype sendtype, MPI_Datatype recvtype, int _tag) +void init_comm_pkg(CommPkg** comm_ptr, + MPI_Datatype sendtype, + MPI_Datatype recvtype, + int _tag) { CommPkg* comm = (CommPkg*)malloc(sizeof(CommPkg)); @@ -24,4 +27,3 @@ void destroy_comm_pkg(CommPkg* comm) free(comm); } - diff --git a/src/communicator/comm_pkg.h b/src/communicator/comm_pkg.h index 7cac6ff03..241081cfe 100644 --- a/src/communicator/comm_pkg.h +++ b/src/communicator/comm_pkg.h @@ -10,8 +10,10 @@ typedef struct _CommPkg int tag; } CommPkg; -void init_comm_pkg(CommPkg** comm_ptr, MPI_Datatype sendtype, - MPI_Datatype recvtype, int _tag); +void init_comm_pkg(CommPkg** comm_ptr, + MPI_Datatype sendtype, + MPI_Datatype recvtype, + int _tag); void finalize_comm_pkg(CommPkg* comm); void destroy_comm_pkg(CommPkg* comm); diff --git a/src/communicator/locality_comm.c b/src/communicator/locality_comm.c index b5f050a66..92dab2aa2 100644 --- a/src/communicator/locality_comm.c +++ b/src/communicator/locality_comm.c @@ -1,7 +1,9 @@ #include "locality_comm.h" -void init_locality_comm(LocalityComm** locality_ptr, MPIX_Comm* mpix_comm, - MPI_Datatype sendtype, MPI_Datatype recvtype) +void init_locality_comm(LocalityComm** locality_ptr, + MPIX_Comm* mpix_comm, + MPI_Datatype sendtype, + MPI_Datatype recvtype) { LocalityComm* locality = (LocalityComm*)malloc(sizeof(LocalityComm)); @@ -42,10 +44,10 @@ void destroy_locality_comm(LocalityComm* locality) } void get_local_comm_data(LocalityComm* locality, - int* max_local_num, - int* max_local_size, - int* max_non_local_num, - int* max_non_local_size) + int* max_local_num, + int* max_local_size, + int* max_non_local_num, + int* max_non_local_size) { int sizes[4]; int max_sizes[4]; @@ -76,11 +78,8 @@ void get_local_comm_data(LocalityComm* locality, MPI_Allreduce(MPI_IN_PLACE, max_sizes, 4, MPI_INT, MPI_MAX, MPI_COMM_WORLD); - *max_local_num = sizes[0]; - *max_local_size = sizes[1]; - *max_non_local_num = sizes[2]; + *max_local_num = sizes[0]; + *max_local_size = sizes[1]; + *max_non_local_num = sizes[2]; *max_non_local_size = sizes[3]; - } - - diff --git a/src/communicator/locality_comm.h b/src/communicator/locality_comm.h index 23dd4578c..90558b5db 100644 --- a/src/communicator/locality_comm.h +++ b/src/communicator/locality_comm.h @@ -1,17 +1,15 @@ #ifndef MPI_ADVANCE_LOCALITY_COMM_H #define MPI_ADVANCE_LOCALITY_COMM_H +#include + #include "comm_pkg.h" #include "mpix_comm.h" -#include - // Declarations of C++ methods #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - typedef struct _LocalityComm { @@ -19,22 +17,22 @@ typedef struct _LocalityComm CommPkg* local_S_comm; CommPkg* local_R_comm; CommPkg* global_comm; - + MPIX_Comm* communicators; } LocalityComm; -void init_locality_comm(LocalityComm** locality_ptr, MPIX_Comm* comm, - MPI_Datatype sendtype, MPI_Datatype recvtype); +void init_locality_comm(LocalityComm** locality_ptr, + MPIX_Comm* comm, + MPI_Datatype sendtype, + MPI_Datatype recvtype); void finalize_locality_comm(LocalityComm* locality); void destroy_locality_comm(LocalityComm* locality); void get_local_comm_data(LocalityComm* locality, - int* max_local_num, - int* max_local_size, - int* max_non_local_num, - int* max_non_local_size); - - + int* max_local_num, + int* max_local_size, + int* max_non_local_num, + int* max_non_local_size); // Declarations of C++ methods #ifdef __cplusplus diff --git a/src/communicator/mpix_comm.c b/src/communicator/mpix_comm.c index 8c0472b75..fb1b18375 100644 --- a/src/communicator/mpix_comm.c +++ b/src/communicator/mpix_comm.c @@ -6,24 +6,24 @@ int MPIX_Comm_init(MPIX_Comm** xcomm_ptr, MPI_Comm global_comm) MPI_Comm_rank(global_comm, &rank); MPI_Comm_size(global_comm, &num_procs); - MPIX_Comm* xcomm = (MPIX_Comm*)malloc(sizeof(MPIX_Comm)); + MPIX_Comm* xcomm = (MPIX_Comm*)malloc(sizeof(MPIX_Comm)); xcomm->global_comm = global_comm; xcomm->local_comm = MPI_COMM_NULL; xcomm->group_comm = MPI_COMM_NULL; - xcomm->leader_comm = MPI_COMM_NULL; + xcomm->leader_comm = MPI_COMM_NULL; xcomm->leader_group_comm = MPI_COMM_NULL; xcomm->leader_local_comm = MPI_COMM_NULL; xcomm->neighbor_comm = MPI_COMM_NULL; - xcomm->win = MPI_WIN_NULL; + xcomm->win = MPI_WIN_NULL; xcomm->win_array = NULL; xcomm->win_bytes = 0; - xcomm->requests = NULL; - xcomm->statuses = NULL; + xcomm->requests = NULL; + xcomm->statuses = NULL; xcomm->n_requests = 0; int flag; @@ -31,7 +31,7 @@ int MPIX_Comm_init(MPIX_Comm** xcomm_ptr, MPI_Comm global_comm) xcomm->tag = 126 % xcomm->max_tag; xcomm->global_rank_to_local = NULL; - xcomm->global_rank_to_node = NULL; + xcomm->global_rank_to_node = NULL; xcomm->ordered_global_ranks = NULL; #ifdef GPU @@ -43,7 +43,6 @@ int MPIX_Comm_init(MPIX_Comm** xcomm_ptr, MPI_Comm global_comm) return MPI_SUCCESS; } - int MPIX_Comm_topo_init(MPIX_Comm* xcomm) { int rank, num_procs; @@ -52,20 +51,17 @@ int MPIX_Comm_topo_init(MPIX_Comm* xcomm) // Split global comm into local (per node) communicators MPI_Comm_split_type(xcomm->global_comm, - MPI_COMM_TYPE_SHARED, - rank, - MPI_INFO_NULL, - &(xcomm->local_comm)); + MPI_COMM_TYPE_SHARED, + rank, + MPI_INFO_NULL, + &(xcomm->local_comm)); int local_rank, ppn; MPI_Comm_rank(xcomm->local_comm, &local_rank); MPI_Comm_size(xcomm->local_comm, &ppn); // Split global comm into group (per local rank) communicators - MPI_Comm_split(xcomm->global_comm, - local_rank, - rank, - &(xcomm->group_comm)); + MPI_Comm_split(xcomm->global_comm, local_rank, rank, &(xcomm->group_comm)); int node; MPI_Comm_rank(xcomm->group_comm, &node); @@ -74,22 +70,29 @@ int MPIX_Comm_topo_init(MPIX_Comm* xcomm) // These arrays allow for these methods to work with any ordering // No longer relying on SMP ordering of processes to nodes! // Does rely on constant ppn - xcomm->global_rank_to_local = (int*)malloc(num_procs*sizeof(int)); - xcomm->global_rank_to_node = (int*)malloc(num_procs*sizeof(int)); - MPI_Allgather(&local_rank, 1, MPI_INT, xcomm->global_rank_to_local, 1, MPI_INT, xcomm->global_comm); - MPI_Allgather(&node, 1, MPI_INT, xcomm->global_rank_to_node, 1, MPI_INT, xcomm->global_comm); - - xcomm->ordered_global_ranks = (int*)malloc(num_procs*sizeof(int)); + xcomm->global_rank_to_local = (int*)malloc(num_procs * sizeof(int)); + xcomm->global_rank_to_node = (int*)malloc(num_procs * sizeof(int)); + MPI_Allgather(&local_rank, + 1, + MPI_INT, + xcomm->global_rank_to_local, + 1, + MPI_INT, + xcomm->global_comm); + MPI_Allgather( + &node, 1, MPI_INT, xcomm->global_rank_to_node, 1, MPI_INT, xcomm->global_comm); + + xcomm->ordered_global_ranks = (int*)malloc(num_procs * sizeof(int)); for (int i = 0; i < num_procs; i++) { - int local = xcomm->global_rank_to_local[i]; - int node = xcomm->global_rank_to_node[i]; - xcomm->ordered_global_ranks[node*ppn + local] = i; + int local = xcomm->global_rank_to_local[i]; + int node = xcomm->global_rank_to_node[i]; + xcomm->ordered_global_ranks[node * ppn + local] = i; } // Set xcomm variables MPI_Comm_size(xcomm->local_comm, &(xcomm->ppn)); - xcomm->num_nodes = ((num_procs-1) / xcomm->ppn) + 1; + xcomm->num_nodes = ((num_procs - 1) / xcomm->ppn) + 1; xcomm->rank_node = get_node(xcomm, rank); return MPI_SUCCESS; @@ -101,26 +104,20 @@ int MPIX_Comm_leader_init(MPIX_Comm* xcomm, int procs_per_leader) MPI_Comm_rank(xcomm->global_comm, &rank); MPI_Comm_size(xcomm->global_comm, &num_procs); - MPI_Comm_split(xcomm->global_comm, - rank / procs_per_leader, - rank, - &(xcomm->leader_comm)); + MPI_Comm_split( + xcomm->global_comm, rank / procs_per_leader, rank, &(xcomm->leader_comm)); int leader_rank; MPI_Comm_rank(xcomm->leader_comm, &leader_rank); - MPI_Comm_split(xcomm->global_comm, - leader_rank, - rank, - &(xcomm->leader_group_comm)); + MPI_Comm_split(xcomm->global_comm, leader_rank, rank, &(xcomm->leader_group_comm)); if (xcomm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(xcomm); + } - MPI_Comm_split(xcomm->local_comm, - leader_rank, - rank, - &(xcomm->leader_local_comm)); + MPI_Comm_split(xcomm->local_comm, leader_rank, rank, &(xcomm->leader_local_comm)); return MPI_SUCCESS; } @@ -129,7 +126,9 @@ int MPIX_Comm_device_init(MPIX_Comm* xcomm) { #ifdef GPU if (xcomm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(xcomm); + } int local_rank, ierr; MPI_Comm_rank(xcomm->local_comm, &local_rank); @@ -138,7 +137,7 @@ int MPIX_Comm_device_init(MPIX_Comm* xcomm) if (xcomm->gpus_per_node) { xcomm->rank_gpu = local_rank; - ierr = gpuStreamCreate(&(xcomm->proc_stream)); + ierr = gpuStreamCreate(&(xcomm->proc_stream)); gpu_check(ierr); } #endif @@ -152,32 +151,37 @@ int MPIX_Comm_win_init(MPIX_Comm* xcomm, int bytes, int type_bytes) MPI_Comm_rank(xcomm->global_comm, &rank); MPI_Comm_size(xcomm->global_comm, &num_procs); - - xcomm->win_bytes = bytes; + xcomm->win_bytes = bytes; xcomm->win_type_bytes = type_bytes; MPI_Alloc_mem(xcomm->win_bytes, MPI_INFO_NULL, &(xcomm->win_array)); - MPI_Win_create(xcomm->win_array, xcomm->win_bytes, - xcomm->win_type_bytes, MPI_INFO_NULL, - xcomm->global_comm, &(xcomm->win)); + MPI_Win_create(xcomm->win_array, + xcomm->win_bytes, + xcomm->win_type_bytes, + MPI_INFO_NULL, + xcomm->global_comm, + &(xcomm->win)); return MPI_SUCCESS; } int MPIX_Comm_req_resize(MPIX_Comm* xcomm, int n) { - if (n <= 0) return MPI_SUCCESS; + if (n <= 0) + { + return MPI_SUCCESS; + } xcomm->n_requests = n; - xcomm->requests = (MPI_Request*)realloc(xcomm->requests, n*sizeof(MPI_Request)); - xcomm->statuses = (MPI_Status*)realloc(xcomm->statuses, n*sizeof(MPI_Status)); + xcomm->requests = (MPI_Request*)realloc(xcomm->requests, n * sizeof(MPI_Request)); + xcomm->statuses = (MPI_Status*)realloc(xcomm->statuses, n * sizeof(MPI_Status)); return MPI_SUCCESS; } int MPIX_Comm_tag(MPIX_Comm* xcomm, int* tag) { - *tag = xcomm->tag; - xcomm->tag = ((xcomm->tag + 1 ) % xcomm->max_tag); + *tag = xcomm->tag; + xcomm->tag = ((xcomm->tag + 1) % xcomm->max_tag); return MPI_SUCCESS; } @@ -187,10 +191,14 @@ int MPIX_Comm_free(MPIX_Comm** xcomm_ptr) MPIX_Comm* xcomm = *xcomm_ptr; if (xcomm->n_requests > 0) + { free(xcomm->requests); + } if (xcomm->neighbor_comm != MPI_COMM_NULL) + { MPI_Comm_free(&(xcomm->neighbor_comm)); + } MPIX_Comm_topo_free(xcomm); MPIX_Comm_leader_free(xcomm); @@ -205,16 +213,26 @@ int MPIX_Comm_free(MPIX_Comm** xcomm_ptr) int MPIX_Comm_topo_free(MPIX_Comm* xcomm) { if (xcomm->local_comm != MPI_COMM_NULL) - MPI_Comm_free(&(xcomm->local_comm)); + { + MPI_Comm_free(&(xcomm->local_comm)); + } if (xcomm->group_comm != MPI_COMM_NULL) - MPI_Comm_free(&(xcomm->group_comm)); + { + MPI_Comm_free(&(xcomm->group_comm)); + } if (xcomm->global_rank_to_local != NULL) + { free(xcomm->global_rank_to_local); + } if (xcomm->global_rank_to_node != NULL) + { free(xcomm->global_rank_to_node); + } if (xcomm->ordered_global_ranks != NULL) - free(xcomm->ordered_global_ranks); + { + free(xcomm->ordered_global_ranks); + } return MPI_SUCCESS; } @@ -222,11 +240,17 @@ int MPIX_Comm_topo_free(MPIX_Comm* xcomm) int MPIX_Comm_leader_free(MPIX_Comm* xcomm) { if (xcomm->leader_comm != MPI_COMM_NULL) - MPI_Comm_free(&(xcomm->leader_comm)); + { + MPI_Comm_free(&(xcomm->leader_comm)); + } if (xcomm->leader_group_comm != MPI_COMM_NULL) - MPI_Comm_free(&(xcomm->leader_group_comm)); + { + MPI_Comm_free(&(xcomm->leader_group_comm)); + } if (xcomm->leader_local_comm != MPI_COMM_NULL) + { MPI_Comm_free(&(xcomm->leader_local_comm)); + } return MPI_SUCCESS; } @@ -237,12 +261,16 @@ int MPIX_Comm_win_free(MPIX_Comm* xcomm) MPI_Comm_rank(xcomm->global_comm, &rank); MPI_Comm_size(xcomm->global_comm, &num_procs); - if (xcomm->win != MPI_WIN_NULL) - MPI_Win_free(&(xcomm->win)); - if (xcomm->win_array != NULL) - MPI_Free_mem(xcomm->win_array); - xcomm->win_bytes = 0; - xcomm->win_type_bytes = 0; + if (xcomm->win != MPI_WIN_NULL) + { + MPI_Win_free(&(xcomm->win)); + } + if (xcomm->win_array != NULL) + { + MPI_Free_mem(xcomm->win_array); + } + xcomm->win_bytes = 0; + xcomm->win_type_bytes = 0; return MPI_SUCCESS; } @@ -252,20 +280,19 @@ int MPIX_Comm_device_free(MPIX_Comm* xcomm) #ifdef GPU int ierr = gpuSuccess; if (xcomm->gpus_per_node) + { ierr = gpuStreamDestroy(xcomm->proc_stream); + } gpu_check(ierr); #endif return MPI_SUCCESS; } - - - /**** Topology Functions ****/ int get_node(const MPIX_Comm* data, const int proc) { - return data->global_rank_to_node[proc]; + return data->global_rank_to_node[proc]; } int get_local_proc(const MPIX_Comm* data, const int proc) @@ -287,49 +314,56 @@ void update_locality(MPIX_Comm* xcomm, int ppn) MPI_Comm_size(xcomm->global_comm, &num_procs); if (xcomm->local_comm != MPI_COMM_NULL) + { MPI_Comm_free(&(xcomm->local_comm)); + } if (xcomm->group_comm != MPI_COMM_NULL) + { MPI_Comm_free(&(xcomm->group_comm)); + } - MPI_Comm_split(xcomm->global_comm, - rank / ppn, - rank, - &(xcomm->local_comm)); - - + MPI_Comm_split(xcomm->global_comm, rank / ppn, rank, &(xcomm->local_comm)); int local_rank; MPI_Comm_rank(xcomm->local_comm, &local_rank); - MPI_Comm_split(xcomm->global_comm, - local_rank, - rank, - &(xcomm->group_comm)); + MPI_Comm_split(xcomm->global_comm, local_rank, rank, &(xcomm->group_comm)); int node; MPI_Comm_rank(xcomm->group_comm, &node); - if (xcomm->global_rank_to_local == NULL) - xcomm->global_rank_to_local = (int*)malloc(num_procs*sizeof(int)); + { + xcomm->global_rank_to_local = (int*)malloc(num_procs * sizeof(int)); + } if (xcomm->global_rank_to_node == NULL) - xcomm->global_rank_to_node = (int*)malloc(num_procs*sizeof(int)); + { + xcomm->global_rank_to_node = (int*)malloc(num_procs * sizeof(int)); + } - MPI_Allgather(&local_rank, 1, MPI_INT, xcomm->global_rank_to_local, 1, MPI_INT, xcomm->global_comm); - MPI_Allgather(&node, 1, MPI_INT, xcomm->global_rank_to_node, 1, MPI_INT, xcomm->global_comm); + MPI_Allgather(&local_rank, + 1, + MPI_INT, + xcomm->global_rank_to_local, + 1, + MPI_INT, + xcomm->global_comm); + MPI_Allgather( + &node, 1, MPI_INT, xcomm->global_rank_to_node, 1, MPI_INT, xcomm->global_comm); if (xcomm->ordered_global_ranks == NULL) - xcomm->ordered_global_ranks = (int*)malloc(num_procs*sizeof(int)); + { + xcomm->ordered_global_ranks = (int*)malloc(num_procs * sizeof(int)); + } for (int i = 0; i < num_procs; i++) { - int local = xcomm->global_rank_to_local[i]; - int node = xcomm->global_rank_to_node[i]; - xcomm->ordered_global_ranks[node*ppn + local] = i; + int local = xcomm->global_rank_to_local[i]; + int node = xcomm->global_rank_to_node[i]; + xcomm->ordered_global_ranks[node * ppn + local] = i; } MPI_Comm_size(xcomm->local_comm, &(xcomm->ppn)); xcomm->num_nodes = ((num_procs - 1) / xcomm->ppn) + 1; xcomm->rank_node = get_node(xcomm, rank); } - diff --git a/src/communicator/mpix_comm.h b/src/communicator/mpix_comm.h index 7f693b942..c91ed4dab 100644 --- a/src/communicator/mpix_comm.h +++ b/src/communicator/mpix_comm.h @@ -1,4 +1,4 @@ -// TODO Currently Assumes SMP Ordering +// TODO Currently Assumes SMP Ordering // And equal number of processes per node #ifndef MPI_ADVANCE_TOPOLOGY_H @@ -11,8 +11,7 @@ #include "utils/utils.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif typedef struct _MPIX_Comm @@ -52,9 +51,9 @@ typedef struct _MPIX_Comm int* ordered_global_ranks; #ifdef GPU - int gpus_per_node; - int rank_gpu; - gpuStream_t proc_stream; + int gpus_per_node; + int rank_gpu; + gpuStream_t proc_stream; #endif } MPIX_Comm; @@ -88,7 +87,4 @@ void update_locality(MPIX_Comm* xcomm, int ppn); } #endif - - - #endif diff --git a/src/heterogeneous/gpu_alltoall.c b/src/heterogeneous/gpu_alltoall.c index 02ff8d755..2ac43c397 100644 --- a/src/heterogeneous/gpu_alltoall.c +++ b/src/heterogeneous/gpu_alltoall.c @@ -1,16 +1,17 @@ +#include "gpu_alltoall.h" + #include "collective/alltoall.h" #include "collective/collective.h" -#include "gpu_alltoall.h" // ASSUMES 1 CPU CORE PER GPU (Standard for applications) int gpu_aware_alltoall(alltoall_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { int num_procs; MPI_Comm_size(comm->global_comm, &num_procs); @@ -35,50 +36,50 @@ int gpu_aware_alltoall(alltoall_ftn f, return ierr; } -int gpu_aware_alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int gpu_aware_alltoall_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { return gpu_aware_alltoall(alltoall_pairwise, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm); + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); } -int gpu_aware_alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int gpu_aware_alltoall_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { return gpu_aware_alltoall(alltoall_nonblocking, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm); + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); } int copy_to_cpu_alltoall(alltoall_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { int ierr = 0; @@ -108,131 +109,135 @@ int copy_to_cpu_alltoall(alltoall_ftn f, gpuFreeHost(cpu_sendbuf); gpuFreeHost(cpu_recvbuf); - + return ierr; } -int copy_to_cpu_alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int copy_to_cpu_alltoall_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { return copy_to_cpu_alltoall(alltoall_pairwise, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm); - + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); } -int copy_to_cpu_alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) +int copy_to_cpu_alltoall_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { return copy_to_cpu_alltoall(alltoall_nonblocking, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm); + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); } - #ifdef OPENMP int threaded_alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { int ierr = 0; - + int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); - + int send_bytes, recv_bytes; MPI_Type_size(sendtype, &send_bytes); MPI_Type_size(recvtype, &recv_bytes); - + int total_bytes_s = sendcount * send_bytes * num_procs; int total_bytes_r = recvcount * recv_bytes * num_procs; - + char* cpu_sendbuf; char* cpu_recvbuf; gpuMallocHost((void**)&cpu_sendbuf, total_bytes_s); gpuMallocHost((void**)&cpu_recvbuf, total_bytes_r); - + // Copy from GPU to CPU ierr += gpuMemcpy(cpu_sendbuf, sendbuf, total_bytes_s, gpuMemcpyDeviceToHost); memcpy(cpu_recvbuf + (rank * recvcount * recv_bytes), - cpu_sendbuf + (rank * sendcount * send_bytes), - sendcount * send_bytes); + cpu_sendbuf + (rank * sendcount * send_bytes), + sendcount * send_bytes); #pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) -{ - MPI_Status status; - int tag; - MPIX_Comm_tag(comm, &tag); - int send_proc, recv_proc; - int send_pos, recv_pos; - - int n_msgs = num_procs - 1; - int thread_id = omp_get_thread_num(); - int num_threads = omp_get_num_threads(); - - int n_msgs_per_thread = n_msgs / num_threads; - int extra_msgs = n_msgs % num_threads; - int thread_n_msgs = n_msgs_per_thread; - if (extra_msgs > thread_id) - thread_n_msgs++; - - if (thread_n_msgs) { - int idx = thread_id + 1; - for (int i = 0; i < thread_n_msgs; i++) + MPI_Status status; + int tag; + MPIX_Comm_tag(comm, &tag); + int send_proc, recv_proc; + int send_pos, recv_pos; + + int n_msgs = num_procs - 1; + int thread_id = omp_get_thread_num(); + int num_threads = omp_get_num_threads(); + + int n_msgs_per_thread = n_msgs / num_threads; + int extra_msgs = n_msgs % num_threads; + int thread_n_msgs = n_msgs_per_thread; + if (extra_msgs > thread_id) { - send_proc = rank + idx; - if (send_proc >= num_procs) - send_proc -= num_procs; - recv_proc = rank - idx; - if (recv_proc < 0) - recv_proc += num_procs; - send_pos = send_proc * sendcount * send_bytes; - recv_pos = recv_proc * recvcount * recv_bytes; - - MPI_Sendrecv(cpu_sendbuf + send_pos, - sendcount, - sendtype, - send_proc, - tag, - cpu_recvbuf + recv_pos, - recvcount, - recvtype, - recv_proc, - tag, - comm->global_comm, - &status); - - idx += num_threads; + thread_n_msgs++; + } + + if (thread_n_msgs) + { + int idx = thread_id + 1; + for (int i = 0; i < thread_n_msgs; i++) + { + send_proc = rank + idx; + if (send_proc >= num_procs) + { + send_proc -= num_procs; + } + recv_proc = rank - idx; + if (recv_proc < 0) + { + recv_proc += num_procs; + } + send_pos = send_proc * sendcount * send_bytes; + recv_pos = recv_proc * recvcount * recv_bytes; + + MPI_Sendrecv(cpu_sendbuf + send_pos, + sendcount, + sendtype, + send_proc, + tag, + cpu_recvbuf + recv_pos, + recvcount, + recvtype, + recv_proc, + tag, + comm->global_comm, + &status); + + idx += num_threads; + } } } -} ierr += gpuMemcpy(recvbuf, cpu_recvbuf, total_bytes_r, gpuMemcpyHostToDevice); @@ -243,12 +248,12 @@ int threaded_alltoall_pairwise(const void* sendbuf, } int threaded_alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm) { int num_procs, rank; MPI_Comm_rank(comm->global_comm, &rank); @@ -257,7 +262,7 @@ int threaded_alltoall_nonblocking(const void* sendbuf, int send_bytes, recv_bytes; MPI_Type_size(sendtype, &send_bytes); MPI_Type_size(recvtype, &recv_bytes); - + int total_bytes_s = sendcount * send_bytes * num_procs; int total_bytes_r = recvcount * recv_bytes * num_procs; @@ -270,64 +275,71 @@ int threaded_alltoall_nonblocking(const void* sendbuf, ierr += gpuMemcpy(cpu_sendbuf, sendbuf, total_bytes_s, gpuMemcpyDeviceToHost); memcpy(cpu_recvbuf + (rank * recvcount * recv_bytes), - cpu_sendbuf + (rank * sendcount * send_bytes), - sendcount * send_bytes); + cpu_sendbuf + (rank * sendcount * send_bytes), + sendcount * send_bytes); #pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) -{ - int tag; - MPIX_Comm_tag(comm, &tag); - int send_proc, recv_proc; - int send_pos, recv_pos; - - int n_msgs = num_procs - 1; - int thread_id = omp_get_thread_num(); - int num_threads = omp_get_num_threads(); - - int n_msgs_per_thread = n_msgs / num_threads; - int extra_msgs = n_msgs % num_threads; - int thread_n_msgs = n_msgs_per_thread; - if (extra_msgs > thread_id) - thread_n_msgs++; - - if (thread_n_msgs) { - MPI_Request* requests = (MPI_Request*)malloc(2*thread_n_msgs*sizeof(MPI_Request)); - - int idx = thread_id + 1; - for (int i = 0; i < thread_n_msgs; i++) + int tag; + MPIX_Comm_tag(comm, &tag); + int send_proc, recv_proc; + int send_pos, recv_pos; + + int n_msgs = num_procs - 1; + int thread_id = omp_get_thread_num(); + int num_threads = omp_get_num_threads(); + + int n_msgs_per_thread = n_msgs / num_threads; + int extra_msgs = n_msgs % num_threads; + int thread_n_msgs = n_msgs_per_thread; + if (extra_msgs > thread_id) { - send_proc = rank + idx; - if (send_proc >= num_procs) - send_proc -= num_procs; - recv_proc = rank - idx; - if (recv_proc < 0) - recv_proc += num_procs; - send_pos = send_proc * sendcount * send_bytes; - recv_pos = recv_proc * recvcount * recv_bytes; - - MPI_Isend(cpu_sendbuf + send_pos, - sendcount, - sendtype, - send_proc, - tag, - comm->global_comm, - &(requests[i])); - MPI_Irecv(cpu_recvbuf + recv_pos, - recvcount, - recvtype, - recv_proc, - tag, - comm->global_comm, - &(requests[thread_n_msgs + i])); - idx += num_threads; + thread_n_msgs++; } - MPI_Waitall(2*thread_n_msgs, requests, MPI_STATUSES_IGNORE); - - free(requests); + if (thread_n_msgs) + { + MPI_Request* requests = + (MPI_Request*)malloc(2 * thread_n_msgs * sizeof(MPI_Request)); + + int idx = thread_id + 1; + for (int i = 0; i < thread_n_msgs; i++) + { + send_proc = rank + idx; + if (send_proc >= num_procs) + { + send_proc -= num_procs; + } + recv_proc = rank - idx; + if (recv_proc < 0) + { + recv_proc += num_procs; + } + send_pos = send_proc * sendcount * send_bytes; + recv_pos = recv_proc * recvcount * recv_bytes; + + MPI_Isend(cpu_sendbuf + send_pos, + sendcount, + sendtype, + send_proc, + tag, + comm->global_comm, + &(requests[i])); + MPI_Irecv(cpu_recvbuf + recv_pos, + recvcount, + recvtype, + recv_proc, + tag, + comm->global_comm, + &(requests[thread_n_msgs + i])); + idx += num_threads; + } + + MPI_Waitall(2 * thread_n_msgs, requests, MPI_STATUSES_IGNORE); + + free(requests); + } } -} ierr += gpuMemcpy(recvbuf, cpu_recvbuf, total_bytes_r, gpuMemcpyHostToDevice); gpuFreeHost(cpu_sendbuf); diff --git a/src/heterogeneous/gpu_alltoall.h b/src/heterogeneous/gpu_alltoall.h index 9bf537452..74bf2bd3f 100644 --- a/src/heterogeneous/gpu_alltoall.h +++ b/src/heterogeneous/gpu_alltoall.h @@ -5,76 +5,74 @@ #include "collective/collective.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif int gpu_aware_alltoall(alltoall_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); int copy_to_cpu_alltoall(alltoall_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); -int gpu_aware_alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); -int gpu_aware_alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); -int copy_to_cpu_alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); -int copy_to_cpu_alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); +int gpu_aware_alltoall_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); +int gpu_aware_alltoall_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); +int copy_to_cpu_alltoall_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); +int copy_to_cpu_alltoall_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); #ifdef OPENMP #include int threaded_alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); int threaded_alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIX_Comm* comm); #endif - #ifdef __cplusplus } #endif diff --git a/src/heterogeneous/gpu_alltoallv.c b/src/heterogeneous/gpu_alltoallv.c index 6fef2c502..ae3f570c3 100644 --- a/src/heterogeneous/gpu_alltoallv.c +++ b/src/heterogeneous/gpu_alltoallv.c @@ -1,126 +1,129 @@ +#include "gpu_alltoallv.h" + #include "collective/alltoallv.h" #include "collective/collective.h" -#include "gpu_alltoallv.h" // ASSUMES 1 CPU CORE PER GPU (Standard for applications) int gpu_aware_alltoallv(alltoallv_ftn f, - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { - return f(sendbuf, sendcounts, sdispls, sendtype, recvbuf, recvcounts, rdispls, recvtype, - comm); + return f(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); } - - - -int gpu_aware_alltoallv_pairwise(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) +int gpu_aware_alltoallv_pairwise(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { return gpu_aware_alltoallv(alltoallv_pairwise, - sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); + sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); } -int gpu_aware_alltoallv_nonblocking(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) +int gpu_aware_alltoallv_nonblocking(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { return gpu_aware_alltoallv(alltoallv_nonblocking, - sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); + sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); } int gpu_aware_alltoallv_batch(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { return gpu_aware_alltoallv(alltoallv_batch, - sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); + sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); } int gpu_aware_alltoallv_batch_async(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { return gpu_aware_alltoallv(alltoallv_batch_async, - sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); + sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); } - - int copy_to_cpu_alltoallv(alltoallv_ftn f, - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { int ierr = 0; @@ -151,117 +154,123 @@ int copy_to_cpu_alltoallv(alltoallv_ftn f, ierr += gpuMemcpy(cpu_sendbuf, sendbuf, total_bytes_s, gpuMemcpyDeviceToHost); // Collective Among CPUs - ierr += f(cpu_sendbuf, sendcounts, sdispls, sendtype, - cpu_recvbuf, recvcounts, rdispls, recvtype, comm); + ierr += f(cpu_sendbuf, + sendcounts, + sdispls, + sendtype, + cpu_recvbuf, + recvcounts, + rdispls, + recvtype, + comm); // Copy from CPU to GPU ierr += gpuMemcpy(recvbuf, cpu_recvbuf, total_bytes_r, gpuMemcpyHostToDevice); gpuFreeHost(cpu_sendbuf); gpuFreeHost(cpu_recvbuf); - + return ierr; } int copy_to_cpu_alltoallv_pairwise(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { return copy_to_cpu_alltoallv(alltoallv_pairwise, - sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); + sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); } int copy_to_cpu_alltoallv_nonblocking(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { return copy_to_cpu_alltoallv(alltoallv_nonblocking, - sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); + sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); } int copy_to_cpu_alltoallv_batch(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { return copy_to_cpu_alltoallv(alltoallv_batch, - sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); + sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); } int copy_to_cpu_alltoallv_batch_async(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { return copy_to_cpu_alltoallv(alltoallv_batch_async, - sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); + sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); } - #ifdef OPENMP int threaded_alltoallv_pairwise(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { int ierr = 0; @@ -293,58 +302,64 @@ int threaded_alltoallv_pairwise(const void* sendbuf, ierr += gpuMemcpy(cpu_sendbuf, sendbuf, total_bytes_s, gpuMemcpyDeviceToHost); memcpy(cpu_recvbuf + (rdispls[rank] * recv_bytes), - cpu_sendbuf + (sdispls[rank] * send_bytes), - sendcounts[rank] * send_bytes); - + cpu_sendbuf + (sdispls[rank] * send_bytes), + sendcounts[rank] * send_bytes); + #pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) -{ - MPI_Status status; - int tag; - MPIX_Comm_tag(comm, &tag); - int send_proc, recv_proc; - int send_pos, recv_pos; - - int n_msgs = num_procs - 1; - int thread_id = omp_get_thread_num(); - int num_threads = omp_get_num_threads(); - - int n_msgs_per_thread = n_msgs / num_threads; - int extra_msgs = n_msgs % num_threads; - int thread_n_msgs = n_msgs_per_thread; - if (extra_msgs > thread_id) - thread_n_msgs++; - - if (thread_n_msgs) { - int idx = thread_id + 1; - for (int i = 0; i < thread_n_msgs; i++) + MPI_Status status; + int tag; + MPIX_Comm_tag(comm, &tag); + int send_proc, recv_proc; + int send_pos, recv_pos; + + int n_msgs = num_procs - 1; + int thread_id = omp_get_thread_num(); + int num_threads = omp_get_num_threads(); + + int n_msgs_per_thread = n_msgs / num_threads; + int extra_msgs = n_msgs % num_threads; + int thread_n_msgs = n_msgs_per_thread; + if (extra_msgs > thread_id) { - send_proc = rank + idx; - if (send_proc >= num_procs) - send_proc -= num_procs; - recv_proc = rank - idx; - if (recv_proc < 0) - recv_proc += num_procs; - send_pos = sdispls[send_proc] * send_bytes; - recv_pos = rdispls[recv_proc] * recv_bytes; - - MPI_Sendrecv(cpu_sendbuf + send_pos, - sendcounts[send_proc], - sendtype, - send_proc, - tag, - cpu_recvbuf + recv_pos, - recvcounts[recv_proc], - recvtype, - recv_proc, - tag, - comm->global_comm, - &status); - - idx += num_threads; + thread_n_msgs++; + } + + if (thread_n_msgs) + { + int idx = thread_id + 1; + for (int i = 0; i < thread_n_msgs; i++) + { + send_proc = rank + idx; + if (send_proc >= num_procs) + { + send_proc -= num_procs; + } + recv_proc = rank - idx; + if (recv_proc < 0) + { + recv_proc += num_procs; + } + send_pos = sdispls[send_proc] * send_bytes; + recv_pos = rdispls[recv_proc] * recv_bytes; + + MPI_Sendrecv(cpu_sendbuf + send_pos, + sendcounts[send_proc], + sendtype, + send_proc, + tag, + cpu_recvbuf + recv_pos, + recvcounts[recv_proc], + recvtype, + recv_proc, + tag, + comm->global_comm, + &status); + + idx += num_threads; + } } } -} ierr += gpuMemcpy(recvbuf, cpu_recvbuf, total_bytes_r, gpuMemcpyHostToDevice); @@ -355,14 +370,14 @@ int threaded_alltoallv_pairwise(const void* sendbuf, } int threaded_alltoallv_nonblocking(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { int ierr = 0; @@ -394,64 +409,71 @@ int threaded_alltoallv_nonblocking(const void* sendbuf, ierr += gpuMemcpy(cpu_sendbuf, sendbuf, total_bytes_s, gpuMemcpyDeviceToHost); memcpy(cpu_recvbuf + (rdispls[rank] * recv_bytes), - cpu_sendbuf + (sdispls[rank] * send_bytes), - sendcounts[rank] * send_bytes); - + cpu_sendbuf + (sdispls[rank] * send_bytes), + sendcounts[rank] * send_bytes); + #pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) -{ - int tag; - MPIX_Comm_tag(comm, &tag); - int send_proc, recv_proc; - int send_pos, recv_pos; - - int n_msgs = num_procs - 1; - int thread_id = omp_get_thread_num(); - int num_threads = omp_get_num_threads(); - - int n_msgs_per_thread = n_msgs / num_threads; - int extra_msgs = n_msgs % num_threads; - int thread_n_msgs = n_msgs_per_thread; - if (extra_msgs > thread_id) - thread_n_msgs++; - - if (thread_n_msgs) { - MPI_Request* requests = (MPI_Request*)malloc(2*thread_n_msgs*sizeof(MPI_Request)); - - int idx = thread_id + 1; - for (int i = 0; i < thread_n_msgs; i++) + int tag; + MPIX_Comm_tag(comm, &tag); + int send_proc, recv_proc; + int send_pos, recv_pos; + + int n_msgs = num_procs - 1; + int thread_id = omp_get_thread_num(); + int num_threads = omp_get_num_threads(); + + int n_msgs_per_thread = n_msgs / num_threads; + int extra_msgs = n_msgs % num_threads; + int thread_n_msgs = n_msgs_per_thread; + if (extra_msgs > thread_id) { - send_proc = rank + idx; - if (send_proc >= num_procs) - send_proc -= num_procs; - recv_proc = rank - idx; - if (recv_proc < 0) - recv_proc += num_procs; - send_pos = sdispls[send_proc] * send_bytes; - recv_pos = rdispls[recv_proc] * recv_bytes; - - MPI_Isend(cpu_sendbuf + send_pos, - sendcounts[send_proc], - sendtype, - send_proc, - tag, - comm->global_comm, - &(requests[i])); - MPI_Irecv(cpu_recvbuf + recv_pos, - recvcounts[recv_proc], - recvtype, - recv_proc, - tag, - comm->global_comm, - &(requests[thread_n_msgs + i])); - idx += num_threads; + thread_n_msgs++; } - MPI_Waitall(2*thread_n_msgs, requests, MPI_STATUSES_IGNORE); - - free(requests); + if (thread_n_msgs) + { + MPI_Request* requests = + (MPI_Request*)malloc(2 * thread_n_msgs * sizeof(MPI_Request)); + + int idx = thread_id + 1; + for (int i = 0; i < thread_n_msgs; i++) + { + send_proc = rank + idx; + if (send_proc >= num_procs) + { + send_proc -= num_procs; + } + recv_proc = rank - idx; + if (recv_proc < 0) + { + recv_proc += num_procs; + } + send_pos = sdispls[send_proc] * send_bytes; + recv_pos = rdispls[recv_proc] * recv_bytes; + + MPI_Isend(cpu_sendbuf + send_pos, + sendcounts[send_proc], + sendtype, + send_proc, + tag, + comm->global_comm, + &(requests[i])); + MPI_Irecv(cpu_recvbuf + recv_pos, + recvcounts[recv_proc], + recvtype, + recv_proc, + tag, + comm->global_comm, + &(requests[thread_n_msgs + i])); + idx += num_threads; + } + + MPI_Waitall(2 * thread_n_msgs, requests, MPI_STATUSES_IGNORE); + + free(requests); + } } -} ierr += gpuMemcpy(recvbuf, cpu_recvbuf, total_bytes_r, gpuMemcpyHostToDevice); @@ -462,5 +484,3 @@ int threaded_alltoallv_nonblocking(const void* sendbuf, } #endif - - diff --git a/src/heterogeneous/gpu_alltoallv.h b/src/heterogeneous/gpu_alltoallv.h index 5608f2a56..2f1a9ba48 100644 --- a/src/heterogeneous/gpu_alltoallv.h +++ b/src/heterogeneous/gpu_alltoallv.h @@ -5,139 +5,132 @@ #include "collective/collective.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif int gpu_aware_alltoallv(alltoallv_ftn f, - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); int copy_to_cpu_alltoallv(alltoallv_ftn f, - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); - - -int gpu_aware_alltoallv_pairwise(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); - -int gpu_aware_alltoallv_nonblocking(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); - -int gpu_aware_alltoallv_batch(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); - -int gpu_aware_alltoallv_batch_async(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); - - - - -int copy_to_cpu_alltoallv_pairwise(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); + +int gpu_aware_alltoallv_pairwise(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); + +int gpu_aware_alltoallv_nonblocking(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); + +int gpu_aware_alltoallv_batch(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); + +int gpu_aware_alltoallv_batch_async(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); + +int copy_to_cpu_alltoallv_pairwise(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); int copy_to_cpu_alltoallv_nonblocking(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); int copy_to_cpu_alltoallv_batch(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); int copy_to_cpu_alltoallv_batch_async(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); - - + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); #ifdef OPENMP #include int threaded_alltoallv_pairwise(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); int threaded_alltoallv_nonblocking(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); #endif #ifdef __cplusplus diff --git a/src/mpi_advance.h b/src/mpi_advance.h index 537843b48..8bf1197b5 100644 --- a/src/mpi_advance.h +++ b/src/mpi_advance.h @@ -1,29 +1,25 @@ #ifndef MPI_ADVANCE_H #define MPI_ADVANCE_H -#include "utils/utils.h" - +#include "collective/alltoall.h" +#include "collective/alltoallv.h" +#include "collective/collective.h" #include "communicator/comm_data.h" #include "communicator/comm_pkg.h" #include "communicator/locality_comm.h" #include "communicator/mpix_comm.h" - -#include "persistent/persistent.h" -#include "persistent/neighbor_persistent.h" - -#include "collective/collective.h" -#include "collective/alltoall.h" -#include "collective/alltoallv.h" - #include "neighborhood/dist_graph.h" #include "neighborhood/dist_topo.h" #include "neighborhood/neighbor.h" #include "neighborhood/neighbor_init.h" #include "neighborhood/sparse_coll.h" +#include "persistent/neighbor_persistent.h" +#include "persistent/persistent.h" +#include "utils/utils.h" #ifdef GPU - #include "heterogeneous/gpu_alltoall.h" - #include "heterogeneous/gpu_alltoallv.h" +#include "heterogeneous/gpu_alltoall.h" +#include "heterogeneous/gpu_alltoallv.h" #endif #endif diff --git a/src/neighborhood/alltoall_crs.cpp b/src/neighborhood/alltoall_crs.cpp index 0ee5a18dc..88ed23748 100644 --- a/src/neighborhood/alltoall_crs.cpp +++ b/src/neighborhood/alltoall_crs.cpp @@ -1,13 +1,21 @@ -#include "sparse_coll.h" #include #include +#include "sparse_coll.h" -int alltoall_crs_rma(const int send_nnz, const int* dest, const int sendcount, - MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz_ptr, int** src_ptr, int recvcount, MPI_Datatype recvtype, - void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) -{ +int alltoall_crs_rma(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz_ptr, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) +{ int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); @@ -15,16 +23,19 @@ int alltoall_crs_rma(const int send_nnz, const int* dest, const int sendcount, // Get bytes per datatype, total bytes to be recvd (size of win_array) char* send_buffer; if (send_nnz) + { send_buffer = (char*)(sendvals); + } int send_bytes, recv_bytes; MPI_Type_size(sendtype, &send_bytes); MPI_Type_size(recvtype, &recv_bytes); int bytes = num_procs * recvcount * recv_bytes; - if (comm->win_bytes != bytes - || comm->win_type_bytes != 1) + if (comm->win_bytes != bytes || comm->win_type_bytes != 1) + { MPIX_Comm_win_free(comm); + } if (comm->win == MPI_WIN_NULL) { @@ -38,14 +49,20 @@ int alltoall_crs_rma(const int send_nnz, const int* dest, const int sendcount, recv_bytes *= recvcount; MPI_Barrier(MPI_COMM_WORLD); - MPI_Win_fence(MPI_MODE_NOSTORE|MPI_MODE_NOPRECEDE, comm->win); + MPI_Win_fence(MPI_MODE_NOSTORE | MPI_MODE_NOPRECEDE, comm->win); for (int i = 0; i < send_nnz; i++) { - MPI_Put(&(send_buffer[i*send_bytes]), send_bytes, MPI_CHAR, - dest[i], rank*recv_bytes, recv_bytes, MPI_CHAR, comm->win); + MPI_Put(&(send_buffer[i * send_bytes]), + send_bytes, + MPI_CHAR, + dest[i], + rank * recv_bytes, + recv_bytes, + MPI_CHAR, + comm->win); } MPI_Barrier(MPI_COMM_WORLD); - MPI_Win_fence(MPI_MODE_NOPUT|MPI_MODE_NOSUCCEED, comm->win); + MPI_Win_fence(MPI_MODE_NOPUT | MPI_MODE_NOSUCCEED, comm->win); std::vector src; std::vector recv_buffer; @@ -55,7 +72,7 @@ int alltoall_crs_rma(const int send_nnz, const int* dest, const int sendcount, flag = 0; for (int j = 0; j < recv_bytes; j++) { - if (comm->win_array[i*recv_bytes+j]) + if (comm->win_array[i * recv_bytes + j]) { flag = 1; break; @@ -64,27 +81,35 @@ int alltoall_crs_rma(const int send_nnz, const int* dest, const int sendcount, if (flag) { src.push_back(i); - recv_buffer.insert(recv_buffer.end(), &(comm->win_array[i*recv_bytes]), - &(comm->win_array[i*recv_bytes]) + recv_bytes); + recv_buffer.insert(recv_buffer.end(), + &(comm->win_array[i * recv_bytes]), + &(comm->win_array[i * recv_bytes]) + recv_bytes); } } *recv_nnz_ptr = src.size(); - MPIX_Alloc((void**)src_ptr, src.size()*sizeof(int)); + MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); MPIX_Alloc(recvvals_ptr, recv_buffer.size()); //(*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); //(*recvvals_ptr) = MPIalloc(recv_buffer.size()); - memcpy((*src_ptr), src.data(), src.size()*sizeof(int)); + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvvals_ptr), recv_buffer.data(), recv_buffer.size()); - return MPI_SUCCESS; } -int alltoall_crs_personalized(const int send_nnz, const int* dest, const int sendcount, - MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int** src_ptr, int recvcount, MPI_Datatype recvtype, - void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) +int alltoall_crs_personalized(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -97,7 +122,9 @@ int alltoall_crs_personalized(const int send_nnz, const int* dest, const int sen char* send_buffer; if (send_nnz) + { send_buffer = (char*)(sendvals); + } int send_bytes, recv_bytes; MPI_Type_size(sendtype, &send_bytes); MPI_Type_size(recvtype, &recv_bytes); @@ -106,31 +133,41 @@ int alltoall_crs_personalized(const int send_nnz, const int* dest, const int sen if (!(xinfo->crs_num_initialized)) { - int* msg_counts = (int*)malloc(num_procs*sizeof(int)); - memset(msg_counts, 0, num_procs*sizeof(int)); + int* msg_counts = (int*)malloc(num_procs * sizeof(int)); + memset(msg_counts, 0, num_procs * sizeof(int)); for (int i = 0; i < send_nnz; i++) + { msg_counts[dest[i]] = 1; - MPI_Allreduce(MPI_IN_PLACE, msg_counts, num_procs, MPI_INT, MPI_SUM, comm->global_comm); + } + MPI_Allreduce( + MPI_IN_PLACE, msg_counts, num_procs, MPI_INT, MPI_SUM, comm->global_comm); *recv_nnz = msg_counts[rank]; free(msg_counts); } int* src; char* recvvals; - MPIX_Alloc((void**)&src, *recv_nnz*sizeof(int)); - MPIX_Alloc((void**)&recvvals, *recv_nnz*recv_bytes); -// int* src = (int*)MPIalloc(*recv_nnz*sizeof(int)); -// void* recvvals = MPIalloc(*recv_nnz*recv_bytes); + MPIX_Alloc((void**)&src, *recv_nnz * sizeof(int)); + MPIX_Alloc((void**)&recvvals, *recv_nnz * recv_bytes); + // int* src = (int*)MPIalloc(*recv_nnz*sizeof(int)); + // void* recvvals = MPIalloc(*recv_nnz*recv_bytes); if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } for (int i = 0; i < send_nnz; i++) { proc = dest[i]; - MPI_Isend(&(send_buffer[i*send_bytes]), send_bytes, MPI_BYTE, proc, tag, comm->global_comm, - &(comm->requests[i])); + MPI_Isend(&(send_buffer[i * send_bytes]), + send_bytes, + MPI_BYTE, + proc, + tag, + comm->global_comm, + &(comm->requests[i])); } ctr = 0; @@ -138,27 +175,41 @@ int alltoall_crs_personalized(const int send_nnz, const int* dest, const int sen { char* recv_buffer = (char*)recvvals; MPI_Probe(MPI_ANY_SOURCE, tag, comm->global_comm, &recv_status); - proc = recv_status.MPI_SOURCE; + proc = recv_status.MPI_SOURCE; src[ctr] = proc; - MPI_Recv(&(recv_buffer[ctr*send_bytes]), send_bytes, MPI_BYTE, proc, tag, - comm->global_comm, &recv_status); + MPI_Recv(&(recv_buffer[ctr * send_bytes]), + send_bytes, + MPI_BYTE, + proc, + tag, + comm->global_comm, + &recv_status); ctr++; } if (send_nnz) + { MPI_Waitall(send_nnz, comm->requests, MPI_STATUSES_IGNORE); + } - *src_ptr = src; + *src_ptr = src; *recvvals_ptr = recvvals; return MPI_SUCCESS; } - -int alltoall_crs_nonblocking(const int send_nnz, const int* dest, const int sendcount, - MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int** src_ptr, int recvcount, MPI_Datatype recvtype, - void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) +int alltoall_crs_nonblocking(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -166,7 +217,9 @@ int alltoall_crs_nonblocking(const int send_nnz, const int* dest, const int send char* send_buffer; if (send_nnz) + { send_buffer = (char*)(sendvals); + } int send_bytes, recv_bytes; MPI_Type_size(sendtype, &send_bytes); MPI_Type_size(recvtype, &recv_bytes); @@ -183,35 +236,49 @@ int alltoall_crs_nonblocking(const int send_nnz, const int* dest, const int send std::vector recv_buffer; if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } for (int i = 0; i < send_nnz; i++) { proc = dest[i]; - MPI_Issend(&(send_buffer[i*send_bytes]), send_bytes, MPI_BYTE, proc, tag, - comm->global_comm, &(comm->requests[i])); + MPI_Issend(&(send_buffer[i * send_bytes]), + send_bytes, + MPI_BYTE, + proc, + tag, + comm->global_comm, + &(comm->requests[i])); } ibar = 0; - ctr = 0; + ctr = 0; while (1) { MPI_Iprobe(MPI_ANY_SOURCE, tag, comm->global_comm, &flag, &recv_status); if (flag) { - //char* recv_buffer = (char*)recvvals; + // char* recv_buffer = (char*)recvvals; proc = recv_status.MPI_SOURCE; src.push_back(proc); - recv_buffer.resize(src.size()*recv_bytes); - MPI_Recv(&(recv_buffer[ctr*recv_bytes]), recv_bytes, MPI_BYTE, proc, tag, - comm->global_comm, &recv_status); + recv_buffer.resize(src.size() * recv_bytes); + MPI_Recv(&(recv_buffer[ctr * recv_bytes]), + recv_bytes, + MPI_BYTE, + proc, + tag, + comm->global_comm, + &recv_status); ctr++; } if (ibar) { - MPI_Test(&bar_req, &flag, &recv_status); - if (flag) - break; + MPI_Test(&bar_req, &flag, &recv_status); + if (flag) + { + break; + } } else { @@ -225,20 +292,26 @@ int alltoall_crs_nonblocking(const int send_nnz, const int* dest, const int send } *recv_nnz = src.size(); - MPIX_Alloc((void**)src_ptr, src.size()*sizeof(int)); + MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); MPIX_Alloc(recvvals_ptr, recv_buffer.size()); //(*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); //(*recvvals_ptr) = MPIalloc(recv_buffer.size()); - memcpy((*src_ptr), src.data(), src.size()*sizeof(int)); + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvvals_ptr), recv_buffer.data(), recv_buffer.size()); - return MPI_SUCCESS; } -void local_redistribute(int node_recv_size, std::vector& recv_buf, std::vector& origins, - int* recv_nnz, int** src_ptr, int recvcount, MPI_Datatype recvtype, - void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) +void local_redistribute(int node_recv_size, + std::vector& recv_buf, + std::vector& origins, + int* recv_nnz, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -261,67 +334,97 @@ void local_redistribute(int node_recv_size, std::vector& recv_buf, std::ve ctr = 0; while (ctr < node_recv_size) { - MPI_Unpack(recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); + MPI_Unpack( + recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); proc -= (comm->rank_node * PPN); ctr += recv_bytes; msg_counts[proc] += recv_bytes + int_bytes; } - std::vector displs(PPN+1); + std::vector displs(PPN + 1); displs[0] = 0; for (int i = 0; i < PPN; i++) - displs[i+1] = displs[i] + msg_counts[i]; - + { + displs[i + 1] = displs[i] + msg_counts[i]; + } + std::vector local_send_buffer; if (recv_buf.size()) + { local_send_buffer.resize(recv_buf.size()); + } ctr = 0; idx = 0; while (ctr < node_recv_size) { - MPI_Unpack(recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); + MPI_Unpack( + recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); proc -= (comm->rank_node * PPN); - MPI_Pack(&(origins[idx++]), 1, MPI_INT, local_send_buffer.data(), - local_send_buffer.size(), &(displs[proc]), comm->local_comm); - MPI_Pack(&(recv_buf[ctr]), recv_bytes, MPI_PACKED, local_send_buffer.data(), - local_send_buffer.size(), &(displs[proc]), comm->local_comm); + MPI_Pack(&(origins[idx++]), + 1, + MPI_INT, + local_send_buffer.data(), + local_send_buffer.size(), + &(displs[proc]), + comm->local_comm); + MPI_Pack(&(recv_buf[ctr]), + recv_bytes, + MPI_PACKED, + local_send_buffer.data(), + local_send_buffer.size(), + &(displs[proc]), + comm->local_comm); ctr += recv_bytes; } displs[0] = 0; for (int i = 0; i < PPN; i++) - displs[i+1] = displs[i] + msg_counts[i]; + { + displs[i + 1] = displs[i] + msg_counts[i]; + } int tag; MPIX_Comm_tag(comm, &tag); - MPI_Allreduce(MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); + MPI_Allreduce( + MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); int recv_count = msg_counts[local_rank]; if (PPN > comm->n_requests) + { MPIX_Comm_req_resize(comm, PPN); + } // Send a message to every process that I will need data from // Tell them which global indices I need from them int n_sends = 0; for (int i = 0; i < PPN; i++) { - if (displs[i+1] == displs[i]) + if (displs[i + 1] == displs[i]) + { continue; + } - MPI_Isend(&(local_send_buffer[displs[i]]), displs[i+1] - displs[i], MPI_PACKED, i, tag, - comm->local_comm, &(comm->requests[n_sends++])); + MPI_Isend(&(local_send_buffer[displs[i]]), + displs[i + 1] - displs[i], + MPI_PACKED, + i, + tag, + comm->local_comm, + &(comm->requests[n_sends++])); } std::vector local_recv_buffer; ctr = recv_count * (recv_bytes + int_bytes); if (ctr) + { local_recv_buffer.resize(ctr); + } // Wait to receive values // until I have received fewer than the number of global indices I am waiting on ctr = 0; - while(ctr < recv_count) + while (ctr < recv_count) { // Wait for a message MPI_Probe(MPI_ANY_SOURCE, tag, comm->local_comm, &recv_status); @@ -331,50 +434,77 @@ void local_redistribute(int node_recv_size, std::vector& recv_buf, std::ve MPI_Get_count(&recv_status, MPI_PACKED, &count); // Receive the message, and add local indices to send_comm - MPI_Recv(&(local_recv_buffer[ctr]), count, MPI_PACKED, proc, tag, - comm->local_comm, MPI_STATUS_IGNORE); + MPI_Recv(&(local_recv_buffer[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->local_comm, + MPI_STATUS_IGNORE); ctr += count; } - if (n_sends) MPI_Waitall(n_sends, comm->requests, MPI_STATUSES_IGNORE); + if (n_sends) + { + MPI_Waitall(n_sends, comm->requests, MPI_STATUSES_IGNORE); + } // Last Step : Step through recvbuf to find proc of origin, size, and indices std::vector src; std::vector recv_buffer; - idx = 0; + idx = 0; new_idx = 0; while (idx < ctr) { - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &idx, - &proc, 1, MPI_INT, comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &idx, + &proc, + 1, + MPI_INT, + comm->local_comm); src.push_back(proc); recv_buffer.resize(src.size() * recv_bytes); - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &idx, - &(recv_buffer[new_idx]), recv_bytes, MPI_BYTE, comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &idx, + &(recv_buffer[new_idx]), + recv_bytes, + MPI_BYTE, + comm->local_comm); new_idx += recv_bytes; } *recv_nnz = src.size(); - MPIX_Alloc((void**)src_ptr, src.size()*sizeof(int)); + MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); MPIX_Alloc(recvvals_ptr, recv_buffer.size()); -// (*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); -// (*recvvals_ptr) = MPIalloc(recv_buffer.size()); - memcpy((*src_ptr), src.data(), src.size()*sizeof(int)); + // (*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); + // (*recvvals_ptr) = MPIalloc(recv_buffer.size()); + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvvals_ptr), recv_buffer.data(), recv_buffer.size()); } /* Assumes SMP Ordering of ranks across nodes (aggregates ranks 0-PPN) */ -int alltoall_crs_personalized_loc(const int send_nnz, const int* dest, const int sendcount, - MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int** src_ptr, int recvcount, MPI_Datatype recvtype, - void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) +int alltoall_crs_personalized_loc(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); - + } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); @@ -388,7 +518,9 @@ int alltoall_crs_personalized_loc(const int send_nnz, const int* dest, const int recv_bytes *= recvcount; if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } MPI_Status recv_status; int proc, ctr, start, end; @@ -405,16 +537,19 @@ int alltoall_crs_personalized_loc(const int send_nnz, const int* dest, const int count = send_nnz * (send_bytes + int_bytes); if (count) + { node_send_buffer.resize(count); + } // Send a message to every process that I will need data from // Tell them which global indices I need from them - int group_procs, group_rank;; + int group_procs, group_rank; + ; MPI_Comm_size(comm->group_comm, &group_procs); MPI_Comm_rank(comm->group_comm, &group_rank); std::vector msg_counts(group_procs, 0); - std::vector msg_displs(group_procs+1); + std::vector msg_displs(group_procs + 1); int node = -1; for (int i = 0; i < send_nnz; i++) { @@ -423,7 +558,9 @@ int alltoall_crs_personalized_loc(const int send_nnz, const int* dest, const int } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } for (int i = 0; i < send_nnz; i++) { @@ -432,35 +569,58 @@ int alltoall_crs_personalized_loc(const int send_nnz, const int* dest, const int { node = proc / PPN; } - MPI_Pack(&proc, 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), - &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(send_buffer[i*send_bytes]), sendcount, sendtype, node_send_buffer.data(), - node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); + MPI_Pack(&proc, + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(send_buffer[i * send_bytes]), + sendcount, + sendtype, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } - MPI_Allreduce(MPI_IN_PLACE, msg_counts.data(), group_procs, MPI_INT, MPI_SUM, comm->group_comm); + MPI_Allreduce( + MPI_IN_PLACE, msg_counts.data(), group_procs, MPI_INT, MPI_SUM, comm->group_comm); int node_recv_size = msg_counts[group_rank]; if (send_nnz > 0) + { node = dest[0] / PPN; + } n_sends = 0; for (int i = 0; i < group_procs; i++) { start = msg_displs[i]; - end = msg_displs[i+1]; + end = msg_displs[i + 1]; if (end - start) { - MPI_Isend(&(node_send_buffer[start]), end-start, MPI_PACKED, i, tag, comm->group_comm, &(comm->requests[n_sends++])); + MPI_Isend(&(node_send_buffer[start]), + end - start, + MPI_PACKED, + i, + tag, + comm->group_comm, + &(comm->requests[n_sends++])); } } std::vector recv_buf; if (node_recv_size) + { recv_buf.resize(node_recv_size); + } std::vector origins; ctr = 0; @@ -470,41 +630,65 @@ int alltoall_crs_personalized_loc(const int send_nnz, const int* dest, const int { // Wait for a message MPI_Probe(MPI_ANY_SOURCE, tag, comm->group_comm, &recv_status); - + // Get the source process and message size proc = recv_status.MPI_SOURCE; MPI_Get_count(&recv_status, MPI_PACKED, &count); - //recv_buf.resize(ctr + count); + // recv_buf.resize(ctr + count); // Receive the message, and add local indices to send_comm - MPI_Recv(&(recv_buf[ctr]), count, MPI_PACKED, proc, tag, comm->group_comm, - &recv_status); + MPI_Recv(&(recv_buf[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->group_comm, + &recv_status); ctr += count; n_msgs = count / (recv_bytes + int_bytes); for (int i = 0; i < n_msgs; i++) - origins.push_back(proc*PPN + local_rank); + { + origins.push_back(proc * PPN + local_rank); + } } - local_redistribute(node_recv_size, recv_buf, origins, recv_nnz, src_ptr, - recvcount, recvtype, recvvals_ptr, xinfo, comm); + local_redistribute(node_recv_size, + recv_buf, + origins, + recv_nnz, + src_ptr, + recvcount, + recvtype, + recvvals_ptr, + xinfo, + comm); return MPI_SUCCESS; } - /* Assumes SMP Ordering of ranks across nodes (aggregates ranks 0-PPN) */ -int alltoall_crs_nonblocking_loc(const int send_nnz, const int* dest, const int sendcount, - MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int** src_ptr, int recvcount, MPI_Datatype recvtype, - void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) -{ +int alltoall_crs_nonblocking_loc(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) +{ int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); @@ -518,7 +702,9 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, const int* dest, const int recv_bytes *= recvcount; if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } MPI_Status recv_status; MPI_Request bar_req; @@ -533,16 +719,19 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, const int* dest, const int count = send_nnz * (send_bytes + int_bytes); if (count) + { node_send_buffer.resize(count); + } - int group_procs, group_rank;; + int group_procs, group_rank; + ; MPI_Comm_size(comm->group_comm, &group_procs); MPI_Comm_rank(comm->group_comm, &group_rank); // Send a message to every process that I will need data from // Tell them which global indices I need from them std::vector msg_counts(group_procs, 0); - std::vector msg_displs(group_procs+1); + std::vector msg_displs(group_procs + 1); int node = -1; for (int i = 0; i < send_nnz; i++) { @@ -551,7 +740,9 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, const int* dest, const int } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } for (int i = 0; i < send_nnz; i++) { @@ -560,34 +751,53 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, const int* dest, const int { node = proc / PPN; } - MPI_Pack(&proc, 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), - &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(send_buffer[i*send_bytes]), sendcount, sendtype, node_send_buffer.data(), - node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); + MPI_Pack(&proc, + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(send_buffer[i * send_bytes]), + sendcount, + sendtype, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } if (send_nnz > 0) + { node = dest[0] / PPN; + } n_sends = 0; for (int i = 0; i < group_procs; i++) { start = msg_displs[i]; - end = msg_displs[i+1]; + end = msg_displs[i + 1]; if (end - start) { - MPI_Issend(&(node_send_buffer[start]), end-start, MPI_PACKED, i, tag, comm->group_comm, &(comm->requests[n_sends++])); + MPI_Issend(&(node_send_buffer[start]), + end - start, + MPI_PACKED, + i, + tag, + comm->group_comm, + &(comm->requests[n_sends++])); } } - std::vector recv_buf; std::vector origins; ibar = 0; - ctr = 0; + ctr = 0; // Wait to receive values // until I have received fewer than the number of global indices I am waiting on while (1) @@ -602,24 +812,31 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, const int* dest, const int recv_buf.resize(ctr + count); // Receive the message, and add local indices to send_comm - MPI_Recv(&(recv_buf[ctr]), count, MPI_PACKED, proc, tag, comm->group_comm, - &recv_status); + MPI_Recv(&(recv_buf[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->group_comm, + &recv_status); ctr += count; - n_msgs = count / (recv_bytes + int_bytes); for (int i = 0; i < n_msgs; i++) - origins.push_back(proc*PPN+local_rank); - + { + origins.push_back(proc * PPN + local_rank); + } } - // If I have already called my Ibarrier, check if all processes have reached // If all processes have reached the Ibarrier, all messages have been sent if (ibar) { MPI_Test(&bar_req, &flag, &recv_status); - if (flag) break; + if (flag) + { + break; + } } else { @@ -635,12 +852,16 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, const int* dest, const int } int node_recv_size = recv_buf.size(); - local_redistribute(node_recv_size, recv_buf, origins, recv_nnz, src_ptr, - recvcount, recvtype, recvvals_ptr, xinfo, comm); + local_redistribute(node_recv_size, + recv_buf, + origins, + recv_nnz, + src_ptr, + recvcount, + recvtype, + recvvals_ptr, + xinfo, + comm); return MPI_SUCCESS; } - - - - diff --git a/src/neighborhood/alltoallv_crs.cpp b/src/neighborhood/alltoallv_crs.cpp index c4b02ebc1..4ed1a24a5 100644 --- a/src/neighborhood/alltoallv_crs.cpp +++ b/src/neighborhood/alltoallv_crs.cpp @@ -1,12 +1,24 @@ -#include "sparse_coll.h" #include #include +#include "sparse_coll.h" -int alltoallv_crs_personalized(const int send_nnz, const int send_size, const int* dest, - const int* sendcounts, const int* sdispls, MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int* recv_size, int** src_ptr, int** recvcounts_ptr, - int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) +int alltoallv_crs_personalized(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -26,25 +38,33 @@ int alltoallv_crs_personalized(const int send_nnz, const int send_size, const in // TODO - Could use reduce scatter (useful if large) std::vector msg_counts(num_procs, 0); for (int i = 0; i < send_nnz; i++) - msg_counts[dest[i]] = sendcounts[i]*send_bytes; - MPI_Allreduce(MPI_IN_PLACE, msg_counts.data(), num_procs, MPI_INT, - MPI_SUM, comm->global_comm); + { + msg_counts[dest[i]] = sendcounts[i] * send_bytes; + } + MPI_Allreduce( + MPI_IN_PLACE, msg_counts.data(), num_procs, MPI_INT, MPI_SUM, comm->global_comm); *recv_size = msg_counts[rank] / recv_bytes; // Allocate recvvals to size determined in allreduce char* recvvals; - MPIX_Alloc((void**)&recvvals, *recv_size*recv_bytes); + MPIX_Alloc((void**)&recvvals, *recv_size * recv_bytes); if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); - + } // Send each message for (int i = 0; i < send_nnz; i++) { proc = dest[i]; - MPI_Isend(&(send_buffer[sdispls[i]*send_bytes]), sendcounts[i]*send_bytes, MPI_BYTE, - proc, tag, comm->global_comm, &(comm->requests[i])); + MPI_Isend(&(send_buffer[sdispls[i] * send_bytes]), + sendcounts[i] * send_bytes, + MPI_BYTE, + proc, + tag, + comm->global_comm, + &(comm->requests[i])); } ctr = 0; @@ -65,34 +85,51 @@ int alltoallv_crs_personalized(const int send_nnz, const int send_size, const in count /= recv_bytes; recvcounts.push_back(count); - MPI_Recv(&(recvvals[ctr*recv_bytes]), count*recv_bytes, MPI_BYTE, proc, tag, - comm->global_comm, &recv_status); + MPI_Recv(&(recvvals[ctr * recv_bytes]), + count * recv_bytes, + MPI_BYTE, + proc, + tag, + comm->global_comm, + &recv_status); ctr += count; rdispls.push_back(ctr); } - *recv_nnz = src.size(); + *recv_nnz = src.size(); *recvvals_ptr = recvvals; - MPIX_Alloc((void**)src_ptr, src.size()*sizeof(int)); - MPIX_Alloc((void**)recvcounts_ptr, recvcounts.size()*sizeof(int)); - MPIX_Alloc((void**)rdispls_ptr, rdispls.size()*sizeof(int)); - memcpy((*src_ptr), src.data(), src.size()*sizeof(int)); - memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size()*sizeof(int)); - memcpy((*rdispls_ptr), rdispls.data(), rdispls.size()*sizeof(int)); + MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); + MPIX_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); + MPIX_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); + memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size() * sizeof(int)); + memcpy((*rdispls_ptr), rdispls.data(), rdispls.size() * sizeof(int)); if (send_nnz) + { MPI_Waitall(send_nnz, comm->requests, MPI_STATUSES_IGNORE); + } return MPI_SUCCESS; } - - -int alltoallv_crs_nonblocking(const int send_nnz, const int send_size, const int* dest, - const int* sendcounts, const int* sdispls, MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int* recv_size, int** src_ptr, int** recvcounts_ptr, - int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) +int alltoallv_crs_nonblocking(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -110,17 +147,24 @@ int alltoallv_crs_nonblocking(const int send_nnz, const int send_size, const int MPIX_Comm_tag(comm, &tag); if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } for (int i = 0; i < send_nnz; i++) { proc = dest[i]; - MPI_Issend(&(send_buffer[sdispls[i]*send_bytes]), sendcounts[i]*send_bytes, MPI_BYTE, - proc, tag, comm->global_comm, &(comm->requests[i])); + MPI_Issend(&(send_buffer[sdispls[i] * send_bytes]), + sendcounts[i] * send_bytes, + MPI_BYTE, + proc, + tag, + comm->global_comm, + &(comm->requests[i])); } ibar = 0; - ctr = 0; + ctr = 0; std::vector src; std::vector recvcounts; std::vector rdispls; @@ -142,17 +186,24 @@ int alltoallv_crs_nonblocking(const int send_nnz, const int send_size, const int count /= recv_bytes; recvcounts.push_back(count); - MPI_Recv(&(recvvals[ctr*recv_bytes]), count*recv_bytes, MPI_BYTE, proc, tag, - comm->global_comm, &recv_status); + MPI_Recv(&(recvvals[ctr * recv_bytes]), + count * recv_bytes, + MPI_BYTE, + proc, + tag, + comm->global_comm, + &recv_status); ctr += count; rdispls.push_back(ctr); } if (ibar) { - MPI_Test(&bar_req, &flag, &recv_status); - if (flag) - break; + MPI_Test(&bar_req, &flag, &recv_status); + if (flag) + { + break; + } } else { @@ -164,32 +215,39 @@ int alltoallv_crs_nonblocking(const int send_nnz, const int send_size, const int } } } - *recv_nnz = src.size(); + *recv_nnz = src.size(); *recv_size = ctr; - - MPIX_Alloc((void**)src_ptr, src.size()*sizeof(int)); - MPIX_Alloc((void**)recvcounts_ptr, recvcounts.size()*sizeof(int)); - MPIX_Alloc((void**)rdispls_ptr, rdispls.size()*sizeof(int)); - MPIX_Alloc((void**)recvvals_ptr, recvvals.size()); + MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); + MPIX_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); + MPIX_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); + MPIX_Alloc((void**)recvvals_ptr, recvvals.size()); //(*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); //(*recvcounts_ptr) = (int*)MPIalloc(recvcounts.size()*sizeof(int)); //(*rdispls_ptr) = (int*)MPIalloc(rdispls.size()*sizeof(int)); //(*recvvals_ptr) = MPIalloc(recvvals.size()); - memcpy((*src_ptr), src.data(), src.size()*sizeof(int)); - memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size()*sizeof(int)); - memcpy((*rdispls_ptr), rdispls.data(), rdispls.size()*sizeof(int)); + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); + memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size() * sizeof(int)); + memcpy((*rdispls_ptr), rdispls.data(), rdispls.size() * sizeof(int)); memcpy((*recvvals_ptr), recvvals.data(), recvvals.size()); - + return MPI_SUCCESS; } - -void local_redistribute(int n_recvs, std::vector& origins, std::vector& origin_displs, - std::vector& recv_buf, int* recv_nnz, int* recv_size, int** src_ptr, - int** recvcounts_ptr, int** rdispls_ptr, MPI_Datatype recvtype, - void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) +void local_redistribute(int n_recvs, + std::vector& origins, + std::vector& origin_displs, + std::vector& recv_buf, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -197,66 +255,121 @@ void local_redistribute(int n_recvs, std::vector& origins, std::vector MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); - MPI_Status recv_status; + MPI_Status recv_status; int count, ctr, tag, proc, n_sends; int recv_bytes, int_bytes; MPI_Type_size(recvtype, &recv_bytes); MPI_Type_size(MPI_INT, &int_bytes); std::vector recv_sizes(PPN, 0); - std::vector recv_displs(PPN+1); + std::vector recv_displs(PPN + 1); std::vector local_buf; if (origin_displs[n_recvs]) + { local_buf.resize(origin_displs[n_recvs]); + } int idx = 0; std::vector recv_byte_buf; if (recv_buf.size()) { recv_byte_buf.resize(recv_buf.size()); - MPI_Pack(recv_buf.data(), recv_buf.size(), MPI_BYTE, recv_byte_buf.data(), recv_byte_buf.size(), &idx, comm->local_comm); + MPI_Pack(recv_buf.data(), + recv_buf.size(), + MPI_BYTE, + recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + comm->local_comm); } idx = 0; while (idx < origin_displs[n_recvs]) { - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &proc, 1, MPI_INT, comm->local_comm); - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &count, 1, MPI_INT, comm->local_comm); - proc -= (comm->rank_node*PPN); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &proc, + 1, + MPI_INT, + comm->local_comm); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &count, + 1, + MPI_INT, + comm->local_comm); + proc -= (comm->rank_node * PPN); idx += count; - recv_sizes[proc] += count + 2*int_bytes; - } + recv_sizes[proc] += count + 2 * int_bytes; + } recv_displs[0] = 0; for (int i = 0; i < PPN; i++) - recv_displs[i+1] = recv_displs[i] + recv_sizes[i]; + { + recv_displs[i + 1] = recv_displs[i] + recv_sizes[i]; + } idx = 0; for (int i = 0; i < n_recvs; i++) { int origin = origins[i]; - while (idx < origin_displs[i+1]) + while (idx < origin_displs[i + 1]) { - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &proc, 1, MPI_INT, comm->local_comm); - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &count, 1, MPI_INT, comm->local_comm); - proc -= (comm->rank_node*PPN); - - MPI_Pack(&origin, 1, MPI_INT, local_buf.data(), local_buf.size(), &(recv_displs[proc]), comm->local_comm); - MPI_Pack(&count, 1, MPI_INT, local_buf.data(), local_buf.size(), &(recv_displs[proc]), comm->local_comm); - - MPI_Pack(&(recv_byte_buf[idx]), count, MPI_PACKED, local_buf.data(), local_buf.size(), &(recv_displs[proc]), comm->local_comm); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &proc, + 1, + MPI_INT, + comm->local_comm); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &count, + 1, + MPI_INT, + comm->local_comm); + proc -= (comm->rank_node * PPN); + + MPI_Pack(&origin, + 1, + MPI_INT, + local_buf.data(), + local_buf.size(), + &(recv_displs[proc]), + comm->local_comm); + MPI_Pack(&count, + 1, + MPI_INT, + local_buf.data(), + local_buf.size(), + &(recv_displs[proc]), + comm->local_comm); + + MPI_Pack(&(recv_byte_buf[idx]), + count, + MPI_PACKED, + local_buf.data(), + local_buf.size(), + &(recv_displs[proc]), + comm->local_comm); idx += count; } } recv_displs[0] = 0; for (int i = 0; i < PPN; i++) - recv_displs[i+1] = recv_displs[i] + recv_sizes[i]; + { + recv_displs[i + 1] = recv_displs[i] + recv_sizes[i]; + } // STEP 2 : Local Communication - MPI_Allreduce(MPI_IN_PLACE, recv_sizes.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); + MPI_Allreduce( + MPI_IN_PLACE, recv_sizes.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); int local_size_msgs = recv_sizes[local_rank]; // Send a message to every process that I will need data from @@ -268,11 +381,16 @@ void local_redistribute(int n_recvs, std::vector& origins, std::vector n_sends = 0; for (int i = 0; i < PPN; i++) { - int s = recv_displs[i+1] - recv_displs[i]; + int s = recv_displs[i + 1] - recv_displs[i]; if (s) { - MPI_Isend(&(local_buf[recv_displs[i]]), s, MPI_PACKED, i, tag, - comm->local_comm, &(local_req[n_sends++])); + MPI_Isend(&(local_buf[recv_displs[i]]), + s, + MPI_PACKED, + i, + tag, + comm->local_comm, + &(local_req[n_sends++])); } } @@ -291,11 +409,19 @@ void local_redistribute(int n_recvs, std::vector& origins, std::vector MPI_Get_count(&recv_status, MPI_PACKED, &count); // Receive the message, and add local indices to send_comm - MPI_Recv(&(local_recv_buffer[ctr]), count, MPI_PACKED, proc, tag, comm->local_comm, &recv_status); + MPI_Recv(&(local_recv_buffer[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->local_comm, + &recv_status); ctr += count; } - if (n_sends) MPI_Waitall(n_sends, local_req.data(), MPI_STATUSES_IGNORE); - + if (n_sends) + { + MPI_Waitall(n_sends, local_req.data(), MPI_STATUSES_IGNORE); + } // Last Step : Step through recvbuf to find proc of origin, size, and indices int byte_ctr = 0; @@ -307,59 +433,89 @@ void local_redistribute(int n_recvs, std::vector& origins, std::vector ctr = 0; while (byte_ctr < local_size_msgs) { - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &byte_ctr, &proc, 1, - MPI_INT, comm->local_comm); - src.push_back(proc); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &byte_ctr, + &proc, + 1, + MPI_INT, + comm->local_comm); + src.push_back(proc); - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &byte_ctr, &count, 1, - MPI_INT, comm->local_comm); - - recvvals.resize(recvvals.size() + count); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &byte_ctr, + &count, + 1, + MPI_INT, + comm->local_comm); + + recvvals.resize(recvvals.size() + count); count = count / recv_bytes; - recvcounts.push_back(count); + recvcounts.push_back(count); - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &byte_ctr, - &(recvvals[ctr*recv_bytes]), count, recvtype, comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &byte_ctr, + &(recvvals[ctr * recv_bytes]), + count, + recvtype, + comm->local_comm); - ctr += count; - rdispls.push_back(ctr); + ctr += count; + rdispls.push_back(ctr); } // Set send sizes - *recv_nnz = src.size(); + *recv_nnz = src.size(); *recv_size = ctr; - MPIX_Alloc((void**)src_ptr, src.size()*sizeof(int)); - MPIX_Alloc((void**)recvcounts_ptr, recvcounts.size()*sizeof(int)); - MPIX_Alloc((void**)rdispls_ptr, rdispls.size()*sizeof(int)); - MPIX_Alloc(recvvals_ptr, recvvals.size()); + MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); + MPIX_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); + MPIX_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); + MPIX_Alloc(recvvals_ptr, recvvals.size()); //(*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); //(*recvcounts_ptr) = (int*)MPIalloc(recvcounts.size()*sizeof(int)); //(*rdispls_ptr) = (int*)MPIalloc(rdispls.size()*sizeof(int)); //(*recvvals_ptr) = MPIalloc(recvvals.size()); - memcpy((*src_ptr), src.data(), src.size()*sizeof(int)); - memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size()*sizeof(int)); - memcpy((*rdispls_ptr), rdispls.data(), rdispls.size()*sizeof(int)); + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); + memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size() * sizeof(int)); + memcpy((*rdispls_ptr), rdispls.data(), rdispls.size() * sizeof(int)); memcpy((*recvvals_ptr), recvvals.data(), recvvals.size()); } - -int alltoallv_crs_personalized_loc(const int send_nnz, const int send_size, const int* dest, - const int* sendcounts, const int* sdispls, MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int* recv_size, int** src_ptr, int** recvcounts_ptr, - int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) +int alltoallv_crs_personalized_loc(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } int tag; MPIX_Comm_tag(comm, &tag); @@ -370,59 +526,91 @@ int alltoallv_crs_personalized_loc(const int send_nnz, const int send_size, cons MPI_Type_size(recvtype, &recv_bytes); MPI_Type_size(MPI_INT, &int_bytes); - std::vector node_send_buffer(send_size*send_bytes + 2*send_nnz*int_bytes); + std::vector node_send_buffer(send_size * send_bytes + 2 * send_nnz * int_bytes); std::vector sizes(PPN, 0); int proc, count, ctr, start, end; MPI_Status recv_status; - int group_procs, group_rank;; + int group_procs, group_rank; + ; MPI_Comm_size(comm->group_comm, &group_procs); MPI_Comm_rank(comm->group_comm, &group_rank); // Send a message to every process that I will need data from // Tell them which global indices I need from them std::vector msg_counts(group_procs, 0); - std::vector msg_displs(group_procs+1); + std::vector msg_displs(group_procs + 1); int node = -1; for (int i = 0; i < send_nnz; i++) { proc = dest[i] / PPN; - msg_counts[proc] += 2*int_bytes + sendcounts[i]*send_bytes; + msg_counts[proc] += 2 * int_bytes + sendcounts[i] * send_bytes; } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } for (int i = 0; i < send_nnz; i++) { - proc = dest[i]; - int s = sendcounts[i]*send_bytes; + proc = dest[i]; + int s = sendcounts[i] * send_bytes; if (proc / PPN != node) { node = proc / PPN; } - MPI_Pack(&proc, 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(s), 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(send_buffer[sdispls[i]*send_bytes]), sendcounts[i], sendtype, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); + MPI_Pack(&proc, + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(s), + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(send_buffer[sdispls[i] * send_bytes]), + sendcounts[i], + sendtype, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } - MPI_Allreduce(MPI_IN_PLACE, msg_counts.data(), group_procs, MPI_INT, MPI_SUM, comm->group_comm); + MPI_Allreduce( + MPI_IN_PLACE, msg_counts.data(), group_procs, MPI_INT, MPI_SUM, comm->group_comm); int node_recv_size = msg_counts[group_rank]; if (send_nnz > 0) + { node = dest[0] / PPN; + } int n_sends = 0; for (int i = 0; i < group_procs; i++) { start = msg_displs[i]; - end = msg_displs[i+1]; + end = msg_displs[i + 1]; if (end - start) { - MPI_Isend(&(node_send_buffer[start]), end-start, MPI_PACKED, i, tag, comm->group_comm, &(comm->requests[n_sends++])); + MPI_Isend(&(node_send_buffer[start]), + end - start, + MPI_PACKED, + i, + tag, + comm->group_comm, + &(comm->requests[n_sends++])); } } @@ -446,13 +634,19 @@ int alltoallv_crs_personalized_loc(const int send_nnz, const int send_size, cons proc = recv_status.MPI_SOURCE; MPI_Get_count(&recv_status, MPI_PACKED, &count); - recv_buf.resize((count+ctr)); + recv_buf.resize((count + ctr)); // Receive the message, and add local indices to send_comm - MPI_Recv(&(recv_buf[ctr]), count, MPI_PACKED, proc, tag, comm->group_comm, MPI_STATUS_IGNORE); - + MPI_Recv(&(recv_buf[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->group_comm, + MPI_STATUS_IGNORE); + ctr += count; - origins.push_back(proc*PPN+local_rank); + origins.push_back(proc * PPN + local_rank); origin_displs.push_back(ctr); n_recvs++; @@ -460,31 +654,55 @@ int alltoallv_crs_personalized_loc(const int send_nnz, const int send_size, cons MPI_Waitall(n_sends, comm->requests, MPI_STATUSES_IGNORE); + local_redistribute(n_recvs, + origins, + origin_displs, + recv_buf, + recv_nnz, + recv_size, + src_ptr, + recvcounts_ptr, + rdispls_ptr, + recvtype, + recvvals_ptr, + xinfo, + comm); - local_redistribute(n_recvs, origins, origin_displs, recv_buf, recv_nnz, recv_size, - src_ptr, recvcounts_ptr, rdispls_ptr, recvtype, recvvals_ptr, - xinfo, comm); - - - return MPI_SUCCESS; + return MPI_SUCCESS; } -int alltoallv_crs_nonblocking_loc(const int send_nnz, const int send_size, const int* dest, - const int* sendcounts, const int* sdispls, MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int* recv_size, int** src_ptr, int** recvcounts_ptr, - int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, MPIX_Info* xinfo, MPIX_Comm* comm) +int alltoallv_crs_nonblocking_loc(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } int tag; MPIX_Comm_tag(comm, &tag); @@ -495,62 +713,92 @@ int alltoallv_crs_nonblocking_loc(const int send_nnz, const int send_size, const MPI_Type_size(recvtype, &recv_bytes); MPI_Type_size(MPI_INT, &int_bytes); - std::vector node_send_buffer(send_size*send_bytes + 2*send_nnz*int_bytes); + std::vector node_send_buffer(send_size * send_bytes + 2 * send_nnz * int_bytes); std::vector sizes(PPN, 0); int proc, count, ctr, flag, start, end; int ibar = 0; MPI_Status recv_status; MPI_Request bar_req; - int group_procs, group_rank;; + int group_procs, group_rank; + ; MPI_Comm_size(comm->group_comm, &group_procs); MPI_Comm_rank(comm->group_comm, &group_rank); // Send a message to every process that I will need data from // Tell them which global indices I need from them std::vector msg_counts(group_procs, 0); - std::vector msg_displs(group_procs+1); + std::vector msg_displs(group_procs + 1); int node = -1; for (int i = 0; i < send_nnz; i++) { proc = dest[i] / PPN; - msg_counts[proc] += 2*int_bytes + sendcounts[i]*send_bytes; + msg_counts[proc] += 2 * int_bytes + sendcounts[i] * send_bytes; } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } for (int i = 0; i < send_nnz; i++) { - proc = dest[i]; - int s = sendcounts[i]*send_bytes; + proc = dest[i]; + int s = sendcounts[i] * send_bytes; if (proc / PPN != node) { node = proc / PPN; } - MPI_Pack(&proc, 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(s), 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(send_buffer[sdispls[i]*send_bytes]), sendcounts[i], sendtype, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); + MPI_Pack(&proc, + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(s), + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(send_buffer[sdispls[i] * send_bytes]), + sendcounts[i], + sendtype, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } if (send_nnz > 0) + { node = dest[0] / PPN; + } int n_sends = 0; for (int i = 0; i < group_procs; i++) { start = msg_displs[i]; - end = msg_displs[i+1]; + end = msg_displs[i + 1]; if (end - start) { - MPI_Issend(&(node_send_buffer[start]), end-start, MPI_PACKED, i, tag, comm->group_comm, &(comm->requests[n_sends++])); + MPI_Issend(&(node_send_buffer[start]), + end - start, + MPI_PACKED, + i, + tag, + comm->group_comm, + &(comm->requests[n_sends++])); } } - // Wait to receive values // until I have received fewer than the number of global indices I am waiting on // @@ -573,25 +821,33 @@ int alltoallv_crs_nonblocking_loc(const int send_nnz, const int send_size, const proc = recv_status.MPI_SOURCE; MPI_Get_count(&recv_status, MPI_PACKED, &count); - recv_buf.resize((count+ctr)); + recv_buf.resize((count + ctr)); // Receive the message, and add local indices to send_comm - MPI_Recv(&(recv_buf[ctr]), count, MPI_PACKED, proc, tag, comm->group_comm, MPI_STATUS_IGNORE); - + MPI_Recv(&(recv_buf[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->group_comm, + MPI_STATUS_IGNORE); + ctr += count; - origins.push_back(proc*PPN+local_rank); + origins.push_back(proc * PPN + local_rank); origin_displs.push_back(ctr); n_recvs++; } - - + // If I have already called my Ibarrier, check if all processes have reached // If all processes have reached the Ibarrier, all messages have been sent if (ibar) { MPI_Test(&bar_req, &flag, MPI_STATUS_IGNORE); - if (flag) break; + if (flag) + { + break; + } } else { @@ -602,16 +858,23 @@ int alltoallv_crs_nonblocking_loc(const int send_nnz, const int send_size, const { ibar = 1; MPI_Ibarrier(comm->group_comm, &bar_req); - } + } } } - local_redistribute(n_recvs, origins, origin_displs, recv_buf, recv_nnz, recv_size, - src_ptr, recvcounts_ptr, rdispls_ptr, recvtype, recvvals_ptr, - xinfo, comm); - + local_redistribute(n_recvs, + origins, + origin_displs, + recv_buf, + recv_nnz, + recv_size, + src_ptr, + recvcounts_ptr, + rdispls_ptr, + recvtype, + recvvals_ptr, + xinfo, + comm); return MPI_SUCCESS; - } - diff --git a/src/neighborhood/dist_graph.c b/src/neighborhood/dist_graph.c index 83849b2ac..228a3dd40 100644 --- a/src/neighborhood/dist_graph.c +++ b/src/neighborhood/dist_graph.c @@ -1,15 +1,15 @@ #include "dist_graph.h" -int MPIX_Dist_graph_create_adjacent(MPI_Comm comm_old, - int indegree, - const int sources[], - const int sourceweights[], - int outdegree, - const int destinations[], - const int destweights[], - MPIX_Info* info, - int reorder, - MPIX_Comm** comm_dist_graph_ptr) +int MPIX_Dist_graph_create_adjacent(MPI_Comm comm_old, + int indegree, + const int sources[], + const int sourceweights[], + int outdegree, + const int destinations[], + const int destweights[], + MPIX_Info* info, + int reorder, + MPIX_Comm** comm_dist_graph_ptr) { int rank, num_procs; MPI_Comm_rank(comm_old, &rank); @@ -19,25 +19,28 @@ int MPIX_Dist_graph_create_adjacent(MPI_Comm comm_old, MPIX_Comm_init(&comm_dist_graph, comm_old); const int* s = sources; - if (indegree == 0) s = MPI_WEIGHTS_EMPTY; + if (indegree == 0) + { + s = MPI_WEIGHTS_EMPTY; + } const int* d = destinations; - if (outdegree == 0) d = MPI_WEIGHTS_EMPTY; + if (outdegree == 0) + { + d = MPI_WEIGHTS_EMPTY; + } MPI_Dist_graph_create_adjacent(comm_dist_graph->global_comm, - indegree, - s, - MPI_UNWEIGHTED, - outdegree, - d, - MPI_UNWEIGHTED, - MPI_INFO_NULL, - reorder, - &(comm_dist_graph->neighbor_comm)); + indegree, + s, + MPI_UNWEIGHTED, + outdegree, + d, + MPI_UNWEIGHTED, + MPI_INFO_NULL, + reorder, + &(comm_dist_graph->neighbor_comm)); *comm_dist_graph_ptr = comm_dist_graph; return 0; } - - - diff --git a/src/neighborhood/dist_graph.h b/src/neighborhood/dist_graph.h index a13fa9796..ee204a9f3 100644 --- a/src/neighborhood/dist_graph.h +++ b/src/neighborhood/dist_graph.h @@ -1,30 +1,28 @@ #ifndef MPI_ADVANCE_DIST_GRAPH_H #define MPI_ADVANCE_DIST_GRAPH_H -#include "mpi.h" #include "communicator/locality_comm.h" #include "communicator/mpix_comm.h" +#include "mpi.h" // Declarations of C++ methods #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif -int MPIX_Dist_graph_create_adjacent(MPI_Comm comm_old, - int indegree, - const int sources[], - const int sourceweights[], - int outdegree, - const int destinations[], - const int destweights[], - MPIX_Info* info, - int reorder, - MPIX_Comm** comm_dist_graph_ptr); +int MPIX_Dist_graph_create_adjacent(MPI_Comm comm_old, + int indegree, + const int sources[], + const int sourceweights[], + int outdegree, + const int destinations[], + const int destweights[], + MPIX_Info* info, + int reorder, + MPIX_Comm** comm_dist_graph_ptr); #ifdef __cplusplus } #endif - #endif diff --git a/src/neighborhood/dist_topo.c b/src/neighborhood/dist_topo.c index 0ef65a731..20133dda4 100644 --- a/src/neighborhood/dist_topo.c +++ b/src/neighborhood/dist_topo.c @@ -1,33 +1,33 @@ #include "dist_topo.h" + #include -int MPIX_Topo_init( - int indegree, - const int sources[], - const int sourceweights[], - int outdegree, - const int destinations[], - const int destweights[], - MPIX_Info* info, - MPIX_Topo** mpix_topo_ptr) +int MPIX_Topo_init(int indegree, + const int sources[], + const int sourceweights[], + int outdegree, + const int destinations[], + const int destweights[], + MPIX_Info* info, + MPIX_Topo** mpix_topo_ptr) { MPIX_Topo* mpix_topo = (MPIX_Topo*)malloc(sizeof(MPIX_Topo)); // Copy indegree and outdegree into MPIX_Topo struct - mpix_topo->indegree = indegree; + mpix_topo->indegree = indegree; mpix_topo->outdegree = outdegree; // Create copy of sources/destinations in MPIX_Topo struct - mpix_topo->sources = NULL; + mpix_topo->sources = NULL; mpix_topo->destinations = NULL; if (indegree) { - mpix_topo->sources = (int *)malloc(indegree * sizeof(int)); + mpix_topo->sources = (int*)malloc(indegree * sizeof(int)); memcpy(mpix_topo->sources, sources, indegree * sizeof(int)); - if(sourceweights != MPI_UNWEIGHTED) + if (sourceweights != MPI_UNWEIGHTED) { - mpix_topo->sourceweights = (int *)malloc(indegree * sizeof(int)); + mpix_topo->sourceweights = (int*)malloc(indegree * sizeof(int)); memcpy(mpix_topo->sourceweights, sourceweights, indegree * sizeof(int)); } else @@ -38,12 +38,12 @@ int MPIX_Topo_init( if (outdegree) { - mpix_topo->destinations = (int *)malloc(outdegree * sizeof(int)); + mpix_topo->destinations = (int*)malloc(outdegree * sizeof(int)); memcpy(mpix_topo->destinations, destinations, outdegree * sizeof(int)); - if(destweights != MPI_UNWEIGHTED) + if (destweights != MPI_UNWEIGHTED) { - mpix_topo->destweights = (int *)malloc(outdegree * sizeof(int)); + mpix_topo->destweights = (int*)malloc(outdegree * sizeof(int)); memcpy(mpix_topo->destweights, destweights, outdegree * sizeof(int)); } else @@ -57,30 +57,25 @@ int MPIX_Topo_init( return MPI_SUCCESS; } -int MPIX_Topo_from_neighbor_comm( - MPIX_Comm* comm, - MPIX_Topo** mpix_topo_ptr) +int MPIX_Topo_from_neighbor_comm(MPIX_Comm* comm, MPIX_Topo** mpix_topo_ptr) { MPIX_Topo* mpix_topo = (MPIX_Topo*)malloc(sizeof(MPIX_Topo)); int weighted; MPI_Dist_graph_neighbors_count( - comm->neighbor_comm, - &(mpix_topo->indegree), - &(mpix_topo->outdegree), - &weighted); + comm->neighbor_comm, &(mpix_topo->indegree), &(mpix_topo->outdegree), &weighted); // Create copy of sources/destinations in MPIX_Topo struct - mpix_topo->sources = NULL; + mpix_topo->sources = NULL; mpix_topo->destinations = NULL; if (mpix_topo->indegree) { - mpix_topo->sources = (int *)malloc(mpix_topo->indegree * sizeof(int)); - if(weighted) + mpix_topo->sources = (int*)malloc(mpix_topo->indegree * sizeof(int)); + if (weighted) { - mpix_topo->sourceweights = (int *)malloc(mpix_topo->indegree * sizeof(int)); - } + mpix_topo->sourceweights = (int*)malloc(mpix_topo->indegree * sizeof(int)); + } else { mpix_topo->sourceweights = MPI_UNWEIGHTED; @@ -89,10 +84,10 @@ int MPIX_Topo_from_neighbor_comm( if (mpix_topo->outdegree) { - mpix_topo->destinations = (int *)malloc(mpix_topo->outdegree * sizeof(int)); + mpix_topo->destinations = (int*)malloc(mpix_topo->outdegree * sizeof(int)); if (weighted) { - mpix_topo->destweights = (int *)malloc(mpix_topo->outdegree * sizeof(int)); + mpix_topo->destweights = (int*)malloc(mpix_topo->outdegree * sizeof(int)); } else { @@ -100,39 +95,40 @@ int MPIX_Topo_from_neighbor_comm( } } - MPI_Dist_graph_neighbors( - comm->neighbor_comm, - mpix_topo->indegree, - mpix_topo->sources, - mpix_topo->sourceweights, - mpix_topo->outdegree, - mpix_topo->destinations, - mpix_topo->destweights); + MPI_Dist_graph_neighbors(comm->neighbor_comm, + mpix_topo->indegree, + mpix_topo->sources, + mpix_topo->sourceweights, + mpix_topo->outdegree, + mpix_topo->destinations, + mpix_topo->destweights); *mpix_topo_ptr = mpix_topo; return MPI_SUCCESS; - } int MPIX_Topo_free(MPIX_Topo** mpix_topo_ptr) { MPIX_Topo* mpix_topo = *mpix_topo_ptr; - if(mpix_topo->indegree) + if (mpix_topo->indegree) { free(mpix_topo->sources); - if(mpix_topo->sourceweights != MPI_UNWEIGHTED) + if (mpix_topo->sourceweights != MPI_UNWEIGHTED) + { free(mpix_topo->sourceweights); + } } - if(mpix_topo->outdegree) + if (mpix_topo->outdegree) { free(mpix_topo->destinations); - if(mpix_topo->destweights != MPI_UNWEIGHTED) + if (mpix_topo->destweights != MPI_UNWEIGHTED) + { free(mpix_topo->destweights); + } } free(mpix_topo); return MPI_SUCCESS; } - diff --git a/src/neighborhood/dist_topo.h b/src/neighborhood/dist_topo.h index e7ddf7044..144a84b8f 100644 --- a/src/neighborhood/dist_topo.h +++ b/src/neighborhood/dist_topo.h @@ -1,16 +1,14 @@ #ifndef MPI_ADVANCE_DIST_TOPO_H #define MPI_ADVANCE_DIST_TOPO_H -#include "mpi.h" #include "communicator/locality_comm.h" #include "communicator/mpix_comm.h" +#include "mpi.h" // Declarations of C++ methods #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - typedef struct _MPIX_Topo { @@ -23,26 +21,21 @@ typedef struct _MPIX_Topo int reorder; } MPIX_Topo; -int MPIX_Topo_init( - int indegree, - const int sources[], - const int sourceweights[], - int outdegree, - const int destinations[], - const int destweights[], - MPIX_Info* info, - MPIX_Topo** mpix_topo_ptr); +int MPIX_Topo_init(int indegree, + const int sources[], + const int sourceweights[], + int outdegree, + const int destinations[], + const int destweights[], + MPIX_Info* info, + MPIX_Topo** mpix_topo_ptr); -int MPIX_Topo_from_neighbor_comm( - MPIX_Comm* comm, - MPIX_Topo** mpix_topo_ptr); +int MPIX_Topo_from_neighbor_comm(MPIX_Comm* comm, MPIX_Topo** mpix_topo_ptr); int MPIX_Topo_free(MPIX_Topo** topo); - #ifdef __cplusplus } #endif - #endif diff --git a/src/neighborhood/neighbor.c b/src/neighborhood/neighbor.c index 110ed2a3c..82e257b30 100644 --- a/src/neighborhood/neighbor.c +++ b/src/neighborhood/neighbor.c @@ -1,27 +1,30 @@ #include "neighbor.h" -#include "sparse_coll.h" + #include +#include "sparse_coll.h" + // Standard Method is default -NeighborAlltoallvMethod mpix_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_STANDARD; +NeighborAlltoallvMethod mpix_neighbor_alltoallv_implementation = + NEIGHBOR_ALLTOALLV_STANDARD; // Topology object based neighbor alltoallv -int MPIX_Neighbor_alltoallv_topo( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm) +int MPIX_Neighbor_alltoallv_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm) { -int rank; MPI_Comm_rank(comm->global_comm, &rank); + int rank; + MPI_Comm_rank(comm->global_comm, &rank); neighbor_alltoallv_ftn method; - + switch (mpix_neighbor_alltoallv_implementation) { case NEIGHBOR_ALLTOALLV_STANDARD: @@ -35,64 +38,83 @@ int rank; MPI_Comm_rank(comm->global_comm, &rank); break; } - return method(sendbuf, sendcounts, sdispls, sendtype, - recvbuf, recvcounts, rdispls, recvtype, topo, comm); + return method(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + topo, + comm); } - -int MPIX_Neighbor_alltoallv( - const void* sendbuffer, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuffer, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm) +int MPIX_Neighbor_alltoallv(const void* sendbuffer, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuffer, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm) { MPIX_Topo* topo; MPIX_Topo_from_neighbor_comm(comm, &topo); - MPIX_Neighbor_alltoallv_topo(sendbuffer, sendcounts, sdispls, sendtype, - recvbuffer, recvcounts, rdispls, recvtype, topo, comm); + MPIX_Neighbor_alltoallv_topo(sendbuffer, + sendcounts, + sdispls, + sendtype, + recvbuffer, + recvcounts, + rdispls, + recvtype, + topo, + comm); MPIX_Topo_free(&topo); return MPI_SUCCESS; - } - // Standard, non-persistent neighbor collective -int neighbor_alltoallv_standard( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm) +int neighbor_alltoallv_standard(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm) { int tag; MPIX_Comm_tag(comm, &tag); if (topo->indegree + topo->outdegree == 0) + { return MPI_SUCCESS; + } if (comm->n_requests < topo->indegree + topo->outdegree) + { MPIX_Comm_req_resize(comm, topo->indegree + topo->outdegree); + } const char* send_buffer = NULL; - char* recv_buffer = NULL; + char* recv_buffer = NULL; if (topo->indegree) + { recv_buffer = (char*)recvbuf; + } if (topo->outdegree) + { send_buffer = (char*)sendbuf; + } int send_size, recv_size; MPI_Type_size(sendtype, &send_size); @@ -102,25 +124,29 @@ int neighbor_alltoallv_standard( for (int i = 0; i < topo->indegree; i++) { if (recvcounts[i]) - MPI_Irecv(&(recv_buffer[rdispls[i]*recv_size]), - recvcounts[i], - recvtype, - topo->sources[i], - tag, - comm->global_comm, - &(comm->requests[count++])); + { + MPI_Irecv(&(recv_buffer[rdispls[i] * recv_size]), + recvcounts[i], + recvtype, + topo->sources[i], + tag, + comm->global_comm, + &(comm->requests[count++])); + } } for (int i = 0; i < topo->outdegree; i++) { if (sendcounts[i]) - MPI_Isend(&(send_buffer[sdispls[i]*send_size]), - sendcounts[i], - sendtype, - topo->destinations[i], - tag, - comm->global_comm, - &(comm->requests[count++])); + { + MPI_Isend(&(send_buffer[sdispls[i] * send_size]), + sendcounts[i], + sendtype, + topo->destinations[i], + tag, + comm->global_comm, + &(comm->requests[count++])); + } } MPI_Waitall(count, comm->requests, comm->statuses); @@ -129,26 +155,27 @@ int neighbor_alltoallv_standard( } // Non-persistent, locality-aware == call dynamic version -int neighbor_alltoallv_locality( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm) +int neighbor_alltoallv_locality(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); - int send_nnz = topo->outdegree; + int send_nnz = topo->outdegree; int send_size = 0; for (int i = 0; i < send_nnz; i++) + { send_size += sendcounts[i]; + } int recv_nnz, recv_size; int *src_tmp, *recvcounts_tmp, *rdispls_tmp; @@ -160,15 +187,27 @@ int neighbor_alltoallv_locality( MPIX_Info* xinfo; MPIX_Info_init(&xinfo); - alltoallv_crs_personalized(send_nnz, send_size, topo->destinations, sendcounts, - sdispls, sendtype, sendbuf, &recv_nnz, &recv_size, &src_tmp, - &recvcounts_tmp, &rdispls_tmp, recvtype, (void**) &recvvals_tmp, - xinfo, comm); + alltoallv_crs_personalized(send_nnz, + send_size, + topo->destinations, + sendcounts, + sdispls, + sendtype, + sendbuf, + &recv_nnz, + &recv_size, + &src_tmp, + &recvcounts_tmp, + &rdispls_tmp, + recvtype, + (void**)&recvvals_tmp, + xinfo, + comm); char* recvvals = (char*)recvbuf; int idx, proc; - int* new_proc_idx = (int*)malloc(num_procs*sizeof(int)); + int* new_proc_idx = (int*)malloc(num_procs * sizeof(int)); for (int i = 0; i < recv_nnz; i++) { new_proc_idx[src_tmp[i]] = i; @@ -176,13 +215,15 @@ int neighbor_alltoallv_locality( for (int i = 0; i < recv_nnz; i++) { proc = topo->sources[i]; - idx = new_proc_idx[proc]; - memcpy(&(recvvals[rdispls[i]*recv_bytes]), &(recvvals_tmp[rdispls_tmp[idx]*recv_bytes]), recvcounts[i]*recv_bytes); + idx = new_proc_idx[proc]; + memcpy(&(recvvals[rdispls[i] * recv_bytes]), + &(recvvals_tmp[rdispls_tmp[idx] * recv_bytes]), + recvcounts[i] * recv_bytes); } free(new_proc_idx); MPIX_Info_free(&xinfo); - + MPIX_Free(src_tmp); MPIX_Free(recvcounts_tmp); MPIX_Free(rdispls_tmp); @@ -190,5 +231,3 @@ int neighbor_alltoallv_locality( return MPI_SUCCESS; } - - diff --git a/src/neighborhood/neighbor.h b/src/neighborhood/neighbor.h index ed0c52f78..f657db894 100644 --- a/src/neighborhood/neighbor.h +++ b/src/neighborhood/neighbor.h @@ -3,76 +3,81 @@ #include #include + +#include "communicator/locality_comm.h" #include "dist_graph.h" #include "dist_topo.h" #include "persistent/persistent.h" -#include "communicator/locality_comm.h" #include "utils/utils.h" // Declarations of C++ methods #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif -enum NeighborAlltoallvMethod { NEIGHBOR_ALLTOALLV_STANDARD, NEIGHBOR_ALLTOALLV_LOCALITY}; +enum NeighborAlltoallvMethod +{ + NEIGHBOR_ALLTOALLV_STANDARD, + NEIGHBOR_ALLTOALLV_LOCALITY +}; extern NeighborAlltoallvMethod mpix_neighbor_alltoallv_implementation; -typedef int (*neighbor_alltoallv_ftn)(const void* sendbuffer, const int sendcounts[], - const int sdispls[], MPI_Datatype sendtype, void* recvbuf, const int recvcounts[], - const int rdispls[], MPI_Datatype recvtype, MPIX_Topo* topo, MPIX_Comm* comm); +typedef int (*neighbor_alltoallv_ftn)(const void* sendbuffer, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm); // Standard Neighbor Alltoallv // Extension takes array of requests instead of single request // 'requests' must be of size indegree+outdegree! -int MPIX_Neighbor_alltoallv_topo( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm); - -int MPIX_Neighbor_alltoallv( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm); - +int MPIX_Neighbor_alltoallv_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm); -int neighbor_alltoallv_standard( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm); +int MPIX_Neighbor_alltoallv(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm); -int neighbor_alltoallv_locality( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm); +int neighbor_alltoallv_standard(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm); +int neighbor_alltoallv_locality(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm); #ifdef __cplusplus } diff --git a/src/neighborhood/neighbor_init.c b/src/neighborhood/neighbor_init.c index fa50e7f7d..b4c1ee50d 100644 --- a/src/neighborhood/neighbor_init.c +++ b/src/neighborhood/neighbor_init.c @@ -1,30 +1,38 @@ -#include "neighbor.h" #include "neighbor_init.h" + +#include "neighbor.h" #include "persistent/neighbor_persistent.h" NeighborAlltoallvInitMethod mpix_neighbor_alltoallv_init_implementation = - NEIGHBOR_ALLTOALLV_INIT_STANDARD; - -int init_communication(const void* sendbuffer, int n_sends, const int* send_procs, - const int* send_ptr, MPI_Datatype sendtype, void* recvbuffer, int n_recvs, - const int* recv_procs, const int* recv_ptr, MPI_Datatype recvtype, int tag, - MPI_Comm comm, int* n_request_ptr, MPI_Request** request_ptr); - - - -int MPIX_Neighbor_alltoallv_init_topo( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) + NEIGHBOR_ALLTOALLV_INIT_STANDARD; + +int init_communication(const void* sendbuffer, + int n_sends, + const int* send_procs, + const int* send_ptr, + MPI_Datatype sendtype, + void* recvbuffer, + int n_recvs, + const int* recv_procs, + const int* recv_ptr, + MPI_Datatype recvtype, + int tag, + MPI_Comm comm, + int* n_request_ptr, + MPI_Request** request_ptr); + +int MPIX_Neighbor_alltoallv_init_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr) { neighbor_alltoallv_init_ftn method; @@ -39,114 +47,168 @@ int MPIX_Neighbor_alltoallv_init_topo( default: method = neighbor_alltoallv_init_standard; break; - } + } - return method(sendbuf, sendcounts, sdispls, sendtype, recvbuf, recvcounts, - rdispls, recvtype, topo, comm, info, request_ptr); + return method(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + topo, + comm, + info, + request_ptr); } // Standard Persistent Neighbor Alltoallv // Extension takes array of requests instead of single request // 'requests' must be of size indegree+outdegree! -int MPIX_Neighbor_alltoallv_init( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) +int MPIX_Neighbor_alltoallv_init(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr) { MPIX_Topo* topo; MPIX_Topo_from_neighbor_comm(comm, &topo); - MPIX_Neighbor_alltoallv_init_topo(sendbuf, sendcounts, sdispls, sendtype, - recvbuf, recvcounts, rdispls, recvtype, topo, comm, info, request_ptr); + MPIX_Neighbor_alltoallv_init_topo(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + topo, + comm, + info, + request_ptr); MPIX_Topo_free(&topo); return MPI_SUCCESS; } - -int MPIX_Neighbor_alltoallv_init_ext_topo( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) -{ +int MPIX_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr) +{ switch (mpix_neighbor_alltoallv_init_implementation) { case NEIGHBOR_ALLTOALLV_INIT_STANDARD: - return neighbor_alltoallv_init_standard(sendbuf, sendcounts, - sdispls, sendtype, recvbuf, recvcounts, rdispls, - recvtype, topo, comm, info, request_ptr); + return neighbor_alltoallv_init_standard(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + topo, + comm, + info, + request_ptr); case NEIGHBOR_ALLTOALLV_INIT_LOCALITY: - return neighbor_alltoallv_init_locality_ext(sendbuf, sendcounts, - sdispls, global_sindices, sendtype, recvbuf, - recvcounts, rdispls, global_rindices, recvtype, - topo, comm, info, request_ptr); + return neighbor_alltoallv_init_locality_ext(sendbuf, + sendcounts, + sdispls, + global_sindices, + sendtype, + recvbuf, + recvcounts, + rdispls, + global_rindices, + recvtype, + topo, + comm, + info, + request_ptr); default: - return neighbor_alltoallv_init_standard(sendbuf, sendcounts, - sdispls, sendtype, recvbuf, recvcounts, rdispls, - recvtype, topo, comm, info, request_ptr); - } + return neighbor_alltoallv_init_standard(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + topo, + comm, + info, + request_ptr); + } } -int MPIX_Neighbor_alltoallv_init_ext( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) +int MPIX_Neighbor_alltoallv_init_ext(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr) { MPIX_Topo* topo; MPIX_Topo_from_neighbor_comm(comm, &topo); - MPIX_Neighbor_alltoallv_init_ext_topo(sendbuf, sendcounts, sdispls, global_sindices, sendtype, - recvbuf, recvcounts, rdispls, global_rindices, recvtype, topo, comm, info, request_ptr); + MPIX_Neighbor_alltoallv_init_ext_topo(sendbuf, + sendcounts, + sdispls, + global_sindices, + sendtype, + recvbuf, + recvcounts, + rdispls, + global_rindices, + recvtype, + topo, + comm, + info, + request_ptr); MPIX_Topo_free(&topo); return MPI_SUCCESS; } - -int neighbor_alltoallv_init_standard( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) +int neighbor_alltoallv_init_standard(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr) { MPIX_Request* request; init_neighbor_request(&request); @@ -154,11 +216,11 @@ int neighbor_alltoallv_init_standard( int tag; MPIX_Comm_tag(comm, &tag); - request->global_n_msgs = topo->indegree+topo->outdegree; + request->global_n_msgs = topo->indegree + topo->outdegree; allocate_requests(request->global_n_msgs, &(request->global_requests)); const char* send_buffer = (const char*)(sendbuf); - char* recv_buffer = (char*)(recvbuf); + char* recv_buffer = (char*)(recvbuf); int send_size, recv_size; MPI_Type_size(sendtype, &send_size); MPI_Type_size(recvtype, &recv_size); @@ -167,51 +229,51 @@ int neighbor_alltoallv_init_standard( for (int i = 0; i < topo->indegree; i++) { - ierr += MPI_Recv_init(&(recv_buffer[rdispls[i]*recv_size]), - recvcounts[i], - recvtype, - topo->sources[i], - tag, - comm->global_comm, - &(request->global_requests[i])); + ierr += MPI_Recv_init(&(recv_buffer[rdispls[i] * recv_size]), + recvcounts[i], + recvtype, + topo->sources[i], + tag, + comm->global_comm, + &(request->global_requests[i])); } for (int i = 0; i < topo->outdegree; i++) { - ierr += MPI_Send_init(&(send_buffer[sdispls[i]*send_size]), - sendcounts[i], - sendtype, - topo->destinations[i], - tag, - comm->global_comm, - &(request->global_requests[topo->indegree+i])); + ierr += MPI_Send_init(&(send_buffer[sdispls[i] * send_size]), + sendcounts[i], + sendtype, + topo->destinations[i], + tag, + comm->global_comm, + &(request->global_requests[topo->indegree + i])); } *request_ptr = request; return ierr; - } -int neighbor_alltoallv_init_locality( - const void* sendbuffer, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuffer, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) +int neighbor_alltoallv_init_locality(const void* sendbuffer, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuffer, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr) { - int rank; + int rank; MPI_Comm_rank(comm->global_comm, &rank); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } int* global_sdispls = NULL; int* global_rdispls = NULL; @@ -220,8 +282,8 @@ int neighbor_alltoallv_init_locality( if (topo->indegree) { - global_rdispls = (int*)malloc(topo->indegree*sizeof(int)); - ctr = 0; + global_rdispls = (int*)malloc(topo->indegree * sizeof(int)); + ctr = 0; for (int i = 0; i < topo->indegree; i++) { global_rdispls[i] = ctr; @@ -231,8 +293,8 @@ int neighbor_alltoallv_init_locality( if (topo->outdegree) { - global_sdispls = (int*)malloc(topo->outdegree*sizeof(int)); - ctr = 0; + global_sdispls = (int*)malloc(topo->outdegree * sizeof(int)); + ctr = 0; for (int i = 0; i < topo->outdegree; i++) { global_sdispls[i] = ctr; @@ -243,30 +305,62 @@ int neighbor_alltoallv_init_locality( long send_size = 0; long recv_size = 0; for (int i = 0; i < topo->indegree; i++) + { recv_size += recvcounts[i]; + } for (int i = 0; i < topo->outdegree; i++) + { send_size += sendcounts[i]; + } long first_send; MPI_Exscan(&send_size, &first_send, 1, MPI_LONG, MPI_SUM, comm->global_comm); - if (rank == 0) first_send = 0; + if (rank == 0) + { + first_send = 0; + } long* global_send_indices = NULL; long* global_recv_indices = NULL; if (recv_size) - global_recv_indices = (long*)malloc(recv_size*sizeof(long)); + { + global_recv_indices = (long*)malloc(recv_size * sizeof(long)); + } if (send_size) - global_send_indices = (long*)malloc(send_size*sizeof(long)); + { + global_send_indices = (long*)malloc(send_size * sizeof(long)); + } for (int i = 0; i < send_size; i++) + { global_send_indices[i] = first_send + i; + } - MPIX_Neighbor_alltoallv_topo(global_send_indices, sendcounts, global_sdispls, MPI_LONG, - global_recv_indices, recvcounts, global_rdispls, MPI_LONG, topo, comm); - - int err = neighbor_alltoallv_init_locality_ext(sendbuffer, sendcounts, sdispls, - global_send_indices, sendtype, recvbuffer, recvcounts, rdispls, - global_recv_indices, recvtype, topo, comm, info, request_ptr); + MPIX_Neighbor_alltoallv_topo(global_send_indices, + sendcounts, + global_sdispls, + MPI_LONG, + global_recv_indices, + recvcounts, + global_rdispls, + MPI_LONG, + topo, + comm); + + int err = neighbor_alltoallv_init_locality_ext(sendbuffer, + sendcounts, + sdispls, + global_send_indices, + sendtype, + recvbuffer, + recvcounts, + rdispls, + global_recv_indices, + recvtype, + topo, + comm, + info, + request_ptr); free(global_send_indices); free(global_recv_indices); @@ -277,28 +371,27 @@ int neighbor_alltoallv_init_locality( return err; } - - // Locality-Aware Extension to Persistent Neighbor Alltoallv // Needs global indices for each send and receive -int neighbor_alltoallv_init_locality_ext( - const void* sendbuffer, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuffer, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) +int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuffer, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr) { if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } MPIX_Request* request; init_neighbor_request(&request); @@ -307,167 +400,161 @@ int neighbor_alltoallv_init_locality_ext( // E.G. Determine which processes talk to eachother at every step // TODO : instead of mpi_comm, use comm // - will need to create local_comm in dist_graph_create_adjacent... - init_locality(topo->outdegree, - topo->destinations, - sdispls, - sendcounts, - topo->indegree, - topo->sources, - rdispls, - recvcounts, - global_sindices, - global_rindices, - sendtype, - recvtype, - comm, // communicator used in dist_graph_create_adjacent - request); - + init_locality(topo->outdegree, + topo->destinations, + sdispls, + sendcounts, + topo->indegree, + topo->sources, + rdispls, + recvcounts, + global_sindices, + global_rindices, + sendtype, + recvtype, + comm, // communicator used in dist_graph_create_adjacent + request); request->sendbuf = sendbuffer; request->recvbuf = recvbuffer; MPI_Type_size(recvtype, &(request->recv_size)); // Local L Communication - //init_communication(sendbuffer, + // init_communication(sendbuffer, init_communication(request->locality->local_L_comm->send_data->buffer, - request->locality->local_L_comm->send_data->num_msgs, - request->locality->local_L_comm->send_data->procs, - request->locality->local_L_comm->send_data->indptr, - sendtype, - request->locality->local_L_comm->recv_data->buffer, - request->locality->local_L_comm->recv_data->num_msgs, - request->locality->local_L_comm->recv_data->procs, - request->locality->local_L_comm->recv_data->indptr, - recvtype, - request->locality->local_L_comm->tag, - comm->local_comm, - &(request->local_L_n_msgs), - &(request->local_L_requests)); + request->locality->local_L_comm->send_data->num_msgs, + request->locality->local_L_comm->send_data->procs, + request->locality->local_L_comm->send_data->indptr, + sendtype, + request->locality->local_L_comm->recv_data->buffer, + request->locality->local_L_comm->recv_data->num_msgs, + request->locality->local_L_comm->recv_data->procs, + request->locality->local_L_comm->recv_data->indptr, + recvtype, + request->locality->local_L_comm->tag, + comm->local_comm, + &(request->local_L_n_msgs), + &(request->local_L_requests)); // Local S Communication init_communication(request->locality->local_S_comm->send_data->buffer, - request->locality->local_S_comm->send_data->num_msgs, - request->locality->local_S_comm->send_data->procs, - request->locality->local_S_comm->send_data->indptr, - sendtype, - request->locality->local_S_comm->recv_data->buffer, - request->locality->local_S_comm->recv_data->num_msgs, - request->locality->local_S_comm->recv_data->procs, - request->locality->local_S_comm->recv_data->indptr, - recvtype, - request->locality->local_S_comm->tag, - comm->local_comm, - &(request->local_S_n_msgs), - &(request->local_S_requests)); + request->locality->local_S_comm->send_data->num_msgs, + request->locality->local_S_comm->send_data->procs, + request->locality->local_S_comm->send_data->indptr, + sendtype, + request->locality->local_S_comm->recv_data->buffer, + request->locality->local_S_comm->recv_data->num_msgs, + request->locality->local_S_comm->recv_data->procs, + request->locality->local_S_comm->recv_data->indptr, + recvtype, + request->locality->local_S_comm->tag, + comm->local_comm, + &(request->local_S_n_msgs), + &(request->local_S_requests)); // Global Communication init_communication(request->locality->global_comm->send_data->buffer, - request->locality->global_comm->send_data->num_msgs, - request->locality->global_comm->send_data->procs, - request->locality->global_comm->send_data->indptr, - sendtype, - request->locality->global_comm->recv_data->buffer, - request->locality->global_comm->recv_data->num_msgs, - request->locality->global_comm->recv_data->procs, - request->locality->global_comm->recv_data->indptr, - recvtype, - request->locality->global_comm->tag, - comm->global_comm, - &(request->global_n_msgs), - &(request->global_requests)); + request->locality->global_comm->send_data->num_msgs, + request->locality->global_comm->send_data->procs, + request->locality->global_comm->send_data->indptr, + sendtype, + request->locality->global_comm->recv_data->buffer, + request->locality->global_comm->recv_data->num_msgs, + request->locality->global_comm->recv_data->procs, + request->locality->global_comm->recv_data->indptr, + recvtype, + request->locality->global_comm->tag, + comm->global_comm, + &(request->global_n_msgs), + &(request->global_requests)); // Local R Communication init_communication(request->locality->local_R_comm->send_data->buffer, - request->locality->local_R_comm->send_data->num_msgs, - request->locality->local_R_comm->send_data->procs, - request->locality->local_R_comm->send_data->indptr, - sendtype, - request->locality->local_R_comm->recv_data->buffer, - request->locality->local_R_comm->recv_data->num_msgs, - request->locality->local_R_comm->recv_data->procs, - request->locality->local_R_comm->recv_data->indptr, - recvtype, - request->locality->local_R_comm->tag, - comm->local_comm, - &(request->local_R_n_msgs), - &(request->local_R_requests)); + request->locality->local_R_comm->send_data->num_msgs, + request->locality->local_R_comm->send_data->procs, + request->locality->local_R_comm->send_data->indptr, + sendtype, + request->locality->local_R_comm->recv_data->buffer, + request->locality->local_R_comm->recv_data->num_msgs, + request->locality->local_R_comm->recv_data->procs, + request->locality->local_R_comm->recv_data->indptr, + recvtype, + request->locality->local_R_comm->tag, + comm->local_comm, + &(request->local_R_n_msgs), + &(request->local_R_requests)); *request_ptr = request; return MPI_SUCCESS; } - - - void init_neighbor_request(MPIX_Request** request_ptr) { init_request(request_ptr); MPIX_Request* request = *request_ptr; request->start_function = neighbor_start; - request->wait_function = neighbor_wait; + request->wait_function = neighbor_wait; } int init_communication(const void* sendbuffer, - int n_sends, - const int* send_procs, - const int* send_ptr, - MPI_Datatype sendtype, - void* recvbuffer, - int n_recvs, - const int* recv_procs, - const int* recv_ptr, - MPI_Datatype recvtype, - int tag, - MPI_Comm comm, - int* n_request_ptr, - MPI_Request** request_ptr) + int n_sends, + const int* send_procs, + const int* send_ptr, + MPI_Datatype sendtype, + void* recvbuffer, + int n_recvs, + const int* recv_procs, + const int* recv_ptr, + MPI_Datatype recvtype, + int tag, + MPI_Comm comm, + int* n_request_ptr, + MPI_Request** request_ptr) { int ierr = 0; int start, size; int send_size, recv_size; - char* send_buffer = (char*) sendbuffer; - char* recv_buffer = (char*) recvbuffer; + char* send_buffer = (char*)sendbuffer; + char* recv_buffer = (char*)recvbuffer; MPI_Type_size(sendtype, &send_size); MPI_Type_size(recvtype, &recv_size); MPI_Request* requests; - *n_request_ptr = n_recvs+n_sends; + *n_request_ptr = n_recvs + n_sends; allocate_requests(*n_request_ptr, &requests); for (int i = 0; i < n_recvs; i++) { start = recv_ptr[i]; - size = recv_ptr[i+1] - start; - - ierr += MPI_Recv_init(&(recv_buffer[start*recv_size]), - size, - recvtype, - recv_procs[i], - tag, - comm, - &(requests[i])); + size = recv_ptr[i + 1] - start; + + ierr += MPI_Recv_init(&(recv_buffer[start * recv_size]), + size, + recvtype, + recv_procs[i], + tag, + comm, + &(requests[i])); } for (int i = 0; i < n_sends; i++) { start = send_ptr[i]; - size = send_ptr[i+1] - start; - - ierr += MPI_Send_init(&(send_buffer[start*send_size]), - size, - sendtype, - send_procs[i], - tag, - comm, - &(requests[n_recvs+i])); + size = send_ptr[i + 1] - start; + + ierr += MPI_Send_init(&(send_buffer[start * send_size]), + size, + sendtype, + send_procs[i], + tag, + comm, + &(requests[n_recvs + i])); } *request_ptr = requests; return ierr; } - - diff --git a/src/neighborhood/neighbor_init.h b/src/neighborhood/neighbor_init.h index d727c7397..d76343158 100644 --- a/src/neighborhood/neighbor_init.h +++ b/src/neighborhood/neighbor_init.h @@ -2,30 +2,32 @@ #define MPI_ADVANCE_NEIGHBOR_PERSISTENT_H #include "communicator/locality_comm.h" -#include "persistent/persistent.h" #include "neighbor.h" +#include "persistent/persistent.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif -enum NeighborAlltoallvInitMethod { NEIGHBOR_ALLTOALLV_INIT_STANDARD, NEIGHBOR_ALLTOALLV_INIT_LOCALITY}; +enum NeighborAlltoallvInitMethod +{ + NEIGHBOR_ALLTOALLV_INIT_STANDARD, + NEIGHBOR_ALLTOALLV_INIT_LOCALITY +}; extern NeighborAlltoallvInitMethod mpix_neighbor_alltoallv_init_implementation; -typedef int (*neighbor_alltoallv_init_ftn)( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); +typedef int (*neighbor_alltoallv_init_ftn)(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr); // Starting locality-aware requests // 1. Start Local_L @@ -33,146 +35,127 @@ typedef int (*neighbor_alltoallv_init_ftn)( // 3. Start global int neighbor_start(MPIX_Request* request); - // Wait for locality-aware requests // 1. Wait for global // 2. Start and wait for local_R // 3. Wait for local_L int neighbor_wait(MPIX_Request* request, MPI_Status* status); - void init_neighbor_request(MPIX_Request** request_ptr); - // Standard Persistent Neighbor Alltoallv // Extension takes array of requests instead of single request // 'requests' must be of size indegree+outdegree! -int MPIX_Neighbor_alltoallv_init_topo( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); -int MPIX_Neighbor_alltoallv_init( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); - +int MPIX_Neighbor_alltoallv_init_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr); +int MPIX_Neighbor_alltoallv_init(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr); // Locality-Aware Extension to Persistent Neighbor Alltoallv // Needs global indices for each send and receive -int MPIX_Neighbor_alltoallv_init_ext_topo( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); -int MPIX_Neighbor_alltoallv_init_ext( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); - - - - -int neighbor_alltoallv_init_standard( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); -int neighbor_alltoallv_init_locality( - const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); -int neighbor_alltoallv_init_locality_ext( - const void* sendbuffer, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuffer, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); - - - - +int MPIX_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr); +int MPIX_Neighbor_alltoallv_init_ext(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr); + +int neighbor_alltoallv_init_standard(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr); +int neighbor_alltoallv_init_locality(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr); +int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuffer, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIX_Topo* topo, + MPIX_Comm* comm, + MPIX_Info* info, + MPIX_Request** request_ptr); void init_locality(const int n_sends, - const int* send_procs, - const int* send_indptr, - const int* sendcounts, - const int n_recvs, - const int* recv_procs, - const int* recv_indptr, - const int* recvcounts, - const long* global_send_indices, - const long* global_recv_indices, - const MPI_Datatype sendtype, - const MPI_Datatype recvtype, - MPIX_Comm* mpix_comm, - MPIX_Request* request); + const int* send_procs, + const int* send_indptr, + const int* sendcounts, + const int n_recvs, + const int* recv_procs, + const int* recv_indptr, + const int* recvcounts, + const long* global_send_indices, + const long* global_recv_indices, + const MPI_Datatype sendtype, + const MPI_Datatype recvtype, + MPIX_Comm* mpix_comm, + MPIX_Request* request); #ifdef __cplusplus } #endif #endif - diff --git a/src/neighborhood/neighbor_locality.cpp b/src/neighborhood/neighbor_locality.cpp index 1f6023123..5abcb7729 100644 --- a/src/neighborhood/neighbor_locality.cpp +++ b/src/neighborhood/neighbor_locality.cpp @@ -1,9 +1,10 @@ +#include +#include +#include + #include "neighbor.h" #include "neighbor_init.h" #include "persistent/neighbor_persistent.h" -#include -#include -#include /****************************************** **** @@ -11,18 +12,30 @@ **** ******************************************/ -void map_procs_to_nodes(LocalityComm* locality, const int orig_num_msgs, - const int* orig_procs, const int* orig_counts, - std::vector& msg_nodes, std::vector& msg_node_to_local, - bool incr); -void form_local_comm(const int orig_num_sends, const int* orig_send_procs, - const int* orig_send_ptr, const int* orig_sendcounts, const long* orig_send_indices, - const std::vector& nodes_to_local, CommData* send_data, - CommData* recv_data, CommData* local_data, - std::vector& recv_idx_nodes, - LocalityComm* locality, const int tag); -void form_global_comm(CommData* local_data, CommData* global_data, - std::vector& local_data_nodes, const MPIX_Comm* mpix_comm, int tag); +void map_procs_to_nodes(LocalityComm* locality, + const int orig_num_msgs, + const int* orig_procs, + const int* orig_counts, + std::vector& msg_nodes, + std::vector& msg_node_to_local, + bool incr); +void form_local_comm(const int orig_num_sends, + const int* orig_send_procs, + const int* orig_send_ptr, + const int* orig_sendcounts, + const long* orig_send_indices, + const std::vector& nodes_to_local, + CommData* send_data, + CommData* recv_data, + CommData* local_data, + std::vector& recv_idx_nodes, + LocalityComm* locality, + const int tag); +void form_global_comm(CommData* local_data, + CommData* global_data, + std::vector& local_data_nodes, + const MPIX_Comm* mpix_comm, + int tag); void update_global_comm(LocalityComm* locality); void form_global_map(const CommData* map_data, std::map& global_map); void map_indices(CommData* idx_data, std::map& global_map); @@ -30,10 +43,9 @@ void map_indices(CommData* idx_data, const CommData* map_data); void remove_duplicates(CommData* comm_pkg); void remove_duplicates(CommPkg* data); void remove_duplicates(LocalityComm* locality); -void update_indices(LocalityComm* locality, - std::map& send_global_to_local, - std::map& recv_global_to_local); - +void update_indices(LocalityComm* locality, + std::map& send_global_to_local, + std::map& recv_global_to_local); /****************************************** **** @@ -43,20 +55,20 @@ void update_indices(LocalityComm* locality, // Initialize NAPComm* structure, to be used for any number of // instances of communication -void init_locality(const int n_sends, - const int* send_procs, - const int* send_indptr, - const int* sendcounts, - const int n_recvs, - const int* recv_procs, - const int* recv_indptr, - const int* recvcounts, - const long* global_send_indices, - const long* global_recv_indices, - const MPI_Datatype sendtype, - const MPI_Datatype recvtype, - MPIX_Comm* mpix_comm, - MPIX_Request* request) +void init_locality(const int n_sends, + const int* send_procs, + const int* send_indptr, + const int* sendcounts, + const int n_recvs, + const int* recv_procs, + const int* recv_indptr, + const int* recvcounts, + const long* global_send_indices, + const long* global_recv_indices, + const MPI_Datatype sendtype, + const MPI_Datatype recvtype, + MPIX_Comm* mpix_comm, + MPIX_Request* request) { // Get MPI Information int rank, num_procs; @@ -70,68 +82,68 @@ void init_locality(const int n_sends, // Find global send nodes std::vector send_nodes; std::vector send_node_to_local; - map_procs_to_nodes(locality_comm, - n_sends, - send_procs, - sendcounts, - send_nodes, - send_node_to_local, - true); + map_procs_to_nodes(locality_comm, + n_sends, + send_procs, + sendcounts, + send_nodes, + send_node_to_local, + true); // Form initial send local comm std::vector recv_idx_nodes; - form_local_comm(n_sends, - send_procs, - send_indptr, - sendcounts, - global_send_indices, - send_node_to_local, - locality_comm->local_S_comm->send_data, - locality_comm->local_S_comm->recv_data, - locality_comm->local_L_comm->send_data, - recv_idx_nodes, - locality_comm, - 19483); + form_local_comm(n_sends, + send_procs, + send_indptr, + sendcounts, + global_send_indices, + send_node_to_local, + locality_comm->local_S_comm->send_data, + locality_comm->local_S_comm->recv_data, + locality_comm->local_L_comm->send_data, + recv_idx_nodes, + locality_comm, + 19483); // Form global send data - form_global_comm(locality_comm->local_S_comm->recv_data, - locality_comm->global_comm->send_data, - recv_idx_nodes, - mpix_comm, - 93284); + form_global_comm(locality_comm->local_S_comm->recv_data, + locality_comm->global_comm->send_data, + recv_idx_nodes, + mpix_comm, + 93284); // Find global recv nodes std::vector recv_nodes; std::vector recv_node_to_local; - map_procs_to_nodes(locality_comm, - n_recvs, - recv_procs, - recvcounts, - recv_nodes, - recv_node_to_local, - false); + map_procs_to_nodes(locality_comm, + n_recvs, + recv_procs, + recvcounts, + recv_nodes, + recv_node_to_local, + false); // Form final recv local comm std::vector send_idx_nodes; form_local_comm(n_recvs, - recv_procs, - recv_indptr, - recvcounts, - global_recv_indices, - recv_node_to_local, - locality_comm->local_R_comm->recv_data, - locality_comm->local_R_comm->send_data, - locality_comm->local_L_comm->recv_data, - send_idx_nodes, - locality_comm, - 32048); + recv_procs, + recv_indptr, + recvcounts, + global_recv_indices, + recv_node_to_local, + locality_comm->local_R_comm->recv_data, + locality_comm->local_R_comm->send_data, + locality_comm->local_L_comm->recv_data, + send_idx_nodes, + locality_comm, + 32048); // Form global recv data form_global_comm(locality_comm->local_R_comm->send_data, - locality_comm->global_comm->recv_data, - send_idx_nodes, - locality_comm->communicators, - 93284); + locality_comm->global_comm->recv_data, + send_idx_nodes, + locality_comm->communicators, + 93284); // Update procs for global_comm send and recvs update_global_comm(locality_comm); @@ -144,30 +156,32 @@ void init_locality(const int n_sends, for (int i = 0; i < n_sends; i++) { start = send_indptr[i]; - end = start + sendcounts[i]; + end = start + sendcounts[i]; for (int j = start; j < end; j++) + { send_global_to_local[global_send_indices[ctr++]] = j; + } } ctr = 0; for (int i = 0; i < n_recvs; i++) { start = recv_indptr[i]; - end = start + recvcounts[i]; + end = start + recvcounts[i]; for (int j = start; j < end; j++) + { recv_global_to_local[global_recv_indices[ctr++]] = j; + } } - update_indices(locality_comm, - send_global_to_local, - recv_global_to_local); + update_indices(locality_comm, send_global_to_local, recv_global_to_local); // Initialize final variable (MPI_Request arrays, etc.) finalize_locality_comm(locality_comm); // Copy to pointer for return request->locality = locality_comm; - request->tag = locality_comm->global_comm->tag; + request->tag = locality_comm->global_comm->tag; } // Destroy NAPComm* structure @@ -176,8 +190,6 @@ void destroy_locality(MPIX_Request* request) destroy_locality_comm(request->locality); } - - /****************************************** **** **** Helper Methods @@ -185,10 +197,13 @@ void destroy_locality(MPIX_Request* request) ******************************************/ // Map original communication processes to nodes on which they lie // And assign local processes to each node -void map_procs_to_nodes(LocalityComm* locality, const int orig_num_msgs, - const int* orig_procs, const int* orig_counts, - std::vector& msg_nodes, std::vector& msg_node_to_local, - bool incr) +void map_procs_to_nodes(LocalityComm* locality, + const int orig_num_msgs, + const int* orig_procs, + const int* orig_counts, + std::vector& msg_nodes, + std::vector& msg_node_to_local, + bool incr) { int rank, num_procs; int local_rank, local_num_procs; @@ -217,7 +232,12 @@ void map_procs_to_nodes(LocalityComm* locality, const int orig_num_msgs, } // Gather all send nodes and sizes among ranks local to node - MPI_Allreduce(MPI_IN_PLACE, node_sizes.data(), num_nodes, MPI_INT, MPI_SUM, locality->communicators->local_comm); + MPI_Allreduce(MPI_IN_PLACE, + node_sizes.data(), + num_nodes, + MPI_INT, + MPI_SUM, + locality->communicators->local_comm); for (int i = 0; i < num_nodes; i++) { if (node_sizes[i] && i != rank_node) @@ -225,47 +245,57 @@ void map_procs_to_nodes(LocalityComm* locality, const int orig_num_msgs, msg_nodes.push_back(i); } } - std::sort(msg_nodes.begin(), msg_nodes.end(), - [&](const int i, const int j) - { - return node_sizes[i] > node_sizes[j]; - }); + std::sort(msg_nodes.begin(), msg_nodes.end(), [&](const int i, const int j) { + return node_sizes[i] > node_sizes[j]; + }); // Map send_nodes to local ranks msg_node_to_local.resize(num_nodes, -1); if (incr) { local_proc = 0; - inc = 1; + inc = 1; } else { local_proc = local_num_procs - 1; - inc = -1; + inc = -1; } for (size_t i = 0; i < msg_nodes.size(); i++) { - node = msg_nodes[i]; + node = msg_nodes[i]; msg_node_to_local[node] = local_proc; if (local_proc == local_num_procs - 1 && inc == 1) + { inc = -1; + } else if (local_proc == 0 && inc == -1) - inc = 1; + { + inc = 1; + } else + { local_proc += inc; + } } } // Form step of local communication (either initial local_S communicator // or final local_L communicator) along with the corresponding portion // of the fully local (local_L) communicator. -void form_local_comm(const int orig_num_sends, const int* orig_send_procs, - const int* orig_send_ptr, const int* orig_sendcounts, const long* orig_send_indices, - const std::vector& nodes_to_local, CommData* send_data, - CommData* recv_data, CommData* local_data, - std::vector& recv_idx_nodes, - LocalityComm* locality, const int tag) +void form_local_comm(const int orig_num_sends, + const int* orig_send_procs, + const int* orig_send_ptr, + const int* orig_sendcounts, + const long* orig_send_indices, + const std::vector& nodes_to_local, + CommData* send_data, + CommData* recv_data, + CommData* local_data, + std::vector& recv_idx_nodes, + LocalityComm* locality, + const int tag) { // MPI_Information int rank, num_procs; @@ -302,20 +332,20 @@ void form_local_comm(const int orig_num_sends, const int* orig_send_procs, init_num_msgs(local_data, local_num_procs); // Form local_S_comm - send_data->num_msgs = 0; + send_data->num_msgs = 0; local_data->num_msgs = 0; - recv_data->num_msgs = 0; + recv_data->num_msgs = 0; for (int i = 0; i < orig_num_sends; i++) { global_proc = orig_send_procs[i]; - size = orig_sendcounts[i]; - node = get_node(locality->communicators, global_proc); + size = orig_sendcounts[i]; + node = get_node(locality->communicators, global_proc); if (locality->communicators->rank_node != node) { local_proc = nodes_to_local[node]; if (send_sizes[local_proc] == 0) { - local_idx[local_proc] = send_data->num_msgs; + local_idx[local_proc] = send_data->num_msgs; send_data->procs[send_data->num_msgs++] = local_proc; } orig_to_node[i] = node; @@ -324,7 +354,8 @@ void form_local_comm(const int orig_num_sends, const int* orig_send_procs, else { orig_to_node[i] = -1; - local_data->procs[local_data->num_msgs] = get_local_proc(locality->communicators, global_proc); + local_data->procs[local_data->num_msgs] = + get_local_proc(locality->communicators, global_proc); local_data->size_msgs += size; local_data->num_msgs++; local_data->indptr[local_data->num_msgs] = local_data->size_msgs; @@ -334,9 +365,9 @@ void form_local_comm(const int orig_num_sends, const int* orig_send_procs, for (int i = 0; i < send_data->num_msgs; i++) { - local_proc = send_data->procs[i]; - send_data->indptr[i+1] = send_data->indptr[i] + send_sizes[local_proc]; - send_sizes[local_proc] = 0; + local_proc = send_data->procs[i]; + send_data->indptr[i + 1] = send_data->indptr[i] + send_sizes[local_proc]; + send_sizes[local_proc] = 0; } send_data->size_msgs = send_data->indptr[send_data->num_msgs]; @@ -345,82 +376,98 @@ void form_local_comm(const int orig_num_sends, const int* orig_send_procs, std::vector send_idx_node(send_data->size_msgs); local_data->size_msgs = 0; - ctr = 0; + ctr = 0; for (int i = 0; i < orig_num_sends; i++) { - node = orig_to_node[i]; + node = orig_to_node[i]; start = orig_send_ptr[i]; - end = orig_send_ptr[i] + orig_sendcounts[i]; + end = orig_send_ptr[i] + orig_sendcounts[i]; if (node == -1) { for (int j = start; j < end; j++) { - global_idx = orig_send_indices[ctr++]; + global_idx = orig_send_indices[ctr++]; local_data->indices[local_data->size_msgs++] = global_idx; } } else { local_proc = nodes_to_local[node]; - proc_idx = local_idx[local_proc]; + proc_idx = local_idx[local_proc]; for (int j = start; j < end; j++) { global_idx = orig_send_indices[ctr++]; - idx = send_data->indptr[proc_idx] + send_sizes[local_proc]++; + idx = send_data->indptr[proc_idx] + send_sizes[local_proc]++; send_data->indices[idx] = global_idx; - send_idx_node[idx] = node; + send_idx_node[idx] = node; } } } // Send 'local_S_comm send' info (to form local_S recv) - MPI_Allreduce(MPI_IN_PLACE, send_sizes.data(), local_num_procs, - MPI_INT, MPI_SUM, locality->communicators->local_comm); + MPI_Allreduce(MPI_IN_PLACE, + send_sizes.data(), + local_num_procs, + MPI_INT, + MPI_SUM, + locality->communicators->local_comm); recv_data->size_msgs = send_sizes[local_rank]; init_size_msgs(recv_data, recv_data->size_msgs); recv_idx_nodes.resize(recv_data->size_msgs); - send_buffer.resize(2*send_data->size_msgs); + send_buffer.resize(2 * send_data->size_msgs); send_requests.resize(send_data->num_msgs); - ctr = 0; + ctr = 0; start_ctr = 0; for (int i = 0; i < send_data->num_msgs; i++) { - proc = send_data->procs[i]; + proc = send_data->procs[i]; start = send_data->indptr[i]; - end = send_data->indptr[i+1]; + end = send_data->indptr[i + 1]; for (int j = start; j < end; j++) { send_buffer[ctr++] = send_data->indices[j]; send_buffer[ctr++] = send_idx_node[j]; } - MPI_Isend(&send_buffer[start_ctr], ctr - start_ctr , - MPI_INT, proc, tag, locality->communicators->local_comm, &send_requests[i]); + MPI_Isend(&send_buffer[start_ctr], + ctr - start_ctr, + MPI_INT, + proc, + tag, + locality->communicators->local_comm, + &send_requests[i]); start_ctr = ctr; } - std::vector proc_pos(local_num_procs, -1); std::vector recv_idx(recv_data->size_msgs); std::vector tmpnodes(recv_data->size_msgs); - std::vector recvptr(local_num_procs+1); + std::vector recvptr(local_num_procs + 1); recvptr[0] = 0; - ctr = 0; + ctr = 0; while (ctr < recv_data->size_msgs) { MPI_Probe(MPI_ANY_SOURCE, tag, locality->communicators->local_comm, &recv_status); proc = recv_status.MPI_SOURCE; MPI_Get_count(&recv_status, MPI_INT, &size); - if (size > (int) recv_buffer.size()) + if (size > (int)recv_buffer.size()) + { recv_buffer.resize(size); - MPI_Recv(recv_buffer.data(), size, MPI_INT, proc, tag, locality->communicators->local_comm, &recv_status); + } + MPI_Recv(recv_buffer.data(), + size, + MPI_INT, + proc, + tag, + locality->communicators->local_comm, + &recv_status); proc_pos[proc] = recv_data->num_msgs; for (int i = 0; i < size; i += 2) { - recv_idx[ctr] = recv_buffer[i]; - tmpnodes[ctr++] = recv_buffer[i+1]; + recv_idx[ctr] = recv_buffer[i]; + tmpnodes[ctr++] = recv_buffer[i + 1]; } - recvptr[recv_data->num_msgs+1] = recvptr[recv_data->num_msgs] + (size/2); + recvptr[recv_data->num_msgs + 1] = recvptr[recv_data->num_msgs] + (size / 2); recv_data->num_msgs++; } @@ -429,34 +476,38 @@ void form_local_comm(const int orig_num_sends, const int* orig_send_procs, int pos, old_start, new_start; for (int i = 0; i < local_num_procs; i++) { - if (proc_pos[i] == -1) continue; + if (proc_pos[i] == -1) + { + continue; + } - recv_data->procs[ctr] = i; - pos = proc_pos[i]; - old_start = recvptr[pos]; - new_start = recv_data->indptr[ctr]; - size = recvptr[pos+1] - old_start; + recv_data->procs[ctr] = i; + pos = proc_pos[i]; + old_start = recvptr[pos]; + new_start = recv_data->indptr[ctr]; + size = recvptr[pos + 1] - old_start; recv_data->indptr[++ctr] = new_start + size; for (int j = 0; j < size; j++) { - recv_data->indices[new_start+j] = recv_idx[old_start+j]; - recv_idx_nodes[new_start+j] = tmpnodes[old_start+j]; + recv_data->indices[new_start + j] = recv_idx[old_start + j]; + recv_idx_nodes[new_start + j] = tmpnodes[old_start + j]; } } - if (send_data->num_msgs) { MPI_Waitall(send_data->num_msgs, send_requests.data(), MPI_STATUSES_IGNORE); } } - // Form portion of inter-node communication (data corresponding to // either global send or global recv), with node id currently in // place of process with which to communicate -void form_global_comm(CommData* local_data, CommData* global_data, - std::vector& local_data_nodes, const MPIX_Comm* mpix_comm, int tag) +void form_global_comm(CommData* local_data, + CommData* global_data, + std::vector& local_data_nodes, + const MPIX_Comm* mpix_comm, + int tag) { std::vector tmp_send_indices; std::vector node_sizes; @@ -488,7 +539,7 @@ void form_global_comm(CommData* local_data, CommData* global_data, init_num_msgs(global_data, global_data->num_msgs); node_ctr.resize(global_data->num_msgs, 0); - global_data->num_msgs = 0; + global_data->num_msgs = 0; global_data->indptr[0] = 0; for (int i = 0; i < num_nodes; i++) { @@ -506,12 +557,12 @@ void form_global_comm(CommData* local_data, CommData* global_data, for (int i = 0; i < local_data->num_msgs; i++) { start = local_data->indptr[i]; - end = local_data->indptr[i+1]; + end = local_data->indptr[i + 1]; for (int j = start; j < end; j++) { - node = local_data_nodes[j]; + node = local_data_nodes[j]; node_idx = node_sizes[node]; - idx = global_data->indptr[node_idx] + node_ctr[node_idx]++; + idx = global_data->indptr[node_idx] + node_ctr[node_idx]++; global_data->indices[idx] = local_data->indices[j]; } } @@ -528,11 +579,11 @@ void update_global_comm(LocalityComm* locality) MPI_Comm_size(locality->communicators->local_comm, &local_num_procs); int num_nodes = locality->communicators->num_nodes; - int n_sends = locality->global_comm->send_data->num_msgs; - int n_recvs = locality->global_comm->recv_data->num_msgs; - int n_msgs = n_sends + n_recvs; + int n_sends = locality->global_comm->send_data->num_msgs; + int n_recvs = locality->global_comm->recv_data->num_msgs; + int n_msgs = n_sends + n_recvs; MPI_Request* requests = NULL; - int* send_buffer = NULL; + int* send_buffer = NULL; int send_tag, recv_tag; MPIX_Comm_tag(locality->communicators, &send_tag); MPIX_Comm_tag(locality->communicators, &recv_tag); @@ -543,58 +594,104 @@ void update_global_comm(LocalityComm* locality) std::vector recv_nodes(num_nodes, 0); if (n_msgs) { - requests = new MPI_Request[n_msgs]; + requests = new MPI_Request[n_msgs]; send_buffer = new int[n_msgs]; } std::vector comm_procs(num_procs, 0); for (int i = 0; i < n_sends; i++) { - node = locality->global_comm->send_data->procs[i]; + node = locality->global_comm->send_data->procs[i]; global_proc = get_global_proc(locality->communicators, node, local_rank); comm_procs[global_proc]++; send_buffer[i] = locality->communicators->rank_node; - MPI_Isend(&(send_buffer[i]), 1, MPI_INT, global_proc, send_tag, - locality->communicators->global_comm, &(requests[i])); + MPI_Isend(&(send_buffer[i]), + 1, + MPI_INT, + global_proc, + send_tag, + locality->communicators->global_comm, + &(requests[i])); } - MPI_Allreduce(MPI_IN_PLACE, comm_procs.data(), num_procs, MPI_INT, - MPI_SUM, locality->communicators->global_comm); + MPI_Allreduce(MPI_IN_PLACE, + comm_procs.data(), + num_procs, + MPI_INT, + MPI_SUM, + locality->communicators->global_comm); num_to_recv = comm_procs[rank]; for (int i = 0; i < num_procs; i++) + { comm_procs[i] = 0; + } for (int i = 0; i < num_to_recv; i++) { - MPI_Probe(MPI_ANY_SOURCE, send_tag, locality->communicators->global_comm, &recv_status); + MPI_Probe( + MPI_ANY_SOURCE, send_tag, locality->communicators->global_comm, &recv_status); global_proc = recv_status.MPI_SOURCE; - MPI_Recv(&node, 1, MPI_INT, global_proc, send_tag, locality->communicators->global_comm, &recv_status); + MPI_Recv(&node, + 1, + MPI_INT, + global_proc, + send_tag, + locality->communicators->global_comm, + &recv_status); recv_nodes[node] = global_proc; } - + for (int i = 0; i < n_recvs; i++) { - node = locality->global_comm->recv_data->procs[i]; + node = locality->global_comm->recv_data->procs[i]; global_proc = get_global_proc(locality->communicators, node, local_rank); comm_procs[global_proc]++; send_buffer[n_sends + i] = locality->communicators->rank_node; - MPI_Isend(&(send_buffer[n_sends + i]), 1, MPI_INT, global_proc, recv_tag, - locality->communicators->global_comm, &(requests[n_sends + i])); + MPI_Isend(&(send_buffer[n_sends + i]), + 1, + MPI_INT, + global_proc, + recv_tag, + locality->communicators->global_comm, + &(requests[n_sends + i])); } - MPI_Allreduce(MPI_IN_PLACE, comm_procs.data(), num_procs, MPI_INT, - MPI_SUM, locality->communicators->global_comm); + MPI_Allreduce(MPI_IN_PLACE, + comm_procs.data(), + num_procs, + MPI_INT, + MPI_SUM, + locality->communicators->global_comm); num_to_recv = comm_procs[rank]; for (int i = 0; i < num_to_recv; i++) { - MPI_Probe(MPI_ANY_SOURCE, recv_tag, locality->communicators->global_comm, &recv_status); + MPI_Probe( + MPI_ANY_SOURCE, recv_tag, locality->communicators->global_comm, &recv_status); global_proc = recv_status.MPI_SOURCE; - MPI_Recv(&node, 1, MPI_INT, global_proc, recv_tag, locality->communicators->global_comm, &recv_status); + MPI_Recv(&node, + 1, + MPI_INT, + global_proc, + recv_tag, + locality->communicators->global_comm, + &recv_status); send_nodes[node] = global_proc; } if (n_sends + n_recvs) + { MPI_Waitall(n_sends + n_recvs, requests, MPI_STATUSES_IGNORE); + } - MPI_Allreduce(MPI_IN_PLACE, send_nodes.data(), num_nodes, MPI_INT, MPI_MAX, locality->communicators->local_comm); - MPI_Allreduce(MPI_IN_PLACE, recv_nodes.data(), num_nodes, MPI_INT, MPI_MAX, locality->communicators->local_comm); + MPI_Allreduce(MPI_IN_PLACE, + send_nodes.data(), + num_nodes, + MPI_INT, + MPI_MAX, + locality->communicators->local_comm); + MPI_Allreduce(MPI_IN_PLACE, + recv_nodes.data(), + num_nodes, + MPI_INT, + MPI_MAX, + locality->communicators->local_comm); for (int i = 0; i < n_sends; i++) { @@ -607,8 +704,14 @@ void update_global_comm(LocalityComm* locality) locality->global_comm->recv_data->procs[i] = recv_nodes[node]; } - if (requests) delete[] requests; - if (send_buffer) delete[] send_buffer; + if (requests) + { + delete[] requests; + } + if (send_buffer) + { + delete[] send_buffer; + } } // Update indices: @@ -622,7 +725,7 @@ void form_global_map(const CommData* map_data, std::map& global_map) for (int i = 0; i < map_data->size_msgs; i++) { - idx = map_data->indices[i]; + idx = map_data->indices[i]; global_map[idx] = i; } } @@ -632,7 +735,7 @@ void map_indices(CommData* idx_data, std::map& global_map) for (int i = 0; i < idx_data->size_msgs; i++) { - idx = idx_data->indices[i]; + idx = idx_data->indices[i]; idx_data->indices[i] = global_map[idx]; } } @@ -644,8 +747,9 @@ void map_indices(CommData* idx_data, const CommData* map_data) map_indices(idx_data, global_map); } -int cmpfunc (const void * a, const void * b) { - return ( *(int*)a - *(int*)b ); +int cmpfunc(const void* a, const void* b) +{ + return (*(int*)a - *(int*)b); } void remove_duplicates(CommData* comm_pkg) @@ -655,32 +759,32 @@ void remove_duplicates(CommData* comm_pkg) for (int i = 0; i < comm_pkg->num_msgs; i++) { start = comm_pkg->indptr[i]; - end = comm_pkg->indptr[i+1]; - std::sort(comm_pkg->indices+start, comm_pkg->indices+end); + end = comm_pkg->indptr[i + 1]; + std::sort(comm_pkg->indices + start, comm_pkg->indices + end); } comm_pkg->size_msgs = 0; - start = comm_pkg->indptr[0]; + start = comm_pkg->indptr[0]; for (int i = 0; i < comm_pkg->num_msgs; i++) { - end = comm_pkg->indptr[i+1]; + end = comm_pkg->indptr[i + 1]; comm_pkg->indices[comm_pkg->size_msgs++] = comm_pkg->indices[start]; - for (int j = start; j < end - 1; j++) + for (int j = start; j < end - 1; j++) { - if (comm_pkg->indices[j+1] != comm_pkg->indices[j]) + if (comm_pkg->indices[j + 1] != comm_pkg->indices[j]) { - comm_pkg->indices[comm_pkg->size_msgs++] = comm_pkg->indices[j+1]; + comm_pkg->indices[comm_pkg->size_msgs++] = comm_pkg->indices[j + 1]; } } - start = end; - comm_pkg->indptr[i+1] = comm_pkg->size_msgs; + start = end; + comm_pkg->indptr[i + 1] = comm_pkg->size_msgs; } } void remove_duplicates(CommPkg* data) { - remove_duplicates(data->send_data); - remove_duplicates(data->recv_data); + remove_duplicates(data->send_data); + remove_duplicates(data->recv_data); } void remove_duplicates(LocalityComm* locality) @@ -690,10 +794,9 @@ void remove_duplicates(LocalityComm* locality) remove_duplicates(locality->global_comm); } - -void update_indices(LocalityComm* locality, - std::map& send_global_to_local, - std::map& recv_global_to_local) +void update_indices(LocalityComm* locality, + std::map& send_global_to_local, + std::map& recv_global_to_local) { // Remove duplicates remove_duplicates(locality); @@ -718,4 +821,3 @@ void update_indices(LocalityComm* locality, locality->global_comm->recv_data->indices = NULL; } } - diff --git a/src/neighborhood/sparse_coll.c b/src/neighborhood/sparse_coll.c index 7c1cdb18b..91794de61 100644 --- a/src/neighborhood/sparse_coll.c +++ b/src/neighborhood/sparse_coll.c @@ -1,45 +1,66 @@ #include "sparse_coll.h" + #include #include -int MPIX_Alltoall_crs( - const int send_nnz, - const int* dest, - const int sendcount, - MPI_Datatype sendtype, - const void* sendvals, - int* recv_nnz, - int** src_ptr, - int recvcount, - MPI_Datatype recvtype, - void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* xcomm) +int MPIX_Alltoall_crs(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* xcomm) { - return alltoall_crs_personalized(send_nnz, dest, sendcount, sendtype, sendvals, - recv_nnz, src_ptr, recvcount, recvtype, recvvals_ptr, xinfo, xcomm); + return alltoall_crs_personalized(send_nnz, + dest, + sendcount, + sendtype, + sendvals, + recv_nnz, + src_ptr, + recvcount, + recvtype, + recvvals_ptr, + xinfo, + xcomm); } -int MPIX_Alltoallv_crs( - const int send_nnz, - const int send_size, - const int* dest, - const int* sendcounts, - const int* sdispls, - MPI_Datatype sendtype, - const void* sendvals, - int* recv_nnz, - int* recv_size, - int** src_ptr, - int** recvcounts_ptr, - int** rdispls_ptr, - MPI_Datatype recvtype, - void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* xcomm) +int MPIX_Alltoallv_crs(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* xcomm) { - return alltoallv_crs_personalized(send_nnz, send_size, dest, sendcounts, sdispls, - sendtype, sendvals, recv_nnz, recv_size, src_ptr, recvcounts_ptr, - rdispls_ptr, recvtype, recvvals_ptr, xinfo, xcomm); + return alltoallv_crs_personalized(send_nnz, + send_size, + dest, + sendcounts, + sdispls, + sendtype, + sendvals, + recv_nnz, + recv_size, + src_ptr, + recvcounts_ptr, + rdispls_ptr, + recvtype, + recvvals_ptr, + xinfo, + xcomm); } - diff --git a/src/neighborhood/sparse_coll.h b/src/neighborhood/sparse_coll.h index bf35104e7..af8d58454 100644 --- a/src/neighborhood/sparse_coll.h +++ b/src/neighborhood/sparse_coll.h @@ -1,111 +1,182 @@ #ifndef MPI_ADVANCE_SPARSE_COLL_H #define MPI_ADVANCE_SPARSE_COLL_H -#include "mpi.h" #include "communicator/locality_comm.h" #include "communicator/mpix_comm.h" +#include "mpi.h" #include "utils/utils.h" // Declarations of C++ methods #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - - -int MPIX_Alltoall_crs( - const int send_nnz, - const int* dest, - const int sendcount, - MPI_Datatype sendtype, - const void* sendvals, - int* recv_nnz, - int** src_ptr, - int recvcount, - MPI_Datatype recvtype, - void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* xcomm - ); - -int MPIX_Alltoallv_crs( - const int send_nnz, - const int send_size, - const int* dest, - const int* sendcounts, - const int* sdispls, - MPI_Datatype sendtype, - const void* sendvals, - int* recv_nnz, - int* recv_size, - int** src_ptr, - int** recvcounts_ptr, - int** rdispls_ptr, - MPI_Datatype recvtype, - void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm); - - -int alltoall_crs_rma(const int send_nnz, const int* dest, const int sendcount, - MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int** src, int recvcount, MPI_Datatype recvtype, - void** recvvals, MPIX_Info* xinfo, MPIX_Comm* comm); - -int alltoall_crs_personalized(const int send_nnz, const int* dest, const int sendcount, - MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int** src, int recvcount, MPI_Datatype recvtype, - void** recvvals, MPIX_Info* xinfo, MPIX_Comm* comm); - -int alltoall_crs_personalized_loc(const int send_nnz, const int* dest, const int sendcount, - MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int** src, int recvcount, MPI_Datatype recvtype, - void** recvvals, MPIX_Info* xinfo, MPIX_Comm* comm); - -int alltoall_crs_nonblocking(const int send_nnz, const int* dest, const int sendcount, - MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int** src, int recvcount, MPI_Datatype recvtype, - void** recvvals, MPIX_Info* xinfo, MPIX_Comm* comm); - -int alltoall_crs_nonblocking_loc(const int send_nnz, const int* dest, const int sendcount, - MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int** src, int recvcount, MPI_Datatype recvtype, - void** recvvals, MPIX_Info* xinfo, MPIX_Comm* comm); - - - -int alltoallv_crs_personalized(const int send_nnz, const int send_size, const int* dest, - const int* sendcounts, const int* sdispls, MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int* recv_size, int** src_ptr, int** recvcounts_ptr, - int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, MPIX_Info* xinfo, - MPIX_Comm* comm); - -int alltoallv_crs_personalized_loc(const int send_nnz, const int send_size, const int* dest, - const int* sendcounts, const int* sdispls, MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int* recv_size, int** src_ptr, int** recvcounts_ptr, - int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, MPIX_Info* xinfo, - MPIX_Comm* comm); - -int alltoallv_crs_nonblocking(const int send_nnz, const int send_size, const int* dest, - const int* sendcounts, const int* sdispls, MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int* recv_size, int** src_ptr, int** recvcounts_ptr, - int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, MPIX_Info* xinfo, - MPIX_Comm* comm); - -int alltoallv_crs_nonblocking_loc(const int send_nnz, const int send_size, const int* dest, - const int* sendcounts, const int* sdispls, MPI_Datatype sendtype, const void* sendvals, - int* recv_nnz, int* recv_size, int** src_ptr, int** recvcounts_ptr, - int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, MPIX_Info* xinfo, - MPIX_Comm* comm); - +int MPIX_Alltoall_crs(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* xcomm); + +int MPIX_Alltoallv_crs(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm); + +int alltoall_crs_rma(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src, + int recvcount, + MPI_Datatype recvtype, + void** recvvals, + MPIX_Info* xinfo, + MPIX_Comm* comm); + +int alltoall_crs_personalized(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src, + int recvcount, + MPI_Datatype recvtype, + void** recvvals, + MPIX_Info* xinfo, + MPIX_Comm* comm); + +int alltoall_crs_personalized_loc(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src, + int recvcount, + MPI_Datatype recvtype, + void** recvvals, + MPIX_Info* xinfo, + MPIX_Comm* comm); + +int alltoall_crs_nonblocking(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src, + int recvcount, + MPI_Datatype recvtype, + void** recvvals, + MPIX_Info* xinfo, + MPIX_Comm* comm); + +int alltoall_crs_nonblocking_loc(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src, + int recvcount, + MPI_Datatype recvtype, + void** recvvals, + MPIX_Info* xinfo, + MPIX_Comm* comm); + +int alltoallv_crs_personalized(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm); + +int alltoallv_crs_personalized_loc(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm); + +int alltoallv_crs_nonblocking(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm); + +int alltoallv_crs_nonblocking_loc(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIX_Info* xinfo, + MPIX_Comm* comm); #ifdef __cplusplus } - - #endif - #endif diff --git a/src/neighborhood/sparse_coll_utils.cpp b/src/neighborhood/sparse_coll_utils.cpp index 18f81f4c1..4b12c9ef5 100644 --- a/src/neighborhood/sparse_coll_utils.cpp +++ b/src/neighborhood/sparse_coll_utils.cpp @@ -1,20 +1,30 @@ -#include "sparse_coll.h" #include #include +#include "sparse_coll.h" + /* Assumes SMP Ordering of ranks across nodes (aggregates ranks 0-PPN) */ -int alltoall_crs_personalized_loc(int send_nnz, int* dest, int sendcount, - MPI_Datatype sendtype, void* sendvals, - int* recv_nnz, int* src, int recvcount, MPI_Datatype recvtype, - void* recvvals, MPIX_Info* xinfo, MPIX_Comm* comm) +int alltoall_crs_personalized_loc(int send_nnz, + int* dest, + int sendcount, + MPI_Datatype sendtype, + void* sendvals, + int* recv_nnz, + int* src, + int recvcount, + MPI_Datatype recvtype, + void* recvvals, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); - + } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); @@ -29,7 +39,9 @@ int alltoall_crs_personalized_loc(int send_nnz, int* dest, int sendcount, recv_bytes *= recvcount; if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } MPI_Status recv_status; int proc, ctr, start, end; @@ -43,16 +55,19 @@ int alltoall_crs_personalized_loc(int send_nnz, int* dest, int sendcount, count = send_nnz * (send_bytes + int_bytes); if (count) + { node_send_buffer.resize(count); + } // Send a message to every process that I will need data from // Tell them which global indices I need from them - int group_procs, group_rank;; + int group_procs, group_rank; + ; MPI_Comm_size(comm->group_comm, &group_procs); MPI_Comm_rank(comm->group_comm, &group_rank); std::vector msg_counts(group_procs, 0); - std::vector msg_displs(group_procs+1); + std::vector msg_displs(group_procs + 1); int node = -1; for (int i = 0; i < send_nnz; i++) { @@ -61,7 +76,9 @@ int alltoall_crs_personalized_loc(int send_nnz, int* dest, int sendcount, } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } for (int i = 0; i < send_nnz; i++) { @@ -70,35 +87,58 @@ int alltoall_crs_personalized_loc(int send_nnz, int* dest, int sendcount, { node = proc / PPN; } - MPI_Pack(&proc, 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), - &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(send_buffer[i*send_bytes]), sendcount, sendtype, node_send_buffer.data(), - node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); + MPI_Pack(&proc, + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(send_buffer[i * send_bytes]), + sendcount, + sendtype, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } - MPI_Allreduce(MPI_IN_PLACE, msg_counts.data(), group_procs, MPI_INT, MPI_SUM, comm->group_comm); + MPI_Allreduce( + MPI_IN_PLACE, msg_counts.data(), group_procs, MPI_INT, MPI_SUM, comm->group_comm); int node_recv_size = msg_counts[group_rank]; if (send_nnz > 0) + { node = dest[0] / PPN; + } n_sends = 0; for (int i = 0; i < group_procs; i++) { start = msg_displs[i]; - end = msg_displs[i+1]; + end = msg_displs[i + 1]; if (end - start) { - MPI_Isend(&(node_send_buffer[start]), end-start, MPI_PACKED, i, tag, comm->group_comm, &(comm->requests[n_sends++])); + MPI_Isend(&(node_send_buffer[start]), + end - start, + MPI_PACKED, + i, + tag, + comm->group_comm, + &(comm->requests[n_sends++])); } } std::vector recv_buf; if (node_recv_size) + { recv_buf.resize(node_recv_size); + } std::vector origins; ctr = 0; @@ -108,20 +148,27 @@ int alltoall_crs_personalized_loc(int send_nnz, int* dest, int sendcount, { // Wait for a message MPI_Probe(MPI_ANY_SOURCE, tag, comm->group_comm, &recv_status); - + // Get the source process and message size proc = recv_status.MPI_SOURCE; MPI_Get_count(&recv_status, MPI_PACKED, &count); - //recv_buf.resize(ctr + count); + // recv_buf.resize(ctr + count); // Receive the message, and add local indices to send_comm - MPI_Recv(&(recv_buf[ctr]), count, MPI_PACKED, proc, tag, comm->group_comm, - &recv_status); + MPI_Recv(&(recv_buf[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->group_comm, + &recv_status); ctr += count; n_msgs = count / (recv_bytes + int_bytes); for (int i = 0; i < n_msgs; i++) - origins.push_back(proc*PPN + local_rank); + { + origins.push_back(proc * PPN + local_rank); + } } msg_counts.resize(PPN); @@ -130,65 +177,95 @@ int alltoall_crs_personalized_loc(int send_nnz, int* dest, int sendcount, ctr = 0; while (ctr < node_recv_size) { - MPI_Unpack(recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); + MPI_Unpack( + recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); proc -= (comm->rank_node * PPN); ctr += recv_bytes; msg_counts[proc] += recv_bytes + int_bytes; } - std::vector displs(PPN+1); + std::vector displs(PPN + 1); displs[0] = 0; for (int i = 0; i < PPN; i++) - displs[i+1] = displs[i] + msg_counts[i]; - + { + displs[i + 1] = displs[i] + msg_counts[i]; + } + count = recv_buf.size(); if (count) + { local_send_buffer.resize(count); + } ctr = 0; idx = 0; while (ctr < node_recv_size) { - MPI_Unpack(recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); + MPI_Unpack( + recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); proc -= (comm->rank_node * PPN); - MPI_Pack(&(origins[idx++]), 1, MPI_INT, local_send_buffer.data(), - local_send_buffer.size(), &(displs[proc]), comm->local_comm); - MPI_Pack(&(recv_buf[ctr]), recv_bytes, MPI_PACKED, local_send_buffer.data(), - local_send_buffer.size(), &(displs[proc]), comm->local_comm); + MPI_Pack(&(origins[idx++]), + 1, + MPI_INT, + local_send_buffer.data(), + local_send_buffer.size(), + &(displs[proc]), + comm->local_comm); + MPI_Pack(&(recv_buf[ctr]), + recv_bytes, + MPI_PACKED, + local_send_buffer.data(), + local_send_buffer.size(), + &(displs[proc]), + comm->local_comm); ctr += recv_bytes; } displs[0] = 0; for (int i = 0; i < PPN; i++) - displs[i+1] = displs[i] + msg_counts[i]; + { + displs[i + 1] = displs[i] + msg_counts[i]; + } MPIX_Comm_tag(comm, &tag); - MPI_Allreduce(MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); + MPI_Allreduce( + MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); int recv_count = msg_counts[local_rank]; if (PPN > comm->n_requests) + { MPIX_Comm_req_resize(comm, PPN); + } // Send a message to every process that I will need data from // Tell them which global indices I need from them n_sends = 0; for (int i = 0; i < PPN; i++) { - if (displs[i+1] == displs[i]) + if (displs[i + 1] == displs[i]) + { continue; + } - MPI_Isend(&(local_send_buffer[displs[i]]), displs[i+1] - displs[i], MPI_PACKED, i, tag, - comm->local_comm, &(comm->requests[n_sends++])); + MPI_Isend(&(local_send_buffer[displs[i]]), + displs[i + 1] - displs[i], + MPI_PACKED, + i, + tag, + comm->local_comm, + &(comm->requests[n_sends++])); } count = recv_count * (recv_bytes + int_bytes); if (count) + { local_recv_buffer.resize(count); + } // Wait to receive values // until I have received fewer than the number of global indices I am waiting on ctr = 0; - while(ctr < recv_count) + while (ctr < recv_count) { // Wait for a message MPI_Probe(MPI_ANY_SOURCE, tag, comm->local_comm, &recv_status); @@ -198,22 +275,40 @@ int alltoall_crs_personalized_loc(int send_nnz, int* dest, int sendcount, MPI_Get_count(&recv_status, MPI_PACKED, &count); // Receive the message, and add local indices to send_comm - MPI_Recv(&(local_recv_buffer[ctr]), count, MPI_PACKED, proc, tag, - comm->local_comm, MPI_STATUS_IGNORE); + MPI_Recv(&(local_recv_buffer[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->local_comm, + MPI_STATUS_IGNORE); ctr += count; } - if (n_sends) MPI_Waitall(n_sends, comm->requests, MPI_STATUSES_IGNORE); + if (n_sends) + { + MPI_Waitall(n_sends, comm->requests, MPI_STATUSES_IGNORE); + } // Last Step : Step through recvbuf to find proc of origin, size, and indices - idx = 0; + idx = 0; new_idx = 0; n_recvs = 0; while (idx < ctr) { - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &idx, - &proc, 1, MPI_INT, comm->local_comm); - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &idx, - &(recv_buffer[new_idx]), recv_bytes, MPI_BYTE, comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &idx, + &proc, + 1, + MPI_INT, + comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &idx, + &(recv_buffer[new_idx]), + recv_bytes, + MPI_BYTE, + comm->local_comm); src[n_recvs++] = proc; new_idx += recv_bytes; } @@ -222,20 +317,28 @@ int alltoall_crs_personalized_loc(int send_nnz, int* dest, int sendcount, return MPI_SUCCESS; } - - /* Assumes SMP Ordering of ranks across nodes (aggregates ranks 0-PPN) */ -int alltoall_crs_nonblocking_loc(int send_nnz, int* dest, int sendcount, - MPI_Datatype sendtype, void* sendvals, - int* recv_nnz, int* src, int recvcount, MPI_Datatype recvtype, - void* recvvals, MPIX_Info* xinfo, MPIX_Comm* comm) -{ +int alltoall_crs_nonblocking_loc(int send_nnz, + int* dest, + int sendcount, + MPI_Datatype sendtype, + void* sendvals, + int* recv_nnz, + int* src, + int recvcount, + MPI_Datatype recvtype, + void* recvvals, + MPIX_Info* xinfo, + MPIX_Comm* comm) +{ int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); @@ -250,7 +353,9 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int* dest, int sendcount, recv_bytes *= recvcount; if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } MPI_Status recv_status; MPI_Request bar_req; @@ -265,16 +370,19 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int* dest, int sendcount, count = send_nnz * (send_bytes + int_bytes); if (count) + { node_send_buffer.resize(count); + } - int group_procs, group_rank;; + int group_procs, group_rank; + ; MPI_Comm_size(comm->group_comm, &group_procs); MPI_Comm_rank(comm->group_comm, &group_rank); // Send a message to every process that I will need data from // Tell them which global indices I need from them std::vector msg_counts(group_procs, 0); - std::vector msg_displs(group_procs+1); + std::vector msg_displs(group_procs + 1); int node = -1; for (int i = 0; i < send_nnz; i++) { @@ -283,7 +391,9 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int* dest, int sendcount, } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } for (int i = 0; i < send_nnz; i++) { @@ -292,34 +402,53 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int* dest, int sendcount, { node = proc / PPN; } - MPI_Pack(&proc, 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), - &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(send_buffer[i*send_bytes]), sendcount, sendtype, node_send_buffer.data(), - node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); + MPI_Pack(&proc, + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(send_buffer[i * send_bytes]), + sendcount, + sendtype, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } if (send_nnz > 0) + { node = dest[0] / PPN; + } n_sends = 0; for (int i = 0; i < group_procs; i++) { start = msg_displs[i]; - end = msg_displs[i+1]; + end = msg_displs[i + 1]; if (end - start) { - MPI_Issend(&(node_send_buffer[start]), end-start, MPI_PACKED, i, tag, comm->group_comm, &(comm->requests[n_sends++])); + MPI_Issend(&(node_send_buffer[start]), + end - start, + MPI_PACKED, + i, + tag, + comm->group_comm, + &(comm->requests[n_sends++])); } } - std::vector recv_buf; std::vector origins; ibar = 0; - ctr = 0; + ctr = 0; // Wait to receive values // until I have received fewer than the number of global indices I am waiting on while (1) @@ -334,24 +463,31 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int* dest, int sendcount, recv_buf.resize(ctr + count); // Receive the message, and add local indices to send_comm - MPI_Recv(&(recv_buf[ctr]), count, MPI_PACKED, proc, tag, comm->group_comm, - &recv_status); + MPI_Recv(&(recv_buf[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->group_comm, + &recv_status); ctr += count; - n_msgs = count / (recv_bytes + int_bytes); for (int i = 0; i < n_msgs; i++) - origins.push_back(proc*PPN+local_rank); - + { + origins.push_back(proc * PPN + local_rank); + } } - // If I have already called my Ibarrier, check if all processes have reached // If all processes have reached the Ibarrier, all messages have been sent if (ibar) { MPI_Test(&bar_req, &flag, &recv_status); - if (flag) break; + if (flag) + { + break; + } } else { @@ -372,66 +508,95 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int* dest, int sendcount, ctr = 0; while (ctr < node_recv_size) { - MPI_Unpack(recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); + MPI_Unpack( + recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); proc -= (comm->rank_node * PPN); ctr += recv_bytes; msg_counts[proc] += recv_bytes + int_bytes; } - std::vector displs(PPN+1); + std::vector displs(PPN + 1); displs[0] = 0; for (int i = 0; i < PPN; i++) - displs[i+1] = displs[i] + msg_counts[i]; - + { + displs[i + 1] = displs[i] + msg_counts[i]; + } + count = recv_buf.size(); if (count) + { local_send_buffer.resize(count); - + } ctr = 0; idx = 0; while (ctr < node_recv_size) { - MPI_Unpack(recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); + MPI_Unpack( + recv_buf.data(), node_recv_size, &ctr, &proc, 1, MPI_INT, comm->group_comm); proc -= (comm->rank_node * PPN); - MPI_Pack(&(origins[idx++]), 1, MPI_INT, local_send_buffer.data(), - local_send_buffer.size(), &(displs[proc]), comm->local_comm); - MPI_Pack(&(recv_buf[ctr]), recv_bytes, MPI_PACKED, local_send_buffer.data(), - local_send_buffer.size(), &(displs[proc]), comm->local_comm); + MPI_Pack(&(origins[idx++]), + 1, + MPI_INT, + local_send_buffer.data(), + local_send_buffer.size(), + &(displs[proc]), + comm->local_comm); + MPI_Pack(&(recv_buf[ctr]), + recv_bytes, + MPI_PACKED, + local_send_buffer.data(), + local_send_buffer.size(), + &(displs[proc]), + comm->local_comm); ctr += recv_bytes; } displs[0] = 0; for (int i = 0; i < PPN; i++) - displs[i+1] = displs[i] + msg_counts[i]; + { + displs[i + 1] = displs[i] + msg_counts[i]; + } MPIX_Comm_tag(comm, &tag); - MPI_Allreduce(MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); + MPI_Allreduce( + MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); int recv_count = msg_counts[local_rank]; if (PPN > comm->n_requests) + { MPIX_Comm_req_resize(comm, PPN); + } // Send a message to every process that I will need data from // Tell them which global indices I need from them n_sends = 0; for (int i = 0; i < PPN; i++) { - if (displs[i+1] == displs[i]) + if (displs[i + 1] == displs[i]) + { continue; + } - MPI_Isend(&(local_send_buffer[displs[i]]), displs[i+1] - displs[i], MPI_PACKED, i, tag, - comm->local_comm, &(comm->requests[n_sends++])); + MPI_Isend(&(local_send_buffer[displs[i]]), + displs[i + 1] - displs[i], + MPI_PACKED, + i, + tag, + comm->local_comm, + &(comm->requests[n_sends++])); } count = recv_count * (recv_bytes + int_bytes); if (count) + { local_recv_buffer.resize(count); + } // Wait to receive values // until I have received fewer than the number of global indices I am waiting on ctr = 0; - while(ctr < recv_count) + while (ctr < recv_count) { // Wait for a message MPI_Probe(MPI_ANY_SOURCE, tag, comm->local_comm, &recv_status); @@ -441,22 +606,40 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int* dest, int sendcount, MPI_Get_count(&recv_status, MPI_PACKED, &count); // Receive the message, and add local indices to send_comm - MPI_Recv(&(local_recv_buffer[ctr]), count, MPI_PACKED, proc, tag, - comm->local_comm, MPI_STATUS_IGNORE); + MPI_Recv(&(local_recv_buffer[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->local_comm, + MPI_STATUS_IGNORE); ctr += count; } - if (n_sends) MPI_Waitall(n_sends, comm->requests, MPI_STATUSES_IGNORE); + if (n_sends) + { + MPI_Waitall(n_sends, comm->requests, MPI_STATUSES_IGNORE); + } // Last Step : Step through recvbuf to find proc of origin, size, and indices - idx = 0; + idx = 0; new_idx = 0; n_recvs = 0; while (idx < ctr) { - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &idx, - &proc, 1, MPI_INT, comm->local_comm); - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &idx, - &(recv_buffer[new_idx]), recv_bytes, MPI_PACKED, comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &idx, + &proc, + 1, + MPI_INT, + comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &idx, + &(recv_buffer[new_idx]), + recv_bytes, + MPI_PACKED, + comm->local_comm); src[n_recvs++] = proc; new_idx += recv_bytes; } @@ -465,24 +648,38 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int* dest, int sendcount, return MPI_SUCCESS; } - - -int alltoallv_crs_personalized_loc(int send_nnz, int send_size, int* dest, int* sendcounts, - int* sdispls, MPI_Datatype sendtype, void* sendvals, - int* recv_nnz, int* recv_size, int* src, int* recvcounts, - int* rdispls, MPI_Datatype recvtype, void* recvvals, MPIX_Info* xinfo, MPIX_Comm* comm) +int alltoallv_crs_personalized_loc(int send_nnz, + int send_size, + int* dest, + int* sendcounts, + int* sdispls, + MPI_Datatype sendtype, + void* sendvals, + int* recv_nnz, + int* recv_size, + int* src, + int* recvcounts, + int* rdispls, + MPI_Datatype recvtype, + void* recvvals, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } int tag; MPIX_Comm_tag(comm, &tag); @@ -494,59 +691,91 @@ int alltoallv_crs_personalized_loc(int send_nnz, int send_size, int* dest, int* MPI_Type_size(recvtype, &recv_bytes); MPI_Type_size(MPI_INT, &int_bytes); - std::vector node_send_buffer(send_size*send_bytes + 2*send_nnz*int_bytes); + std::vector node_send_buffer(send_size * send_bytes + 2 * send_nnz * int_bytes); std::vector sizes(PPN, 0); int proc, count, ctr, start, end; MPI_Status recv_status; - int group_procs, group_rank;; + int group_procs, group_rank; + ; MPI_Comm_size(comm->group_comm, &group_procs); MPI_Comm_rank(comm->group_comm, &group_rank); // Send a message to every process that I will need data from // Tell them which global indices I need from them std::vector msg_counts(group_procs, 0); - std::vector msg_displs(group_procs+1); + std::vector msg_displs(group_procs + 1); int node = -1; for (int i = 0; i < send_nnz; i++) { proc = dest[i] / PPN; - msg_counts[proc] += 2*int_bytes + sendcounts[i]*send_bytes; + msg_counts[proc] += 2 * int_bytes + sendcounts[i] * send_bytes; } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } for (int i = 0; i < send_nnz; i++) { - proc = dest[i]; - int s = sendcounts[i]*send_bytes; + proc = dest[i]; + int s = sendcounts[i] * send_bytes; if (proc / PPN != node) { node = proc / PPN; } - MPI_Pack(&proc, 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(s), 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(send_buffer[sdispls[i]*send_bytes]), sendcounts[i], sendtype, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); + MPI_Pack(&proc, + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(s), + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(send_buffer[sdispls[i] * send_bytes]), + sendcounts[i], + sendtype, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } - MPI_Allreduce(MPI_IN_PLACE, msg_counts.data(), group_procs, MPI_INT, MPI_SUM, comm->group_comm); + MPI_Allreduce( + MPI_IN_PLACE, msg_counts.data(), group_procs, MPI_INT, MPI_SUM, comm->group_comm); int node_recv_size = msg_counts[group_rank]; if (send_nnz > 0) + { node = dest[0] / PPN; + } int n_sends = 0; for (int i = 0; i < group_procs; i++) { start = msg_displs[i]; - end = msg_displs[i+1]; + end = msg_displs[i + 1]; if (end - start) { - MPI_Isend(&(node_send_buffer[start]), end-start, MPI_PACKED, i, tag, comm->group_comm, &(comm->requests[n_sends++])); + MPI_Isend(&(node_send_buffer[start]), + end - start, + MPI_PACKED, + i, + tag, + comm->group_comm, + &(comm->requests[n_sends++])); } } @@ -570,13 +799,19 @@ int alltoallv_crs_personalized_loc(int send_nnz, int send_size, int* dest, int* proc = recv_status.MPI_SOURCE; MPI_Get_count(&recv_status, MPI_PACKED, &count); - recv_buf.resize((count+ctr)); + recv_buf.resize((count + ctr)); // Receive the message, and add local indices to send_comm - MPI_Recv(&(recv_buf[ctr]), count, MPI_PACKED, proc, tag, comm->group_comm, MPI_STATUS_IGNORE); - + MPI_Recv(&(recv_buf[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->group_comm, + MPI_STATUS_IGNORE); + ctr += count; - origins.push_back(proc*PPN+local_rank); + origins.push_back(proc * PPN + local_rank); origin_displs.push_back(ctr); n_recvs++; @@ -585,59 +820,114 @@ int alltoallv_crs_personalized_loc(int send_nnz, int send_size, int* dest, int* MPI_Waitall(n_sends, comm->requests, MPI_STATUSES_IGNORE); std::vector recv_sizes(PPN, 0); - std::vector recv_displs(PPN+1); + std::vector recv_displs(PPN + 1); std::vector local_buf; if (origin_displs[n_recvs]) + { local_buf.resize(origin_displs[n_recvs]); + } int idx = 0; std::vector recv_byte_buf; if (recv_buf.size()) { recv_byte_buf.resize(recv_buf.size()); - MPI_Pack(recv_buf.data(), recv_buf.size(), MPI_BYTE, recv_byte_buf.data(), recv_byte_buf.size(), &idx, comm->local_comm); + MPI_Pack(recv_buf.data(), + recv_buf.size(), + MPI_BYTE, + recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + comm->local_comm); } idx = 0; while (idx < origin_displs[n_recvs]) { - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &proc, 1, MPI_INT, comm->local_comm); - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &count, 1, MPI_INT, comm->local_comm); - proc -= (comm->rank_node*PPN); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &proc, + 1, + MPI_INT, + comm->local_comm); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &count, + 1, + MPI_INT, + comm->local_comm); + proc -= (comm->rank_node * PPN); idx += count; - recv_sizes[proc] += count + 2*int_bytes; - } + recv_sizes[proc] += count + 2 * int_bytes; + } recv_displs[0] = 0; for (int i = 0; i < PPN; i++) - recv_displs[i+1] = recv_displs[i] + recv_sizes[i]; + { + recv_displs[i + 1] = recv_displs[i] + recv_sizes[i]; + } idx = 0; for (int i = 0; i < n_recvs; i++) { int origin = origins[i]; - while (idx < origin_displs[i+1]) + while (idx < origin_displs[i + 1]) { - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &proc, 1, MPI_INT, comm->local_comm); - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &count, 1, MPI_INT, comm->local_comm); - proc -= (comm->rank_node*PPN); - - MPI_Pack(&origin, 1, MPI_INT, local_buf.data(), local_buf.size(), &(recv_displs[proc]), comm->local_comm); - MPI_Pack(&count, 1, MPI_INT, local_buf.data(), local_buf.size(), &(recv_displs[proc]), comm->local_comm); - - MPI_Pack(&(recv_byte_buf[idx]), count, MPI_PACKED, local_buf.data(), local_buf.size(), &(recv_displs[proc]), comm->local_comm); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &proc, + 1, + MPI_INT, + comm->local_comm); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &count, + 1, + MPI_INT, + comm->local_comm); + proc -= (comm->rank_node * PPN); + + MPI_Pack(&origin, + 1, + MPI_INT, + local_buf.data(), + local_buf.size(), + &(recv_displs[proc]), + comm->local_comm); + MPI_Pack(&count, + 1, + MPI_INT, + local_buf.data(), + local_buf.size(), + &(recv_displs[proc]), + comm->local_comm); + + MPI_Pack(&(recv_byte_buf[idx]), + count, + MPI_PACKED, + local_buf.data(), + local_buf.size(), + &(recv_displs[proc]), + comm->local_comm); idx += count; } } recv_displs[0] = 0; for (int i = 0; i < PPN; i++) - recv_displs[i+1] = recv_displs[i] + recv_sizes[i]; + { + recv_displs[i + 1] = recv_displs[i] + recv_sizes[i]; + } // STEP 2 : Local Communication - MPI_Allreduce(MPI_IN_PLACE, recv_sizes.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); + MPI_Allreduce( + MPI_IN_PLACE, recv_sizes.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); int local_size_msgs = recv_sizes[local_rank]; // Send a message to every process that I will need data from @@ -649,11 +939,16 @@ int alltoallv_crs_personalized_loc(int send_nnz, int send_size, int* dest, int* n_sends = 0; for (int i = 0; i < PPN; i++) { - int s = recv_displs[i+1] - recv_displs[i]; + int s = recv_displs[i + 1] - recv_displs[i]; if (s) { - MPI_Isend(&(local_buf[recv_displs[i]]), s, MPI_PACKED, i, tag, - comm->local_comm, &(local_req[n_sends++])); + MPI_Isend(&(local_buf[recv_displs[i]]), + s, + MPI_PACKED, + i, + tag, + comm->local_comm, + &(local_req[n_sends++])); } } @@ -672,52 +967,93 @@ int alltoallv_crs_personalized_loc(int send_nnz, int send_size, int* dest, int* MPI_Get_count(&recv_status, MPI_PACKED, &count); // Receive the message, and add local indices to send_comm - MPI_Recv(&(local_recv_buffer[ctr]), count, MPI_PACKED, proc, tag, comm->local_comm, &recv_status); + MPI_Recv(&(local_recv_buffer[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->local_comm, + &recv_status); ctr += count; } - if (n_sends) MPI_Waitall(n_sends, local_req.data(), MPI_STATUSES_IGNORE); - + if (n_sends) + { + MPI_Waitall(n_sends, local_req.data(), MPI_STATUSES_IGNORE); + } -// Last Step : Step through recvbuf to find proc of origin, size, and indices - rdispls[0] = 0; - n_recvs = 0; + // Last Step : Step through recvbuf to find proc of origin, size, and indices + rdispls[0] = 0; + n_recvs = 0; int byte_ctr = 0; while (byte_ctr < local_size_msgs) { - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &byte_ctr, &(src[n_recvs]), 1, MPI_INT, comm->local_comm); - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &byte_ctr, &count, 1, MPI_INT, comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &byte_ctr, + &(src[n_recvs]), + 1, + MPI_INT, + comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &byte_ctr, + &count, + 1, + MPI_INT, + comm->local_comm); count = count / recv_bytes; - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &byte_ctr, &(recv_buffer[rdispls[n_recvs]*recv_bytes]), count, recvtype, comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &byte_ctr, + &(recv_buffer[rdispls[n_recvs] * recv_bytes]), + count, + recvtype, + comm->local_comm); - recvcounts[n_recvs] = count; - rdispls[n_recvs+1] = rdispls[n_recvs] + count; + recvcounts[n_recvs] = count; + rdispls[n_recvs + 1] = rdispls[n_recvs] + count; n_recvs++; } // Set send sizes - *recv_nnz = n_recvs; + *recv_nnz = n_recvs; *recv_size = rdispls[n_recvs]; - return MPI_SUCCESS; + return MPI_SUCCESS; } - -int alltoallv_crs_nonblocking_loc(int send_nnz, int send_size, int* dest, int* sendcounts, - int* sdispls, MPI_Datatype sendtype, void* sendvals, - int* recv_nnz, int* recv_size, int* src, int* recvcounts, - int* rdispls, MPI_Datatype recvtype, void* recvvals, MPIX_Info* xinfo, MPIX_Comm* comm) +int alltoallv_crs_nonblocking_loc(int send_nnz, + int send_size, + int* dest, + int* sendcounts, + int* sdispls, + MPI_Datatype sendtype, + void* sendvals, + int* recv_nnz, + int* recv_size, + int* src, + int* recvcounts, + int* rdispls, + MPI_Datatype recvtype, + void* recvvals, + MPIX_Info* xinfo, + MPIX_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); if (comm->local_comm == MPI_COMM_NULL) + { MPIX_Comm_topo_init(comm); + } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); if (comm->n_requests < send_nnz) + { MPIX_Comm_req_resize(comm, send_nnz); + } int tag; MPIX_Comm_tag(comm, &tag); @@ -729,62 +1065,92 @@ int alltoallv_crs_nonblocking_loc(int send_nnz, int send_size, int* dest, int* s MPI_Type_size(recvtype, &recv_bytes); MPI_Type_size(MPI_INT, &int_bytes); - std::vector node_send_buffer(send_size*send_bytes + 2*send_nnz*int_bytes); + std::vector node_send_buffer(send_size * send_bytes + 2 * send_nnz * int_bytes); std::vector sizes(PPN, 0); int proc, count, ctr, flag, start, end; int ibar = 0; MPI_Status recv_status; MPI_Request bar_req; - int group_procs, group_rank;; + int group_procs, group_rank; + ; MPI_Comm_size(comm->group_comm, &group_procs); MPI_Comm_rank(comm->group_comm, &group_rank); // Send a message to every process that I will need data from // Tell them which global indices I need from them std::vector msg_counts(group_procs, 0); - std::vector msg_displs(group_procs+1); + std::vector msg_displs(group_procs + 1); int node = -1; for (int i = 0; i < send_nnz; i++) { proc = dest[i] / PPN; - msg_counts[proc] += 2*int_bytes + sendcounts[i]*send_bytes; + msg_counts[proc] += 2 * int_bytes + sendcounts[i] * send_bytes; } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } for (int i = 0; i < send_nnz; i++) { - proc = dest[i]; - int s = sendcounts[i]*send_bytes; + proc = dest[i]; + int s = sendcounts[i] * send_bytes; if (proc / PPN != node) { node = proc / PPN; } - MPI_Pack(&proc, 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(s), 1, MPI_INT, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); - MPI_Pack(&(send_buffer[sdispls[i]*send_bytes]), sendcounts[i], sendtype, node_send_buffer.data(), node_send_buffer.size(), &(msg_displs[node]), comm->group_comm); + MPI_Pack(&proc, + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(s), + 1, + MPI_INT, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); + MPI_Pack(&(send_buffer[sdispls[i] * send_bytes]), + sendcounts[i], + sendtype, + node_send_buffer.data(), + node_send_buffer.size(), + &(msg_displs[node]), + comm->group_comm); } msg_displs[0] = 0; for (int i = 0; i < group_procs; i++) - msg_displs[i+1] = msg_displs[i] + msg_counts[i]; + { + msg_displs[i + 1] = msg_displs[i] + msg_counts[i]; + } if (send_nnz > 0) + { node = dest[0] / PPN; + } int n_sends = 0; for (int i = 0; i < group_procs; i++) { start = msg_displs[i]; - end = msg_displs[i+1]; + end = msg_displs[i + 1]; if (end - start) { - MPI_Issend(&(node_send_buffer[start]), end-start, MPI_PACKED, i, tag, comm->group_comm, &(comm->requests[n_sends++])); + MPI_Issend(&(node_send_buffer[start]), + end - start, + MPI_PACKED, + i, + tag, + comm->group_comm, + &(comm->requests[n_sends++])); } } - // Wait to receive values // until I have received fewer than the number of global indices I am waiting on // @@ -807,25 +1173,33 @@ int alltoallv_crs_nonblocking_loc(int send_nnz, int send_size, int* dest, int* s proc = recv_status.MPI_SOURCE; MPI_Get_count(&recv_status, MPI_PACKED, &count); - recv_buf.resize((count+ctr)); + recv_buf.resize((count + ctr)); // Receive the message, and add local indices to send_comm - MPI_Recv(&(recv_buf[ctr]), count, MPI_PACKED, proc, tag, comm->group_comm, MPI_STATUS_IGNORE); - + MPI_Recv(&(recv_buf[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->group_comm, + MPI_STATUS_IGNORE); + ctr += count; - origins.push_back(proc*PPN+local_rank); + origins.push_back(proc * PPN + local_rank); origin_displs.push_back(ctr); n_recvs++; } - - + // If I have already called my Ibarrier, check if all processes have reached // If all processes have reached the Ibarrier, all messages have been sent if (ibar) { MPI_Test(&bar_req, &flag, MPI_STATUS_IGNORE); - if (flag) break; + if (flag) + { + break; + } } else { @@ -836,64 +1210,119 @@ int alltoallv_crs_nonblocking_loc(int send_nnz, int send_size, int* dest, int* s { ibar = 1; MPI_Ibarrier(comm->group_comm, &bar_req); - } + } } } std::vector recv_sizes(PPN, 0); - std::vector recv_displs(PPN+1); + std::vector recv_displs(PPN + 1); std::vector local_buf; if (origin_displs[n_recvs]) + { local_buf.resize(origin_displs[n_recvs]); + } int idx = 0; std::vector recv_byte_buf; if (recv_buf.size()) { recv_byte_buf.resize(recv_buf.size()); - MPI_Pack(recv_buf.data(), recv_buf.size(), MPI_BYTE, recv_byte_buf.data(), recv_byte_buf.size(), &idx, comm->local_comm); + MPI_Pack(recv_buf.data(), + recv_buf.size(), + MPI_BYTE, + recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + comm->local_comm); } idx = 0; while (idx < origin_displs[n_recvs]) { - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &proc, 1, MPI_INT, comm->local_comm); - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &count, 1, MPI_INT, comm->local_comm); - proc -= (comm->rank_node*PPN); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &proc, + 1, + MPI_INT, + comm->local_comm); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &count, + 1, + MPI_INT, + comm->local_comm); + proc -= (comm->rank_node * PPN); idx += count; - recv_sizes[proc] += count + 2*int_bytes; - } + recv_sizes[proc] += count + 2 * int_bytes; + } recv_displs[0] = 0; for (int i = 0; i < PPN; i++) - recv_displs[i+1] = recv_displs[i] + recv_sizes[i]; + { + recv_displs[i + 1] = recv_displs[i] + recv_sizes[i]; + } idx = 0; for (int i = 0; i < n_recvs; i++) { int origin = origins[i]; - while (idx < origin_displs[i+1]) + while (idx < origin_displs[i + 1]) { - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &proc, 1, MPI_INT, comm->local_comm); - MPI_Unpack(recv_byte_buf.data(), recv_byte_buf.size(), &idx, &count, 1, MPI_INT, comm->local_comm); - proc -= (comm->rank_node*PPN); - - MPI_Pack(&origin, 1, MPI_INT, local_buf.data(), local_buf.size(), &(recv_displs[proc]), comm->local_comm); - MPI_Pack(&count, 1, MPI_INT, local_buf.data(), local_buf.size(), &(recv_displs[proc]), comm->local_comm); - - MPI_Pack(&(recv_byte_buf[idx]), count, MPI_PACKED, local_buf.data(), local_buf.size(), &(recv_displs[proc]), comm->local_comm); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &proc, + 1, + MPI_INT, + comm->local_comm); + MPI_Unpack(recv_byte_buf.data(), + recv_byte_buf.size(), + &idx, + &count, + 1, + MPI_INT, + comm->local_comm); + proc -= (comm->rank_node * PPN); + + MPI_Pack(&origin, + 1, + MPI_INT, + local_buf.data(), + local_buf.size(), + &(recv_displs[proc]), + comm->local_comm); + MPI_Pack(&count, + 1, + MPI_INT, + local_buf.data(), + local_buf.size(), + &(recv_displs[proc]), + comm->local_comm); + + MPI_Pack(&(recv_byte_buf[idx]), + count, + MPI_PACKED, + local_buf.data(), + local_buf.size(), + &(recv_displs[proc]), + comm->local_comm); idx += count; } } recv_displs[0] = 0; for (int i = 0; i < PPN; i++) - recv_displs[i+1] = recv_displs[i] + recv_sizes[i]; + { + recv_displs[i + 1] = recv_displs[i] + recv_sizes[i]; + } // STEP 2 : Local Communication - MPI_Allreduce(MPI_IN_PLACE, recv_sizes.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); + MPI_Allreduce( + MPI_IN_PLACE, recv_sizes.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); int local_size_msgs = recv_sizes[local_rank]; // Send a message to every process that I will need data from @@ -905,11 +1334,16 @@ int alltoallv_crs_nonblocking_loc(int send_nnz, int send_size, int* dest, int* s n_sends = 0; for (int i = 0; i < PPN; i++) { - int s = recv_displs[i+1] - recv_displs[i]; + int s = recv_displs[i + 1] - recv_displs[i]; if (s) { - MPI_Isend(&(local_buf[recv_displs[i]]), s, MPI_PACKED, i, tag, - comm->local_comm, &(local_req[n_sends++])); + MPI_Isend(&(local_buf[recv_displs[i]]), + s, + MPI_PACKED, + i, + tag, + comm->local_comm, + &(local_req[n_sends++])); } } @@ -928,32 +1362,57 @@ int alltoallv_crs_nonblocking_loc(int send_nnz, int send_size, int* dest, int* s MPI_Get_count(&recv_status, MPI_PACKED, &count); // Receive the message, and add local indices to send_comm - MPI_Recv(&(local_recv_buffer[ctr]), count, MPI_PACKED, proc, tag, comm->local_comm, &recv_status); + MPI_Recv(&(local_recv_buffer[ctr]), + count, + MPI_PACKED, + proc, + tag, + comm->local_comm, + &recv_status); ctr += count; } - if (n_sends) MPI_Waitall(n_sends, local_req.data(), MPI_STATUSES_IGNORE); - + if (n_sends) + { + MPI_Waitall(n_sends, local_req.data(), MPI_STATUSES_IGNORE); + } -// Last Step : Step through recvbuf to find proc of origin, size, and indices - rdispls[0] = 0; - n_recvs = 0; + // Last Step : Step through recvbuf to find proc of origin, size, and indices + rdispls[0] = 0; + n_recvs = 0; int byte_ctr = 0; while (byte_ctr < local_size_msgs) { - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &byte_ctr, &(src[n_recvs]), 1, MPI_INT, comm->local_comm); - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &byte_ctr, &count, 1, MPI_INT, comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &byte_ctr, + &(src[n_recvs]), + 1, + MPI_INT, + comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &byte_ctr, + &count, + 1, + MPI_INT, + comm->local_comm); count = count / recv_bytes; - MPI_Unpack(local_recv_buffer.data(), local_recv_buffer.size(), &byte_ctr, &(recv_buffer[rdispls[n_recvs]*recv_bytes]), count, recvtype, comm->local_comm); + MPI_Unpack(local_recv_buffer.data(), + local_recv_buffer.size(), + &byte_ctr, + &(recv_buffer[rdispls[n_recvs] * recv_bytes]), + count, + recvtype, + comm->local_comm); - recvcounts[n_recvs] = count; - rdispls[n_recvs+1] = rdispls[n_recvs] + count; + recvcounts[n_recvs] = count; + rdispls[n_recvs + 1] = rdispls[n_recvs] + count; n_recvs++; } // Set send sizes - *recv_nnz = n_recvs; + *recv_nnz = n_recvs; *recv_size = rdispls[n_recvs]; - return MPI_SUCCESS; + return MPI_SUCCESS; } - diff --git a/src/persistent/neighbor_persistent.c b/src/persistent/neighbor_persistent.c index bc7eb85da..34001e565 100644 --- a/src/persistent/neighbor_persistent.c +++ b/src/persistent/neighbor_persistent.c @@ -3,17 +3,19 @@ int neighbor_start(MPIX_Request* request) { if (request == NULL) + { return 0; + } int ierr = 0; int idx; char* send_buffer = NULL; - int recv_size = 0; + int recv_size = 0; if (request->recv_size) { send_buffer = (char*)(request->sendbuf); - recv_size = request->recv_size; + recv_size = request->recv_size; } // Local L sends sendbuf @@ -23,13 +25,14 @@ int neighbor_start(MPIX_Request* request) { idx = request->locality->local_L_comm->send_data->indices[i]; for (int j = 0; j < recv_size; j++) - request->locality->local_L_comm->send_data->buffer[i*recv_size+j] = send_buffer[idx*recv_size+j]; + { + request->locality->local_L_comm->send_data->buffer[i * recv_size + j] = + send_buffer[idx * recv_size + j]; + } } ierr += MPI_Startall(request->local_L_n_msgs, request->local_L_requests); } - - // Local S sends sendbuf if (request->local_S_n_msgs) { @@ -38,30 +41,38 @@ int neighbor_start(MPIX_Request* request) idx = request->locality->local_S_comm->send_data->indices[i]; for (int j = 0; j < recv_size; j++) - request->locality->local_S_comm->send_data->buffer[i*recv_size+j] = send_buffer[idx*recv_size+j]; + { + request->locality->local_S_comm->send_data->buffer[i * recv_size + j] = + send_buffer[idx * recv_size + j]; + } } ierr += MPI_Startall(request->local_S_n_msgs, request->local_S_requests); - ierr += MPI_Waitall(request->local_S_n_msgs, request->local_S_requests, MPI_STATUSES_IGNORE); - + ierr += MPI_Waitall( + request->local_S_n_msgs, request->local_S_requests, MPI_STATUSES_IGNORE); // Copy into global->send_data->buffer for (int i = 0; i < request->locality->global_comm->send_data->size_msgs; i++) { idx = request->locality->global_comm->send_data->indices[i]; for (int j = 0; j < recv_size; j++) - request->locality->global_comm->send_data->buffer[i*recv_size+j] = request->locality->local_S_comm->recv_data->buffer[idx*recv_size+j]; + { + request->locality->global_comm->send_data->buffer[i * recv_size + j] = + request->locality->local_S_comm->recv_data + ->buffer[idx * recv_size + j]; + } } } // Global sends buffer in locality, sendbuf in standard if (request->global_n_msgs) + { ierr += MPI_Startall(request->global_n_msgs, request->global_requests); + } return ierr; } - // Wait for locality-aware requests // 1. Wait for global // 2. Start and wait for local_R @@ -70,32 +81,40 @@ int neighbor_start(MPIX_Request* request) int neighbor_wait(MPIX_Request* request, MPI_Status* status) { if (request == NULL) + { return 0; + } int ierr = 0; int idx; char* recv_buffer = NULL; - int recv_size = 0; + int recv_size = 0; if (request->recv_size) { - recv_buffer = (char*)(request->recvbuf); - recv_size = request->recv_size; + recv_buffer = (char*)(request->recvbuf); + recv_size = request->recv_size; } - // Global waits for recvs if (request->global_n_msgs) { - ierr += MPI_Waitall(request->global_n_msgs, request->global_requests, MPI_STATUSES_IGNORE); + ierr += MPI_Waitall( + request->global_n_msgs, request->global_requests, MPI_STATUSES_IGNORE); if (request->local_R_n_msgs) { - for (int i = 0; i < request->locality->local_R_comm->send_data->size_msgs; i++) + for (int i = 0; i < request->locality->local_R_comm->send_data->size_msgs; + i++) { idx = request->locality->local_R_comm->send_data->indices[i]; for (int j = 0; j < recv_size; j++) - request->locality->local_R_comm->send_data->buffer[i*recv_size+j] = request->locality->global_comm->recv_data->buffer[idx*recv_size+j]; + { + request->locality->local_R_comm->send_data + ->buffer[i * recv_size + j] = + request->locality->global_comm->recv_data + ->buffer[idx * recv_size + j]; + } } } } @@ -104,31 +123,36 @@ int neighbor_wait(MPIX_Request* request, MPI_Status* status) if (request->local_R_n_msgs) { ierr += MPI_Startall(request->local_R_n_msgs, request->local_R_requests); - ierr += MPI_Waitall(request->local_R_n_msgs, request->local_R_requests, MPI_STATUSES_IGNORE); + ierr += MPI_Waitall( + request->local_R_n_msgs, request->local_R_requests, MPI_STATUSES_IGNORE); for (int i = 0; i < request->locality->local_R_comm->recv_data->size_msgs; i++) { idx = request->locality->local_R_comm->recv_data->indices[i]; for (int j = 0; j < recv_size; j++) - recv_buffer[idx*recv_size+j] = request->locality->local_R_comm->recv_data->buffer[i*recv_size+j]; + { + recv_buffer[idx * recv_size + j] = + request->locality->local_R_comm->recv_data->buffer[i * recv_size + j]; + } } } // Wait for local_L recvs if (request->local_L_n_msgs) { - ierr += MPI_Waitall(request->local_L_n_msgs, request->local_L_requests, MPI_STATUSES_IGNORE); + ierr += MPI_Waitall( + request->local_L_n_msgs, request->local_L_requests, MPI_STATUSES_IGNORE); for (int i = 0; i < request->locality->local_L_comm->recv_data->size_msgs; i++) { idx = request->locality->local_L_comm->recv_data->indices[i]; for (int j = 0; j < recv_size; j++) - recv_buffer[idx*recv_size+j] = request->locality->local_L_comm->recv_data->buffer[i*recv_size+j]; + { + recv_buffer[idx * recv_size + j] = + request->locality->local_L_comm->recv_data->buffer[i * recv_size + j]; + } } } - return ierr; } - - diff --git a/src/persistent/neighbor_persistent.h b/src/persistent/neighbor_persistent.h index 781c3c22a..999224509 100644 --- a/src/persistent/neighbor_persistent.h +++ b/src/persistent/neighbor_persistent.h @@ -2,12 +2,11 @@ #define MPI_ADVANCE_NEIGHBOR_INIT_H #include "communicator/locality_comm.h" -#include "persistent.h" #include "neighborhood/neighbor.h" +#include "persistent.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif // Starting locality-aware requests @@ -16,7 +15,6 @@ extern "C" // 3. Start global int neighbor_start(MPIX_Request* request); - // Wait for locality-aware requests // 1. Wait for global // 2. Start and wait for local_R diff --git a/src/persistent/persistent.c b/src/persistent/persistent.c index 925334337..00c3809a7 100644 --- a/src/persistent/persistent.c +++ b/src/persistent/persistent.c @@ -1,29 +1,29 @@ #include "persistent.h" void init_request(MPIX_Request** request_ptr) -{ +{ MPIX_Request* request = (MPIX_Request*)malloc(sizeof(MPIX_Request)); - + request->locality = NULL; - + request->local_L_n_msgs = 0; request->local_S_n_msgs = 0; request->local_R_n_msgs = 0; - request->global_n_msgs = 0; - + request->global_n_msgs = 0; + request->local_L_requests = NULL; request->local_S_requests = NULL; request->local_R_requests = NULL; - request->global_requests = NULL; - - request->recv_size = 0; + request->global_requests = NULL; + + request->recv_size = 0; request->block_size = 1; #ifdef GPU request->cpu_sendbuf = NULL; request->cpu_recvbuf = NULL; #endif - + *request_ptr = request; } @@ -31,10 +31,13 @@ void allocate_requests(int n_requests, MPI_Request** request_ptr) { if (n_requests) { - MPI_Request* request = (MPI_Request*)malloc(sizeof(MPI_Request)*n_requests); - *request_ptr = request; + MPI_Request* request = (MPI_Request*)malloc(sizeof(MPI_Request) * n_requests); + *request_ptr = request; + } + else + { + *request_ptr = NULL; } - else *request_ptr = NULL; } // Starting locality-aware requests @@ -44,13 +47,14 @@ void allocate_requests(int n_requests, MPI_Request** request_ptr) int MPIX_Start(MPIX_Request* request) { if (request == NULL) + { return 0; + } mpix_start_ftn start_function = (mpix_start_ftn)(request->start_function); return start_function(request); } - // Wait for locality-aware requests // 1. Wait for global // 2. Start and wait for local_R @@ -59,13 +63,14 @@ int MPIX_Start(MPIX_Request* request) int MPIX_Wait(MPIX_Request* request, MPI_Status* status) { if (request == NULL) + { return 0; + } mpix_wait_ftn wait_function = (mpix_wait_ftn)(request->wait_function); return wait_function(request, status); } - int MPIX_Request_free(MPIX_Request** request_ptr) { MPIX_Request* request = *request_ptr; @@ -73,34 +78,44 @@ int MPIX_Request_free(MPIX_Request** request_ptr) if (request->local_L_n_msgs) { for (int i = 0; i < request->local_L_n_msgs; i++) + { MPI_Request_free(&(request->local_L_requests[i])); + } free(request->local_L_requests); } if (request->local_S_n_msgs) { for (int i = 0; i < request->local_S_n_msgs; i++) + { MPI_Request_free(&(request->local_S_requests[i])); + } free(request->local_S_requests); } if (request->local_R_n_msgs) { for (int i = 0; i < request->local_R_n_msgs; i++) + { MPI_Request_free(&(request->local_R_requests[i])); + } free(request->local_R_requests); } if (request->global_n_msgs) { for (int i = 0; i < request->global_n_msgs; i++) + { MPI_Request_free(&(request->global_requests[i])); + } free(request->global_requests); } // If Locality-Aware if (request->locality != NULL) + { destroy_locality_comm(request->locality); + } // TODO : for safety, may want to check if allocated with malloc? -#ifdef GPU // Assuming cpu buffers allocated in pinned memory +#ifdef GPU // Assuming cpu buffers allocated in pinned memory int ierr; if (request->cpu_sendbuf) { @@ -118,4 +133,3 @@ int MPIX_Request_free(MPIX_Request** request_ptr) return 0; } - diff --git a/src/persistent/persistent.h b/src/persistent/persistent.h index 05c205c62..3f2bfcc97 100644 --- a/src/persistent/persistent.h +++ b/src/persistent/persistent.h @@ -6,11 +6,10 @@ #include "utils/utils.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif -struct _MPIX_Request; // forward declaration +struct _MPIX_Request; // forward declaration typedef struct _MPIX_Request MPIX_Request; struct _MPIX_Request @@ -33,8 +32,8 @@ struct _MPIX_Request LocalityComm* locality; // Pointer to sendbuf and recvbuf - const void* sendbuf; // pointer to sendbuf (where original data begins) - void* recvbuf; // pointer to recvbuf (where final data goes) + const void* sendbuf; // pointer to sendbuf (where original data begins) + void* recvbuf; // pointer to recvbuf (where final data goes) // Number of bytes per receive object (for locality-aware) int recv_size; @@ -47,8 +46,8 @@ struct _MPIX_Request // For allocating cpu buffers for heterogeneous communication #ifdef GPU - void* cpu_sendbuf; // for copy-to-cpu - void* cpu_recvbuf; // for copy-to-cpu + void* cpu_sendbuf; // for copy-to-cpu + void* cpu_recvbuf; // for copy-to-cpu #endif // Keep track of which start/wait functions to call for given request @@ -65,7 +64,6 @@ typedef int (*mpix_wait_ftn)(MPIX_Request* request, MPI_Status* status); // 3. Start global int MPIX_Start(MPIX_Request* request); - // Wait for locality-aware requests // 1. Wait for global // 2. Start and wait for local_R @@ -78,11 +76,8 @@ void init_request(MPIX_Request** request_ptr); void allocate_requests(int n_requests, MPI_Request** request_ptr); void destroy_request(MPIX_Request* request); - - #ifdef __cplusplus } #endif - #endif diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index bc40d5c8e..bc17fa261 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -1,6 +1,8 @@ #include "utils.h" + #include #include + #include "mpi.h" #include "stdio.h" @@ -11,8 +13,8 @@ // MPIX Info Object Routines int MPIX_Info_init(MPIX_Info** info_ptr) { - MPIX_Info* xinfo = (MPIX_Info*)malloc(sizeof(MPIX_Info)); - xinfo->crs_num_initialized = 0; + MPIX_Info* xinfo = (MPIX_Info*)malloc(sizeof(MPIX_Info)); + xinfo->crs_num_initialized = 0; xinfo->crs_size_initialized = 0; *info_ptr = xinfo; @@ -28,49 +30,52 @@ int MPIX_Info_free(MPIX_Info** info_ptr) return MPI_SUCCESS; } - void sort(int n_objects, int* object_indices, int* object_values) { - std::sort(object_indices, object_indices+n_objects, - [&](const int i, const int j) - { - return object_values[i] > object_values[j]; - }); + std::sort(object_indices, object_indices + n_objects, [&](const int i, const int j) { + return object_values[i] > object_values[j]; + }); } -void rotate(void* recvbuf, - int new_first_byte, - int last_byte) +void rotate(void* recvbuf, int new_first_byte, int last_byte) { char* recv_buffer = (char*)(recvbuf); std::rotate(recv_buffer, &(recv_buffer[new_first_byte]), &(recv_buffer[last_byte])); -} +} void reverse(void* recvbuf, int n_bytes, int var_bytes) { char* recv_buffer = (char*)(recvbuf); - int n_vars = n_bytes / var_bytes; + int n_vars = n_bytes / var_bytes; for (int i = 0; i < n_vars / 2; i++) + { for (int j = 0; j < var_bytes; j++) - std::swap(recv_buffer[i*var_bytes+j], recv_buffer[(n_vars-i-1)*var_bytes+j]); + { + std::swap(recv_buffer[i * var_bytes + j], + recv_buffer[(n_vars - i - 1) * var_bytes + j]); + } + } } - - // Repack Data on Device #ifdef GPU -__global__ void device_repack(char* __restrict__ sendbuf, char* __restrict__ recvbuf, - int size_x, int size_y, int size_z) +__global__ void device_repack(char* __restrict__ sendbuf, + char* __restrict__ recvbuf, + int size_x, + int size_y, + int size_z) { const int tid_x = threadIdx.x + blockIdx.x * blockDim.x; const int tid_y = threadIdx.y + blockIdx.y * blockDim.y; const int tid_z = threadIdx.z + blockIdx.z * blockDim.z; if (tid_x >= size_x || tid_y >= size_y || tid_z >= size_z) + { return; + } - recvbuf[(tid_y*size_x+tid_x)*size_z+tid_z] = - sendbuf[(tid_x*size_y+tid_y)*size_z+tid_z]; + recvbuf[(tid_y * size_x + tid_x) * size_z + tid_z] = + sendbuf[(tid_x * size_y + tid_y) * size_z + tid_z]; } void gpu_repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf) @@ -86,7 +91,9 @@ void gpu_repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf void gpu_check(int ierr) { if (ierr != gpuSuccess) + { printf("Error in Device Function!\n"); + } } #endif @@ -97,46 +104,65 @@ void repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf) gpuMemoryType send_type, recv_type; get_mem_types(sendbuf, recvbuf, &send_type, &recv_type); - if (send_type == gpuMemoryTypeDevice && - recv_type == gpuMemoryTypeDevice) - { - //gpu_repack(size_i, size_j, size_k, sendbuf, recvbuf); - for (int i = 0; i < size_i; i++) - for (int j = 0; j < size_j; j++) - gpuMemcpy(recvbuf + (j*size_i+i)*size_k, - sendbuf + (i*size_j+j)*size_k, - size_k, gpuMemcpyDeviceToDevice); - } + if (send_type == gpuMemoryTypeDevice && recv_type == gpuMemoryTypeDevice) + { + // gpu_repack(size_i, size_j, size_k, sendbuf, recvbuf); + for (int i = 0; i < size_i; i++) + { + for (int j = 0; j < size_j; j++) + { + gpuMemcpy(recvbuf + (j * size_i + i) * size_k, + sendbuf + (i * size_j + j) * size_k, + size_k, + gpuMemcpyDeviceToDevice); + } + } + } else if (send_type == gpuMemoryTypeDevice) - { - for (int i = 0; i < size_i; i++) - for (int j = 0; j < size_j; j++) - gpuMemcpy(recvbuf + (j*size_i+i)*size_k, - sendbuf + (i*size_j+j)*size_k, - size_k, gpuMemcpyHostToDevice); - } - else if (recv_type == gpuMemoryTypeDevice) - { - for (int i = 0; i < size_i; i++) - for (int j = 0; j < size_j; j++) - gpuMemcpy(recvbuf + (j*size_i+i)*size_k, - sendbuf + (i*size_j+j)*size_k, - size_k, gpuMemcpyDeviceToHost); - } - else -#endif - for (int i = 0; i < size_i; i++) - for (int j = 0; j < size_j; j++) - memcpy(recvbuf + (j*size_i+i)*size_k, - sendbuf + (i*size_j+j)*size_k, - size_k); + { + for (int i = 0; i < size_i; i++) + { + for (int j = 0; j < size_j; j++) + { + gpuMemcpy(recvbuf + (j * size_i + i) * size_k, + sendbuf + (i * size_j + j) * size_k, + size_k, + gpuMemcpyHostToDevice); + } + } + } + else if (recv_type == gpuMemoryTypeDevice) + { + for (int i = 0; i < size_i; i++) + { + for (int j = 0; j < size_j; j++) + { + gpuMemcpy(recvbuf + (j * size_i + i) * size_k, + sendbuf + (i * size_j + j) * size_k, + size_k, + gpuMemcpyDeviceToHost); + } + } + } + else +#endif + for (int i = 0; i < size_i; i++) + { + for (int j = 0; j < size_j; j++) + { + memcpy(recvbuf + (j * size_i + i) * size_k, + sendbuf + (i * size_j + j) * size_k, + size_k); + } + } } - // GPU Method to find where memory was allocated #ifdef GPU -void get_mem_types(const void* sendbuf, const void* recvbuf, - gpuMemoryType* send_ptr, gpuMemoryType* recv_ptr) +void get_mem_types(const void* sendbuf, + const void* recvbuf, + gpuMemoryType* send_ptr, + gpuMemoryType* recv_ptr) { gpuMemoryType send_type, recv_type; @@ -144,55 +170,72 @@ void get_mem_types(const void* sendbuf, const void* recvbuf, gpuPointerGetAttributes(&mem, sendbuf); int ierr = gpuGetLastError(); if (ierr == gpuErrorInvalidValue) + { send_type = gpuMemoryTypeHost; + } else + { send_type = mem.type; + } gpuPointerGetAttributes(&mem, recvbuf); ierr = gpuGetLastError(); if (ierr == gpuErrorInvalidValue) + { recv_type = gpuMemoryTypeHost; + } else + { recv_type = mem.type; + } *send_ptr = send_type; *recv_ptr = recv_type; } -void get_memcpy_kind(gpuMemoryType send_type, gpuMemoryType recv_type, - gpuMemcpyKind* memcpy_kind) +void get_memcpy_kind(gpuMemoryType send_type, + gpuMemoryType recv_type, + gpuMemcpyKind* memcpy_kind) { - if (send_type == gpuMemoryTypeDevice && - recv_type == gpuMemoryTypeDevice) + if (send_type == gpuMemoryTypeDevice && recv_type == gpuMemoryTypeDevice) + { *memcpy_kind = gpuMemcpyDeviceToDevice; + } else if (send_type == gpuMemoryTypeDevice) + { *memcpy_kind = gpuMemcpyDeviceToHost; + } else if (recv_type == gpuMemoryTypeDevice) + { *memcpy_kind = gpuMemcpyHostToDevice; + } else + { *memcpy_kind = gpuMemcpyHostToHost; + } } #endif - - - int MPIX_Alloc(void** pointer, const int bytes) { if (bytes == 0) + { *pointer = NULL; + } else + { *pointer = new char[bytes]; + } return MPI_SUCCESS; } int MPIX_Free(void* pointer) { if (pointer != NULL) - { - char* char_ptr = (char*)pointer; + { + char* char_ptr = (char*)pointer; delete[] char_ptr; - } - + } + return MPI_SUCCESS; } diff --git a/src/utils/utils.h b/src/utils/utils.h index 51dd20657..d56a74f60 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -9,13 +9,10 @@ #include "utils_cuda.h" #endif - #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - // MPIX Info Object typedef struct _MPIX_Info { @@ -26,29 +23,30 @@ typedef struct _MPIX_Info int MPIX_Info_init(MPIX_Info** info); int MPIX_Info_free(MPIX_Info** info); - // If using GPU, specific gpu methods (for either NCCL or HIP) #ifdef GPU - __global__ void device_repack(char* __restrict__ sendbuf, - char* __restrict__ recvbuf, - int size_x, int size_y, int size_z); - void get_mem_types(const void* sendbuf, const void* recvbuf, - gpuMemoryType* send_type, gpuMemoryType* recv_type); - void get_memcpy_kind(gpuMemoryType send_type, gpuMemoryType recv_type, - gpuMemcpyKind* memcpy_kind); - void gpu_repack(int size_i, int size_j, int size_k, char* sendbuf, - char* recvbuf); - void gpu_check(int ierr); +__global__ void device_repack(char* __restrict__ sendbuf, + char* __restrict__ recvbuf, + int size_x, + int size_y, + int size_z); +void get_mem_types(const void* sendbuf, + const void* recvbuf, + gpuMemoryType* send_type, + gpuMemoryType* recv_type); +void get_memcpy_kind(gpuMemoryType send_type, + gpuMemoryType recv_type, + gpuMemcpyKind* memcpy_kind); +void gpu_repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf); +void gpu_check(int ierr); #endif - // General utility methods (that use C++ functions) void sort(int n_objects, int* object_indices, int* object_values); void rotate(void* ref, int new_start_byte, int end_byte); void reverse(void* recvbuf, int n_bytes, int var_bytes); void repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf); - // Allocate Vector in MPI int MPIX_Alloc(void** pointer, const int bytes); int MPIX_Free(void* pointer); @@ -57,5 +55,4 @@ int MPIX_Free(void* pointer); } #endif - #endif diff --git a/src/utils/utils_cuda.h b/src/utils/utils_cuda.h index 3c455c7cd..55475caae 100644 --- a/src/utils/utils_cuda.h +++ b/src/utils/utils_cuda.h @@ -28,7 +28,7 @@ // Streams #define gpuStream_t cudaStream_t #define gpuStreamCreate cudaStreamCreate -#define gpuStreamDestroy cudaStreamDestroy +#define gpuStreamDestroy cudaStreamDestroy // Synchronization #define gpuDeviceSynchronize cudaDeviceSynchronize diff --git a/src/utils/utils_hip.h b/src/utils/utils_hip.h index 37445a7d6..a3d59bd53 100644 --- a/src/utils/utils_hip.h +++ b/src/utils/utils_hip.h @@ -5,7 +5,7 @@ #define __HIP_PLATFORM_AMD__ 1 #endif -//#include "hip/hip_runtime_api.h" +// #include "hip/hip_runtime_api.h" #include "hip/hip_runtime.h" // Devices @@ -35,7 +35,7 @@ // Streams #define gpuStream_t hipStream_t #define gpuStreamCreate hipStreamCreate -#define gpuStreamDestroy hipStreamDestroy +#define gpuStreamDestroy hipStreamDestroy // Synchronization #define gpuDeviceSynchronize hipDeviceSynchronize From 8a598edac3a99f7cca94e6eb32906ea85e5554ff Mon Sep 17 00:00:00 2001 From: Amanda Bienz Date: Tue, 9 Sep 2025 10:40:52 -0600 Subject: [PATCH 03/15] Added convert petsc script --- test_data/PetscBinaryIO.pyc | Bin 15713 -> 0 bytes test_data/convert_petsc.py | 6 ++++++ test_data/convert_petsc.pyc | Bin 452 -> 0 bytes 3 files changed, 6 insertions(+) delete mode 100644 test_data/PetscBinaryIO.pyc create mode 100644 test_data/convert_petsc.py delete mode 100644 test_data/convert_petsc.pyc diff --git a/test_data/PetscBinaryIO.pyc b/test_data/PetscBinaryIO.pyc deleted file mode 100644 index 95a81ea77597b945a857ddb4e7c326259dbeb7a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15713 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 Ok)20&9-2I|^S=P2PEnZv diff --git a/test_data/convert_petsc.py b/test_data/convert_petsc.py new file mode 100644 index 000000000..e563ba967 --- /dev/null +++ b/test_data/convert_petsc.py @@ -0,0 +1,6 @@ +import scipy.io, PetscBinaryIO + +def convert(file_in, file_out): + A = scipy.io.mmread(file_in) + A = A.tocsr() + PetscBinaryIO.PetscBinaryIO().writeMatSciPy(open(file_out,'w'), A) diff --git a/test_data/convert_petsc.pyc b/test_data/convert_petsc.pyc deleted file mode 100644 index 85b1bad4c03d1590f84e35d762f58fe0d6eb5b68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 452 zcmaJ-OHRWu5Pf!9sDMg{6&FCtswY5*!ioi@YIfaJ!7-AfBrfrkDpFa6LvT(w0L)me z*kO5Ip6BysCdv2sh?T3Sqa7OCDl Date: Mon, 22 Sep 2025 12:58:08 -0500 Subject: [PATCH 04/15] Feat rename mpix to mpil, mpi_advance to locality_aware (#44) * library target changed to locality aware * changed file names from mpix to mpil * blanket replacement of MPIX to MPIL, caps only * fixed targeting problem in unit tests * added in namespace scope to make install * changed mpi_advance.h to locality_aware.h in benchmark calls * change to CMakelist in Heter. to point to locality_aware * fixed link to enum name * added 'enum' to external enum declarations to avoid error when compling with c code --- CMakeLists.txt | 49 +++--- benchmarks/CMakeLists.txt | 2 +- benchmarks/alltoall_crs.cpp | 58 +++---- benchmarks/alltoallv_crs.cpp | 62 ++++---- benchmarks/gpu_alltoall.cpp | 10 +- benchmarks/microbenchmarks.cpp | 2 +- benchmarks/p2p_alltoall.cpp | 16 +- benchmarks/p2p_alltoallv.cpp | 16 +- cmake/locality_aware-config.cmake.in | 2 +- src/collective/alltoall.c | 68 ++++---- src/collective/alltoall.h | 32 ++-- src/collective/alltoallv.c | 26 +-- src/collective/alltoallv.h | 16 +- src/collective/collective.h | 8 +- src/collective/tests/CMakeLists.txt | 2 +- src/collective/tests/test_alltoall.cpp | 86 +++++----- src/collective/tests/test_alltoall_enum.cpp | 148 +++++++++--------- src/collective/tests/test_alltoallv.cpp | 50 +++--- src/collective/tests/test_alltoallv_enum.cpp | 68 ++++---- .../tests/test_suitesparse_alltoallv.cpp | 54 +++---- src/communicator/locality_comm.c | 10 +- src/communicator/locality_comm.h | 6 +- src/communicator/{mpix_comm.c => mpil_comm.c} | 50 +++--- src/communicator/{mpix_comm.h => mpil_comm.h} | 36 ++--- src/heterogeneous/gpu_alltoall.c | 20 +-- src/heterogeneous/gpu_alltoall.h | 16 +- src/heterogeneous/gpu_alltoallv.c | 28 ++-- src/heterogeneous/gpu_alltoallv.h | 24 +-- src/heterogeneous/tests/CMakeLists.txt | 2 +- src/heterogeneous/tests/test_gpu_alltoall.cpp | 10 +- .../tests/test_gpu_alltoallv.cpp | 10 +- src/{mpi_advance.h => locality_aware.h} | 2 +- src/neighborhood/alltoall_crs.cpp | 79 +++++----- src/neighborhood/alltoallv_crs.cpp | 77 +++++---- src/neighborhood/dist_graph.c | 10 +- src/neighborhood/dist_graph.h | 8 +- src/neighborhood/dist_topo.c | 22 +-- src/neighborhood/dist_topo.h | 16 +- src/neighborhood/neighbor.c | 44 +++--- src/neighborhood/neighbor.h | 24 +-- src/neighborhood/neighbor_init.c | 92 +++++------ src/neighborhood/neighbor_init.h | 80 +++++----- src/neighborhood/neighbor_locality.cpp | 14 +- src/neighborhood/sparse_coll.c | 12 +- src/neighborhood/sparse_coll.h | 50 +++--- src/neighborhood/sparse_coll_utils.cpp | 52 +++--- src/neighborhood/tests/CMakeLists.txt | 2 +- src/neighborhood/tests/neighbor_data.hpp | 6 +- .../tests/test_neighbor_alltoallv_init.cpp | 54 +++---- .../tests/test_neighbor_reorder.cpp | 34 ++-- .../test_neighbor_topo_alltoallv_init.cpp | 46 +++--- .../tests/test_suitesparse_alltoall_crs.cpp | 36 ++--- .../tests/test_suitesparse_alltoallv_crs.cpp | 48 +++--- .../test_suitesparse_neighbor_alltoallv.cpp | 26 +-- ...st_suitesparse_neighbor_alltoallv_enum.cpp | 22 +-- ...st_suitesparse_neighbor_alltoallv_init.cpp | 46 +++--- .../test_suitesparse_neighbor_reorder.cpp | 30 ++-- ...itesparse_neighbor_topo_alltoallv_init.cpp | 58 +++---- src/persistent/neighbor_persistent.c | 4 +- src/persistent/neighbor_persistent.h | 4 +- src/persistent/persistent.c | 12 +- src/persistent/persistent.h | 26 +-- src/tests/compare.hpp | 2 +- src/utils/utils.cpp | 14 +- src/utils/utils.h | 14 +- 65 files changed, 1027 insertions(+), 1026 deletions(-) rename src/communicator/{mpix_comm.c => mpil_comm.c} (88%) rename src/communicator/{mpix_comm.h => mpil_comm.h} (57%) rename src/{mpi_advance.h => locality_aware.h} (94%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d6f92d5e..28289d68a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.21 FATAL_ERROR) cmake_policy(VERSION 3.21.1...3.27) # Create project -project(locality_aware LANGUAGES C CXX) +project(locality_aware VERSION 2.0.0 LANGUAGES C CXX) # Set C,CXX standards set(CMAKE_CXX_STANDARD 11) @@ -85,7 +85,7 @@ set_source_files_properties( ${heterogeneous_SOURCES} PROPERTIES LANGUAGE ${locality_aware_LANG}) -add_library(mpi_advance +add_library(locality_aware ${utils_SOURCES} ${utils_HEADERS} ${communicator_SOURCES} ${communicator_HEADERS} ${collective_SOURCES} ${collective_HEADERS} @@ -94,16 +94,16 @@ add_library(mpi_advance ${heterogeneous_SOURCES} ${heterogeneous_HEADERS} ) -set_target_properties(mpi_advance PROPERTIES PUBLIC_HEADER src/mpi_advance.h) +set_target_properties(locality_aware PROPERTIES PUBLIC_HEADER src/locality_aware.h) -target_include_directories(mpi_advance PUBLIC +target_include_directories(locality_aware PUBLIC $ $ ) -target_link_libraries(mpi_advance PUBLIC ${EXTERNAL_LIBS}) +target_link_libraries(locality_aware PUBLIC ${EXTERNAL_LIBS}) -install(TARGETS mpi_advance) +install(TARGETS locality_aware) ### Install all headers that locality_aware exports install(FILES ${utils_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/utils) @@ -113,28 +113,41 @@ install(FILES ${persistent_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ install(FILES ${neighborhood_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/neighborhood) install(FILES ${heterogeneous_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/heterogeneous) -add_subdirectory(benchmarks) + add_subdirectory(benchmarks) ### Installation Requirements for Spack Package ### include(CMakePackageConfigHelpers) # Install the actual target(s) and register them for export -install(TARGETS mpi_advance - EXPORT mpiadvanceTargets +install(TARGETS locality_aware + EXPORT locality_awareTargets DESTINATION lib) -# Export the targets into a CMake package -install(EXPORT mpiadvanceTargets - NAMESPACE MPIAdvance:: - DESTINATION share/mpiadvance) + +# Create the configuration file configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/locality_aware-config.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/mpiadvance-config.cmake" - INSTALL_DESTINATION share/mpiadvance) + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/locality_aware-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + INSTALL_DESTINATION share/${PROJECT_NAME} +) + +## Version file +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config-version.cmake + COMPATIBILITY SameMinorVersion +) install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/mpiadvance-config.cmake" - DESTINATION share/mpiadvance) + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config-version.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + DESTINATION share/${PROJECT_NAME} +) + +# Export the targets into a CMake package +install(EXPORT locality_awareTargets + NAMESPACE MPIAdvance:: + DESTINATION share/${PROJECT_NAME}) + ############## End of Spack Section ################ diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 4199f69f9..abe1af5d0 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -26,5 +26,5 @@ foreach(src ${CPP_SOURCES}) add_executable(${exec_name} ${src}) # Link with MPI - target_link_libraries(${exec_name} mpi_advance ${MPI_LIBRARIES}) + target_link_libraries(${exec_name} locality_aware ${MPI_LIBRARIES}) endforeach() diff --git a/benchmarks/alltoall_crs.cpp b/benchmarks/alltoall_crs.cpp index 2276a09cf..b22433884 100644 --- a/benchmarks/alltoall_crs.cpp +++ b/benchmarks/alltoall_crs.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include "tests/sparse_mat.hpp" #include "tests/par_binary_IO.hpp" @@ -57,38 +57,38 @@ int main(int argc, char* argv[]) // Form Communication Package (A.send_comm, A.recv_comm) form_comm(A); - MPIX_Comm* xcomm; + MPIL_Comm* xcomm; - MPIX_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); - // Form MPIX_Comm initial communicator (should be cheap) + // Form MPIL_Comm initial communicator (should be cheap) MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIX_Comm_free(&xcomm); + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_free(&xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIX_Comm_init time %e\n", t0/n_iter); + if (rank == 0) printf("MPIL_Comm_init time %e\n", t0/n_iter); - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); // Split node communicator MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { - MPIX_Comm_topo_init(xcomm); - MPIX_Comm_topo_free(xcomm); + MPIL_Comm_topo_init(xcomm); + MPIL_Comm_topo_free(xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIX_Comm_topo_init time %e\n", t0/n_iter); + if (rank == 0) printf("MPIL_Comm_topo_init time %e\n", t0/n_iter); - MPIX_Comm_topo_init(xcomm); + MPIL_Comm_topo_init(xcomm); // Form Window int bytes = num_procs * sizeof(int); @@ -96,14 +96,14 @@ int main(int argc, char* argv[]) t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { - MPIX_Comm_win_init(xcomm, bytes, sizeof(int)); - MPIX_Comm_win_free(xcomm); + MPIL_Comm_win_init(xcomm, bytes, sizeof(int)); + MPIL_Comm_win_free(xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIX_Comm_win_init_time %e\n", t0/n_iter); + if (rank == 0) printf("MPIL_Comm_win_init_time %e\n", t0/n_iter); - MPIX_Comm_win_init(xcomm, bytes, sizeof(int)); + MPIL_Comm_win_init(xcomm, bytes, sizeof(int)); int n_recvs; int *src, *recvvals; @@ -127,8 +127,8 @@ int main(int argc, char* argv[]) MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); if (rank == 0) printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0/n_iter); compare(n_recvs, src, recvvals, A.send_comm.n_msgs, proc_count.data()); - MPIX_Free(src); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvvals); // Time Personalized MPI_Barrier(MPI_COMM_WORLD); @@ -144,8 +144,8 @@ int main(int argc, char* argv[]) MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); if (rank == 0) printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0/n_iter); compare(n_recvs, src, recvvals, A.send_comm.n_msgs, proc_count.data()); - MPIX_Free(src); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvvals); // Time Nonblocking MPI_Barrier(MPI_COMM_WORLD); @@ -161,8 +161,8 @@ int main(int argc, char* argv[]) MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); if (rank == 0) printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0/n_iter); compare(n_recvs, src, recvvals, A.send_comm.n_msgs, proc_count.data()); - MPIX_Free(src); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvvals); // Time Personalized Locality @@ -179,8 +179,8 @@ int main(int argc, char* argv[]) MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); if (rank == 0) printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0/n_iter); compare(n_recvs, src, recvvals, A.send_comm.n_msgs, proc_count.data()); - MPIX_Free(src); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvvals); // Time Nonblocking Locality MPI_Barrier(MPI_COMM_WORLD); @@ -196,11 +196,11 @@ int main(int argc, char* argv[]) MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); if (rank == 0) printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0/n_iter); compare(n_recvs, src, recvvals, A.send_comm.n_msgs, proc_count.data()); - MPIX_Free(src); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvvals); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); MPI_Finalize(); return 0; } diff --git a/benchmarks/alltoallv_crs.cpp b/benchmarks/alltoallv_crs.cpp index 67faeba58..703b01ee4 100644 --- a/benchmarks/alltoallv_crs.cpp +++ b/benchmarks/alltoallv_crs.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include "tests/sparse_mat.hpp" #include "tests/par_binary_IO.hpp" @@ -75,37 +75,37 @@ int main(int argc, char* argv[]) // Form Communication Package (A.send_comm, A.recv_comm) form_comm(A); - MPIX_Comm* xcomm; + MPIL_Comm* xcomm; - MPIX_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); - // Form MPIX_Comm initial communicator (should be cheap) + // Form MPIL_Comm initial communicator (should be cheap) MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIX_Comm_free(&xcomm); + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_free(&xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIX_Comm_init time %e\n", t0/n_iter); - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); + if (rank == 0) printf("MPIL_Comm_init time %e\n", t0/n_iter); + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); // Split node communicator MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { - MPIX_Comm_topo_init(xcomm); - MPIX_Comm_topo_free(xcomm); + MPIL_Comm_topo_init(xcomm); + MPIL_Comm_topo_free(xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIX_Comm_topo_init time %e\n", t0/n_iter); + if (rank == 0) printf("MPIL_Comm_topo_init time %e\n", t0/n_iter); - MPIX_Comm_topo_init(xcomm); + MPIL_Comm_topo_init(xcomm); update_locality(xcomm, 4); int n_recvs, s_recvs, proc; @@ -141,10 +141,10 @@ int main(int argc, char* argv[]) compare(n_recvs, s_recvs, src, recvcounts, rdispls, recvvals, A.send_comm.n_msgs, A.send_comm.size_msgs, proc_count.data(), proc_displs.data(), orig_indices.data()); - MPIX_Free(src); - MPIX_Free(recvcounts); - MPIX_Free(rdispls); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); // Time Nonblocking MPI_Barrier(MPI_COMM_WORLD); @@ -163,10 +163,10 @@ int main(int argc, char* argv[]) compare(n_recvs, s_recvs, src, recvcounts, rdispls, recvvals, A.send_comm.n_msgs, A.send_comm.size_msgs, proc_count.data(), proc_displs.data(), orig_indices.data()); - MPIX_Free(src); - MPIX_Free(recvcounts); - MPIX_Free(rdispls); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); // Time Personalized Locality MPI_Barrier(MPI_COMM_WORLD); @@ -185,10 +185,10 @@ int main(int argc, char* argv[]) compare(n_recvs, s_recvs, src, recvcounts, rdispls, recvvals, A.send_comm.n_msgs, A.send_comm.size_msgs, proc_count.data(), proc_displs.data(), orig_indices.data()); - MPIX_Free(src); - MPIX_Free(recvcounts); - MPIX_Free(rdispls); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); // Time Nonblocking Locality MPI_Barrier(MPI_COMM_WORLD); @@ -207,14 +207,14 @@ int main(int argc, char* argv[]) compare(n_recvs, s_recvs, src, recvcounts, rdispls, recvvals, A.send_comm.n_msgs, A.send_comm.size_msgs, proc_count.data(), proc_displs.data(), orig_indices.data()); - MPIX_Free(src); - MPIX_Free(recvcounts); - MPIX_Free(rdispls); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); MPI_Finalize(); return 0; diff --git a/benchmarks/gpu_alltoall.cpp b/benchmarks/gpu_alltoall.cpp index b30e18173..d27bfeef8 100644 --- a/benchmarks/gpu_alltoall.cpp +++ b/benchmarks/gpu_alltoall.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -32,9 +32,9 @@ int main(int argc, char* argv[]) gpuMalloc((void**)(&recv_data_d), max_s*num_procs*sizeof(double)); gpuMemcpy(send_data_d, send_data.data(), max_s*num_procs*sizeof(double), gpuMemcpyHostToDevice); - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIX_Comm_topo_init(xcomm); + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_topo_init(xcomm); int local_rank; MPI_Comm_rank(xcomm->local_comm, &local_rank); gpuSetDevice(local_rank); @@ -390,7 +390,7 @@ int main(int argc, char* argv[]) } - MPIX_Comm_free(&xcomm); + MPIL_Comm_free(&xcomm); gpuFree(send_data_d); gpuFree(recv_data_d); diff --git a/benchmarks/microbenchmarks.cpp b/benchmarks/microbenchmarks.cpp index 7638d82a7..3f407af76 100644 --- a/benchmarks/microbenchmarks.cpp +++ b/benchmarks/microbenchmarks.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include diff --git a/benchmarks/p2p_alltoall.cpp b/benchmarks/p2p_alltoall.cpp index d3fd37aca..957710169 100644 --- a/benchmarks/p2p_alltoall.cpp +++ b/benchmarks/p2p_alltoall.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -24,8 +24,8 @@ int main(int argc, char* argv[]) std::vector std_alltoall(max_s*num_procs); std::vector loc_alltoall(max_s*num_procs); - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); for (int i = 0; i < max_i; i++) { @@ -43,7 +43,7 @@ int main(int argc, char* argv[]) MPI_DOUBLE, MPI_COMM_WORLD); - MPIX_Alltoall(local_data.data(), + MPIL_Alltoall(local_data.data(), s, MPI_DOUBLE, loc_alltoall.data(), @@ -91,7 +91,7 @@ int main(int argc, char* argv[]) // Time Loc Alltoall - MPIX_Alltoall(local_data.data(), + MPIL_Alltoall(local_data.data(), s, MPI_DOUBLE, loc_alltoall.data(), @@ -102,7 +102,7 @@ int main(int argc, char* argv[]) t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { - MPIX_Alltoall(local_data.data(), + MPIL_Alltoall(local_data.data(), s, MPI_DOUBLE, loc_alltoall.data(), @@ -112,11 +112,11 @@ int main(int argc, char* argv[]) } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIX_Alltoall Time %e\n", t0); + if (rank == 0) printf("MPIL_Alltoall Time %e\n", t0); } - MPIX_Comm_free(&xcomm); + MPIL_Comm_free(&xcomm); MPI_Finalize(); return 0; diff --git a/benchmarks/p2p_alltoallv.cpp b/benchmarks/p2p_alltoallv.cpp index e2d23f193..f97e2e17f 100644 --- a/benchmarks/p2p_alltoallv.cpp +++ b/benchmarks/p2p_alltoallv.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -31,8 +31,8 @@ int main(int argc, char* argv[]) send_displs[0] = 0; recv_displs[0] = 0; - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); for (int i = 0; i < max_i; i++) { @@ -60,7 +60,7 @@ int main(int argc, char* argv[]) MPI_DOUBLE, MPI_COMM_WORLD); - MPIX_Alltoallv(local_data.data(), + MPIL_Alltoallv(local_data.data(), send_sizes.data(), send_displs.data(), MPI_DOUBLE, @@ -114,7 +114,7 @@ int main(int argc, char* argv[]) // Time Loc Alltoallv - MPIX_Alltoallv(local_data.data(), + MPIL_Alltoallv(local_data.data(), send_sizes.data(), send_displs.data(), MPI_DOUBLE, @@ -127,7 +127,7 @@ int main(int argc, char* argv[]) t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { - MPIX_Alltoallv(local_data.data(), + MPIL_Alltoallv(local_data.data(), send_sizes.data(), send_displs.data(), MPI_DOUBLE, @@ -139,11 +139,11 @@ int main(int argc, char* argv[]) } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIX_Alltoallv Time %e\n", t0); + if (rank == 0) printf("MPIL_Alltoallv Time %e\n", t0); } - MPIX_Comm_free(&xcomm); + MPIL_Comm_free(&xcomm); MPI_Finalize(); return 0; diff --git a/cmake/locality_aware-config.cmake.in b/cmake/locality_aware-config.cmake.in index b489d0198..a0e668292 100644 --- a/cmake/locality_aware-config.cmake.in +++ b/cmake/locality_aware-config.cmake.in @@ -1,6 +1,6 @@ @PACKAGE_INIT@ -include("${CMAKE_CURRENT_LIST_DIR}/mpiadvanceTargets.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/locality_awareTargets.cmake") find_package(OpenMP) find_package(MPI REQUIRED) \ No newline at end of file diff --git a/src/collective/alltoall.c b/src/collective/alltoall.c index 51afdb9a7..efadcd813 100644 --- a/src/collective/alltoall.c +++ b/src/collective/alltoall.c @@ -8,7 +8,7 @@ #endif // Default alltoall is pairwise -AlltoallMethod mpix_alltoall_implementation = ALLTOALL_PAIRWISE; +AlltoallMethod mpil_alltoall_implementation = ALLTOALL_PAIRWISE; /************************************************** * Locality-Aware Point-to-Point Alltoall @@ -24,13 +24,13 @@ AlltoallMethod mpix_alltoall_implementation = ALLTOALL_PAIRWISE; * on-node so that each process holds * the correct final data *************************************************/ -int MPIX_Alltoall(const void* sendbuf, +int MPIL_Alltoall(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* mpi_comm) + MPIL_Comm* mpi_comm) { #ifdef GPU #ifdef GPU_AWARE @@ -46,7 +46,7 @@ int MPIX_Alltoall(const void* sendbuf, #endif alltoall_ftn method; - switch (mpix_alltoall_implementation) + switch (mpil_alltoall_implementation) { case ALLTOALL_PAIRWISE: method = alltoall_pairwise; @@ -158,10 +158,10 @@ int alltoall_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); return pairwise_helper(sendbuf, sendcount, @@ -243,10 +243,10 @@ int alltoall_nonblocking(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); return nonblocking_helper(sendbuf, sendcount, @@ -265,7 +265,7 @@ int alltoall_multileader(alltoall_helper_ftn f, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm, + MPIL_Comm* comm, int n_leaders) { int rank, num_procs; @@ -273,11 +273,11 @@ int alltoall_multileader(alltoall_helper_ftn f, MPI_Comm_size(comm->global_comm, &num_procs); int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } int ppn; @@ -309,7 +309,7 @@ int alltoall_multileader(alltoall_helper_ftn f, // If leader comm does not exist, create it if (comm->leader_comm == MPI_COMM_NULL) { - MPIX_Comm_leader_init(comm, procs_per_leader); + MPIL_Comm_leader_init(comm, procs_per_leader); } local_comm = comm->leader_comm; @@ -426,7 +426,7 @@ int alltoall_hierarchical(alltoall_helper_ftn f, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_multileader( f, sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm, 1); @@ -438,7 +438,7 @@ int alltoall_hierarchical_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_hierarchical(pairwise_helper, sendbuf, @@ -456,7 +456,7 @@ int alltoall_hierarchical_nonblocking(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_hierarchical(nonblocking_helper, sendbuf, @@ -474,7 +474,7 @@ int alltoall_multileader_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_multileader(pairwise_helper, sendbuf, @@ -493,7 +493,7 @@ int alltoall_multileader_nonblocking(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_multileader(nonblocking_helper, sendbuf, @@ -513,7 +513,7 @@ int alltoall_locality_aware_helper(alltoall_helper_ftn f, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm, + MPIL_Comm* comm, int groups_per_node, MPI_Comm local_comm, MPI_Comm group_comm, @@ -597,7 +597,7 @@ int alltoall_locality_aware(alltoall_helper_ftn f, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm, + MPIL_Comm* comm, int groups_per_node) { int rank, num_procs; @@ -605,11 +605,11 @@ int alltoall_locality_aware(alltoall_helper_ftn f, MPI_Comm_size(comm->global_comm, &num_procs); int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } int ppn; @@ -638,7 +638,7 @@ int alltoall_locality_aware(alltoall_helper_ftn f, if (comm->leader_comm == MPI_COMM_NULL) { - MPIX_Comm_leader_init(comm, procs_per_group); + MPIL_Comm_leader_init(comm, procs_per_group); } local_comm = comm->leader_comm; @@ -666,7 +666,7 @@ int alltoall_node_aware(alltoall_helper_ftn f, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_locality_aware( f, sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm, 1); @@ -678,7 +678,7 @@ int alltoall_node_aware_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_node_aware(pairwise_helper, sendbuf, @@ -696,7 +696,7 @@ int alltoall_node_aware_nonblocking(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_node_aware(nonblocking_helper, sendbuf, @@ -714,7 +714,7 @@ int alltoall_locality_aware_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_locality_aware(pairwise_helper, sendbuf, @@ -733,7 +733,7 @@ int alltoall_locality_aware_nonblocking(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_locality_aware(nonblocking_helper, sendbuf, @@ -753,18 +753,18 @@ int alltoall_multileader_locality(alltoall_helper_ftn f, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } int local_rank, ppn; @@ -778,7 +778,7 @@ int alltoall_multileader_locality(alltoall_helper_ftn f, { num_leaders_per_node = ppn; } - MPIX_Comm_leader_init(comm, ppn / num_leaders_per_node); + MPIL_Comm_leader_init(comm, ppn / num_leaders_per_node); } int procs_per_leader, leader_rank; @@ -940,7 +940,7 @@ int alltoall_multileader_locality_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_multileader_locality(pairwise_helper, sendbuf, @@ -958,7 +958,7 @@ int alltoall_multileader_locality_nonblocking(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return alltoall_multileader_locality(nonblocking_helper, sendbuf, @@ -977,7 +977,7 @@ int alltoall_pmpi(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return PMPI_Alltoall( sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm->global_comm); diff --git a/src/collective/alltoall.h b/src/collective/alltoall.h index 5c04c1fd7..3cfe1bbb2 100644 --- a/src/collective/alltoall.h +++ b/src/collective/alltoall.h @@ -6,7 +6,7 @@ #include #include "collective.h" -#include "communicator/mpix_comm.h" +#include "communicator/mpil_comm.h" #include "utils/utils.h" #ifdef __cplusplus @@ -30,10 +30,10 @@ enum AlltoallMethod ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING, ALLTOALL_PMPI }; -extern AlltoallMethod mpix_alltoall_implementation; +extern enum AlltoallMethod mpil_alltoall_implementation; typedef int (*alltoall_ftn)( - const void*, const int, MPI_Datatype, void*, const int, MPI_Datatype, MPIX_Comm*); + const void*, const int, MPI_Datatype, void*, const int, MPI_Datatype, MPIL_Comm*); typedef int (*alltoall_helper_ftn)(const void*, const int, MPI_Datatype, @@ -49,14 +49,14 @@ int alltoall_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_hierarchical_pairwise(const void* sendbuf, const int sendcount, @@ -64,28 +64,28 @@ int alltoall_hierarchical_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_hierarchical_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_multileader_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_multileader_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_node_aware_pairwise(const void* sendbuf, const int sendcount, @@ -93,28 +93,28 @@ int alltoall_node_aware_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_node_aware_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_locality_aware_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_locality_aware_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_multileader_locality_pairwise(const void* sendbuf, const int sendcount, @@ -122,14 +122,14 @@ int alltoall_multileader_locality_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoall_multileader_locality_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); // Calls underlying MPI implementation int alltoall_pmpi(const void* sendbuf, @@ -138,7 +138,7 @@ int alltoall_pmpi(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); #ifdef __cplusplus } diff --git a/src/collective/alltoallv.c b/src/collective/alltoallv.c index 0dbe47858..086874f6e 100644 --- a/src/collective/alltoallv.c +++ b/src/collective/alltoallv.c @@ -8,7 +8,7 @@ #endif // Default alltoallv is pairwise -AlltoallvMethod mpix_alltoallv_implementation = ALLTOALLV_PAIRWISE; +AlltoallvMethod mpil_alltoallv_implementation = ALLTOALLV_PAIRWISE; /************************************************** * Locality-Aware Point-to-Point Alltoallv @@ -29,7 +29,7 @@ AlltoallvMethod mpix_alltoallv_implementation = ALLTOALLV_PAIRWISE; * - Load balacing is too expensive for * non-persistent Alltoallv *************************************************/ -int MPIX_Alltoallv(const void* sendbuf, +int MPIL_Alltoallv(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, @@ -37,7 +37,7 @@ int MPIX_Alltoallv(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* mpi_comm) + MPIL_Comm* mpi_comm) { #ifdef GPU #ifdef GPU_AWARE @@ -54,7 +54,7 @@ int MPIX_Alltoallv(const void* sendbuf, #endif alltoallv_ftn method; - switch (mpix_alltoallv_implementation) + switch (mpil_alltoallv_implementation) { case ALLTOALLV_PAIRWISE: method = alltoallv_pairwise; @@ -95,14 +95,14 @@ int alltoallv_pairwise(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); MPI_Comm_size(comm->global_comm, &num_procs); int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; @@ -158,7 +158,7 @@ int alltoallv_nonblocking(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -178,7 +178,7 @@ int alltoallv_nonblocking(const void* sendbuf, } int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; @@ -241,7 +241,7 @@ int alltoallv_batch(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -263,7 +263,7 @@ int alltoallv_batch(const void* sendbuf, } int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); int ctr; int send_proc, recv_proc; @@ -337,7 +337,7 @@ int alltoallv_batch_async(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -359,7 +359,7 @@ int alltoallv_batch_async(const void* sendbuf, } int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; @@ -432,7 +432,7 @@ int alltoallv_pmpi(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return PMPI_Alltoallv(sendbuf, sendcounts, diff --git a/src/collective/alltoallv.h b/src/collective/alltoallv.h index ce3826a19..6943c4739 100644 --- a/src/collective/alltoallv.h +++ b/src/collective/alltoallv.h @@ -6,7 +6,7 @@ #include #include "collective.h" -#include "communicator/mpix_comm.h" +#include "communicator/mpil_comm.h" #ifdef __cplusplus extern "C" { @@ -21,7 +21,7 @@ enum AlltoallvMethod ALLTOALLV_BATCH_ASYNC, ALLTOALLV_PMPI }; -extern AlltoallvMethod mpix_alltoallv_implementation; +extern enum AlltoallvMethod mpil_alltoallv_implementation; typedef int (*alltoallv_ftn)(const void*, const int*, @@ -31,7 +31,7 @@ typedef int (*alltoallv_ftn)(const void*, const int*, const int*, MPI_Datatype, - MPIX_Comm*); + MPIL_Comm*); // Helper Functions int alltoallv_pairwise(const void* sendbuf, @@ -42,7 +42,7 @@ int alltoallv_pairwise(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoallv_nonblocking(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -51,7 +51,7 @@ int alltoallv_nonblocking(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoallv_batch(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -60,7 +60,7 @@ int alltoallv_batch(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoallv_batch_async(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -69,7 +69,7 @@ int alltoallv_batch_async(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int alltoallv_pmpi(const void* sendbuf, const int sendcounts[], @@ -79,7 +79,7 @@ int alltoallv_pmpi(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); #ifdef __cplusplus } diff --git a/src/collective/collective.h b/src/collective/collective.h index cc5356d1d..61692fd43 100644 --- a/src/collective/collective.h +++ b/src/collective/collective.h @@ -13,13 +13,13 @@ extern "C" { #endif -int MPIX_Alltoall(const void* sendbuf, +int MPIL_Alltoall(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int MPI_Alltoallv(const void* sendbuf, const int sendcounts[], @@ -30,7 +30,7 @@ int MPI_Alltoallv(const void* sendbuf, const int rdispls[], MPI_Datatype recvtype, MPI_Comm comm); -int MPIX_Alltoallv(const void* sendbuf, +int MPIL_Alltoallv(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, @@ -38,7 +38,7 @@ int MPIX_Alltoallv(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); #ifdef __cplusplus } diff --git a/src/collective/tests/CMakeLists.txt b/src/collective/tests/CMakeLists.txt index 5e3dccce1..308faa42d 100644 --- a/src/collective/tests/CMakeLists.txt +++ b/src/collective/tests/CMakeLists.txt @@ -14,7 +14,7 @@ foreach(src ${CPP_SOURCES}) add_executable(${exec_name} ${src}) # Link with MPI - target_link_libraries(${exec_name} mpi_advance ${MPI_LIBRARIES}) + target_link_libraries(${exec_name} locality_aware ${MPI_LIBRARIES}) # Add to CTEST add_test(NAME ${exec_name}_Test COMMAND ${MPIRUN} -n 16 ./${exec_name}) diff --git a/src/collective/tests/test_alltoall.cpp b/src/collective/tests/test_alltoall.cpp index a228a4b2c..9b47ab38f 100644 --- a/src/collective/tests/test_alltoall.cpp +++ b/src/collective/tests/test_alltoall.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -7,17 +7,17 @@ #include #include -void compare_alltoall_results(std::vector& pmpi, std::vector& mpix, int s) +void compare_alltoall_results(std::vector& pmpi, std::vector& mpil, int s) { int num_procs; MPI_Comm_size(MPI_COMM_WORLD, &num_procs); for (int j = 0; j < s*num_procs; j++) { - if (pmpi[j] != mpix[j]) + if (pmpi[j] != mpil[j]) { - fprintf(stderr, "MPIX Alltoall != PMPI, position %d, pmpi %d, mpix %d\n", - j, pmpi[j], mpix[j]); + fprintf(stderr, "MPIL Alltoall != PMPI, position %d, pmpi %d, mpil %d\n", + j, pmpi[j], mpil[j]); MPI_Abort(MPI_COMM_WORLD, -1); } } @@ -38,10 +38,10 @@ int main(int argc, char** argv) std::vector local_data(max_s*num_procs); std::vector pmpi_alltoall(max_s*num_procs); - std::vector mpix_alltoall(max_s*num_procs); + std::vector mpil_alltoall(max_s*num_procs); - MPIX_Comm* locality_comm; - MPIX_Comm_init(&locality_comm, MPI_COMM_WORLD); + MPIL_Comm* locality_comm; + MPIL_Comm_init(&locality_comm, MPI_COMM_WORLD); update_locality(locality_comm, 4); for (int i = 0; i < max_i; i++) @@ -63,151 +63,151 @@ int main(int argc, char** argv) MPI_COMM_WORLD); // Locality-Aware Pairwise Alltoall - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Standard Pairwise - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); alltoall_pairwise(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Standard Nonblocking alltoall_nonblocking(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Hierarchical + Pairwise - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); alltoall_hierarchical_pairwise(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Hierarchical + Nonblocking alltoall_hierarchical_nonblocking(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Multileader + Pairwise - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); alltoall_multileader_pairwise(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Multileader + Nonblocking alltoall_multileader_nonblocking(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Node Aware + Pairwise - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); alltoall_node_aware_pairwise(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Node Aware + Nonblocking alltoall_node_aware_nonblocking(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Locality Aware + Pairwise - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); alltoall_locality_aware_pairwise(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Locality Aware + Nonblocking alltoall_locality_aware_nonblocking(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Multileader + Locality Aware + Pairwise - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); alltoall_multileader_locality_pairwise(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); // Test Multileader + Locality Aware + Nonblocking alltoall_multileader_locality_nonblocking(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); } - MPIX_Comm_free(&locality_comm); + MPIL_Comm_free(&locality_comm); MPI_Finalize(); return 0; diff --git a/src/collective/tests/test_alltoall_enum.cpp b/src/collective/tests/test_alltoall_enum.cpp index 91c7c64b3..7822a5c15 100644 --- a/src/collective/tests/test_alltoall_enum.cpp +++ b/src/collective/tests/test_alltoall_enum.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -7,17 +7,17 @@ #include #include -void compare_alltoall_results(std::vector& pmpi, std::vector& mpix, int s) +void compare_alltoall_results(std::vector& pmpi, std::vector& mpil, int s) { int num_procs; MPI_Comm_size(MPI_COMM_WORLD, &num_procs); for (int j = 0; j < s*num_procs; j++) { - if (pmpi[j] != mpix[j]) + if (pmpi[j] != mpil[j]) { - fprintf(stderr, "MPIX Alltoall != PMPI, position %d, pmpi %d, mpix %d\n", - j, pmpi[j], mpix[j]); + fprintf(stderr, "MPIL Alltoall != PMPI, position %d, pmpi %d, mpil %d\n", + j, pmpi[j], mpil[j]); MPI_Abort(MPI_COMM_WORLD, -1); } } @@ -38,10 +38,10 @@ int main(int argc, char** argv) std::vector local_data(max_s*num_procs); std::vector pmpi_alltoall(max_s*num_procs); - std::vector mpix_alltoall(max_s*num_procs); + std::vector mpil_alltoall(max_s*num_procs); - MPIX_Comm* locality_comm; - MPIX_Comm_init(&locality_comm, MPI_COMM_WORLD); + MPIL_Comm* locality_comm; + MPIL_Comm_init(&locality_comm, MPI_COMM_WORLD); update_locality(locality_comm, 4); for (int i = 0; i < max_i; i++) @@ -62,165 +62,165 @@ int main(int argc, char** argv) MPI_INT, MPI_COMM_WORLD); - mpix_alltoall_implementation = ALLTOALL_PAIRWISE; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_NONBLOCKING; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_HIERARCHICAL_PAIRWISE; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_HIERARCHICAL_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_HIERARCHICAL_NONBLOCKING; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_HIERARCHICAL_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_MULTILEADER_PAIRWISE; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_MULTILEADER_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_MULTILEADER_NONBLOCKING; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_MULTILEADER_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_NODE_AWARE_PAIRWISE; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_NODE_AWARE_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_NODE_AWARE_NONBLOCKING; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_NODE_AWARE_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_LOCALITY_AWARE_PAIRWISE; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_LOCALITY_AWARE_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_LOCALITY_AWARE_NONBLOCKING; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_LOCALITY_AWARE_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - mpix_alltoall_implementation = ALLTOALL_PMPI; - std::fill(mpix_alltoall.begin(), mpix_alltoall.end(), 0); - MPIX_Alltoall(local_data.data(), + mpil_alltoall_implementation = ALLTOALL_PMPI; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), s, MPI_INT, - mpix_alltoall.data(), + mpil_alltoall.data(), s, MPI_INT, locality_comm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); } - MPIX_Comm_free(&locality_comm); + MPIL_Comm_free(&locality_comm); MPI_Finalize(); diff --git a/src/collective/tests/test_alltoallv.cpp b/src/collective/tests/test_alltoallv.cpp index e25df16fb..5f9964d68 100644 --- a/src/collective/tests/test_alltoallv.cpp +++ b/src/collective/tests/test_alltoallv.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -7,17 +7,17 @@ #include #include -void compare_alltoallv_results(std::vector& pmpi, std::vector& mpix, int s) +void compare_alltoallv_results(std::vector& pmpi, std::vector& mpil, int s) { int num_procs; MPI_Comm_size(MPI_COMM_WORLD, &num_procs); for (int j = 0; j < s*num_procs; j++) { - if (pmpi[j] != mpix[j]) + if (pmpi[j] != mpil[j]) { - fprintf(stderr, "MPIX Alltoallv != PMPI, position %d, pmpi %d, mpix %d\n", - j, pmpi[j], mpix[j]); + fprintf(stderr, "MPIL Alltoallv != PMPI, position %d, pmpi %d, mpil %d\n", + j, pmpi[j], mpil[j]); MPI_Abort(MPI_COMM_WORLD, -1); } } @@ -38,13 +38,13 @@ int main(int argc, char** argv) std::vector local_data(max_s*num_procs); std::vector pmpi_alltoallv(max_s*num_procs); - std::vector mpix_alltoallv(max_s*num_procs); + std::vector mpil_alltoallv(max_s*num_procs); std::vector sizes(num_procs); std::vector displs(num_procs+1); - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); update_locality(xcomm, 4); for (int i = 0; i < max_i; i++) @@ -72,69 +72,69 @@ int main(int argc, char** argv) MPI_INT, MPI_COMM_WORLD); - std::fill(mpix_alltoallv.begin(), mpix_alltoallv.end(), 0); - MPIX_Alltoallv(local_data.data(), + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), sizes.data(), displs.data(), MPI_INT, - mpix_alltoallv.data(), + mpil_alltoallv.data(), sizes.data(), displs.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpix_alltoallv, s); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - std::fill(mpix_alltoallv.begin(), mpix_alltoallv.end(), 0); + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); alltoallv_pairwise(local_data.data(), sizes.data(), displs.data(), MPI_INT, - mpix_alltoallv.data(), + mpil_alltoallv.data(), sizes.data(), displs.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpix_alltoallv, s); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - std::fill(mpix_alltoallv.begin(), mpix_alltoallv.end(), 0); + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); alltoallv_nonblocking(local_data.data(), sizes.data(), displs.data(), MPI_INT, - mpix_alltoallv.data(), + mpil_alltoallv.data(), sizes.data(), displs.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpix_alltoallv, s); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - std::fill(mpix_alltoallv.begin(), mpix_alltoallv.end(), 0); + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); alltoallv_batch(local_data.data(), sizes.data(), displs.data(), MPI_INT, - mpix_alltoallv.data(), + mpil_alltoallv.data(), sizes.data(), displs.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpix_alltoallv, s); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - std::fill(mpix_alltoallv.begin(), mpix_alltoallv.end(), 0); + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); alltoallv_batch_async(local_data.data(), sizes.data(), displs.data(), MPI_INT, - mpix_alltoallv.data(), + mpil_alltoallv.data(), sizes.data(), displs.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpix_alltoallv, s); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); } - MPIX_Comm_free(&xcomm); + MPIL_Comm_free(&xcomm); MPI_Finalize(); diff --git a/src/collective/tests/test_alltoallv_enum.cpp b/src/collective/tests/test_alltoallv_enum.cpp index c4eaf3cfa..6d797077f 100644 --- a/src/collective/tests/test_alltoallv_enum.cpp +++ b/src/collective/tests/test_alltoallv_enum.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -7,17 +7,17 @@ #include #include -void compare_alltoallv_results(std::vector& pmpi, std::vector& mpix, int s) +void compare_alltoallv_results(std::vector& pmpi, std::vector& mpil, int s) { int num_procs; MPI_Comm_size(MPI_COMM_WORLD, &num_procs); for (int j = 0; j < s*num_procs; j++) { - if (pmpi[j] != mpix[j]) + if (pmpi[j] != mpil[j]) { - fprintf(stderr, "MPIX Alltoallv != PMPI, position %d, pmpi %d, mpix %d\n", - j, pmpi[j], mpix[j]); + fprintf(stderr, "MPIL Alltoallv != PMPI, position %d, pmpi %d, mpil %d\n", + j, pmpi[j], mpil[j]); MPI_Abort(MPI_COMM_WORLD, -1); } } @@ -38,13 +38,13 @@ int main(int argc, char** argv) std::vector local_data(max_s*num_procs); std::vector pmpi_alltoallv(max_s*num_procs); - std::vector mpix_alltoallv(max_s*num_procs); + std::vector mpil_alltoallv(max_s*num_procs); std::vector sizes(num_procs); std::vector displs(num_procs+1); - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); update_locality(xcomm, 4); for (int i = 0; i < max_i; i++) @@ -72,75 +72,75 @@ int main(int argc, char** argv) MPI_INT, MPI_COMM_WORLD); - mpix_alltoallv_implementation = ALLTOALLV_PAIRWISE; - std::fill(mpix_alltoallv.begin(), mpix_alltoallv.end(), 0); - MPIX_Alltoallv(local_data.data(), + mpil_alltoallv_implementation = ALLTOALLV_PAIRWISE; + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), sizes.data(), displs.data(), MPI_INT, - mpix_alltoallv.data(), + mpil_alltoallv.data(), sizes.data(), displs.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpix_alltoallv, s); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - mpix_alltoallv_implementation = ALLTOALLV_NONBLOCKING; - std::fill(mpix_alltoallv.begin(), mpix_alltoallv.end(), 0); - MPIX_Alltoallv(local_data.data(), + mpil_alltoallv_implementation = ALLTOALLV_NONBLOCKING; + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), sizes.data(), displs.data(), MPI_INT, - mpix_alltoallv.data(), + mpil_alltoallv.data(), sizes.data(), displs.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpix_alltoallv, s); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - mpix_alltoallv_implementation = ALLTOALLV_BATCH; - std::fill(mpix_alltoallv.begin(), mpix_alltoallv.end(), 0); - MPIX_Alltoallv(local_data.data(), + mpil_alltoallv_implementation = ALLTOALLV_BATCH; + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), sizes.data(), displs.data(), MPI_INT, - mpix_alltoallv.data(), + mpil_alltoallv.data(), sizes.data(), displs.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpix_alltoallv, s); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - mpix_alltoallv_implementation = ALLTOALLV_BATCH_ASYNC; - std::fill(mpix_alltoallv.begin(), mpix_alltoallv.end(), 0); - MPIX_Alltoallv(local_data.data(), + mpil_alltoallv_implementation = ALLTOALLV_BATCH_ASYNC; + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), sizes.data(), displs.data(), MPI_INT, - mpix_alltoallv.data(), + mpil_alltoallv.data(), sizes.data(), displs.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpix_alltoallv, s); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - mpix_alltoallv_implementation = ALLTOALLV_PMPI; - std::fill(mpix_alltoallv.begin(), mpix_alltoallv.end(), 0); - MPIX_Alltoallv(local_data.data(), + mpil_alltoallv_implementation = ALLTOALLV_PMPI; + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), sizes.data(), displs.data(), MPI_INT, - mpix_alltoallv.data(), + mpil_alltoallv.data(), sizes.data(), displs.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpix_alltoallv, s); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); } - MPIX_Comm_free(&xcomm); + MPIL_Comm_free(&xcomm); MPI_Finalize(); diff --git a/src/collective/tests/test_suitesparse_alltoallv.cpp b/src/collective/tests/test_suitesparse_alltoallv.cpp index 6582f54ad..0465aaa61 100644 --- a/src/collective/tests/test_suitesparse_alltoallv.cpp +++ b/src/collective/tests/test_suitesparse_alltoallv.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -11,14 +11,14 @@ #include "tests/sparse_mat.hpp" #include "tests/par_binary_IO.hpp" -void compare_alltoallv_results(std::vector& pmpi, std::vector& mpix, int s) +void compare_alltoallv_results(std::vector& pmpi, std::vector& mpil, int s) { for (int i = 0; i < s; i++) { - if (pmpi[i] != mpix[i]) + if (pmpi[i] != mpil[i]) { - fprintf(stderr, "MPIX Alltoallv != PMPI, position %d, pmpi %d, mpix %d\n", - i, pmpi[i], mpix[i]); + fprintf(stderr, "MPIL Alltoallv != PMPI, position %d, pmpi %d, mpil %d\n", + i, pmpi[i], mpil[i]); MPI_Abort(MPI_COMM_WORLD, 1); } } @@ -31,8 +31,8 @@ void test_matrix(const char* filename) MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); update_locality(xcomm, 4); // Read suitesparse matrix @@ -85,9 +85,9 @@ void test_matrix(const char* filename) } std::vector pmpi_recv_vals(A.recv_comm.size_msgs); - std::vector mpix_recv_vals(A.recv_comm.size_msgs); + std::vector mpil_recv_vals(A.recv_comm.size_msgs); - communicate(A, send_vals, mpix_recv_vals, MPI_INT); + communicate(A, send_vals, mpil_recv_vals, MPI_INT); PMPI_Alltoallv(alltoallv_send_vals.data(), sendcounts.data(), @@ -98,70 +98,70 @@ void test_matrix(const char* filename) rdispls.data(), MPI_INT, MPI_COMM_WORLD); - compare_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Alltoallv(alltoallv_send_vals.data(), + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Alltoallv(alltoallv_send_vals.data(), sendcounts.data(), sdispls.data(), MPI_INT, - mpix_recv_vals.data(), + mpil_recv_vals.data(), recvcounts.data(), rdispls.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); alltoallv_pairwise(alltoallv_send_vals.data(), sendcounts.data(), sdispls.data(), MPI_INT, - mpix_recv_vals.data(), + mpil_recv_vals.data(), recvcounts.data(), rdispls.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); alltoallv_nonblocking(alltoallv_send_vals.data(), sendcounts.data(), sdispls.data(), MPI_INT, - mpix_recv_vals.data(), + mpil_recv_vals.data(), recvcounts.data(), rdispls.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); alltoallv_batch(alltoallv_send_vals.data(), sendcounts.data(), sdispls.data(), MPI_INT, - mpix_recv_vals.data(), + mpil_recv_vals.data(), recvcounts.data(), rdispls.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); alltoallv_batch_async(alltoallv_send_vals.data(), sendcounts.data(), sdispls.data(), MPI_INT, - mpix_recv_vals.data(), + mpil_recv_vals.data(), recvcounts.data(), rdispls.data(), MPI_INT, xcomm); - compare_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - MPIX_Comm_free(&xcomm); + MPIL_Comm_free(&xcomm); } int main(int argc, char** argv) diff --git a/src/communicator/locality_comm.c b/src/communicator/locality_comm.c index 92dab2aa2..d881efb0c 100644 --- a/src/communicator/locality_comm.c +++ b/src/communicator/locality_comm.c @@ -1,23 +1,23 @@ #include "locality_comm.h" void init_locality_comm(LocalityComm** locality_ptr, - MPIX_Comm* mpix_comm, + MPIL_Comm* mpix_comm, MPI_Datatype sendtype, MPI_Datatype recvtype) { LocalityComm* locality = (LocalityComm*)malloc(sizeof(LocalityComm)); int tag; - MPIX_Comm_tag(mpix_comm, &tag); + MPIL_Comm_tag(mpix_comm, &tag); init_comm_pkg(&(locality->local_L_comm), sendtype, recvtype, tag); - MPIX_Comm_tag(mpix_comm, &tag); + MPIL_Comm_tag(mpix_comm, &tag); init_comm_pkg(&(locality->local_S_comm), sendtype, recvtype, tag); - MPIX_Comm_tag(mpix_comm, &tag); + MPIL_Comm_tag(mpix_comm, &tag); init_comm_pkg(&(locality->local_R_comm), recvtype, recvtype, tag); - MPIX_Comm_tag(mpix_comm, &tag); + MPIL_Comm_tag(mpix_comm, &tag); init_comm_pkg(&(locality->global_comm), recvtype, recvtype, tag); locality->communicators = mpix_comm; diff --git a/src/communicator/locality_comm.h b/src/communicator/locality_comm.h index 90558b5db..31f4cbfbf 100644 --- a/src/communicator/locality_comm.h +++ b/src/communicator/locality_comm.h @@ -4,7 +4,7 @@ #include #include "comm_pkg.h" -#include "mpix_comm.h" +#include "mpil_comm.h" // Declarations of C++ methods #ifdef __cplusplus @@ -18,11 +18,11 @@ typedef struct _LocalityComm CommPkg* local_R_comm; CommPkg* global_comm; - MPIX_Comm* communicators; + MPIL_Comm* communicators; } LocalityComm; void init_locality_comm(LocalityComm** locality_ptr, - MPIX_Comm* comm, + MPIL_Comm* comm, MPI_Datatype sendtype, MPI_Datatype recvtype); void finalize_locality_comm(LocalityComm* locality); diff --git a/src/communicator/mpix_comm.c b/src/communicator/mpil_comm.c similarity index 88% rename from src/communicator/mpix_comm.c rename to src/communicator/mpil_comm.c index fb1b18375..0f516db85 100644 --- a/src/communicator/mpix_comm.c +++ b/src/communicator/mpil_comm.c @@ -1,12 +1,12 @@ -#include "mpix_comm.h" +#include "mpil_comm.h" -int MPIX_Comm_init(MPIX_Comm** xcomm_ptr, MPI_Comm global_comm) +int MPIL_Comm_init(MPIL_Comm** xcomm_ptr, MPI_Comm global_comm) { int rank, num_procs; MPI_Comm_rank(global_comm, &rank); MPI_Comm_size(global_comm, &num_procs); - MPIX_Comm* xcomm = (MPIX_Comm*)malloc(sizeof(MPIX_Comm)); + MPIL_Comm* xcomm = (MPIL_Comm*)malloc(sizeof(MPIL_Comm)); xcomm->global_comm = global_comm; xcomm->local_comm = MPI_COMM_NULL; @@ -43,7 +43,7 @@ int MPIX_Comm_init(MPIX_Comm** xcomm_ptr, MPI_Comm global_comm) return MPI_SUCCESS; } -int MPIX_Comm_topo_init(MPIX_Comm* xcomm) +int MPIL_Comm_topo_init(MPIL_Comm* xcomm) { int rank, num_procs; MPI_Comm_rank(xcomm->global_comm, &rank); @@ -98,7 +98,7 @@ int MPIX_Comm_topo_init(MPIX_Comm* xcomm) return MPI_SUCCESS; } -int MPIX_Comm_leader_init(MPIX_Comm* xcomm, int procs_per_leader) +int MPIL_Comm_leader_init(MPIL_Comm* xcomm, int procs_per_leader) { int rank, num_procs; MPI_Comm_rank(xcomm->global_comm, &rank); @@ -114,7 +114,7 @@ int MPIX_Comm_leader_init(MPIX_Comm* xcomm, int procs_per_leader) if (xcomm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(xcomm); + MPIL_Comm_topo_init(xcomm); } MPI_Comm_split(xcomm->local_comm, leader_rank, rank, &(xcomm->leader_local_comm)); @@ -122,12 +122,12 @@ int MPIX_Comm_leader_init(MPIX_Comm* xcomm, int procs_per_leader) return MPI_SUCCESS; } -int MPIX_Comm_device_init(MPIX_Comm* xcomm) +int MPIL_Comm_device_init(MPIL_Comm* xcomm) { #ifdef GPU if (xcomm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(xcomm); + MPIL_Comm_topo_init(xcomm); } int local_rank, ierr; @@ -145,7 +145,7 @@ int MPIX_Comm_device_init(MPIX_Comm* xcomm) return MPI_SUCCESS; } -int MPIX_Comm_win_init(MPIX_Comm* xcomm, int bytes, int type_bytes) +int MPIL_Comm_win_init(MPIL_Comm* xcomm, int bytes, int type_bytes) { int rank, num_procs; MPI_Comm_rank(xcomm->global_comm, &rank); @@ -164,7 +164,7 @@ int MPIX_Comm_win_init(MPIX_Comm* xcomm, int bytes, int type_bytes) return MPI_SUCCESS; } -int MPIX_Comm_req_resize(MPIX_Comm* xcomm, int n) +int MPIL_Comm_req_resize(MPIL_Comm* xcomm, int n) { if (n <= 0) { @@ -178,7 +178,7 @@ int MPIX_Comm_req_resize(MPIX_Comm* xcomm, int n) return MPI_SUCCESS; } -int MPIX_Comm_tag(MPIX_Comm* xcomm, int* tag) +int MPIL_Comm_tag(MPIL_Comm* xcomm, int* tag) { *tag = xcomm->tag; xcomm->tag = ((xcomm->tag + 1) % xcomm->max_tag); @@ -186,9 +186,9 @@ int MPIX_Comm_tag(MPIX_Comm* xcomm, int* tag) return MPI_SUCCESS; } -int MPIX_Comm_free(MPIX_Comm** xcomm_ptr) +int MPIL_Comm_free(MPIL_Comm** xcomm_ptr) { - MPIX_Comm* xcomm = *xcomm_ptr; + MPIL_Comm* xcomm = *xcomm_ptr; if (xcomm->n_requests > 0) { @@ -200,17 +200,17 @@ int MPIX_Comm_free(MPIX_Comm** xcomm_ptr) MPI_Comm_free(&(xcomm->neighbor_comm)); } - MPIX_Comm_topo_free(xcomm); - MPIX_Comm_leader_free(xcomm); - MPIX_Comm_win_free(xcomm); - MPIX_Comm_device_free(xcomm); + MPIL_Comm_topo_free(xcomm); + MPIL_Comm_leader_free(xcomm); + MPIL_Comm_win_free(xcomm); + MPIL_Comm_device_free(xcomm); free(xcomm); return MPI_SUCCESS; } -int MPIX_Comm_topo_free(MPIX_Comm* xcomm) +int MPIL_Comm_topo_free(MPIL_Comm* xcomm) { if (xcomm->local_comm != MPI_COMM_NULL) { @@ -237,7 +237,7 @@ int MPIX_Comm_topo_free(MPIX_Comm* xcomm) return MPI_SUCCESS; } -int MPIX_Comm_leader_free(MPIX_Comm* xcomm) +int MPIL_Comm_leader_free(MPIL_Comm* xcomm) { if (xcomm->leader_comm != MPI_COMM_NULL) { @@ -255,7 +255,7 @@ int MPIX_Comm_leader_free(MPIX_Comm* xcomm) return MPI_SUCCESS; } -int MPIX_Comm_win_free(MPIX_Comm* xcomm) +int MPIL_Comm_win_free(MPIL_Comm* xcomm) { int rank, num_procs; MPI_Comm_rank(xcomm->global_comm, &rank); @@ -275,7 +275,7 @@ int MPIX_Comm_win_free(MPIX_Comm* xcomm) return MPI_SUCCESS; } -int MPIX_Comm_device_free(MPIX_Comm* xcomm) +int MPIL_Comm_device_free(MPIL_Comm* xcomm) { #ifdef GPU int ierr = gpuSuccess; @@ -290,24 +290,24 @@ int MPIX_Comm_device_free(MPIX_Comm* xcomm) } /**** Topology Functions ****/ -int get_node(const MPIX_Comm* data, const int proc) +int get_node(const MPIL_Comm* data, const int proc) { return data->global_rank_to_node[proc]; } -int get_local_proc(const MPIX_Comm* data, const int proc) +int get_local_proc(const MPIL_Comm* data, const int proc) { return data->global_rank_to_local[proc]; } -int get_global_proc(const MPIX_Comm* data, const int node, const int local_proc) +int get_global_proc(const MPIL_Comm* data, const int node, const int local_proc) { return data->ordered_global_ranks[local_proc + (node * data->ppn)]; } // For testing purposes // Manually update aggregation size (ppn) -void update_locality(MPIX_Comm* xcomm, int ppn) +void update_locality(MPIL_Comm* xcomm, int ppn) { int rank, num_procs; MPI_Comm_rank(xcomm->global_comm, &rank); diff --git a/src/communicator/mpix_comm.h b/src/communicator/mpil_comm.h similarity index 57% rename from src/communicator/mpix_comm.h rename to src/communicator/mpil_comm.h index c91ed4dab..8937c632f 100644 --- a/src/communicator/mpix_comm.h +++ b/src/communicator/mpil_comm.h @@ -14,7 +14,7 @@ extern "C" { #endif -typedef struct _MPIX_Comm +typedef struct _MPIL_Comm { MPI_Comm global_comm; @@ -55,33 +55,33 @@ typedef struct _MPIX_Comm int rank_gpu; gpuStream_t proc_stream; #endif -} MPIX_Comm; +} MPIL_Comm; -int MPIX_Comm_init(MPIX_Comm** xcomm_ptr, MPI_Comm global_comm); -int MPIX_Comm_free(MPIX_Comm** xcomm_ptr); +int MPIL_Comm_init(MPIL_Comm** xcomm_ptr, MPI_Comm global_comm); +int MPIL_Comm_free(MPIL_Comm** xcomm_ptr); -int MPIX_Comm_topo_init(MPIX_Comm* xcomm); -int MPIX_Comm_topo_free(MPIX_Comm* xcomm); +int MPIL_Comm_topo_init(MPIL_Comm* xcomm); +int MPIL_Comm_topo_free(MPIL_Comm* xcomm); -int MPIX_Comm_leader_init(MPIX_Comm* xcomm, int procs_per_leader); -int MPIX_Comm_leader_free(MPIX_Comm* xcomm); +int MPIL_Comm_leader_init(MPIL_Comm* xcomm, int procs_per_leader); +int MPIL_Comm_leader_free(MPIL_Comm* xcomm); -int MPIX_Comm_win_init(MPIX_Comm* xcomm, int bytes, int type_bytes); -int MPIX_Comm_win_free(MPIX_Comm* xcomm); +int MPIL_Comm_win_init(MPIL_Comm* xcomm, int bytes, int type_bytes); +int MPIL_Comm_win_free(MPIL_Comm* xcomm); -int MPIX_Comm_device_init(MPIX_Comm* xcomm); -int MPIX_Comm_device_free(MPIX_Comm* xcomm); +int MPIL_Comm_device_init(MPIL_Comm* xcomm); +int MPIL_Comm_device_free(MPIL_Comm* xcomm); -int MPIX_Comm_req_resize(MPIX_Comm* xcomm, int n); +int MPIL_Comm_req_resize(MPIL_Comm* xcomm, int n); -int MPIX_Comm_tag(MPIX_Comm* comm, int* tag); +int MPIL_Comm_tag(MPIL_Comm* comm, int* tag); -int get_node(const MPIX_Comm* data, const int proc); -int get_local_proc(const MPIX_Comm* data, const int proc); -int get_global_proc(const MPIX_Comm* data, const int node, const int local_proc); +int get_node(const MPIL_Comm* data, const int proc); +int get_local_proc(const MPIL_Comm* data, const int proc); +int get_global_proc(const MPIL_Comm* data, const int node, const int local_proc); // For testing purposes (manually set PPN) -void update_locality(MPIX_Comm* xcomm, int ppn); +void update_locality(MPIL_Comm* xcomm, int ppn); #ifdef __cplusplus } diff --git a/src/heterogeneous/gpu_alltoall.c b/src/heterogeneous/gpu_alltoall.c index 2ac43c397..dceb9b214 100644 --- a/src/heterogeneous/gpu_alltoall.c +++ b/src/heterogeneous/gpu_alltoall.c @@ -11,7 +11,7 @@ int gpu_aware_alltoall(alltoall_ftn f, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int num_procs; MPI_Comm_size(comm->global_comm, &num_procs); @@ -42,7 +42,7 @@ int gpu_aware_alltoall_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return gpu_aware_alltoall(alltoall_pairwise, sendbuf, @@ -60,7 +60,7 @@ int gpu_aware_alltoall_nonblocking(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return gpu_aware_alltoall(alltoall_nonblocking, sendbuf, @@ -79,7 +79,7 @@ int copy_to_cpu_alltoall(alltoall_ftn f, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int ierr = 0; @@ -119,7 +119,7 @@ int copy_to_cpu_alltoall_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return copy_to_cpu_alltoall(alltoall_pairwise, sendbuf, @@ -137,7 +137,7 @@ int copy_to_cpu_alltoall_nonblocking(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return copy_to_cpu_alltoall(alltoall_nonblocking, sendbuf, @@ -156,7 +156,7 @@ int threaded_alltoall_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int ierr = 0; @@ -187,7 +187,7 @@ int threaded_alltoall_pairwise(const void* sendbuf, { MPI_Status status; int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; @@ -253,7 +253,7 @@ int threaded_alltoall_nonblocking(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int num_procs, rank; MPI_Comm_rank(comm->global_comm, &rank); @@ -281,7 +281,7 @@ int threaded_alltoall_nonblocking(const void* sendbuf, #pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) { int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; diff --git a/src/heterogeneous/gpu_alltoall.h b/src/heterogeneous/gpu_alltoall.h index 74bf2bd3f..af824f3ae 100644 --- a/src/heterogeneous/gpu_alltoall.h +++ b/src/heterogeneous/gpu_alltoall.h @@ -15,7 +15,7 @@ int gpu_aware_alltoall(alltoall_ftn f, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int copy_to_cpu_alltoall(alltoall_ftn f, const void* sendbuf, const int sendcount, @@ -23,7 +23,7 @@ int copy_to_cpu_alltoall(alltoall_ftn f, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int gpu_aware_alltoall_pairwise(const void* sendbuf, const int sendcount, @@ -31,28 +31,28 @@ int gpu_aware_alltoall_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int gpu_aware_alltoall_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int copy_to_cpu_alltoall_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int copy_to_cpu_alltoall_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); #ifdef OPENMP #include @@ -62,7 +62,7 @@ int threaded_alltoall_pairwise(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int threaded_alltoall_nonblocking(const void* sendbuf, const int sendcount, @@ -70,7 +70,7 @@ int threaded_alltoall_nonblocking(const void* sendbuf, void* recvbuf, const int recvcount, MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); #endif #ifdef __cplusplus diff --git a/src/heterogeneous/gpu_alltoallv.c b/src/heterogeneous/gpu_alltoallv.c index ae3f570c3..936ac9a03 100644 --- a/src/heterogeneous/gpu_alltoallv.c +++ b/src/heterogeneous/gpu_alltoallv.c @@ -13,7 +13,7 @@ int gpu_aware_alltoallv(alltoallv_ftn f, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return f(sendbuf, sendcounts, @@ -34,7 +34,7 @@ int gpu_aware_alltoallv_pairwise(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return gpu_aware_alltoallv(alltoallv_pairwise, sendbuf, @@ -56,7 +56,7 @@ int gpu_aware_alltoallv_nonblocking(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return gpu_aware_alltoallv(alltoallv_nonblocking, sendbuf, @@ -78,7 +78,7 @@ int gpu_aware_alltoallv_batch(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return gpu_aware_alltoallv(alltoallv_batch, sendbuf, @@ -100,7 +100,7 @@ int gpu_aware_alltoallv_batch_async(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return gpu_aware_alltoallv(alltoallv_batch_async, sendbuf, @@ -123,7 +123,7 @@ int copy_to_cpu_alltoallv(alltoallv_ftn f, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int ierr = 0; @@ -181,7 +181,7 @@ int copy_to_cpu_alltoallv_pairwise(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return copy_to_cpu_alltoallv(alltoallv_pairwise, sendbuf, @@ -203,7 +203,7 @@ int copy_to_cpu_alltoallv_nonblocking(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return copy_to_cpu_alltoallv(alltoallv_nonblocking, sendbuf, @@ -225,7 +225,7 @@ int copy_to_cpu_alltoallv_batch(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return copy_to_cpu_alltoallv(alltoallv_batch, sendbuf, @@ -247,7 +247,7 @@ int copy_to_cpu_alltoallv_batch_async(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { return copy_to_cpu_alltoallv(alltoallv_batch_async, sendbuf, @@ -270,7 +270,7 @@ int threaded_alltoallv_pairwise(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int ierr = 0; @@ -309,7 +309,7 @@ int threaded_alltoallv_pairwise(const void* sendbuf, { MPI_Status status; int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; @@ -377,7 +377,7 @@ int threaded_alltoallv_nonblocking(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { int ierr = 0; @@ -415,7 +415,7 @@ int threaded_alltoallv_nonblocking(const void* sendbuf, #pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) { int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; diff --git a/src/heterogeneous/gpu_alltoallv.h b/src/heterogeneous/gpu_alltoallv.h index 2f1a9ba48..d25a7ebb9 100644 --- a/src/heterogeneous/gpu_alltoallv.h +++ b/src/heterogeneous/gpu_alltoallv.h @@ -17,7 +17,7 @@ int gpu_aware_alltoallv(alltoallv_ftn f, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int copy_to_cpu_alltoallv(alltoallv_ftn f, const void* sendbuf, @@ -28,7 +28,7 @@ int copy_to_cpu_alltoallv(alltoallv_ftn f, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int gpu_aware_alltoallv_pairwise(const void* sendbuf, const int sendcounts[], @@ -38,7 +38,7 @@ int gpu_aware_alltoallv_pairwise(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int gpu_aware_alltoallv_nonblocking(const void* sendbuf, const int sendcounts[], @@ -48,7 +48,7 @@ int gpu_aware_alltoallv_nonblocking(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int gpu_aware_alltoallv_batch(const void* sendbuf, const int sendcounts[], @@ -58,7 +58,7 @@ int gpu_aware_alltoallv_batch(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int gpu_aware_alltoallv_batch_async(const void* sendbuf, const int sendcounts[], @@ -68,7 +68,7 @@ int gpu_aware_alltoallv_batch_async(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int copy_to_cpu_alltoallv_pairwise(const void* sendbuf, const int sendcounts[], @@ -78,7 +78,7 @@ int copy_to_cpu_alltoallv_pairwise(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int copy_to_cpu_alltoallv_nonblocking(const void* sendbuf, const int sendcounts[], @@ -88,7 +88,7 @@ int copy_to_cpu_alltoallv_nonblocking(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int copy_to_cpu_alltoallv_batch(const void* sendbuf, const int sendcounts[], @@ -98,7 +98,7 @@ int copy_to_cpu_alltoallv_batch(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int copy_to_cpu_alltoallv_batch_async(const void* sendbuf, const int sendcounts[], @@ -108,7 +108,7 @@ int copy_to_cpu_alltoallv_batch_async(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); #ifdef OPENMP #include @@ -120,7 +120,7 @@ int threaded_alltoallv_pairwise(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int threaded_alltoallv_nonblocking(const void* sendbuf, const int sendcounts[], @@ -130,7 +130,7 @@ int threaded_alltoallv_nonblocking(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); #endif #ifdef __cplusplus diff --git a/src/heterogeneous/tests/CMakeLists.txt b/src/heterogeneous/tests/CMakeLists.txt index 2e7cf2a84..13363b538 100644 --- a/src/heterogeneous/tests/CMakeLists.txt +++ b/src/heterogeneous/tests/CMakeLists.txt @@ -14,7 +14,7 @@ foreach(src ${CPP_SOURCES}) add_executable(${exec_name} ${src}) # Link with MPI - target_link_libraries(${exec_name} mpi_advance ${MPI_LIBRARIES} ${OpenMP_CXX_LIBRARIES}) + target_link_libraries(${exec_name} locality_aware ${MPI_LIBRARIES} ${OpenMP_CXX_LIBRARIES}) # Add to CTEST add_test(NAME ${exec_name}_Test COMMAND ${MPIRUN} -n 16 ./${exec_name}) diff --git a/src/heterogeneous/tests/test_gpu_alltoall.cpp b/src/heterogeneous/tests/test_gpu_alltoall.cpp index fb95a5a37..ea7ed5be9 100644 --- a/src/heterogeneous/tests/test_gpu_alltoall.cpp +++ b/src/heterogeneous/tests/test_gpu_alltoall.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -40,9 +40,9 @@ int main(int argc, char** argv) std::vector mpix_alltoall(max_s*num_procs); std::vector device_data(max_s*num_procs); - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIX_Comm_device_init(xcomm); + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_device_init(xcomm); int n_gpus; gpuGetDeviceCount(&n_gpus); @@ -174,7 +174,7 @@ int main(int argc, char** argv) gpuFree(local_data_d); gpuFree(alltoall_d); - MPIX_Comm_free(&xcomm); + MPIL_Comm_free(&xcomm); MPI_Finalize(); return 0; diff --git a/src/heterogeneous/tests/test_gpu_alltoallv.cpp b/src/heterogeneous/tests/test_gpu_alltoallv.cpp index 117edb0b1..25a5fdcef 100644 --- a/src/heterogeneous/tests/test_gpu_alltoallv.cpp +++ b/src/heterogeneous/tests/test_gpu_alltoallv.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -30,9 +30,9 @@ void test_matrix(const char* filename) MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIX_Comm_device_init(xcomm); + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_device_init(xcomm); // Read suitesparse matrix ParMat A; @@ -175,7 +175,7 @@ void test_matrix(const char* filename) gpuFree(sendbuf_d); gpuFree(recvbuf_d); - MPIX_Comm_free(&xcomm); + MPIL_Comm_free(&xcomm); } int main(int argc, char** argv) diff --git a/src/mpi_advance.h b/src/locality_aware.h similarity index 94% rename from src/mpi_advance.h rename to src/locality_aware.h index 8bf1197b5..95f84e206 100644 --- a/src/mpi_advance.h +++ b/src/locality_aware.h @@ -7,7 +7,7 @@ #include "communicator/comm_data.h" #include "communicator/comm_pkg.h" #include "communicator/locality_comm.h" -#include "communicator/mpix_comm.h" +#include "communicator/mpil_comm.h" #include "neighborhood/dist_graph.h" #include "neighborhood/dist_topo.h" #include "neighborhood/neighbor.h" diff --git a/src/neighborhood/alltoall_crs.cpp b/src/neighborhood/alltoall_crs.cpp index 88ed23748..fd1561963 100644 --- a/src/neighborhood/alltoall_crs.cpp +++ b/src/neighborhood/alltoall_crs.cpp @@ -13,8 +13,8 @@ int alltoall_crs_rma(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -34,12 +34,12 @@ int alltoall_crs_rma(const int send_nnz, if (comm->win_bytes != bytes || comm->win_type_bytes != 1) { - MPIX_Comm_win_free(comm); + MPIL_Comm_win_free(comm); } if (comm->win == MPI_WIN_NULL) { - MPIX_Comm_win_init(comm, bytes, 1); + MPIL_Comm_win_init(comm, bytes, 1); } // RMA puts to find sizes recvd from each process @@ -88,10 +88,9 @@ int alltoall_crs_rma(const int send_nnz, } *recv_nnz_ptr = src.size(); - MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); - MPIX_Alloc(recvvals_ptr, recv_buffer.size()); - //(*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); - //(*recvvals_ptr) = MPIalloc(recv_buffer.size()); + MPIL_Alloc((void**)src_ptr, src.size() * sizeof(int)); + MPIL_Alloc(recvvals_ptr, recv_buffer.size()); + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvvals_ptr), recv_buffer.data(), recv_buffer.size()); @@ -108,8 +107,8 @@ int alltoall_crs_personalized(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -118,7 +117,7 @@ int alltoall_crs_personalized(const int send_nnz, MPI_Status recv_status; int proc, ctr; int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); char* send_buffer; if (send_nnz) @@ -148,14 +147,12 @@ int alltoall_crs_personalized(const int send_nnz, int* src; char* recvvals; - MPIX_Alloc((void**)&src, *recv_nnz * sizeof(int)); - MPIX_Alloc((void**)&recvvals, *recv_nnz * recv_bytes); - // int* src = (int*)MPIalloc(*recv_nnz*sizeof(int)); - // void* recvvals = MPIalloc(*recv_nnz*recv_bytes); - + MPIL_Alloc((void**)&src, *recv_nnz * sizeof(int)); + MPIL_Alloc((void**)&recvvals, *recv_nnz * recv_bytes); + if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } for (int i = 0; i < send_nnz; i++) @@ -208,8 +205,8 @@ int alltoall_crs_nonblocking(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -230,14 +227,14 @@ int alltoall_crs_nonblocking(const int send_nnz, MPI_Status recv_status; MPI_Request bar_req; int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); std::vector src; std::vector recv_buffer; if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } for (int i = 0; i < send_nnz; i++) @@ -259,7 +256,6 @@ int alltoall_crs_nonblocking(const int send_nnz, MPI_Iprobe(MPI_ANY_SOURCE, tag, comm->global_comm, &flag, &recv_status); if (flag) { - // char* recv_buffer = (char*)recvvals; proc = recv_status.MPI_SOURCE; src.push_back(proc); recv_buffer.resize(src.size() * recv_bytes); @@ -292,10 +288,9 @@ int alltoall_crs_nonblocking(const int send_nnz, } *recv_nnz = src.size(); - MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); - MPIX_Alloc(recvvals_ptr, recv_buffer.size()); - //(*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); - //(*recvvals_ptr) = MPIalloc(recv_buffer.size()); + MPIL_Alloc((void**)src_ptr, src.size() * sizeof(int)); + MPIL_Alloc(recvvals_ptr, recv_buffer.size()); + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvvals_ptr), recv_buffer.data(), recv_buffer.size()); @@ -310,8 +305,8 @@ void local_redistribute(int node_recv_size, int recvcount, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -385,14 +380,14 @@ void local_redistribute(int node_recv_size, } int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); MPI_Allreduce( MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); int recv_count = msg_counts[local_rank]; if (PPN > comm->n_requests) { - MPIX_Comm_req_resize(comm, PPN); + MPIL_Comm_req_resize(comm, PPN); } // Send a message to every process that I will need data from @@ -475,8 +470,8 @@ void local_redistribute(int node_recv_size, } *recv_nnz = src.size(); - MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); - MPIX_Alloc(recvvals_ptr, recv_buffer.size()); + MPIL_Alloc((void**)src_ptr, src.size() * sizeof(int)); + MPIL_Alloc(recvvals_ptr, recv_buffer.size()); // (*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); // (*recvvals_ptr) = MPIalloc(recv_buffer.size()); memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); @@ -494,8 +489,8 @@ int alltoall_crs_personalized_loc(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -503,7 +498,7 @@ int alltoall_crs_personalized_loc(const int send_nnz, if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } MPI_Comm_rank(comm->local_comm, &local_rank); @@ -519,14 +514,14 @@ int alltoall_crs_personalized_loc(const int send_nnz, if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } MPI_Status recv_status; int proc, ctr, start, end; int count, n_msgs, n_sends; int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); std::vector node_send_buffer; std::vector local_send_buffer; @@ -678,8 +673,8 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -687,7 +682,7 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } MPI_Comm_rank(comm->local_comm, &local_rank); @@ -703,7 +698,7 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } MPI_Status recv_status; @@ -711,7 +706,7 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, int proc, ctr, flag, ibar, start, end; int count, n_msgs, n_sends; int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); std::vector node_send_buffer; std::vector local_send_buffer; diff --git a/src/neighborhood/alltoallv_crs.cpp b/src/neighborhood/alltoallv_crs.cpp index 4ed1a24a5..e17190cd9 100644 --- a/src/neighborhood/alltoallv_crs.cpp +++ b/src/neighborhood/alltoallv_crs.cpp @@ -17,8 +17,8 @@ int alltoallv_crs_personalized(const int send_nnz, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -27,7 +27,7 @@ int alltoallv_crs_personalized(const int send_nnz, MPI_Status recv_status; int proc, ctr, count; int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); char* send_buffer = (char*)sendvals; int send_bytes, recv_bytes; @@ -47,11 +47,11 @@ int alltoallv_crs_personalized(const int send_nnz, // Allocate recvvals to size determined in allreduce char* recvvals; - MPIX_Alloc((void**)&recvvals, *recv_size * recv_bytes); + MPIL_Alloc((void**)&recvvals, *recv_size * recv_bytes); if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } // Send each message @@ -99,9 +99,9 @@ int alltoallv_crs_personalized(const int send_nnz, *recv_nnz = src.size(); *recvvals_ptr = recvvals; - MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); - MPIX_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); - MPIX_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); + MPIL_Alloc((void**)src_ptr, src.size() * sizeof(int)); + MPIL_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); + MPIL_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size() * sizeof(int)); memcpy((*rdispls_ptr), rdispls.data(), rdispls.size() * sizeof(int)); @@ -128,8 +128,8 @@ int alltoallv_crs_nonblocking(const int send_nnz, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -144,11 +144,11 @@ int alltoallv_crs_nonblocking(const int send_nnz, MPI_Status recv_status; MPI_Request bar_req; int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } for (int i = 0; i < send_nnz; i++) @@ -218,15 +218,11 @@ int alltoallv_crs_nonblocking(const int send_nnz, *recv_nnz = src.size(); *recv_size = ctr; - MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); - MPIX_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); - MPIX_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); - MPIX_Alloc((void**)recvvals_ptr, recvvals.size()); - //(*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); - //(*recvcounts_ptr) = (int*)MPIalloc(recvcounts.size()*sizeof(int)); - //(*rdispls_ptr) = (int*)MPIalloc(rdispls.size()*sizeof(int)); - //(*recvvals_ptr) = MPIalloc(recvvals.size()); - + MPIL_Alloc((void**)src_ptr, src.size() * sizeof(int)); + MPIL_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); + MPIL_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); + MPIL_Alloc((void**)recvvals_ptr, recvvals.size()); + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size() * sizeof(int)); memcpy((*rdispls_ptr), rdispls.data(), rdispls.size() * sizeof(int)); @@ -246,8 +242,8 @@ void local_redistribute(int n_recvs, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -376,7 +372,7 @@ void local_redistribute(int n_recvs, // Tell them which global indices I need from them std::vector local_req(PPN); - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); n_sends = 0; for (int i = 0; i < PPN; i++) @@ -469,14 +465,11 @@ void local_redistribute(int n_recvs, *recv_nnz = src.size(); *recv_size = ctr; - MPIX_Alloc((void**)src_ptr, src.size() * sizeof(int)); - MPIX_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); - MPIX_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); - MPIX_Alloc(recvvals_ptr, recvvals.size()); - //(*src_ptr) = (int*)MPIalloc(src.size()*sizeof(int)); - //(*recvcounts_ptr) = (int*)MPIalloc(recvcounts.size()*sizeof(int)); - //(*rdispls_ptr) = (int*)MPIalloc(rdispls.size()*sizeof(int)); - //(*recvvals_ptr) = MPIalloc(recvvals.size()); + MPIL_Alloc((void**)src_ptr, src.size() * sizeof(int)); + MPIL_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); + MPIL_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); + MPIL_Alloc(recvvals_ptr, recvvals.size()); + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size() * sizeof(int)); @@ -498,8 +491,8 @@ int alltoallv_crs_personalized_loc(const int send_nnz, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -507,18 +500,18 @@ int alltoallv_crs_personalized_loc(const int send_nnz, if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); char* send_buffer = (char*)sendvals; int send_bytes, recv_bytes, int_bytes; @@ -685,8 +678,8 @@ int alltoallv_crs_nonblocking_loc(const int send_nnz, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -694,18 +687,18 @@ int alltoallv_crs_nonblocking_loc(const int send_nnz, if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); char* send_buffer = (char*)sendvals; int send_bytes, recv_bytes, int_bytes; diff --git a/src/neighborhood/dist_graph.c b/src/neighborhood/dist_graph.c index 228a3dd40..305a64eda 100644 --- a/src/neighborhood/dist_graph.c +++ b/src/neighborhood/dist_graph.c @@ -1,22 +1,22 @@ #include "dist_graph.h" -int MPIX_Dist_graph_create_adjacent(MPI_Comm comm_old, +int MPIL_Dist_graph_create_adjacent(MPI_Comm comm_old, int indegree, const int sources[], const int sourceweights[], int outdegree, const int destinations[], const int destweights[], - MPIX_Info* info, + MPIL_Info* info, int reorder, - MPIX_Comm** comm_dist_graph_ptr) + MPIL_Comm** comm_dist_graph_ptr) { int rank, num_procs; MPI_Comm_rank(comm_old, &rank); MPI_Comm_size(comm_old, &num_procs); - MPIX_Comm* comm_dist_graph; - MPIX_Comm_init(&comm_dist_graph, comm_old); + MPIL_Comm* comm_dist_graph; + MPIL_Comm_init(&comm_dist_graph, comm_old); const int* s = sources; if (indegree == 0) diff --git a/src/neighborhood/dist_graph.h b/src/neighborhood/dist_graph.h index ee204a9f3..e4db4b30b 100644 --- a/src/neighborhood/dist_graph.h +++ b/src/neighborhood/dist_graph.h @@ -2,7 +2,7 @@ #define MPI_ADVANCE_DIST_GRAPH_H #include "communicator/locality_comm.h" -#include "communicator/mpix_comm.h" +#include "communicator/mpil_comm.h" #include "mpi.h" // Declarations of C++ methods @@ -10,16 +10,16 @@ extern "C" { #endif -int MPIX_Dist_graph_create_adjacent(MPI_Comm comm_old, +int MPIL_Dist_graph_create_adjacent(MPI_Comm comm_old, int indegree, const int sources[], const int sourceweights[], int outdegree, const int destinations[], const int destweights[], - MPIX_Info* info, + MPIL_Info* info, int reorder, - MPIX_Comm** comm_dist_graph_ptr); + MPIL_Comm** comm_dist_graph_ptr); #ifdef __cplusplus } diff --git a/src/neighborhood/dist_topo.c b/src/neighborhood/dist_topo.c index 20133dda4..dea879252 100644 --- a/src/neighborhood/dist_topo.c +++ b/src/neighborhood/dist_topo.c @@ -2,22 +2,22 @@ #include -int MPIX_Topo_init(int indegree, +int MPIL_Topo_init(int indegree, const int sources[], const int sourceweights[], int outdegree, const int destinations[], const int destweights[], - MPIX_Info* info, - MPIX_Topo** mpix_topo_ptr) + MPIL_Info* info, + MPIL_Topo** mpix_topo_ptr) { - MPIX_Topo* mpix_topo = (MPIX_Topo*)malloc(sizeof(MPIX_Topo)); + MPIL_Topo* mpix_topo = (MPIL_Topo*)malloc(sizeof(MPIL_Topo)); - // Copy indegree and outdegree into MPIX_Topo struct + // Copy indegree and outdegree into MPIL_Topo struct mpix_topo->indegree = indegree; mpix_topo->outdegree = outdegree; - // Create copy of sources/destinations in MPIX_Topo struct + // Create copy of sources/destinations in MPIL_Topo struct mpix_topo->sources = NULL; mpix_topo->destinations = NULL; @@ -57,15 +57,15 @@ int MPIX_Topo_init(int indegree, return MPI_SUCCESS; } -int MPIX_Topo_from_neighbor_comm(MPIX_Comm* comm, MPIX_Topo** mpix_topo_ptr) +int MPIL_Topo_from_neighbor_comm(MPIL_Comm* comm, MPIL_Topo** mpix_topo_ptr) { - MPIX_Topo* mpix_topo = (MPIX_Topo*)malloc(sizeof(MPIX_Topo)); + MPIL_Topo* mpix_topo = (MPIL_Topo*)malloc(sizeof(MPIL_Topo)); int weighted; MPI_Dist_graph_neighbors_count( comm->neighbor_comm, &(mpix_topo->indegree), &(mpix_topo->outdegree), &weighted); - // Create copy of sources/destinations in MPIX_Topo struct + // Create copy of sources/destinations in MPIL_Topo struct mpix_topo->sources = NULL; mpix_topo->destinations = NULL; @@ -108,9 +108,9 @@ int MPIX_Topo_from_neighbor_comm(MPIX_Comm* comm, MPIX_Topo** mpix_topo_ptr) return MPI_SUCCESS; } -int MPIX_Topo_free(MPIX_Topo** mpix_topo_ptr) +int MPIL_Topo_free(MPIL_Topo** mpix_topo_ptr) { - MPIX_Topo* mpix_topo = *mpix_topo_ptr; + MPIL_Topo* mpix_topo = *mpix_topo_ptr; if (mpix_topo->indegree) { diff --git a/src/neighborhood/dist_topo.h b/src/neighborhood/dist_topo.h index 144a84b8f..7e16dad1d 100644 --- a/src/neighborhood/dist_topo.h +++ b/src/neighborhood/dist_topo.h @@ -2,7 +2,7 @@ #define MPI_ADVANCE_DIST_TOPO_H #include "communicator/locality_comm.h" -#include "communicator/mpix_comm.h" +#include "communicator/mpil_comm.h" #include "mpi.h" // Declarations of C++ methods @@ -10,7 +10,7 @@ extern "C" { #endif -typedef struct _MPIX_Topo +typedef struct _MPIL_Topo { int indegree; int* sources; @@ -19,20 +19,20 @@ typedef struct _MPIX_Topo int* destinations; int* destweights; int reorder; -} MPIX_Topo; +} MPIL_Topo; -int MPIX_Topo_init(int indegree, +int MPIL_Topo_init(int indegree, const int sources[], const int sourceweights[], int outdegree, const int destinations[], const int destweights[], - MPIX_Info* info, - MPIX_Topo** mpix_topo_ptr); + MPIL_Info* info, + MPIL_Topo** mpix_topo_ptr); -int MPIX_Topo_from_neighbor_comm(MPIX_Comm* comm, MPIX_Topo** mpix_topo_ptr); +int MPIL_Topo_from_neighbor_comm(MPIL_Comm* comm, MPIL_Topo** mpix_topo_ptr); -int MPIX_Topo_free(MPIX_Topo** topo); +int MPIL_Topo_free(MPIL_Topo** topo); #ifdef __cplusplus } diff --git a/src/neighborhood/neighbor.c b/src/neighborhood/neighbor.c index 82e257b30..7709bdc2f 100644 --- a/src/neighborhood/neighbor.c +++ b/src/neighborhood/neighbor.c @@ -9,7 +9,7 @@ NeighborAlltoallvMethod mpix_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_STANDARD; // Topology object based neighbor alltoallv -int MPIX_Neighbor_alltoallv_topo(const void* sendbuf, +int MPIL_Neighbor_alltoallv_topo(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, @@ -17,8 +17,8 @@ int MPIX_Neighbor_alltoallv_topo(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm) + MPIL_Topo* topo, + MPIL_Comm* comm) { int rank; MPI_Comm_rank(comm->global_comm, &rank); @@ -50,7 +50,7 @@ int MPIX_Neighbor_alltoallv_topo(const void* sendbuf, comm); } -int MPIX_Neighbor_alltoallv(const void* sendbuffer, +int MPIL_Neighbor_alltoallv(const void* sendbuffer, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, @@ -58,12 +58,12 @@ int MPIX_Neighbor_alltoallv(const void* sendbuffer, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm) + MPIL_Comm* comm) { - MPIX_Topo* topo; - MPIX_Topo_from_neighbor_comm(comm, &topo); + MPIL_Topo* topo; + MPIL_Topo_from_neighbor_comm(comm, &topo); - MPIX_Neighbor_alltoallv_topo(sendbuffer, + MPIL_Neighbor_alltoallv_topo(sendbuffer, sendcounts, sdispls, sendtype, @@ -74,7 +74,7 @@ int MPIX_Neighbor_alltoallv(const void* sendbuffer, topo, comm); - MPIX_Topo_free(&topo); + MPIL_Topo_free(&topo); return MPI_SUCCESS; } @@ -88,11 +88,11 @@ int neighbor_alltoallv_standard(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm) + MPIL_Topo* topo, + MPIL_Comm* comm) { int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); if (topo->indegree + topo->outdegree == 0) { @@ -101,7 +101,7 @@ int neighbor_alltoallv_standard(const void* sendbuf, if (comm->n_requests < topo->indegree + topo->outdegree) { - MPIX_Comm_req_resize(comm, topo->indegree + topo->outdegree); + MPIL_Comm_req_resize(comm, topo->indegree + topo->outdegree); } const char* send_buffer = NULL; @@ -163,8 +163,8 @@ int neighbor_alltoallv_locality(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm) + MPIL_Topo* topo, + MPIL_Comm* comm) { int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -184,8 +184,8 @@ int neighbor_alltoallv_locality(const void* sendbuf, int recv_bytes; MPI_Type_size(recvtype, &recv_bytes); - MPIX_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); alltoallv_crs_personalized(send_nnz, send_size, @@ -222,12 +222,12 @@ int neighbor_alltoallv_locality(const void* sendbuf, } free(new_proc_idx); - MPIX_Info_free(&xinfo); + MPIL_Info_free(&xinfo); - MPIX_Free(src_tmp); - MPIX_Free(recvcounts_tmp); - MPIX_Free(rdispls_tmp); - MPIX_Free(recvvals_tmp); + MPIL_Free(src_tmp); + MPIL_Free(recvcounts_tmp); + MPIL_Free(rdispls_tmp); + MPIL_Free(recvvals_tmp); return MPI_SUCCESS; } diff --git a/src/neighborhood/neighbor.h b/src/neighborhood/neighbor.h index f657db894..e00b95ea6 100644 --- a/src/neighborhood/neighbor.h +++ b/src/neighborhood/neighbor.h @@ -20,7 +20,7 @@ enum NeighborAlltoallvMethod NEIGHBOR_ALLTOALLV_STANDARD, NEIGHBOR_ALLTOALLV_LOCALITY }; -extern NeighborAlltoallvMethod mpix_neighbor_alltoallv_implementation; +extern enum NeighborAlltoallvMethod mpix_neighbor_alltoallv_implementation; typedef int (*neighbor_alltoallv_ftn)(const void* sendbuffer, const int sendcounts[], @@ -30,13 +30,13 @@ typedef int (*neighbor_alltoallv_ftn)(const void* sendbuffer, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm); + MPIL_Topo* topo, + MPIL_Comm* comm); // Standard Neighbor Alltoallv // Extension takes array of requests instead of single request // 'requests' must be of size indegree+outdegree! -int MPIX_Neighbor_alltoallv_topo(const void* sendbuf, +int MPIL_Neighbor_alltoallv_topo(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, @@ -44,10 +44,10 @@ int MPIX_Neighbor_alltoallv_topo(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm); + MPIL_Topo* topo, + MPIL_Comm* comm); -int MPIX_Neighbor_alltoallv(const void* sendbuf, +int MPIL_Neighbor_alltoallv(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, @@ -55,7 +55,7 @@ int MPIX_Neighbor_alltoallv(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm); + MPIL_Comm* comm); int neighbor_alltoallv_standard(const void* sendbuf, const int sendcounts[], @@ -65,8 +65,8 @@ int neighbor_alltoallv_standard(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm); + MPIL_Topo* topo, + MPIL_Comm* comm); int neighbor_alltoallv_locality(const void* sendbuf, const int sendcounts[], @@ -76,8 +76,8 @@ int neighbor_alltoallv_locality(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm); + MPIL_Topo* topo, + MPIL_Comm* comm); #ifdef __cplusplus } diff --git a/src/neighborhood/neighbor_init.c b/src/neighborhood/neighbor_init.c index b4c1ee50d..4da7c4c9e 100644 --- a/src/neighborhood/neighbor_init.c +++ b/src/neighborhood/neighbor_init.c @@ -21,7 +21,7 @@ int init_communication(const void* sendbuffer, int* n_request_ptr, MPI_Request** request_ptr); -int MPIX_Neighbor_alltoallv_init_topo(const void* sendbuf, +int MPIL_Neighbor_alltoallv_init_topo(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, @@ -29,10 +29,10 @@ int MPIX_Neighbor_alltoallv_init_topo(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) { neighbor_alltoallv_init_ftn method; @@ -66,7 +66,7 @@ int MPIX_Neighbor_alltoallv_init_topo(const void* sendbuf, // Standard Persistent Neighbor Alltoallv // Extension takes array of requests instead of single request // 'requests' must be of size indegree+outdegree! -int MPIX_Neighbor_alltoallv_init(const void* sendbuf, +int MPIL_Neighbor_alltoallv_init(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, @@ -74,14 +74,14 @@ int MPIX_Neighbor_alltoallv_init(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) { - MPIX_Topo* topo; - MPIX_Topo_from_neighbor_comm(comm, &topo); + MPIL_Topo* topo; + MPIL_Topo_from_neighbor_comm(comm, &topo); - MPIX_Neighbor_alltoallv_init_topo(sendbuf, + MPIL_Neighbor_alltoallv_init_topo(sendbuf, sendcounts, sdispls, sendtype, @@ -94,12 +94,12 @@ int MPIX_Neighbor_alltoallv_init(const void* sendbuf, info, request_ptr); - MPIX_Topo_free(&topo); + MPIL_Topo_free(&topo); return MPI_SUCCESS; } -int MPIX_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, +int MPIL_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, const int sendcounts[], const int sdispls[], const long global_sindices[], @@ -109,10 +109,10 @@ int MPIX_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, const int rdispls[], const long global_rindices[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) { switch (mpix_neighbor_alltoallv_init_implementation) { @@ -160,7 +160,7 @@ int MPIX_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, } } -int MPIX_Neighbor_alltoallv_init_ext(const void* sendbuf, +int MPIL_Neighbor_alltoallv_init_ext(const void* sendbuf, const int sendcounts[], const int sdispls[], const long global_sindices[], @@ -170,14 +170,14 @@ int MPIX_Neighbor_alltoallv_init_ext(const void* sendbuf, const int rdispls[], const long global_rindices[], MPI_Datatype recvtype, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) { - MPIX_Topo* topo; - MPIX_Topo_from_neighbor_comm(comm, &topo); + MPIL_Topo* topo; + MPIL_Topo_from_neighbor_comm(comm, &topo); - MPIX_Neighbor_alltoallv_init_ext_topo(sendbuf, + MPIL_Neighbor_alltoallv_init_ext_topo(sendbuf, sendcounts, sdispls, global_sindices, @@ -192,7 +192,7 @@ int MPIX_Neighbor_alltoallv_init_ext(const void* sendbuf, info, request_ptr); - MPIX_Topo_free(&topo); + MPIL_Topo_free(&topo); return MPI_SUCCESS; } @@ -205,16 +205,16 @@ int neighbor_alltoallv_init_standard(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) { - MPIX_Request* request; + MPIL_Request* request; init_neighbor_request(&request); int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); request->global_n_msgs = topo->indegree + topo->outdegree; allocate_requests(request->global_n_msgs, &(request->global_requests)); @@ -262,17 +262,17 @@ int neighbor_alltoallv_init_locality(const void* sendbuffer, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) { int rank; MPI_Comm_rank(comm->global_comm, &rank); if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } int* global_sdispls = NULL; @@ -336,7 +336,7 @@ int neighbor_alltoallv_init_locality(const void* sendbuffer, global_send_indices[i] = first_send + i; } - MPIX_Neighbor_alltoallv_topo(global_send_indices, + MPIL_Neighbor_alltoallv_topo(global_send_indices, sendcounts, global_sdispls, MPI_LONG, @@ -383,17 +383,17 @@ int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, const int rdispls[], const long global_rindices[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr) + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) { if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } - MPIX_Request* request; + MPIL_Request* request; init_neighbor_request(&request); // Initialize Locality-Aware Communication Strategy (3-Step) @@ -489,10 +489,10 @@ int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, return MPI_SUCCESS; } -void init_neighbor_request(MPIX_Request** request_ptr) +void init_neighbor_request(MPIL_Request** request_ptr) { init_request(request_ptr); - MPIX_Request* request = *request_ptr; + MPIL_Request* request = *request_ptr; request->start_function = neighbor_start; request->wait_function = neighbor_wait; diff --git a/src/neighborhood/neighbor_init.h b/src/neighborhood/neighbor_init.h index d76343158..0956796eb 100644 --- a/src/neighborhood/neighbor_init.h +++ b/src/neighborhood/neighbor_init.h @@ -14,7 +14,7 @@ enum NeighborAlltoallvInitMethod NEIGHBOR_ALLTOALLV_INIT_STANDARD, NEIGHBOR_ALLTOALLV_INIT_LOCALITY }; -extern NeighborAlltoallvInitMethod mpix_neighbor_alltoallv_init_implementation; +extern enum NeighborAlltoallvInitMethod mpix_neighbor_alltoallv_init_implementation; typedef int (*neighbor_alltoallv_init_ftn)(const void* sendbuf, const int sendcounts[], @@ -24,29 +24,29 @@ typedef int (*neighbor_alltoallv_init_ftn)(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); // Starting locality-aware requests // 1. Start Local_L // 2. Start and wait for local_S // 3. Start global -int neighbor_start(MPIX_Request* request); +int neighbor_start(MPIL_Request* request); // Wait for locality-aware requests // 1. Wait for global // 2. Start and wait for local_R // 3. Wait for local_L -int neighbor_wait(MPIX_Request* request, MPI_Status* status); +int neighbor_wait(MPIL_Request* request, MPI_Status* status); -void init_neighbor_request(MPIX_Request** request_ptr); +void init_neighbor_request(MPIL_Request** request_ptr); // Standard Persistent Neighbor Alltoallv // Extension takes array of requests instead of single request // 'requests' must be of size indegree+outdegree! -int MPIX_Neighbor_alltoallv_init_topo(const void* sendbuf, +int MPIL_Neighbor_alltoallv_init_topo(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, @@ -54,11 +54,11 @@ int MPIX_Neighbor_alltoallv_init_topo(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); -int MPIX_Neighbor_alltoallv_init(const void* sendbuf, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); +int MPIL_Neighbor_alltoallv_init(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, @@ -66,13 +66,13 @@ int MPIX_Neighbor_alltoallv_init(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); // Locality-Aware Extension to Persistent Neighbor Alltoallv // Needs global indices for each send and receive -int MPIX_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, +int MPIL_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, const int sendcounts[], const int sdispls[], const long global_sindices[], @@ -82,11 +82,11 @@ int MPIX_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, const int rdispls[], const long global_rindices[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); -int MPIX_Neighbor_alltoallv_init_ext(const void* sendbuf, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); +int MPIL_Neighbor_alltoallv_init_ext(const void* sendbuf, const int sendcounts[], const int sdispls[], const long global_sindices[], @@ -96,9 +96,9 @@ int MPIX_Neighbor_alltoallv_init_ext(const void* sendbuf, const int rdispls[], const long global_rindices[], MPI_Datatype recvtype, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); int neighbor_alltoallv_init_standard(const void* sendbuf, const int sendcounts[], @@ -108,10 +108,10 @@ int neighbor_alltoallv_init_standard(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); int neighbor_alltoallv_init_locality(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -120,10 +120,10 @@ int neighbor_alltoallv_init_locality(const void* sendbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, const int sendcounts[], const int sdispls[], @@ -134,10 +134,10 @@ int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, const int rdispls[], const long global_rindices[], MPI_Datatype recvtype, - MPIX_Topo* topo, - MPIX_Comm* comm, - MPIX_Info* info, - MPIX_Request** request_ptr); + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); void init_locality(const int n_sends, const int* send_procs, @@ -151,8 +151,8 @@ void init_locality(const int n_sends, const long* global_recv_indices, const MPI_Datatype sendtype, const MPI_Datatype recvtype, - MPIX_Comm* mpix_comm, - MPIX_Request* request); + MPIL_Comm* mpix_comm, + MPIL_Request* request); #ifdef __cplusplus } diff --git a/src/neighborhood/neighbor_locality.cpp b/src/neighborhood/neighbor_locality.cpp index 5abcb7729..ca3c85175 100644 --- a/src/neighborhood/neighbor_locality.cpp +++ b/src/neighborhood/neighbor_locality.cpp @@ -34,7 +34,7 @@ void form_local_comm(const int orig_num_sends, void form_global_comm(CommData* local_data, CommData* global_data, std::vector& local_data_nodes, - const MPIX_Comm* mpix_comm, + const MPIL_Comm* mpix_comm, int tag); void update_global_comm(LocalityComm* locality); void form_global_map(const CommData* map_data, std::map& global_map); @@ -67,8 +67,8 @@ void init_locality(const int n_sends, const long* global_recv_indices, const MPI_Datatype sendtype, const MPI_Datatype recvtype, - MPIX_Comm* mpix_comm, - MPIX_Request* request) + MPIL_Comm* mpix_comm, + MPIL_Request* request) { // Get MPI Information int rank, num_procs; @@ -185,7 +185,7 @@ void init_locality(const int n_sends, } // Destroy NAPComm* structure -void destroy_locality(MPIX_Request* request) +void destroy_locality(MPIL_Request* request) { destroy_locality_comm(request->locality); } @@ -506,7 +506,7 @@ void form_local_comm(const int orig_num_sends, void form_global_comm(CommData* local_data, CommData* global_data, std::vector& local_data_nodes, - const MPIX_Comm* mpix_comm, + const MPIL_Comm* mpix_comm, int tag) { std::vector tmp_send_indices; @@ -585,8 +585,8 @@ void update_global_comm(LocalityComm* locality) MPI_Request* requests = NULL; int* send_buffer = NULL; int send_tag, recv_tag; - MPIX_Comm_tag(locality->communicators, &send_tag); - MPIX_Comm_tag(locality->communicators, &recv_tag); + MPIL_Comm_tag(locality->communicators, &send_tag); + MPIL_Comm_tag(locality->communicators, &recv_tag); int node, global_proc; int num_to_recv; MPI_Status recv_status; diff --git a/src/neighborhood/sparse_coll.c b/src/neighborhood/sparse_coll.c index 91794de61..123a0b18e 100644 --- a/src/neighborhood/sparse_coll.c +++ b/src/neighborhood/sparse_coll.c @@ -3,7 +3,7 @@ #include #include -int MPIX_Alltoall_crs(const int send_nnz, +int MPIL_Alltoall_crs(const int send_nnz, const int* dest, const int sendcount, MPI_Datatype sendtype, @@ -13,8 +13,8 @@ int MPIX_Alltoall_crs(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* xcomm) + MPIL_Info* xinfo, + MPIL_Comm* xcomm) { return alltoall_crs_personalized(send_nnz, dest, @@ -30,7 +30,7 @@ int MPIX_Alltoall_crs(const int send_nnz, xcomm); } -int MPIX_Alltoallv_crs(const int send_nnz, +int MPIL_Alltoallv_crs(const int send_nnz, const int send_size, const int* dest, const int* sendcounts, @@ -44,8 +44,8 @@ int MPIX_Alltoallv_crs(const int send_nnz, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* xcomm) + MPIL_Info* xinfo, + MPIL_Comm* xcomm) { return alltoallv_crs_personalized(send_nnz, send_size, diff --git a/src/neighborhood/sparse_coll.h b/src/neighborhood/sparse_coll.h index af8d58454..f7d10268b 100644 --- a/src/neighborhood/sparse_coll.h +++ b/src/neighborhood/sparse_coll.h @@ -2,7 +2,7 @@ #define MPI_ADVANCE_SPARSE_COLL_H #include "communicator/locality_comm.h" -#include "communicator/mpix_comm.h" +#include "communicator/mpil_comm.h" #include "mpi.h" #include "utils/utils.h" @@ -11,7 +11,7 @@ extern "C" { #endif -int MPIX_Alltoall_crs(const int send_nnz, +int MPIL_Alltoall_crs(const int send_nnz, const int* dest, const int sendcount, MPI_Datatype sendtype, @@ -21,10 +21,10 @@ int MPIX_Alltoall_crs(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* xcomm); + MPIL_Info* xinfo, + MPIL_Comm* xcomm); -int MPIX_Alltoallv_crs(const int send_nnz, +int MPIL_Alltoallv_crs(const int send_nnz, const int send_size, const int* dest, const int* sendcounts, @@ -38,8 +38,8 @@ int MPIX_Alltoallv_crs(const int send_nnz, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm); + MPIL_Info* xinfo, + MPIL_Comm* comm); int alltoall_crs_rma(const int send_nnz, const int* dest, @@ -51,8 +51,8 @@ int alltoall_crs_rma(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals, - MPIX_Info* xinfo, - MPIX_Comm* comm); + MPIL_Info* xinfo, + MPIL_Comm* comm); int alltoall_crs_personalized(const int send_nnz, const int* dest, @@ -64,8 +64,8 @@ int alltoall_crs_personalized(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals, - MPIX_Info* xinfo, - MPIX_Comm* comm); + MPIL_Info* xinfo, + MPIL_Comm* comm); int alltoall_crs_personalized_loc(const int send_nnz, const int* dest, @@ -77,8 +77,8 @@ int alltoall_crs_personalized_loc(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals, - MPIX_Info* xinfo, - MPIX_Comm* comm); + MPIL_Info* xinfo, + MPIL_Comm* comm); int alltoall_crs_nonblocking(const int send_nnz, const int* dest, @@ -90,8 +90,8 @@ int alltoall_crs_nonblocking(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals, - MPIX_Info* xinfo, - MPIX_Comm* comm); + MPIL_Info* xinfo, + MPIL_Comm* comm); int alltoall_crs_nonblocking_loc(const int send_nnz, const int* dest, @@ -103,8 +103,8 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, int recvcount, MPI_Datatype recvtype, void** recvvals, - MPIX_Info* xinfo, - MPIX_Comm* comm); + MPIL_Info* xinfo, + MPIL_Comm* comm); int alltoallv_crs_personalized(const int send_nnz, const int send_size, @@ -120,8 +120,8 @@ int alltoallv_crs_personalized(const int send_nnz, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm); + MPIL_Info* xinfo, + MPIL_Comm* comm); int alltoallv_crs_personalized_loc(const int send_nnz, const int send_size, @@ -137,8 +137,8 @@ int alltoallv_crs_personalized_loc(const int send_nnz, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm); + MPIL_Info* xinfo, + MPIL_Comm* comm); int alltoallv_crs_nonblocking(const int send_nnz, const int send_size, @@ -154,8 +154,8 @@ int alltoallv_crs_nonblocking(const int send_nnz, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm); + MPIL_Info* xinfo, + MPIL_Comm* comm); int alltoallv_crs_nonblocking_loc(const int send_nnz, const int send_size, @@ -171,8 +171,8 @@ int alltoallv_crs_nonblocking_loc(const int send_nnz, int** rdispls_ptr, MPI_Datatype recvtype, void** recvvals_ptr, - MPIX_Info* xinfo, - MPIX_Comm* comm); + MPIL_Info* xinfo, + MPIL_Comm* comm); #ifdef __cplusplus } diff --git a/src/neighborhood/sparse_coll_utils.cpp b/src/neighborhood/sparse_coll_utils.cpp index 4b12c9ef5..9df7c55cd 100644 --- a/src/neighborhood/sparse_coll_utils.cpp +++ b/src/neighborhood/sparse_coll_utils.cpp @@ -14,8 +14,8 @@ int alltoall_crs_personalized_loc(int send_nnz, int recvcount, MPI_Datatype recvtype, void* recvvals, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -23,7 +23,7 @@ int alltoall_crs_personalized_loc(int send_nnz, if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } MPI_Comm_rank(comm->local_comm, &local_rank); @@ -40,14 +40,14 @@ int alltoall_crs_personalized_loc(int send_nnz, if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } MPI_Status recv_status; int proc, ctr, start, end; int count, n_msgs, n_sends, n_recvs, idx, new_idx; int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); std::vector node_send_buffer; std::vector local_send_buffer; @@ -227,14 +227,14 @@ int alltoall_crs_personalized_loc(int send_nnz, displs[i + 1] = displs[i] + msg_counts[i]; } - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); MPI_Allreduce( MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); int recv_count = msg_counts[local_rank]; if (PPN > comm->n_requests) { - MPIX_Comm_req_resize(comm, PPN); + MPIL_Comm_req_resize(comm, PPN); } // Send a message to every process that I will need data from @@ -328,8 +328,8 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int recvcount, MPI_Datatype recvtype, void* recvvals, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -337,7 +337,7 @@ int alltoall_crs_nonblocking_loc(int send_nnz, if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } MPI_Comm_rank(comm->local_comm, &local_rank); @@ -354,7 +354,7 @@ int alltoall_crs_nonblocking_loc(int send_nnz, if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } MPI_Status recv_status; @@ -362,7 +362,7 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int proc, ctr, flag, ibar, start, end; int count, n_msgs, n_sends, n_recvs, idx, new_idx; int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); std::vector node_send_buffer; std::vector local_send_buffer; @@ -558,14 +558,14 @@ int alltoall_crs_nonblocking_loc(int send_nnz, displs[i + 1] = displs[i] + msg_counts[i]; } - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); MPI_Allreduce( MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); int recv_count = msg_counts[local_rank]; if (PPN > comm->n_requests) { - MPIX_Comm_req_resize(comm, PPN); + MPIL_Comm_req_resize(comm, PPN); } // Send a message to every process that I will need data from @@ -662,8 +662,8 @@ int alltoallv_crs_personalized_loc(int send_nnz, int* rdispls, MPI_Datatype recvtype, void* recvvals, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -671,18 +671,18 @@ int alltoallv_crs_personalized_loc(int send_nnz, if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); char* send_buffer = (char*)sendvals; char* recv_buffer = (char*)recvvals; @@ -934,7 +934,7 @@ int alltoallv_crs_personalized_loc(int send_nnz, // Tell them which global indices I need from them std::vector local_req(PPN); - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); n_sends = 0; for (int i = 0; i < PPN; i++) @@ -1036,8 +1036,8 @@ int alltoallv_crs_nonblocking_loc(int send_nnz, int* rdispls, MPI_Datatype recvtype, void* recvvals, - MPIX_Info* xinfo, - MPIX_Comm* comm) + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -1045,18 +1045,18 @@ int alltoallv_crs_nonblocking_loc(int send_nnz, if (comm->local_comm == MPI_COMM_NULL) { - MPIX_Comm_topo_init(comm); + MPIL_Comm_topo_init(comm); } MPI_Comm_rank(comm->local_comm, &local_rank); MPI_Comm_size(comm->local_comm, &PPN); if (comm->n_requests < send_nnz) { - MPIX_Comm_req_resize(comm, send_nnz); + MPIL_Comm_req_resize(comm, send_nnz); } int tag; - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); char* send_buffer = (char*)sendvals; char* recv_buffer = (char*)recvvals; @@ -1329,7 +1329,7 @@ int alltoallv_crs_nonblocking_loc(int send_nnz, // Tell them which global indices I need from them std::vector local_req(PPN); - MPIX_Comm_tag(comm, &tag); + MPIL_Comm_tag(comm, &tag); n_sends = 0; for (int i = 0; i < PPN; i++) diff --git a/src/neighborhood/tests/CMakeLists.txt b/src/neighborhood/tests/CMakeLists.txt index 5e3dccce1..308faa42d 100644 --- a/src/neighborhood/tests/CMakeLists.txt +++ b/src/neighborhood/tests/CMakeLists.txt @@ -14,7 +14,7 @@ foreach(src ${CPP_SOURCES}) add_executable(${exec_name} ${src}) # Link with MPI - target_link_libraries(${exec_name} mpi_advance ${MPI_LIBRARIES}) + target_link_libraries(${exec_name} locality_aware ${MPI_LIBRARIES}) # Add to CTEST add_test(NAME ${exec_name}_Test COMMAND ${MPIRUN} -n 16 ./${exec_name}) diff --git a/src/neighborhood/tests/neighbor_data.hpp b/src/neighborhood/tests/neighbor_data.hpp index 91a0dcb68..bd5bf7fe9 100644 --- a/src/neighborhood/tests/neighbor_data.hpp +++ b/src/neighborhood/tests/neighbor_data.hpp @@ -2,7 +2,7 @@ #define MPI_ADVANCE_TEST_NEIGHBOR_DATA_HPP template -struct MPIX_Data +struct MPIL_Data { int num_msgs; int size_msgs; @@ -16,7 +16,7 @@ struct MPIX_Data // Form random communication template -void form_initial_communicator(int local_size, MPIX_Data* send_data, MPIX_Data* recv_data) +void form_initial_communicator(int local_size, MPIL_Data* send_data, MPIL_Data* recv_data) { // Get MPI Information int rank, num_procs; @@ -116,7 +116,7 @@ void form_initial_communicator(int local_size, MPIX_Data* send_data, MPIX_Dat template -void form_global_indices(int local_size, MPIX_Data send_data, MPIX_Data recv_data, +void form_global_indices(int local_size, MPIL_Data send_data, MPIL_Data recv_data, std::vector& global_send_idx, std::vector& global_recv_idx) { int rank, num_procs; diff --git a/src/neighborhood/tests/test_neighbor_alltoallv_init.cpp b/src/neighborhood/tests/test_neighbor_alltoallv_init.cpp index 6732df7ae..7a271bdf4 100644 --- a/src/neighborhood/tests/test_neighbor_alltoallv_init.cpp +++ b/src/neighborhood/tests/test_neighbor_alltoallv_init.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -15,7 +15,7 @@ void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::v { if (pmpi_recv_vals[i] != mpix_recv_vals[i]) { - fprintf(stderr, "PMPI recv != MPIX: position %d, pmpi %d, mpix %d\n", i, + fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, pmpi_recv_vals[i], mpix_recv_vals[i]); MPI_Abort(MPI_COMM_WORLD, -1); } @@ -33,8 +33,8 @@ int main(int argc, char** argv) // Initial communication info (standard) int local_size = 10000; // Number of variables each rank stores - MPIX_Data send_data; - MPIX_Data recv_data; + MPIL_Data send_data; + MPIL_Data recv_data; form_initial_communicator(local_size, &send_data, &recv_data); std::vector global_send_idx(send_data.size_msgs); std::vector global_recv_idx(recv_data.size_msgs); @@ -65,11 +65,11 @@ int main(int argc, char** argv) MPI_Comm std_comm; MPI_Status status; - MPIX_Comm* xcomm; - MPIX_Request* xrequest; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; - MPIX_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); @@ -86,7 +86,7 @@ int main(int argc, char** argv) &std_comm); // MPI Advance Dist Graph Create - MPIX_Dist_graph_create_adjacent(MPI_COMM_WORLD, + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, recv_data.num_msgs, recv_data.procs.data(), recv_data.counts.data(), @@ -116,7 +116,7 @@ int main(int argc, char** argv) // Simple Persistent MPI Advance Implementation mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), send_data.counts.data(), send_data.indptr.data(), MPI_INT, @@ -127,15 +127,15 @@ int main(int argc, char** argv) xcomm, xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), send_data.counts.data(), send_data.indptr.data(), MPI_INT, @@ -146,9 +146,9 @@ int main(int argc, char** argv) xcomm, xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); @@ -157,7 +157,7 @@ int main(int argc, char** argv) mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), send_data.counts.data(), send_data.indptr.data(), global_send_idx.data(), @@ -170,15 +170,15 @@ int main(int argc, char** argv) xcomm, xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), send_data.counts.data(), send_data.indptr.data(), global_send_idx.data(), @@ -191,14 +191,14 @@ int main(int argc, char** argv) xcomm, xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); MPI_Comm_free(&std_comm); if (send_data.counts.data() == NULL) diff --git a/src/neighborhood/tests/test_neighbor_reorder.cpp b/src/neighborhood/tests/test_neighbor_reorder.cpp index 799fbe3b0..2ea945e6b 100644 --- a/src/neighborhood/tests/test_neighbor_reorder.cpp +++ b/src/neighborhood/tests/test_neighbor_reorder.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -15,7 +15,7 @@ void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::v { if (pmpi_recv_vals[i] != mpix_recv_vals[i]) { - fprintf(stderr, "PMPI recv != MPIX: position %d, pmpi %d, mpix %d\n", i, + fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, pmpi_recv_vals[i], mpix_recv_vals[i]); MPI_Abort(MPI_COMM_WORLD, -1); } @@ -32,8 +32,8 @@ int main(int argc, char** argv) // Initial communication info (standard) int local_size = 10000; // Number of variables each rank stores - MPIX_Data send_data; - MPIX_Data recv_data; + MPIL_Data send_data; + MPIL_Data recv_data; form_initial_communicator(local_size, &send_data, &recv_data); std::vector global_send_idx(send_data.size_msgs); std::vector global_recv_idx(recv_data.size_msgs); @@ -56,11 +56,11 @@ int main(int argc, char** argv) MPI_Comm std_comm; MPI_Status status; - MPIX_Comm* xcomm; - MPIX_Request* xrequest; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; - MPIX_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); // Standard MPI Dist Graph Create MPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, @@ -75,7 +75,7 @@ int main(int argc, char** argv) &std_comm); // MPI Advance Dist Graph Create - MPIX_Dist_graph_create_adjacent(MPI_COMM_WORLD, + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, recv_data.num_msgs, recv_data.procs.data(), recv_data.counts.data(), @@ -113,7 +113,7 @@ int main(int argc, char** argv) // Simple Persistent MPI Advance Implementation - MPIX_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), send_data.counts.data(), send_data.indptr.data(), MPI_INT, @@ -128,19 +128,19 @@ int main(int argc, char** argv) // Reorder during first send/recv std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); xrequest->reorder = 1; - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); // Standard send/recv with reordered recvs std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - MPIX_Request_free(&xrequest); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Request_free(&xrequest); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); MPI_Comm_free(&std_comm); MPI_Finalize(); diff --git a/src/neighborhood/tests/test_neighbor_topo_alltoallv_init.cpp b/src/neighborhood/tests/test_neighbor_topo_alltoallv_init.cpp index 6ecbcc1a3..7b1dec1a0 100644 --- a/src/neighborhood/tests/test_neighbor_topo_alltoallv_init.cpp +++ b/src/neighborhood/tests/test_neighbor_topo_alltoallv_init.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -15,7 +15,7 @@ void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::v { if (pmpi_recv_vals[i] != mpix_recv_vals[i]) { - fprintf(stderr, "PMPI recv != MPIX: position %d, pmpi %d, mpix %d\n", i, + fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, pmpi_recv_vals[i], mpix_recv_vals[i]); MPI_Abort(MPI_COMM_WORLD, -1); } @@ -33,8 +33,8 @@ int main(int argc, char** argv) // Initial communication info (standard) int local_size = 10000; // Number of variables each rank stores - MPIX_Data send_data; - MPIX_Data recv_data; + MPIL_Data send_data; + MPIL_Data recv_data; form_initial_communicator(local_size, &send_data, &recv_data); std::vector global_send_idx(send_data.size_msgs); std::vector global_recv_idx(recv_data.size_msgs); @@ -65,16 +65,16 @@ int main(int argc, char** argv) MPI_Comm std_comm; MPI_Status status; - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); update_locality(xcomm, 4); - MPIX_Request* xrequest; + MPIL_Request* xrequest; - MPIX_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); - MPIX_Topo* topo; - MPIX_Topo_init(recv_data.num_msgs, + MPIL_Topo* topo; + MPIL_Topo_init(recv_data.num_msgs, recv_data.procs.data(), recv_data.counts.data(), send_data.num_msgs, @@ -120,9 +120,9 @@ int main(int argc, char** argv) xcomm, xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); @@ -142,9 +142,9 @@ int main(int argc, char** argv) xcomm, xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); @@ -162,14 +162,14 @@ int main(int argc, char** argv) xcomm, xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - MPIX_Topo_free(&topo); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Topo_free(&topo); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); MPI_Comm_free(&std_comm); if (send_data.counts.data() == NULL) diff --git a/src/neighborhood/tests/test_suitesparse_alltoall_crs.cpp b/src/neighborhood/tests/test_suitesparse_alltoall_crs.cpp index 7f5a1a5b3..76e4b0393 100644 --- a/src/neighborhood/tests/test_suitesparse_alltoall_crs.cpp +++ b/src/neighborhood/tests/test_suitesparse_alltoall_crs.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -36,13 +36,13 @@ void test_matrix(const char* filename) MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - MPIX_Comm* xcomm; - MPIX_Info* xinfo; + MPIL_Comm* xcomm; + MPIL_Info* xinfo; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIX_Comm_topo_init(xcomm); + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_topo_init(xcomm); - MPIX_Info_init(&xinfo); + MPIL_Info_init(&xinfo); // Update so there are 4 PPN rather than what MPI_Comm_split returns update_locality(xcomm, 4); @@ -65,8 +65,8 @@ void test_matrix(const char* filename) A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, (void**)&recvvals, xinfo, xcomm); compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); - MPIX_Free(src); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvvals); /* TEST PERSONALIZED VERSION */ @@ -75,8 +75,8 @@ void test_matrix(const char* filename) A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, (void**)&recvvals, xinfo, xcomm); compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); - MPIX_Free(src); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvvals); /* TEST PERSONALIZED LOCALITY VERSION */ @@ -85,8 +85,8 @@ void test_matrix(const char* filename) A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, (void**)&recvvals, xinfo, xcomm); compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); - MPIX_Free(src); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvvals); /* TEST NONBLOCKING VERSION */ n_recvs = -1; @@ -94,8 +94,8 @@ void test_matrix(const char* filename) A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, (void**)&recvvals, xinfo, xcomm); compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); - MPIX_Free(src); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvvals); /* TEST NONBLOCKING LOCALITY VERSION */ n_recvs = -1; @@ -103,11 +103,11 @@ void test_matrix(const char* filename) A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, (void**)&recvvals, xinfo, xcomm); compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); - MPIX_Free(src); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvvals); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); } diff --git a/src/neighborhood/tests/test_suitesparse_alltoallv_crs.cpp b/src/neighborhood/tests/test_suitesparse_alltoallv_crs.cpp index 67267fbac..d3ec8e8f8 100644 --- a/src/neighborhood/tests/test_suitesparse_alltoallv_crs.cpp +++ b/src/neighborhood/tests/test_suitesparse_alltoallv_crs.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -52,12 +52,12 @@ void test_matrix(const char* filename) MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIX_Comm_topo_init(xcomm); + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_topo_init(xcomm); - MPIX_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); // Update so there are 4 PPN rather than what MPI_Comm_split returns update_locality(xcomm, 4); @@ -87,10 +87,10 @@ void test_matrix(const char* filename) &n_recvs, &s_recvs, &src, &recvcounts, &rdispls, MPI_LONG, (void**)&recvvals, xinfo, xcomm); compare_alltoallv_crs_results(n_recvs, A.send_comm.n_msgs, s_recvs, A.send_comm.size_msgs, src, proc_counts, recvcounts, proc_displs, A.send_comm.idx, rdispls, recvvals, A.first_col); - MPIX_Free(src); - MPIX_Free(recvcounts); - MPIX_Free(rdispls); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); /* TEST NONBLOCKING VERSION */ s_recvs = -1; @@ -100,10 +100,10 @@ void test_matrix(const char* filename) &n_recvs, &s_recvs, &src, &recvcounts, &rdispls, MPI_LONG, (void**)&recvvals, xinfo, xcomm); compare_alltoallv_crs_results(n_recvs, A.send_comm.n_msgs, s_recvs, A.send_comm.size_msgs, src, proc_counts, recvcounts, proc_displs, A.send_comm.idx, rdispls, recvvals, A.first_col); - MPIX_Free(src); - MPIX_Free(recvcounts); - MPIX_Free(rdispls); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); /* TEST PERSONALIZED LOCALITY VERSION */ s_recvs = -1; @@ -113,10 +113,10 @@ void test_matrix(const char* filename) &n_recvs, &s_recvs, &src, &recvcounts, &rdispls, MPI_LONG, (void**)&recvvals, xinfo, xcomm); compare_alltoallv_crs_results(n_recvs, A.send_comm.n_msgs, s_recvs, A.send_comm.size_msgs, src, proc_counts, recvcounts, proc_displs, A.send_comm.idx, rdispls, recvvals, A.first_col); - MPIX_Free(src); - MPIX_Free(recvcounts); - MPIX_Free(rdispls); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); /* TEST PERSONALIZED LOCALITY VERSION */ s_recvs = -1; @@ -126,13 +126,13 @@ void test_matrix(const char* filename) &n_recvs, &s_recvs, &src, &recvcounts, &rdispls, MPI_LONG, (void**)&recvvals, xinfo, xcomm); compare_alltoallv_crs_results(n_recvs, A.send_comm.n_msgs, s_recvs, A.send_comm.size_msgs, src, proc_counts, recvcounts, proc_displs, A.send_comm.idx, rdispls, recvvals, A.first_col); - MPIX_Free(src); - MPIX_Free(recvcounts); - MPIX_Free(rdispls); - MPIX_Free(recvvals); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); } diff --git a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv.cpp b/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv.cpp index 97b2dcc6d..000404218 100644 --- a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv.cpp +++ b/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -17,7 +17,7 @@ void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::v { if (pmpi_recv_vals[i] != mpix_recv_vals[i]) { - fprintf(stderr, "PMPI recv != MPIX: position %d, pmpi %d, mpix %d\n", i, + fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, pmpi_recv_vals[i], mpix_recv_vals[i]); MPI_Abort(MPI_COMM_WORLD, -1); } @@ -70,11 +70,11 @@ void test_matrix(const char* filename) MPI_Comm std_comm; MPI_Status status; - MPIX_Comm* xcomm; - MPIX_Request* xrequest; - MPIX_Info* xinfo; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; + MPIL_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info_init(&xinfo); int* s = A.recv_comm.procs.data(); if (A.recv_comm.n_msgs == 0) @@ -115,7 +115,7 @@ void test_matrix(const char* filename) delete[] recv_counts; compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - MPIX_Dist_graph_create_adjacent(MPI_COMM_WORLD, + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, A.recv_comm.n_msgs, A.recv_comm.procs.data(), MPI_UNWEIGHTED, @@ -130,7 +130,7 @@ void test_matrix(const char* filename) std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), MPI_INT, @@ -142,8 +142,8 @@ void test_matrix(const char* filename) compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - MPIX_Topo *topo; - MPIX_Topo_from_neighbor_comm(xcomm, &topo); + MPIL_Topo *topo; + MPIL_Topo_from_neighbor_comm(xcomm, &topo); // 2. Node-Aware Communication std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); @@ -174,9 +174,9 @@ void test_matrix(const char* filename) xcomm); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - MPIX_Topo_free(&topo); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Topo_free(&topo); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); PMPI_Comm_free(&std_comm); } diff --git a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_enum.cpp b/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_enum.cpp index 837d2856b..b5353403c 100644 --- a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_enum.cpp +++ b/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_enum.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -17,7 +17,7 @@ void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::v { if (pmpi_recv_vals[i] != mpix_recv_vals[i]) { - fprintf(stderr, "PMPI recv != MPIX: position %d, pmpi %d, mpix %d\n", i, + fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, pmpi_recv_vals[i], mpix_recv_vals[i]); MPI_Abort(MPI_COMM_WORLD, -1); } @@ -70,11 +70,11 @@ void test_matrix(const char* filename) MPI_Comm std_comm; MPI_Status status; - MPIX_Comm* xcomm; - MPIX_Request* xrequest; - MPIX_Info* xinfo; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; + MPIL_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info_init(&xinfo); int* s = A.recv_comm.procs.data(); if (A.recv_comm.n_msgs == 0) @@ -115,7 +115,7 @@ void test_matrix(const char* filename) delete[] recv_counts; compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - MPIX_Dist_graph_create_adjacent(MPI_COMM_WORLD, + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, A.recv_comm.n_msgs, A.recv_comm.procs.data(), MPI_UNWEIGHTED, @@ -130,7 +130,7 @@ void test_matrix(const char* filename) mpix_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_STANDARD; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), MPI_INT, @@ -144,7 +144,7 @@ void test_matrix(const char* filename) mpix_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_LOCALITY; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), MPI_INT, @@ -156,8 +156,8 @@ void test_matrix(const char* filename) compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); PMPI_Comm_free(&std_comm); } diff --git a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_init.cpp b/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_init.cpp index 3ea3295b0..800ce14ad 100644 --- a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_init.cpp +++ b/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_init.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -17,7 +17,7 @@ void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::v { if (pmpi_recv_vals[i] != mpix_recv_vals[i]) { - fprintf(stderr, "PMPI recv != MPIX: position %d, pmpi %d, mpix %d\n", i, + fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, pmpi_recv_vals[i], mpix_recv_vals[i]); MPI_Abort(MPI_COMM_WORLD, -1); } @@ -70,11 +70,11 @@ void test_matrix(const char* filename) MPI_Comm std_comm; MPI_Status status; - MPIX_Comm* xcomm; - MPIX_Request* xrequest; - MPIX_Info* xinfo; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; + MPIL_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info_init(&xinfo); int* s = A.recv_comm.procs.data(); if (A.recv_comm.n_msgs == 0) @@ -115,7 +115,7 @@ void test_matrix(const char* filename) delete[] recv_counts; compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - MPIX_Dist_graph_create_adjacent(MPI_COMM_WORLD, + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, A.recv_comm.n_msgs, A.recv_comm.procs.data(), MPI_UNWEIGHTED, @@ -131,7 +131,7 @@ void test_matrix(const char* filename) mpix_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_STANDARD; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), MPI_INT, @@ -146,7 +146,7 @@ void test_matrix(const char* filename) // 2. Node-Aware Communication mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), MPI_INT, @@ -158,16 +158,16 @@ void test_matrix(const char* filename) xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); // 3. MPI Advance - Optimized Communication mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), MPI_INT, @@ -179,16 +179,16 @@ void test_matrix(const char* filename) xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); // Standard from Extended Interface mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), send_indices.data(), @@ -206,7 +206,7 @@ void test_matrix(const char* filename) // Full Locality mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), send_indices.data(), @@ -220,13 +220,13 @@ void test_matrix(const char* filename) xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); PMPI_Comm_free(&std_comm); } diff --git a/src/neighborhood/tests/test_suitesparse_neighbor_reorder.cpp b/src/neighborhood/tests/test_suitesparse_neighbor_reorder.cpp index dee7276f9..1c51e1950 100644 --- a/src/neighborhood/tests/test_suitesparse_neighbor_reorder.cpp +++ b/src/neighborhood/tests/test_suitesparse_neighbor_reorder.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -17,7 +17,7 @@ void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::v { if (pmpi_recv_vals[i] != mpix_recv_vals[i]) { - fprintf(stderr, "PMPI recv != MPIX: position %d, pmpi %d, mpix %d\n", i, + fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, pmpi_recv_vals[i], mpix_recv_vals[i]); MPI_Abort(MPI_COMM_WORLD, -1); } @@ -70,11 +70,11 @@ void test_matrix(const char* filename) MPI_Comm std_comm; MPI_Status status; - MPIX_Comm* neighbor_comm; - MPIX_Request* neighbor_request; - MPIX_Info* xinfo; + MPIL_Comm* neighbor_comm; + MPIL_Request* neighbor_request; + MPIL_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info_init(&xinfo); int* s = A.recv_comm.procs.data(); if (A.recv_comm.n_msgs == 0) @@ -117,7 +117,7 @@ void test_matrix(const char* filename) compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - MPIX_Dist_graph_create_adjacent(MPI_COMM_WORLD, + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, A.recv_comm.n_msgs, A.recv_comm.procs.data(), MPI_UNWEIGHTED, @@ -132,7 +132,7 @@ void test_matrix(const char* filename) // 2. Node-Aware Communication - reorder during first send/recv - MPIX_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), MPI_INT, @@ -146,20 +146,20 @@ void test_matrix(const char* filename) std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); neighbor_request->reorder = 1; - MPIX_Start(neighbor_request); - MPIX_Wait(neighbor_request, &status); + MPIL_Start(neighbor_request); + MPIL_Wait(neighbor_request, &status); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); // Standard send/recv with reordered recvs std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Start(neighbor_request); - MPIX_Wait(neighbor_request, &status); - MPIX_Request_free(&neighbor_request); + MPIL_Start(neighbor_request); + MPIL_Wait(neighbor_request, &status); + MPIL_Request_free(&neighbor_request); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&neighbor_comm); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&neighbor_comm); PMPI_Comm_free(&std_comm); } diff --git a/src/neighborhood/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp b/src/neighborhood/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp index dacccf201..62754dd3a 100644 --- a/src/neighborhood/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp +++ b/src/neighborhood/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include @@ -17,7 +17,7 @@ void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::v { if (pmpi_recv_vals[i] != mpix_recv_vals[i]) { - fprintf(stderr, "PMPI recv != MPIX: position %d, pmpi %d, mpix %d\n", i, + fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, pmpi_recv_vals[i], mpix_recv_vals[i]); MPI_Abort(MPI_COMM_WORLD, -1); } @@ -71,15 +71,15 @@ void test_matrix(const char* filename) MPI_Comm std_comm; MPI_Status status; - MPIX_Request* xrequest; - MPIX_Comm* xcomm; - MPIX_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Request* xrequest; + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); update_locality(xcomm, 4); - MPIX_Info* xinfo; - MPIX_Info_init(&xinfo); + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); - MPIX_Topo* topo; - MPIX_Topo_init(A.recv_comm.n_msgs, + MPIL_Topo* topo; + MPIL_Topo_init(A.recv_comm.n_msgs, A.recv_comm.procs.data(), A.recv_comm.counts.data(), A.send_comm.n_msgs, @@ -130,7 +130,7 @@ void test_matrix(const char* filename) mpix_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_STANDARD; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_topo(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_topo(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), MPI_INT, @@ -145,7 +145,7 @@ void test_matrix(const char* filename) mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init_topo(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init_topo(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), MPI_INT, @@ -158,15 +158,15 @@ void test_matrix(const char* filename) xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init_topo(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init_topo(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), MPI_INT, @@ -179,16 +179,16 @@ void test_matrix(const char* filename) xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); // 3. MPI Advance - Optimized Communication mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init_ext_topo(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init_ext_topo(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), send_indices.data(), @@ -203,15 +203,15 @@ void test_matrix(const char* filename) xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIX_Neighbor_alltoallv_init_ext_topo(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init_ext_topo(alltoallv_send_vals.data(), A.send_comm.counts.data(), A.send_comm.ptr.data(), send_indices.data(), @@ -226,14 +226,14 @@ void test_matrix(const char* filename) xinfo, &xrequest); - MPIX_Start(xrequest); - MPIX_Wait(xrequest, &status); - MPIX_Request_free(&xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - MPIX_Topo_free(&topo); - MPIX_Info_free(&xinfo); - MPIX_Comm_free(&xcomm); + MPIL_Topo_free(&topo); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); PMPI_Comm_free(&std_comm); } diff --git a/src/persistent/neighbor_persistent.c b/src/persistent/neighbor_persistent.c index 34001e565..f236771af 100644 --- a/src/persistent/neighbor_persistent.c +++ b/src/persistent/neighbor_persistent.c @@ -1,6 +1,6 @@ #include "neighbor_persistent.h" -int neighbor_start(MPIX_Request* request) +int neighbor_start(MPIL_Request* request) { if (request == NULL) { @@ -78,7 +78,7 @@ int neighbor_start(MPIX_Request* request) // 2. Start and wait for local_R // 3. Wait for local_L // TODO : Currently ignores the status! -int neighbor_wait(MPIX_Request* request, MPI_Status* status) +int neighbor_wait(MPIL_Request* request, MPI_Status* status) { if (request == NULL) { diff --git a/src/persistent/neighbor_persistent.h b/src/persistent/neighbor_persistent.h index 999224509..69cd50f15 100644 --- a/src/persistent/neighbor_persistent.h +++ b/src/persistent/neighbor_persistent.h @@ -13,13 +13,13 @@ extern "C" { // 1. Start Local_L // 2. Start and wait for local_S // 3. Start global -int neighbor_start(MPIX_Request* request); +int neighbor_start(MPIL_Request* request); // Wait for locality-aware requests // 1. Wait for global // 2. Start and wait for local_R // 3. Wait for local_L -int neighbor_wait(MPIX_Request* request, MPI_Status* status); +int neighbor_wait(MPIL_Request* request, MPI_Status* status); #ifdef __cplusplus } diff --git a/src/persistent/persistent.c b/src/persistent/persistent.c index 00c3809a7..e75076166 100644 --- a/src/persistent/persistent.c +++ b/src/persistent/persistent.c @@ -1,8 +1,8 @@ #include "persistent.h" -void init_request(MPIX_Request** request_ptr) +void init_request(MPIL_Request** request_ptr) { - MPIX_Request* request = (MPIX_Request*)malloc(sizeof(MPIX_Request)); + MPIL_Request* request = (MPIL_Request*)malloc(sizeof(MPIL_Request)); request->locality = NULL; @@ -44,7 +44,7 @@ void allocate_requests(int n_requests, MPI_Request** request_ptr) // 1. Start Local_L // 2. Start and wait for local_S // 3. Start global -int MPIX_Start(MPIX_Request* request) +int MPIL_Start(MPIL_Request* request) { if (request == NULL) { @@ -60,7 +60,7 @@ int MPIX_Start(MPIX_Request* request) // 2. Start and wait for local_R // 3. Wait for local_L // TODO : Currently ignores the status! -int MPIX_Wait(MPIX_Request* request, MPI_Status* status) +int MPIL_Wait(MPIL_Request* request, MPI_Status* status) { if (request == NULL) { @@ -71,9 +71,9 @@ int MPIX_Wait(MPIX_Request* request, MPI_Status* status) return wait_function(request, status); } -int MPIX_Request_free(MPIX_Request** request_ptr) +int MPIL_Request_free(MPIL_Request** request_ptr) { - MPIX_Request* request = *request_ptr; + MPIL_Request* request = *request_ptr; if (request->local_L_n_msgs) { diff --git a/src/persistent/persistent.h b/src/persistent/persistent.h index 3f2bfcc97..f69cafc51 100644 --- a/src/persistent/persistent.h +++ b/src/persistent/persistent.h @@ -2,17 +2,17 @@ #define MPI_ADVANCE_PERSISTENT_H #include "communicator/locality_comm.h" -#include "communicator/mpix_comm.h" +#include "communicator/mpil_comm.h" #include "utils/utils.h" #ifdef __cplusplus extern "C" { #endif -struct _MPIX_Request; // forward declaration -typedef struct _MPIX_Request MPIX_Request; +struct _MPIL_Request; // forward declaration +typedef struct _MPIL_Request MPIL_Request; -struct _MPIX_Request +struct _MPIL_Request { // Message counts // Will only use global unless locality-aware @@ -51,30 +51,30 @@ struct _MPIX_Request #endif // Keep track of which start/wait functions to call for given request - int (*start_function)(MPIX_Request* request); - int (*wait_function)(MPIX_Request* request, MPI_Status* status); + int (*start_function)(MPIL_Request* request); + int (*wait_function)(MPIL_Request* request, MPI_Status* status); }; -typedef int (*mpix_start_ftn)(MPIX_Request* request); -typedef int (*mpix_wait_ftn)(MPIX_Request* request, MPI_Status* status); +typedef int (*mpix_start_ftn)(MPIL_Request* request); +typedef int (*mpix_wait_ftn)(MPIL_Request* request, MPI_Status* status); // Starting locality-aware requests // 1. Start Local_L // 2. Start and wait for local_S // 3. Start global -int MPIX_Start(MPIX_Request* request); +int MPIL_Start(MPIL_Request* request); // Wait for locality-aware requests // 1. Wait for global // 2. Start and wait for local_R // 3. Wait for local_L -int MPIX_Wait(MPIX_Request* request, MPI_Status* status); +int MPIL_Wait(MPIL_Request* request, MPI_Status* status); -int MPIX_Request_free(MPIX_Request** request); +int MPIL_Request_free(MPIL_Request** request); -void init_request(MPIX_Request** request_ptr); +void init_request(MPIL_Request** request_ptr); void allocate_requests(int n_requests, MPI_Request** request_ptr); -void destroy_request(MPIX_Request* request); +void destroy_request(MPIL_Request* request); #ifdef __cplusplus } diff --git a/src/tests/compare.hpp b/src/tests/compare.hpp index fe5e35acc..f15969c80 100644 --- a/src/tests/compare.hpp +++ b/src/tests/compare.hpp @@ -1,4 +1,4 @@ -#include "mpi_advance.h" +#include "locality_aware.h" #include #include #include diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index bc17fa261..e7c1ea29b 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -10,10 +10,10 @@ #include "hip/hip_runtime.h" #endif -// MPIX Info Object Routines -int MPIX_Info_init(MPIX_Info** info_ptr) +// MPIL Info Object Routines +int MPIL_Info_init(MPIL_Info** info_ptr) { - MPIX_Info* xinfo = (MPIX_Info*)malloc(sizeof(MPIX_Info)); + MPIL_Info* xinfo = (MPIL_Info*)malloc(sizeof(MPIL_Info)); xinfo->crs_num_initialized = 0; xinfo->crs_size_initialized = 0; @@ -22,9 +22,9 @@ int MPIX_Info_init(MPIX_Info** info_ptr) return MPI_SUCCESS; } -int MPIX_Info_free(MPIX_Info** info_ptr) +int MPIL_Info_free(MPIL_Info** info_ptr) { - MPIX_Info* xinfo = *info_ptr; + MPIL_Info* xinfo = *info_ptr; free(xinfo); return MPI_SUCCESS; @@ -216,7 +216,7 @@ void get_memcpy_kind(gpuMemoryType send_type, } #endif -int MPIX_Alloc(void** pointer, const int bytes) +int MPIL_Alloc(void** pointer, const int bytes) { if (bytes == 0) { @@ -229,7 +229,7 @@ int MPIX_Alloc(void** pointer, const int bytes) return MPI_SUCCESS; } -int MPIX_Free(void* pointer) +int MPIL_Free(void* pointer) { if (pointer != NULL) { diff --git a/src/utils/utils.h b/src/utils/utils.h index d56a74f60..6b7baf1ea 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -13,15 +13,15 @@ extern "C" { #endif -// MPIX Info Object -typedef struct _MPIX_Info +// MPIL Info Object +typedef struct _MPIL_Info { int crs_num_initialized; int crs_size_initialized; -} MPIX_Info; +} MPIL_Info; -int MPIX_Info_init(MPIX_Info** info); -int MPIX_Info_free(MPIX_Info** info); +int MPIL_Info_init(MPIL_Info** info); +int MPIL_Info_free(MPIL_Info** info); // If using GPU, specific gpu methods (for either NCCL or HIP) #ifdef GPU @@ -48,8 +48,8 @@ void reverse(void* recvbuf, int n_bytes, int var_bytes); void repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf); // Allocate Vector in MPI -int MPIX_Alloc(void** pointer, const int bytes); -int MPIX_Free(void* pointer); +int MPIL_Alloc(void** pointer, const int bytes); +int MPIL_Free(void* pointer); #ifdef __cplusplus } From c5532e1013377daf5b6d3dc315b75e8b4f0cfc15 Mon Sep 17 00:00:00 2001 From: aworley16 <51372652+aworley16@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:33:29 -0500 Subject: [PATCH 05/15] Re-added enable tests to top level CMakeList.txt (#45) --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28289d68a..9dc5bf109 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,10 @@ endif() message(STATUS "Compiling all files as: ${locality_aware_LANG}") +if (ENABLE_UNIT_TESTS) + enable_testing() +endif() + add_subdirectory(src) set_source_files_properties( From 54b7aaffbcca734d4c2d503ea0b58495a8c30c83 Mon Sep 17 00:00:00 2001 From: aworley16 <51372652+aworley16@users.noreply.github.com> Date: Fri, 24 Oct 2025 09:59:59 -0500 Subject: [PATCH 06/15] Reorganize and restructure code base into new MPI Advance project structure (#47) Major locality_aware.h, internal CMake, and source file location changes. Please see PR 47 for full description of changes. Key changes for end users: - New MPIL apis for setting which collective function should be used under the hood - Most internal functions are no longer callable by the end user; some have been upgraded into MPIL functions. --- .gitignore | 3 + CMakeLists.txt | 116 +-- benchmarks/CMakeLists.txt | 22 +- benchmarks/alltoall_crs.cpp | 182 +++- benchmarks/alltoallv_crs.cpp | 252 +++-- benchmarks/gpu_alltoall.cpp | 352 +++---- benchmarks/microbenchmarks.cpp | 434 +++++--- benchmarks/p2p_alltoall.cpp | 118 ++- benchmarks/p2p_alltoallv.cpp | 160 +-- include/CMakeLists.txt | 5 + include/locality_aware.h | 283 +++++ library/CMakeLists.txt | 5 + library/bindings/CMakeLists.txt | 34 + library/bindings/Collective/MPIL_Alltoall.c | 79 ++ .../bindings/Collective/MPIL_Alltoall_crs.c | 51 + library/bindings/Collective/MPIL_Alltoallv.c | 63 ++ .../bindings/Collective/MPIL_Alltoallv_crs.c | 58 ++ library/bindings/MPIL_Alloc/MPIL_Alloc.cpp | 15 + library/bindings/MPIL_Alloc/MPIL_Free.cpp | 12 + library/bindings/MPIL_Comm/CMakeLists.txt | 4 + .../MPIL_Comm/MPIL_Comm_device_free.c | 20 + .../MPIL_Comm/MPIL_Comm_device_init.c | 28 + library/bindings/MPIL_Comm/MPIL_Comm_free.c | 28 + library/bindings/MPIL_Comm/MPIL_Comm_init.c | 47 + .../MPIL_Comm/MPIL_Comm_leader_free.c | 20 + .../MPIL_Comm/MPIL_Comm_leader_init.c | 26 + .../bindings/MPIL_Comm/MPIL_Comm_req_resize.c | 18 + library/bindings/MPIL_Comm/MPIL_Comm_tag.c | 7 + .../bindings/MPIL_Comm/MPIL_Comm_topo_free.c | 31 + .../bindings/MPIL_Comm/MPIL_Comm_topo_init.c | 59 ++ .../MPIL_Comm/MPIL_Comm_update_locality.c | 7 + .../bindings/MPIL_Comm/MPIL_Comm_win_free.c | 24 + .../bindings/MPIL_Comm/MPIL_Comm_win_init.c | 21 + library/bindings/MPIL_Info/MPIL_Info_free.cpp | 12 + library/bindings/MPIL_Info/MPIL_Info_init.cpp | 16 + .../bindings/MPIL_Request/MPIL_Request_free.c | 70 ++ .../MPIL_Request/MPIL_Request_reorder.c | 8 + library/bindings/MPIL_Request/MPIL_Start.c | 19 + library/bindings/MPIL_Request/MPIL_Wait.c | 20 + library/bindings/Neighborhood/CMakeLists.txt | 3 + .../MPIL_Dist_graph_create_adjacent.c | 3 +- .../Neighborhood/MPIL_Neighbor_alltoallv.c | 31 + .../MPIL_Neighbor_alltoallv_init.c | 35 + .../MPIL_Neighbor_alltoallv_init_ext.c | 39 + .../MPIL_Neighbor_alltoallv_init_ext_topo.c | 64 ++ .../MPIL_Neighbor_alltoallv_init_topo.c | 44 + .../MPIL_Neighbor_alltoallv_topo.c | 43 + library/bindings/global_defaults.c | 48 + .../include}/collective/alltoall.h | 136 ++- .../include}/collective/alltoallv.h | 15 +- library/include/communicator/MPIL_Comm.h | 66 ++ library/include/communicator/MPIL_Info.h | 18 + .../include}/communicator/comm_data.h | 2 - .../include}/communicator/comm_pkg.h | 0 .../include}/communicator/locality_comm.h | 5 +- .../include}/heterogeneous/gpu_alltoall.h | 3 +- .../include}/heterogeneous/gpu_alltoallv.h | 2 +- .../include/heterogeneous/gpu_utils.h | 23 +- .../include/heterogeneous}/utils_cuda.h | 1 + .../include/heterogeneous}/utils_hip.h | 1 - library/include/neighborhood/MPIL_Topo.h | 23 + .../include/neighborhood/alltoall_crs.h | 74 +- .../include}/neighborhood/neighbor.h | 35 +- .../include/neighborhood/neighbor_locality.h | 46 + .../include/neighborhood/neighborhood_init.h | 105 ++ .../include/persistent/MPIL_Request.h | 146 ++- library/include/utils/utils.h | 51 + library/source/CMakeLists.txt | 10 + library/source/collective/CMakeLists.txt | 2 + .../source/collective/alltoall/CMakeLists.txt | 23 + .../alltoall/alltoall_hierarchical.c | 14 + .../alltoall_hierarchical_nonblocking.c | 19 + .../alltoall/alltoall_hierarchical_pairwise.c | 19 + .../alltoall/alltoall_locality_aware.c | 71 ++ .../alltoall/alltoall_locality_aware_helper.c | 88 ++ .../alltoall_locality_aware_nonblocking.c | 20 + .../alltoall_locality_aware_pairwise.c | 20 + .../alltoall/alltoall_multileader.c | 165 +++ .../alltoall/alltoall_multileader_locality.c | 192 ++++ ...lltoall_multileader_locality_nonblocking.c | 19 + .../alltoall_multileader_locality_pairwise.c | 19 + .../alltoall_multileader_nonblocking.c | 20 + .../alltoall/alltoall_multileader_pairwise.c | 20 + .../collective/alltoall/alltoall_node_aware.c | 14 + .../alltoall_node_aware_nonblocking.c | 19 + .../alltoall/alltoall_node_aware_pairwise.c | 19 + .../alltoall/alltoall_nonblocking.c | 23 + .../collective/alltoall/alltoall_pairwise.c | 23 + .../collective/alltoall/alltoall_pmpi.c | 13 + .../collective/alltoall/nonblocking_helper.c | 65 ++ .../collective/alltoall/pairwise_helper.c | 58 ++ .../collective/alltoallv/CMakeLists.txt | 7 + .../collective/alltoallv/alltoallv_batch.c | 98 ++ .../alltoallv/alltoallv_batch_async.c | 96 ++ .../alltoallv/alltoallv_nonblocking.c | 85 ++ .../collective/alltoallv/alltoallv_pairwise.c | 65 ++ .../collective/alltoallv/alltoallv_pmpi.c | 23 + library/source/communicator/CMakeLists.txt | 8 + .../source}/communicator/comm_data.c | 4 +- .../source}/communicator/comm_pkg.c | 4 +- library/source/communicator/get_tag.c | 9 + .../source}/communicator/locality_comm.c | 18 +- .../source/communicator/topology_functions.c | 17 + library/source/communicator/update_locality.c | 68 ++ library/source/heterogeneous/CMakeLists.txt | 23 + library/source/heterogeneous/device_repack.c | 23 + library/source/heterogeneous/get_mem_types.c | 37 + .../source/heterogeneous/get_memcpy_kind.c | 27 + .../source}/heterogeneous/gpu_alltoall.c | 41 +- .../source}/heterogeneous/gpu_alltoallv.c | 7 +- library/source/heterogeneous/gpu_check.c | 11 + library/source/heterogeneous/gpu_repack.c | 13 + library/source/heterogeneous/repack.cpp | 63 ++ library/source/neighborhood/CMakeLists.txt | 18 + .../neighborhood/MPIL_Topo/MPIL_Topo_free.c | 29 + .../MPIL_Topo/MPIL_Topo_from_neighbor_comm.c | 55 + .../neighborhood/MPIL_Topo/MPIL_Topo_init.c | 61 ++ .../neighbor_alltoallv_init_locality.c | 123 +++ .../neighbor_alltoallv_init_locality_ext.c | 123 +++ .../locality/neighbor_alltoallv_locality.c | 86 ++ .../locality}/neighbor_locality.cpp | 85 +- .../persistent/init_communication.c | 63 ++ .../persistent/init_neighbor_request.c | 10 + .../neighborhood/persistent/neighbor_start.c | 77 ++ .../neighborhood/persistent/neighbor_wait.c | 246 ++--- .../neighborhood/sparse_col}/alltoall_crs.cpp | 28 +- .../sparse_col}/alltoallv_crs.cpp | 102 +- .../sparse_col}/sparse_coll_utils.cpp | 24 +- .../neighbor_alltoallv_init_standard.c | 62 ++ .../standard/neighbor_alltoallv_standard.c | 83 ++ library/source/persistent/CMakeLists.txt | 4 + library/source/persistent/allocate_requests.c | 16 + library/source/persistent/init_request.c | 31 + library/source/utils/CMakeLists.txt | 1 + library/source/utils/utils.cpp | 30 + library/tests/CMakeLists.txt | 14 + library/tests/alltoall_c.c | 82 ++ library/tests/gpu_tests/CMakeLists.txt | 12 + library/tests/gpu_tests/test_gpu_alltoall.cpp | 154 +++ .../tests/gpu_tests/test_gpu_alltoallv.cpp | 283 +++++ library/tests/test_alltoall.cpp | 230 ++++ library/tests/test_alltoall_enum.cpp | 223 ++++ library/tests/test_alltoallv.cpp | 150 +++ library/tests/test_alltoallv_enum.cpp | 151 +++ .../tests/test_neighbor_alltoallv_init.cpp | 219 ++++ library/tests/test_neighbor_reorder.cpp | 166 +++ .../test_neighbor_topo_alltoallv_init.cpp | 203 ++++ .../tests/test_suitesparse_alltoall_crs.cpp | 193 ++++ library/tests/test_suitesparse_alltoallv.cpp | 194 ++++ .../tests/test_suitesparse_alltoallv_crs.cpp | 274 +++++ .../test_suitesparse_neighbor_alltoallv.cpp | 227 ++++ ...st_suitesparse_neighbor_alltoallv_enum.cpp | 208 ++++ ...st_suitesparse_neighbor_alltoallv_init.cpp | 277 +++++ .../test_suitesparse_neighbor_reorder.cpp | 208 ++++ ...itesparse_neighbor_topo_alltoallv_init.cpp | 285 +++++ library/tests/tests/compare.hpp | 89 ++ .../tests}/tests/neighbor_data.hpp | 104 +- .../tests}/tests/par_binary_IO.hpp | 665 +++++++----- {src => library/tests}/tests/sparse_mat.hpp | 770 ++++++++------ src/CMakeLists.txt | 17 - src/collective/CMakeLists.txt | 6 - src/collective/alltoall.c | 984 ------------------ src/collective/alltoallv.c | 446 -------- src/collective/collective.h | 47 - src/collective/tests/CMakeLists.txt | 23 - src/collective/tests/test_alltoall.cpp | 216 ---- src/collective/tests/test_alltoall_enum.cpp | 230 ---- src/collective/tests/test_alltoallv.cpp | 145 --- src/collective/tests/test_alltoallv_enum.cpp | 151 --- .../tests/test_suitesparse_alltoallv.cpp | 179 ---- src/communicator/CMakeLists.txt | 6 - src/communicator/mpil_comm.c | 369 ------- src/communicator/mpil_comm.h | 90 -- src/communicator/tests/CMakeLists.txt | 1 - src/heterogeneous/CMakeLists.txt | 7 - src/heterogeneous/tests/CMakeLists.txt | 23 - src/heterogeneous/tests/test_gpu_alltoall.cpp | 184 ---- .../tests/test_gpu_alltoallv.cpp | 193 ---- src/locality_aware.h | 25 - src/neighborhood/CMakeLists.txt | 6 - src/neighborhood/dist_graph.h | 28 - src/neighborhood/dist_topo.c | 134 --- src/neighborhood/dist_topo.h | 41 - src/neighborhood/neighbor.c | 233 ----- src/neighborhood/neighbor_init.c | 560 ---------- src/neighborhood/neighbor_init.h | 161 --- src/neighborhood/sparse_coll.c | 66 -- src/neighborhood/tests/CMakeLists.txt | 23 - .../tests/test_neighbor_alltoallv_init.cpp | 214 ---- .../tests/test_neighbor_reorder.cpp | 149 --- .../test_neighbor_topo_alltoallv_init.cpp | 184 ---- .../tests/test_suitesparse_alltoall_crs.cpp | 141 --- .../tests/test_suitesparse_alltoallv_crs.cpp | 165 --- .../test_suitesparse_neighbor_alltoallv.cpp | 210 ---- ...st_suitesparse_neighbor_alltoallv_enum.cpp | 191 ---- ...st_suitesparse_neighbor_alltoallv_init.cpp | 260 ----- .../test_suitesparse_neighbor_reorder.cpp | 192 ---- ...itesparse_neighbor_topo_alltoallv_init.cpp | 267 ----- src/persistent/CMakeLists.txt | 6 - src/persistent/neighbor_persistent.h | 28 - src/persistent/persistent.c | 135 --- src/tests/compare.hpp | 66 -- src/utils/CMakeLists.txt | 20 - src/utils/utils.cpp | 241 ----- 204 files changed, 10394 insertions(+), 8900 deletions(-) create mode 100644 include/CMakeLists.txt create mode 100644 include/locality_aware.h create mode 100644 library/CMakeLists.txt create mode 100644 library/bindings/CMakeLists.txt create mode 100644 library/bindings/Collective/MPIL_Alltoall.c create mode 100644 library/bindings/Collective/MPIL_Alltoall_crs.c create mode 100644 library/bindings/Collective/MPIL_Alltoallv.c create mode 100644 library/bindings/Collective/MPIL_Alltoallv_crs.c create mode 100644 library/bindings/MPIL_Alloc/MPIL_Alloc.cpp create mode 100644 library/bindings/MPIL_Alloc/MPIL_Free.cpp create mode 100644 library/bindings/MPIL_Comm/CMakeLists.txt create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_device_free.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_device_init.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_free.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_init.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_leader_free.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_leader_init.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_req_resize.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_tag.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_topo_free.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_topo_init.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_update_locality.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_win_free.c create mode 100644 library/bindings/MPIL_Comm/MPIL_Comm_win_init.c create mode 100644 library/bindings/MPIL_Info/MPIL_Info_free.cpp create mode 100644 library/bindings/MPIL_Info/MPIL_Info_init.cpp create mode 100644 library/bindings/MPIL_Request/MPIL_Request_free.c create mode 100644 library/bindings/MPIL_Request/MPIL_Request_reorder.c create mode 100644 library/bindings/MPIL_Request/MPIL_Start.c create mode 100644 library/bindings/MPIL_Request/MPIL_Wait.c create mode 100644 library/bindings/Neighborhood/CMakeLists.txt rename src/neighborhood/dist_graph.c => library/bindings/Neighborhood/MPIL_Dist_graph_create_adjacent.c (95%) create mode 100644 library/bindings/Neighborhood/MPIL_Neighbor_alltoallv.c create mode 100644 library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init.c create mode 100644 library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_ext.c create mode 100644 library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_ext_topo.c create mode 100644 library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_topo.c create mode 100644 library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_topo.c create mode 100644 library/bindings/global_defaults.c rename {src => library/include}/collective/alltoall.h (54%) rename {src => library/include}/collective/alltoallv.h (88%) create mode 100644 library/include/communicator/MPIL_Comm.h create mode 100644 library/include/communicator/MPIL_Info.h rename {src => library/include}/communicator/comm_data.h (93%) rename {src => library/include}/communicator/comm_pkg.h (100%) rename {src => library/include}/communicator/locality_comm.h (94%) rename {src => library/include}/heterogeneous/gpu_alltoall.h (98%) rename {src => library/include}/heterogeneous/gpu_alltoallv.h (99%) rename src/utils/utils.h => library/include/heterogeneous/gpu_utils.h (62%) rename {src/utils => library/include/heterogeneous}/utils_cuda.h (98%) rename {src/utils => library/include/heterogeneous}/utils_hip.h (97%) create mode 100644 library/include/neighborhood/MPIL_Topo.h rename src/neighborhood/sparse_coll.h => library/include/neighborhood/alltoall_crs.h (79%) rename {src => library/include}/neighborhood/neighbor.h (60%) create mode 100644 library/include/neighborhood/neighbor_locality.h create mode 100644 library/include/neighborhood/neighborhood_init.h rename src/persistent/persistent.h => library/include/persistent/MPIL_Request.h (61%) create mode 100644 library/include/utils/utils.h create mode 100644 library/source/CMakeLists.txt create mode 100644 library/source/collective/CMakeLists.txt create mode 100644 library/source/collective/alltoall/CMakeLists.txt create mode 100644 library/source/collective/alltoall/alltoall_hierarchical.c create mode 100644 library/source/collective/alltoall/alltoall_hierarchical_nonblocking.c create mode 100644 library/source/collective/alltoall/alltoall_hierarchical_pairwise.c create mode 100644 library/source/collective/alltoall/alltoall_locality_aware.c create mode 100644 library/source/collective/alltoall/alltoall_locality_aware_helper.c create mode 100644 library/source/collective/alltoall/alltoall_locality_aware_nonblocking.c create mode 100644 library/source/collective/alltoall/alltoall_locality_aware_pairwise.c create mode 100644 library/source/collective/alltoall/alltoall_multileader.c create mode 100644 library/source/collective/alltoall/alltoall_multileader_locality.c create mode 100644 library/source/collective/alltoall/alltoall_multileader_locality_nonblocking.c create mode 100644 library/source/collective/alltoall/alltoall_multileader_locality_pairwise.c create mode 100644 library/source/collective/alltoall/alltoall_multileader_nonblocking.c create mode 100644 library/source/collective/alltoall/alltoall_multileader_pairwise.c create mode 100644 library/source/collective/alltoall/alltoall_node_aware.c create mode 100644 library/source/collective/alltoall/alltoall_node_aware_nonblocking.c create mode 100644 library/source/collective/alltoall/alltoall_node_aware_pairwise.c create mode 100644 library/source/collective/alltoall/alltoall_nonblocking.c create mode 100644 library/source/collective/alltoall/alltoall_pairwise.c create mode 100644 library/source/collective/alltoall/alltoall_pmpi.c create mode 100644 library/source/collective/alltoall/nonblocking_helper.c create mode 100644 library/source/collective/alltoall/pairwise_helper.c create mode 100644 library/source/collective/alltoallv/CMakeLists.txt create mode 100644 library/source/collective/alltoallv/alltoallv_batch.c create mode 100644 library/source/collective/alltoallv/alltoallv_batch_async.c create mode 100644 library/source/collective/alltoallv/alltoallv_nonblocking.c create mode 100644 library/source/collective/alltoallv/alltoallv_pairwise.c create mode 100644 library/source/collective/alltoallv/alltoallv_pmpi.c create mode 100644 library/source/communicator/CMakeLists.txt rename {src => library/source}/communicator/comm_data.c (95%) rename {src => library/source}/communicator/comm_pkg.c (91%) create mode 100644 library/source/communicator/get_tag.c rename {src => library/source}/communicator/locality_comm.c (88%) create mode 100644 library/source/communicator/topology_functions.c create mode 100644 library/source/communicator/update_locality.c create mode 100644 library/source/heterogeneous/CMakeLists.txt create mode 100644 library/source/heterogeneous/device_repack.c create mode 100644 library/source/heterogeneous/get_mem_types.c create mode 100644 library/source/heterogeneous/get_memcpy_kind.c rename {src => library/source}/heterogeneous/gpu_alltoall.c (98%) rename {src => library/source}/heterogeneous/gpu_alltoallv.c (99%) create mode 100644 library/source/heterogeneous/gpu_check.c create mode 100644 library/source/heterogeneous/gpu_repack.c create mode 100644 library/source/heterogeneous/repack.cpp create mode 100644 library/source/neighborhood/CMakeLists.txt create mode 100644 library/source/neighborhood/MPIL_Topo/MPIL_Topo_free.c create mode 100644 library/source/neighborhood/MPIL_Topo/MPIL_Topo_from_neighbor_comm.c create mode 100644 library/source/neighborhood/MPIL_Topo/MPIL_Topo_init.c create mode 100644 library/source/neighborhood/locality/neighbor_alltoallv_init_locality.c create mode 100644 library/source/neighborhood/locality/neighbor_alltoallv_init_locality_ext.c create mode 100644 library/source/neighborhood/locality/neighbor_alltoallv_locality.c rename {src/neighborhood => library/source/neighborhood/locality}/neighbor_locality.cpp (90%) create mode 100644 library/source/neighborhood/persistent/init_communication.c create mode 100644 library/source/neighborhood/persistent/init_neighbor_request.c create mode 100644 library/source/neighborhood/persistent/neighbor_start.c rename src/persistent/neighbor_persistent.c => library/source/neighborhood/persistent/neighbor_wait.c (52%) rename {src/neighborhood => library/source/neighborhood/sparse_col}/alltoall_crs.cpp (98%) rename {src/neighborhood => library/source/neighborhood/sparse_col}/alltoallv_crs.cpp (93%) rename {src/neighborhood => library/source/neighborhood/sparse_col}/sparse_coll_utils.cpp (98%) create mode 100644 library/source/neighborhood/standard/neighbor_alltoallv_init_standard.c create mode 100644 library/source/neighborhood/standard/neighbor_alltoallv_standard.c create mode 100644 library/source/persistent/CMakeLists.txt create mode 100644 library/source/persistent/allocate_requests.c create mode 100644 library/source/persistent/init_request.c create mode 100644 library/source/utils/CMakeLists.txt create mode 100644 library/source/utils/utils.cpp create mode 100644 library/tests/CMakeLists.txt create mode 100644 library/tests/alltoall_c.c create mode 100644 library/tests/gpu_tests/CMakeLists.txt create mode 100644 library/tests/gpu_tests/test_gpu_alltoall.cpp create mode 100644 library/tests/gpu_tests/test_gpu_alltoallv.cpp create mode 100644 library/tests/test_alltoall.cpp create mode 100644 library/tests/test_alltoall_enum.cpp create mode 100644 library/tests/test_alltoallv.cpp create mode 100644 library/tests/test_alltoallv_enum.cpp create mode 100644 library/tests/test_neighbor_alltoallv_init.cpp create mode 100644 library/tests/test_neighbor_reorder.cpp create mode 100644 library/tests/test_neighbor_topo_alltoallv_init.cpp create mode 100644 library/tests/test_suitesparse_alltoall_crs.cpp create mode 100644 library/tests/test_suitesparse_alltoallv.cpp create mode 100644 library/tests/test_suitesparse_alltoallv_crs.cpp create mode 100644 library/tests/test_suitesparse_neighbor_alltoallv.cpp create mode 100644 library/tests/test_suitesparse_neighbor_alltoallv_enum.cpp create mode 100644 library/tests/test_suitesparse_neighbor_alltoallv_init.cpp create mode 100644 library/tests/test_suitesparse_neighbor_reorder.cpp create mode 100644 library/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp create mode 100644 library/tests/tests/compare.hpp rename {src/neighborhood => library/tests}/tests/neighbor_data.hpp (55%) rename {src => library/tests}/tests/par_binary_IO.hpp (51%) mode change 100755 => 100644 rename {src => library/tests}/tests/sparse_mat.hpp (52%) mode change 100755 => 100644 delete mode 100644 src/CMakeLists.txt delete mode 100644 src/collective/CMakeLists.txt delete mode 100644 src/collective/alltoall.c delete mode 100644 src/collective/alltoallv.c delete mode 100644 src/collective/collective.h delete mode 100644 src/collective/tests/CMakeLists.txt delete mode 100644 src/collective/tests/test_alltoall.cpp delete mode 100644 src/collective/tests/test_alltoall_enum.cpp delete mode 100644 src/collective/tests/test_alltoallv.cpp delete mode 100644 src/collective/tests/test_alltoallv_enum.cpp delete mode 100644 src/collective/tests/test_suitesparse_alltoallv.cpp delete mode 100644 src/communicator/CMakeLists.txt delete mode 100644 src/communicator/mpil_comm.c delete mode 100644 src/communicator/mpil_comm.h delete mode 100644 src/communicator/tests/CMakeLists.txt delete mode 100644 src/heterogeneous/CMakeLists.txt delete mode 100644 src/heterogeneous/tests/CMakeLists.txt delete mode 100644 src/heterogeneous/tests/test_gpu_alltoall.cpp delete mode 100644 src/heterogeneous/tests/test_gpu_alltoallv.cpp delete mode 100644 src/locality_aware.h delete mode 100644 src/neighborhood/CMakeLists.txt delete mode 100644 src/neighborhood/dist_graph.h delete mode 100644 src/neighborhood/dist_topo.c delete mode 100644 src/neighborhood/dist_topo.h delete mode 100644 src/neighborhood/neighbor.c delete mode 100644 src/neighborhood/neighbor_init.c delete mode 100644 src/neighborhood/neighbor_init.h delete mode 100644 src/neighborhood/sparse_coll.c delete mode 100644 src/neighborhood/tests/CMakeLists.txt delete mode 100644 src/neighborhood/tests/test_neighbor_alltoallv_init.cpp delete mode 100644 src/neighborhood/tests/test_neighbor_reorder.cpp delete mode 100644 src/neighborhood/tests/test_neighbor_topo_alltoallv_init.cpp delete mode 100644 src/neighborhood/tests/test_suitesparse_alltoall_crs.cpp delete mode 100644 src/neighborhood/tests/test_suitesparse_alltoallv_crs.cpp delete mode 100644 src/neighborhood/tests/test_suitesparse_neighbor_alltoallv.cpp delete mode 100644 src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_enum.cpp delete mode 100644 src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_init.cpp delete mode 100644 src/neighborhood/tests/test_suitesparse_neighbor_reorder.cpp delete mode 100644 src/neighborhood/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp delete mode 100644 src/persistent/CMakeLists.txt delete mode 100644 src/persistent/neighbor_persistent.h delete mode 100644 src/persistent/persistent.c delete mode 100644 src/tests/compare.hpp delete mode 100644 src/utils/CMakeLists.txt delete mode 100644 src/utils/utils.cpp diff --git a/.gitignore b/.gitignore index 44af108b0..8596bf421 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ *.swp __pycache__ + +*docs* +**.vscode/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dc5bf109..41f92f7a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,95 +29,81 @@ option(USE_CUDA "Enable NVIDIA CUDA support" OFF) option(USE_HIP "Enable AMD HIP support" OFF) option(GPU_AWARE "Use GPU-Aware MPI, if GPU support is enabled" ON) option(ENABLE_UNIT_TESTS "Enable unit testing" ON) +option(BENCHMARKS "Enable benchmarks" ON) -set(MPICXX "mpicxx" CACHE STRING "MPICXX command") set(MPIRUN "mpirun" CACHE STRING "MPIRUN command") -set(CUDA_ARCH "75" CACHE STRING "CUDA Architecture") # Find MPI find_package(MPI REQUIRED) set(EXTERNAL_LIBS "MPI::MPI_CXX") -# Find OpenMP (not required) -find_package(OpenMP) -if (${OpenMP_CXX_FOUND}) - list(APPEND EXTERNAL_LIBS "OpenMP::OpenMP_CXX") - add_definitions(-DOPENMP) +if(NOT USE_HIP) + # Find OpenMP (not required) + find_package(OpenMP) + if (${OpenMP_CXX_FOUND}) + list(APPEND EXTERNAL_LIBS "OpenMP::OpenMP_CXX") + add_definitions(-DOPENMP) + endif() endif() + # Set default language of .cpp files set(locality_aware_LANG CXX) +add_library(locality_aware "") if (USE_CUDA OR USE_HIP) - if (GPU_AWARE) - add_definitions(-DGPU_AWARE) - message(STATUS "Code will be compiled assuming GPU-Aware support. If your compiler doesn't not support this, set GPU_AWARE to OFF") - endif() - set(USE_GPU ON) - add_definitions(-DGPU) -if (USE_CUDA) - set(CMAKE_CUDA_HOST_COMPILER "${MPICXX}") - set(CMAKE_CUDA_ARCHITECTURES ${CUDA_ARCH}) - enable_language(CUDA) - set(locality_aware_LANG CUDA) - message(STATUS "CUDA support enabled: ${CMAKE_CUDA_FLAGS}") - add_definitions(-DCUDA) - -elseif (USE_HIP) - set(CMAKE_HIP_COMPILER "${MPICXX}") - enable_language(HIP) - set(locality_aware_LANG HIP) - message(STATUS "HIP support enabled") - add_definitions(-DHIP) -endif() + if (GPU_AWARE) + add_definitions(-DGPU_AWARE) + message(STATUS "Code will be compiled assuming GPU-Aware support. If your compiler doesn't not support this, set GPU_AWARE to OFF") + endif() + set(USE_GPU ON) + target_compile_definitions(locality_aware PUBLIC GPU) + define_property(GLOBAL PROPERTY CUDA_SOURCES_GLOBAL) + set_property(GLOBAL PROPERTY GPU_SOURCES_GLOBAL "") + + if (USE_CUDA) + enable_language(CUDA) + set(locality_aware_LANG CUDA) + message(STATUS "CUDA support enabled: ${CMAKE_CUDA_FLAGS}") + target_compile_definitions(locality_aware PUBLIC CUDA) + + elseif (USE_HIP) + enable_language(HIP) + set(locality_aware_LANG HIP) + message(STATUS "HIP support enabled") + target_compile_definitions(locality_aware PUBLIC HIP) + endif() endif() -message(STATUS "Compiling all files as: ${locality_aware_LANG}") - if (ENABLE_UNIT_TESTS) enable_testing() + set(TEST_PROCS "16" CACHE STRING "Number of processes to use when making ctests") + function(make_test file) + #get file name for unique handle + get_filename_component(exec_name ${file} NAME_WE) + add_executable(${exec_name} ${file}) + target_link_libraries(${exec_name} locality_aware ${EXTERNAL_LIBS}) + add_test(NAME ${exec_name}_Test COMMAND ${MPIRUN} -n ${TEST_PROCS} ./${exec_name}) + endfunction() endif() -add_subdirectory(src) - -set_source_files_properties( - ${utils_SOURCES} - ${communicator_SOURCES} - ${collective_SOURCES} - ${persistent_SOURCES} - ${neighborhood_SOURCES} - ${heterogeneous_SOURCES} - PROPERTIES LANGUAGE ${locality_aware_LANG}) - -add_library(locality_aware - ${utils_SOURCES} ${utils_HEADERS} - ${communicator_SOURCES} ${communicator_HEADERS} - ${collective_SOURCES} ${collective_HEADERS} - ${persistent_SOURCES} ${persistent_HEADERS} - ${neighborhood_SOURCES} ${neighborhood_HEADERS} - ${heterogeneous_SOURCES} ${heterogeneous_HEADERS} -) +if (BENCHMARKS) + add_subdirectory(benchmarks) +endif() -set_target_properties(locality_aware PROPERTIES PUBLIC_HEADER src/locality_aware.h) -target_include_directories(locality_aware PUBLIC - $ - $ - ) +set_target_properties(locality_aware PROPERTIES PUBLIC_HEADER include/locality_aware.h) +add_subdirectory(include) +add_subdirectory(library) target_link_libraries(locality_aware PUBLIC ${EXTERNAL_LIBS}) -install(TARGETS locality_aware) - -### Install all headers that locality_aware exports -install(FILES ${utils_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/utils) -install(FILES ${communicator_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/communicator) -install(FILES ${collective_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/collective) -install(FILES ${persistent_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/persistent) -install(FILES ${neighborhood_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/neighborhood) -install(FILES ${heterogeneous_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/heterogeneous) +if (USE_GPU) + get_property(GPU_SOURCES_GLOBAL GLOBAL PROPERTY GPU_SOURCES_GLOBAL) + set_source_files_properties(${GPU_SOURCES_GLOBAL} PROPERTIES LANGUAGE ${locality_aware_LANG}) +endif() - add_subdirectory(benchmarks) +install(TARGETS locality_aware) ### Installation Requirements for Spack Package ### include(CMakePackageConfigHelpers) @@ -127,8 +113,6 @@ install(TARGETS locality_aware EXPORT locality_awareTargets DESTINATION lib) - - # Create the configuration file configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/locality_aware-config.cmake.in diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index abe1af5d0..c441181ec 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -8,23 +8,25 @@ list(APPEND CPP_SOURCES "microbenchmarks.cpp") list(APPEND CPP_SOURCES "p2p_alltoall.cpp") list(APPEND CPP_SOURCES "p2p_alltoallv.cpp") -## But some need special requirements -if(USE_GPU) - list(APPEND CPP_SOURCES "gpu_alltoall.cpp") -endif() +## But some need special requirements -- removed until necessary parameters obtained. +# if(USE_GPU) + # list(APPEND CPP_SOURCES "gpu_alltoall.cpp") +# endif() ## All should be compiled appropriately (cxx vs nvcc vs hipcc) set_source_files_properties(${CPP_SOURCES} PROPERTIES LANGUAGE ${locality_aware_LANG}) +## Assign a matrix file for testing. +set(TEST_MATRIX_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../test_data/bcsstk01.pm") + ## Go through each CPP file foreach(src ${CPP_SOURCES}) - - ## Grab the name without full path or extension get_filename_component(exec_name ${src} NAME_WE) - - ## Create executable add_executable(${exec_name} ${src}) - - # Link with MPI target_link_libraries(${exec_name} locality_aware ${MPI_LIBRARIES}) + install(TARGETS ${exec_name} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + + if(ENABLE_UNIT_TESTS) + add_test(NAME ${exec_name}_Benchmark COMMAND ${MPIRUN} -n ${TEST_PROCS} ./${exec_name} ${TEST_MATRIX_FILE}) + endif() endforeach() diff --git a/benchmarks/alltoall_crs.cpp b/benchmarks/alltoall_crs.cpp index b22433884..53e6483e2 100644 --- a/benchmarks/alltoall_crs.cpp +++ b/benchmarks/alltoall_crs.cpp @@ -1,9 +1,9 @@ -#include "locality_aware.h" -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - #include +#include "locality_aware.h" +#include "par_binary_IO.hpp" +#include "sparse_mat.hpp" + void compare(int n_recvs, int* src, int* counts, int orig_n_recvs, int* orig_proc_counts) { int rank; @@ -11,9 +11,11 @@ void compare(int n_recvs, int* src, int* counts, int orig_n_recvs, int* orig_pro if (n_recvs != orig_n_recvs) { - printf("Num Messages Incorrect! Rank %d got %d, should be %d\n", - rank, n_recvs, orig_n_recvs); - return; + printf("Num Messages Incorrect! Rank %d got %d, should be %d\n", + rank, + n_recvs, + orig_n_recvs); + return; } for (int i = 0; i < n_recvs; i++) @@ -21,13 +23,15 @@ void compare(int n_recvs, int* src, int* counts, int orig_n_recvs, int* orig_pro if (orig_proc_counts[src[i]] != counts[i]) { printf("Rank %d, msgcounts from proc %d incorrect! Got %d, should be %d\n", - rank, src[i], orig_proc_counts[src[i]], counts[i]); + rank, + src[i], + orig_proc_counts[src[i]], + counts[i]); break; } } } - int main(int argc, char* argv[]) { MPI_Init(&argc, &argv); @@ -37,23 +41,31 @@ int main(int argc, char* argv[]) MPI_Comm_size(MPI_COMM_WORLD, &num_procs); double t0, tfinal; - + int n_iter = 1000; - if(num_procs > 1000) - n_iter = 100; + if (num_procs > 1000) + { + n_iter = 100; + } if (argc == 1) { - if (rank == 0) printf("Pass Matrix Filename as Command Line Arg!\n"); + if (rank == 0) + { + printf("Pass Matrix Filename as Command Line Arg!\n"); + } MPI_Finalize(); - return 0; + return 1; } char* filename = argv[1]; // Read suitesparse matrix ParMat A; - readParMatrix(filename, A); - + int file_error = readParMatrix(filename, A); + if (file_error) + { + return 1; + } // Form Communication Package (A.send_comm, A.recv_comm) form_comm(A); @@ -72,12 +84,15 @@ int main(int argc, char* argv[]) } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIL_Comm_init time %e\n", t0/n_iter); + if (rank == 0) + { + printf("MPIL_Comm_init time %e\n", t0 / n_iter); + } MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - // Split node communicator - MPI_Barrier(MPI_COMM_WORLD); + // Split node communicator + MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { @@ -86,7 +101,10 @@ int main(int argc, char* argv[]) } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIL_Comm_topo_init time %e\n", t0/n_iter); + if (rank == 0) + { + printf("MPIL_Comm_topo_init time %e\n", t0 / n_iter); + } MPIL_Comm_topo_init(xcomm); @@ -101,7 +119,10 @@ int main(int argc, char* argv[]) } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIL_Comm_win_init_time %e\n", t0/n_iter); + if (rank == 0) + { + printf("MPIL_Comm_win_init_time %e\n", t0 / n_iter); + } MPIL_Comm_win_init(xcomm, bytes, sizeof(int)); @@ -110,91 +131,156 @@ int main(int argc, char* argv[]) std::vector proc_count(num_procs, -1); for (int i = 0; i < A.send_comm.n_msgs; i++) + { proc_count[A.send_comm.procs[i]] = A.send_comm.counts[i]; + } - - // Time RMA + // Time RMA MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoall_crs(ALLTOALL_CRS_RMA); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { n_recvs = -1; - alltoall_crs_rma(A.recv_comm.n_msgs, A.recv_comm.procs.data(), 1, MPI_INT, - A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, - (void**)&recvvals, xinfo, xcomm); + MPIL_Alltoall_crs(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + 1, + MPI_INT, + A.recv_comm.counts.data(), + &n_recvs, + &src, + 1, + MPI_INT, + (void**)&recvvals, + xinfo, + xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0/n_iter); + if (rank == 0) + { + printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0 / n_iter); + } compare(n_recvs, src, recvvals, A.send_comm.n_msgs, proc_count.data()); MPIL_Free(src); MPIL_Free(recvvals); - // Time Personalized + // Time Personalized MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoall_crs(ALLTOALL_CRS_PERSONALIZED); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { n_recvs = -1; - alltoall_crs_personalized(A.recv_comm.n_msgs, A.recv_comm.procs.data(), 1, MPI_INT, - A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, - (void**)&recvvals, xinfo, xcomm); + MPIL_Alltoall_crs(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + 1, + MPI_INT, + A.recv_comm.counts.data(), + &n_recvs, + &src, + 1, + MPI_INT, + (void**)&recvvals, + xinfo, + xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0/n_iter); + if (rank == 0) + { + printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0 / n_iter); + } compare(n_recvs, src, recvvals, A.send_comm.n_msgs, proc_count.data()); MPIL_Free(src); MPIL_Free(recvvals); - // Time Nonblocking + // Time Nonblocking MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoall_crs(ALLTOALL_CRS_NONBLOCKING); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { n_recvs = -1; - alltoall_crs_nonblocking(A.recv_comm.n_msgs, A.recv_comm.procs.data(), 1, MPI_INT, - A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, - (void**)&recvvals, xinfo, xcomm); + MPIL_Alltoall_crs(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + 1, + MPI_INT, + A.recv_comm.counts.data(), + &n_recvs, + &src, + 1, + MPI_INT, + (void**)&recvvals, + xinfo, + xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0/n_iter); + if (rank == 0) + { + printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0 / n_iter); + } compare(n_recvs, src, recvvals, A.send_comm.n_msgs, proc_count.data()); MPIL_Free(src); MPIL_Free(recvvals); - - // Time Personalized Locality + // Time Personalized Locality MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoall_crs(ALLTOALL_CRS_PERSONALIZED_LOC); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { n_recvs = -1; - alltoall_crs_personalized_loc(A.recv_comm.n_msgs, A.recv_comm.procs.data(), 1, MPI_INT, - A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, - (void**)&recvvals, xinfo, xcomm); + MPIL_Alltoall_crs(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + 1, + MPI_INT, + A.recv_comm.counts.data(), + &n_recvs, + &src, + 1, + MPI_INT, + (void**)&recvvals, + xinfo, + xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0/n_iter); + if (rank == 0) + { + printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0 / n_iter); + } compare(n_recvs, src, recvvals, A.send_comm.n_msgs, proc_count.data()); MPIL_Free(src); MPIL_Free(recvvals); - // Time Nonblocking Locality + // Time Nonblocking Locality MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoall_crs(ALLTOALL_CRS_NONBLOCKING_LOC); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { n_recvs = -1; - alltoall_crs_nonblocking_loc(A.recv_comm.n_msgs, A.recv_comm.procs.data(), 1, MPI_INT, - A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, - (void**)&recvvals, xinfo, xcomm); + MPIL_Alltoall_crs(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + 1, + MPI_INT, + A.recv_comm.counts.data(), + &n_recvs, + &src, + 1, + MPI_INT, + (void**)&recvvals, + xinfo, + xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0/n_iter); + if (rank == 0) + { + printf("MPI_Alltoall_crs Time (RMA VERSION): %e\n", t0 / n_iter); + } compare(n_recvs, src, recvvals, A.send_comm.n_msgs, proc_count.data()); MPIL_Free(src); MPIL_Free(recvvals); diff --git a/benchmarks/alltoallv_crs.cpp b/benchmarks/alltoallv_crs.cpp index 703b01ee4..ef123be03 100644 --- a/benchmarks/alltoallv_crs.cpp +++ b/benchmarks/alltoallv_crs.cpp @@ -1,25 +1,39 @@ -#include "locality_aware.h" -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - #include -void compare(int n_recvs, int s_recvs, int* src, int* counts, int* displs, long* indices, int orig_n_recvs, int orig_s_recvs, int* orig_proc_counts, int* orig_proc_displs, long* orig_indices) +#include "locality_aware.h" +#include "par_binary_IO.hpp" +#include "sparse_mat.hpp" + +void compare(int n_recvs, + int s_recvs, + int* src, + int* counts, + int* displs, + long* indices, + int orig_n_recvs, + int orig_s_recvs, + int* orig_proc_counts, + int* orig_proc_displs, + long* orig_indices) { int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (n_recvs != orig_n_recvs) { - printf("Num Messages Incorrect! Rank %d got %d, should be %d\n", - rank, n_recvs, orig_n_recvs); - return; + printf("Num Messages Incorrect! Rank %d got %d, should be %d\n", + rank, + n_recvs, + orig_n_recvs); + return; } if (s_recvs != orig_s_recvs) { - printf("Size Messages Incorrect! Rank %d got %d, should be %d\n", - rank, s_recvs, orig_s_recvs); + printf("Size Messages Incorrect! Rank %d got %d, should be %d\n", + rank, + s_recvs, + orig_s_recvs); return; } @@ -28,24 +42,31 @@ void compare(int n_recvs, int s_recvs, int* src, int* counts, int* displs, long* if (orig_proc_counts[src[i]] != counts[i]) { printf("Rank %d, msgcounts from proc %d incorrect! Got %d, should be %d\n", - rank, src[i], orig_proc_counts[src[i]], counts[i]); + rank, + src[i], + orig_proc_counts[src[i]], + counts[i]); break; } - for (int j = 0; j < counts[i]; j++) { if (indices[displs[i] + j] != orig_indices[orig_proc_displs[src[i]] + j]) { - printf("Rank %d, indices from proc %d at pos %d incorrect! Got %lu, should be %lu\n", - rank, src[i], j, indices[displs[i]+j], orig_indices[orig_proc_displs[src[i]+j]]); + printf( + "Rank %d, indices from proc %d at pos %d incorrect! Got %lu, should " + "be %lu\n", + rank, + src[i], + j, + indices[displs[i] + j], + orig_indices[orig_proc_displs[src[i] + j]]); break; } } } } - int main(int argc, char* argv[]) { MPI_Init(&argc, &argv); @@ -55,23 +76,31 @@ int main(int argc, char* argv[]) MPI_Comm_size(MPI_COMM_WORLD, &num_procs); double t0, tfinal; - + int n_iter = 10; - if(num_procs > 1000) - n_iter = 100; + if (num_procs > 1000) + { + n_iter = 100; + } if (argc == 1) { - if (rank == 0) printf("Pass Matrix Filename as Command Line Arg!\n"); + if (rank == 0) + { + printf("Pass Matrix Filename as Command Line Arg!\n"); + } MPI_Finalize(); - return 0; + return 1; } char* filename = argv[1]; // Read suitesparse matrix ParMat A; - readParMatrix(filename, A); - + int file_error = readParMatrix(filename, A); + if (file_error) + { + return 1; + } // Form Communication Package (A.send_comm, A.recv_comm) form_comm(A); @@ -90,11 +119,14 @@ int main(int argc, char* argv[]) } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIL_Comm_init time %e\n", t0/n_iter); + if (rank == 0) + { + printf("MPIL_Comm_init time %e\n", t0 / n_iter); + } MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - // Split node communicator - MPI_Barrier(MPI_COMM_WORLD); + // Split node communicator + MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { @@ -103,10 +135,13 @@ int main(int argc, char* argv[]) } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIL_Comm_topo_init time %e\n", t0/n_iter); + if (rank == 0) + { + printf("MPIL_Comm_topo_init time %e\n", t0 / n_iter); + } MPIL_Comm_topo_init(xcomm); - update_locality(xcomm, 4); + MPIL_Comm_update_locality(xcomm, 4); int n_recvs, s_recvs, proc; int *src, *rdispls, *recvcounts; @@ -114,32 +149,58 @@ int main(int argc, char* argv[]) std::vector proc_count(num_procs, -1); std::vector proc_displs(num_procs, -1); - std::vector orig_indices(A.send_comm.size_msgs+1); + std::vector orig_indices(A.send_comm.size_msgs + 1); for (int i = 0; i < A.send_comm.n_msgs; i++) { - proc = A.send_comm.procs[i]; - proc_count[proc] = A.send_comm.counts[i]; + proc = A.send_comm.procs[i]; + proc_count[proc] = A.send_comm.counts[i]; proc_displs[proc] = A.send_comm.ptr[i]; } for (int i = 0; i < A.send_comm.size_msgs; i++) + { orig_indices[i] = A.send_comm.idx[i] + A.first_col; + } - // Time Personalized + // Time Personalized MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoallv_crs(ALLTOALLV_CRS_PERSONALIZED); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { s_recvs = -1; - alltoallv_crs_personalized(A.recv_comm.n_msgs, A.recv_comm.size_msgs, A.recv_comm.procs.data(), - A.recv_comm.counts.data(), A.recv_comm.ptr.data(), MPI_LONG, - A.off_proc_columns.data(), &n_recvs, &s_recvs, &src, &recvcounts, - &rdispls, MPI_LONG, (void**) &recvvals, xinfo, xcomm); + MPIL_Alltoallv_crs(A.recv_comm.n_msgs, + A.recv_comm.size_msgs, + A.recv_comm.procs.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_LONG, + A.off_proc_columns.data(), + &n_recvs, + &s_recvs, + &src, + &recvcounts, + &rdispls, + MPI_LONG, + (void**)&recvvals, + xinfo, + xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPI_Alltoall_crs Time (Personalized VERSION): %e\n", t0/n_iter); - compare(n_recvs, s_recvs, src, recvcounts, rdispls, recvvals, - A.send_comm.n_msgs, A.send_comm.size_msgs, proc_count.data(), proc_displs.data(), + if (rank == 0) + { + printf("MPI_Alltoall_crs Time (Personalized VERSION): %e\n", t0 / n_iter); + } + compare(n_recvs, + s_recvs, + src, + recvcounts, + rdispls, + recvvals, + A.send_comm.n_msgs, + A.send_comm.size_msgs, + proc_count.data(), + proc_displs.data(), orig_indices.data()); MPIL_Free(src); MPIL_Free(recvcounts); @@ -148,20 +209,44 @@ int main(int argc, char* argv[]) // Time Nonblocking MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoallv_crs(ALLTOALLV_CRS_NONBLOCKING); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { s_recvs = -1; - alltoallv_crs_nonblocking(A.recv_comm.n_msgs, A.recv_comm.size_msgs, A.recv_comm.procs.data(), - A.recv_comm.counts.data(), A.recv_comm.ptr.data(), MPI_LONG, - A.off_proc_columns.data(), &n_recvs, &s_recvs, &src, &recvcounts, - &rdispls, MPI_LONG, (void**)&recvvals, xinfo, xcomm); + MPIL_Alltoallv_crs(A.recv_comm.n_msgs, + A.recv_comm.size_msgs, + A.recv_comm.procs.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_LONG, + A.off_proc_columns.data(), + &n_recvs, + &s_recvs, + &src, + &recvcounts, + &rdispls, + MPI_LONG, + (void**)&recvvals, + xinfo, + xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPI_Alltoall_crs Time (Personalized VERSION): %e\n", t0/n_iter); - compare(n_recvs, s_recvs, src, recvcounts, rdispls, recvvals, - A.send_comm.n_msgs, A.send_comm.size_msgs, proc_count.data(), proc_displs.data(), + if (rank == 0) + { + printf("MPI_Alltoall_crs Time (Personalized VERSION): %e\n", t0 / n_iter); + } + compare(n_recvs, + s_recvs, + src, + recvcounts, + rdispls, + recvvals, + A.send_comm.n_msgs, + A.send_comm.size_msgs, + proc_count.data(), + proc_displs.data(), orig_indices.data()); MPIL_Free(src); MPIL_Free(recvcounts); @@ -170,20 +255,44 @@ int main(int argc, char* argv[]) // Time Personalized Locality MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoallv_crs(ALLTOALLV_CRS_PERSONALIZED_LOC); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { s_recvs = -1; - alltoallv_crs_personalized_loc(A.recv_comm.n_msgs, A.recv_comm.size_msgs, A.recv_comm.procs.data(), - A.recv_comm.counts.data(), A.recv_comm.ptr.data(), MPI_LONG, - A.off_proc_columns.data(), &n_recvs, &s_recvs, &src, &recvcounts, - &rdispls, MPI_LONG, (void**) &recvvals, xinfo, xcomm); + MPIL_Alltoallv_crs(A.recv_comm.n_msgs, + A.recv_comm.size_msgs, + A.recv_comm.procs.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_LONG, + A.off_proc_columns.data(), + &n_recvs, + &s_recvs, + &src, + &recvcounts, + &rdispls, + MPI_LONG, + (void**)&recvvals, + xinfo, + xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPI_Alltoall_crs Time (Personalized VERSION): %e\n", t0/n_iter); - compare(n_recvs, s_recvs, src, recvcounts, rdispls, recvvals, - A.send_comm.n_msgs, A.send_comm.size_msgs, proc_count.data(), proc_displs.data(), + if (rank == 0) + { + printf("MPI_Alltoall_crs Time (Personalized VERSION): %e\n", t0 / n_iter); + } + compare(n_recvs, + s_recvs, + src, + recvcounts, + rdispls, + recvvals, + A.send_comm.n_msgs, + A.send_comm.size_msgs, + proc_count.data(), + proc_displs.data(), orig_indices.data()); MPIL_Free(src); MPIL_Free(recvcounts); @@ -192,27 +301,50 @@ int main(int argc, char* argv[]) // Time Nonblocking Locality MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoallv_crs(ALLTOALLV_CRS_NONBLOCKING_LOC); t0 = MPI_Wtime(); for (int i = 0; i < n_iter; i++) { s_recvs = -1; - alltoallv_crs_nonblocking_loc(A.recv_comm.n_msgs, A.recv_comm.size_msgs, A.recv_comm.procs.data(), - A.recv_comm.counts.data(), A.recv_comm.ptr.data(), MPI_LONG, - A.off_proc_columns.data(), &n_recvs, &s_recvs, &src, &recvcounts, - &rdispls, MPI_LONG, (void**) &recvvals, xinfo, xcomm); + MPIL_Alltoallv_crs(A.recv_comm.n_msgs, + A.recv_comm.size_msgs, + A.recv_comm.procs.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_LONG, + A.off_proc_columns.data(), + &n_recvs, + &s_recvs, + &src, + &recvcounts, + &rdispls, + MPI_LONG, + (void**)&recvvals, + xinfo, + xcomm); } tfinal = MPI_Wtime() - t0; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPI_Alltoall_crs Time (Personalized VERSION): %e\n", t0/n_iter); - compare(n_recvs, s_recvs, src, recvcounts, rdispls, recvvals, - A.send_comm.n_msgs, A.send_comm.size_msgs, proc_count.data(), proc_displs.data(), + if (rank == 0) + { + printf("MPI_Alltoall_crs Time (Personalized VERSION): %e\n", t0 / n_iter); + } + compare(n_recvs, + s_recvs, + src, + recvcounts, + rdispls, + recvvals, + A.send_comm.n_msgs, + A.send_comm.size_msgs, + proc_count.data(), + proc_displs.data(), orig_indices.data()); MPIL_Free(src); MPIL_Free(recvcounts); MPIL_Free(rdispls); MPIL_Free(recvvals); - MPIL_Info_free(&xinfo); MPIL_Comm_free(&xcomm); diff --git a/benchmarks/gpu_alltoall.cpp b/benchmarks/gpu_alltoall.cpp index d27bfeef8..2d883d07a 100644 --- a/benchmarks/gpu_alltoall.cpp +++ b/benchmarks/gpu_alltoall.cpp @@ -1,11 +1,15 @@ -#include "locality_aware.h" -#include +#include #include +#include #include + #include -#include -#include #include +#include + +#include "communicator/MPIL_Comm.h" +#include "heterogeneous/gpu_utils.h" +#include "locality_aware.h" int main(int argc, char* argv[]) { @@ -15,22 +19,27 @@ int main(int argc, char* argv[]) MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - int max_i = 20; - int max_s = pow(2, max_i); + int max_i = 20; + int max_s = pow(2, max_i); int n_iter = 100; double t0, tfinal; srand(time(NULL)); - std::vector send_data(max_s*num_procs); - std::vector pmpi_alltoall(max_s*num_procs); - std::vector mpix_alltoall(max_s*num_procs); - for (int j = 0; j < max_s*num_procs; j++) + std::vector send_data(max_s * num_procs); + std::vector pmpi_alltoall(max_s * num_procs); + std::vector mpix_alltoall(max_s * num_procs); + for (int j = 0; j < max_s * num_procs; j++) + { send_data[j] = rand(); + } double* send_data_d; double* recv_data_d; - gpuMalloc((void**)(&send_data_d), max_s*num_procs*sizeof(double)); - gpuMalloc((void**)(&recv_data_d), max_s*num_procs*sizeof(double)); - gpuMemcpy(send_data_d, send_data.data(), max_s*num_procs*sizeof(double), gpuMemcpyHostToDevice); + gpuMalloc((void**)(&send_data_d), max_s * num_procs * sizeof(double)); + gpuMalloc((void**)(&recv_data_d), max_s * num_procs * sizeof(double)); + gpuMemcpy(send_data_d, + send_data.data(), + max_s * num_procs * sizeof(double), + gpuMemcpyHostToDevice); MPIL_Comm* xcomm; MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); @@ -42,143 +51,133 @@ int main(int argc, char* argv[]) for (int i = 0; i < max_i; i++) { int s = pow(2, i); - if (rank == 0) printf("Testing Size %d\n", s); + if (rank == 0) + { + printf("Testing Size %d\n", s); + } // Standard MPI Implementation - PMPI_Alltoall(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - MPI_COMM_WORLD); - gpuMemcpy(pmpi_alltoall.data(), recv_data_d, s*num_procs*sizeof(double), - gpuMemcpyDeviceToHost); - gpuMemset(recv_data_d, 0, s*num_procs*sizeof(int)); - + PMPI_Alltoall( + send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, MPI_COMM_WORLD); + gpuMemcpy(pmpi_alltoall.data(), + recv_data_d, + s * num_procs * sizeof(double), + gpuMemcpyDeviceToHost); + gpuMemset(recv_data_d, 0, s * num_procs * sizeof(int)); // MPI Advance : GPU-Aware Pairwise Exchange - gpu_aware_alltoall_pairwise(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); - gpuMemcpy(mpix_alltoall.data(), recv_data_d, s*num_procs*sizeof(double), - gpuMemcpyDeviceToHost); - gpuMemset(recv_data_d, 0, s*num_procs*sizeof(int)); + MPIL_Set_alltoall_algorithm(ALLTOALL_GPU_PAIRWISE); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); + gpuMemcpy(mpix_alltoall.data(), + recv_data_d, + s * num_procs * sizeof(double), + gpuMemcpyDeviceToHost); + gpuMemset(recv_data_d, 0, s * num_procs * sizeof(int)); for (int j = 0; j < s; j++) - { + { if (fabs(pmpi_alltoall[j] - mpix_alltoall[j]) > 1e-10) { - fprintf(stderr, - "Rank %d, idx %d, pmpi %e, GA-PE %e\n", - rank, j, pmpi_alltoall[j], mpix_alltoall[j]); + fprintf(stderr, + "Rank %d, idx %d, pmpi %e, GA-PE %e\n", + rank, + j, + pmpi_alltoall[j], + mpix_alltoall[j]); MPI_Abort(MPI_COMM_WORLD, 1); return 1; } } // MPI Advance : GPU-Aware Nonblocking (P2P) - gpu_aware_alltoall_nonblocking(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); - gpuMemcpy(mpix_alltoall.data(), recv_data_d, s*num_procs*sizeof(double), - gpuMemcpyDeviceToHost); - gpuMemset(recv_data_d, 0, s*num_procs*sizeof(int)); + MPIL_Set_alltoall_algorithm(ALLTOALL_GPU_NONBLOCKING); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); + gpuMemcpy(mpix_alltoall.data(), + recv_data_d, + s * num_procs * sizeof(double), + gpuMemcpyDeviceToHost); + gpuMemset(recv_data_d, 0, s * num_procs * sizeof(int)); for (int j = 0; j < s; j++) - { + { if (fabs(pmpi_alltoall[j] - mpix_alltoall[j]) > 1e-10) { - fprintf(stderr, - "Rank %d, idx %d, pmpi %e, GA-NB %e\n", - rank, j, pmpi_alltoall[j], mpix_alltoall[j]); + fprintf(stderr, + "Rank %d, idx %d, pmpi %e, GA-NB %e\n", + rank, + j, + pmpi_alltoall[j], + mpix_alltoall[j]); MPI_Abort(MPI_COMM_WORLD, 1); return 1; } } // MPI Advance : Copy to CPU Pairwise Exchange - copy_to_cpu_alltoall_pairwise(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); - gpuMemcpy(mpix_alltoall.data(), recv_data_d, s*num_procs*sizeof(double), - gpuMemcpyDeviceToHost); - gpuMemset(recv_data_d, 0, s*num_procs*sizeof(int)); + MPIL_Set_alltoall_algorithm(ALLTOALL_CTC_PAIRWISE); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); + gpuMemcpy(mpix_alltoall.data(), + recv_data_d, + s * num_procs * sizeof(double), + gpuMemcpyDeviceToHost); + gpuMemset(recv_data_d, 0, s * num_procs * sizeof(int)); for (int j = 0; j < s; j++) - { + { if (fabs(pmpi_alltoall[j] - mpix_alltoall[j]) > 1e-10) { - fprintf(stderr, - "Rank %d, idx %d, pmpi %e, C2C-PE %e\n", - rank, j, pmpi_alltoall[j], mpix_alltoall[j]); + fprintf(stderr, + "Rank %d, idx %d, pmpi %e, C2C-PE %e\n", + rank, + j, + pmpi_alltoall[j], + mpix_alltoall[j]); MPI_Abort(MPI_COMM_WORLD, 1); return 1; } } // MPI Advance : Copy To CPU Nonblocking (P2P) - copy_to_cpu_alltoall_nonblocking(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); - gpuMemcpy(mpix_alltoall.data(), recv_data_d, s*num_procs*sizeof(double), - gpuMemcpyDeviceToHost); - gpuMemset(recv_data_d, 0, s*num_procs*sizeof(int)); + MPIL_Set_alltoall_algorithm(ALLTOALL_CTC_NONBLOCKING); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); + gpuMemcpy(mpix_alltoall.data(), + recv_data_d, + s * num_procs * sizeof(double), + gpuMemcpyDeviceToHost); + gpuMemset(recv_data_d, 0, s * num_procs * sizeof(int)); for (int j = 0; j < s; j++) - { + { if (fabs(pmpi_alltoall[j] - mpix_alltoall[j]) > 1e-10) { - fprintf(stderr, - "Rank %d, idx %d, pmpi %e, C2C-NB %e\n", - rank, j, pmpi_alltoall[j], mpix_alltoall[j]); + fprintf(stderr, + "Rank %d, idx %d, pmpi %e, C2C-NB %e\n", + rank, + j, + pmpi_alltoall[j], + mpix_alltoall[j]); MPI_Abort(MPI_COMM_WORLD, 1); return 1; } } - - - // Time PMPI Alltoall // 1. Warm Up - PMPI_Alltoall(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - MPI_COMM_WORLD); + PMPI_Alltoall( + send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, MPI_COMM_WORLD); // 2. Calculate n_iter (tfinal ~ 1 sec) gpuDeviceSynchronize(); MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); - PMPI_Alltoall(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - MPI_COMM_WORLD); + PMPI_Alltoall( + send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, MPI_COMM_WORLD); tfinal = MPI_Wtime() - t0; MPI_Allreduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); if (t0 >= 1.0) + { n_iter = 1; + } else + { n_iter = (1.0 / tfinal); + } // 3. Measure Timing gpuDeviceSynchronize(); @@ -186,93 +185,74 @@ int main(int argc, char* argv[]) t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { - PMPI_Alltoall(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - MPI_COMM_WORLD); + PMPI_Alltoall( + send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, MPI_COMM_WORLD); } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("PMPI_Alltoall Time %e\n", t0); - + if (rank == 0) + { + printf("PMPI_Alltoall Time %e\n", t0); + } // Time GPU-Aware Pairwise Exchange // 1. Warm Up - gpu_aware_alltoall_pairwise(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); // 2. Calculate n_iter (tfinal ~ 1 sec) gpuDeviceSynchronize(); MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoall_algorithm(ALLTOALL_GPU_PAIRWISE); t0 = MPI_Wtime(); - gpu_aware_alltoall_pairwise(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); tfinal = MPI_Wtime() - t0; MPI_Allreduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); if (t0 >= 1.0) + { n_iter = 1; + } else + { n_iter = (1.0 / tfinal); + } // 3. Measure Timing gpuDeviceSynchronize(); MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoall_algorithm(ALLTOALL_GPU_PAIRWISE); t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { - gpu_aware_alltoall_pairwise(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("GPU-Aware Pairwise Exchange Time %e\n", t0); - + if (rank == 0) + { + printf("GPU-Aware Pairwise Exchange Time %e\n", t0); + } // Time GPU-Aware Nonblocking // 1. Warm-Up - gpu_aware_alltoall_nonblocking(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Set_alltoall_algorithm(ALLTOALL_GPU_NONBLOCKING); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); // 2. Calculate n_iter (tfinal ~ 1 sec) gpuDeviceSynchronize(); MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); - gpu_aware_alltoall_nonblocking(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); tfinal = MPI_Wtime() - t0; MPI_Allreduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); if (t0 >= 1.0) + { n_iter = 1; + } else + { n_iter = (1.0 / tfinal); + } // 3. Measure Timing gpuDeviceSynchronize(); @@ -280,28 +260,19 @@ int main(int argc, char* argv[]) t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { - gpu_aware_alltoall_nonblocking(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("GPU-Aware Nonblocking Time %e\n", t0); - + if (rank == 0) + { + printf("GPU-Aware Nonblocking Time %e\n", t0); + } // Time Copy-to-CPU Pairwise Exchange // 1. Warm-Up - copy_to_cpu_alltoall_pairwise(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Set_alltoall_algorithm(ALLTOALL_CTC_PAIRWISE); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); gpuDeviceSynchronize(); MPI_Barrier(MPI_COMM_WORLD); @@ -309,19 +280,17 @@ int main(int argc, char* argv[]) gpuDeviceSynchronize(); MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); - copy_to_cpu_alltoall_pairwise(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); tfinal = MPI_Wtime() - t0; MPI_Allreduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); if (t0 >= 1.0) + { n_iter = 1; + } else + { n_iter = (1.0 / tfinal); + } // 3. Measure Timing gpuDeviceSynchronize(); @@ -329,46 +298,35 @@ int main(int argc, char* argv[]) t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { - copy_to_cpu_alltoall_pairwise(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("Copy-to-CPU Pairwise Exchange Time %e\n", t0); - + if (rank == 0) + { + printf("Copy-to-CPU Pairwise Exchange Time %e\n", t0); + } // Time Copy-to-CPU Nonblocking // 1. Warm-Up - copy_to_cpu_alltoall_nonblocking(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Set_alltoall_algorithm(ALLTOALL_CTC_NONBLOCKING); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); // 2. Calculate n_iter (tfinal ~ 1 sec) gpuDeviceSynchronize(); MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); - copy_to_cpu_alltoall_nonblocking(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); tfinal = MPI_Wtime() - t0; MPI_Allreduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); if (t0 >= 1.0) + { n_iter = 1; + } else + { n_iter = (1.0 / tfinal); + } // 3. Measure Timing gpuDeviceSynchronize(); @@ -376,18 +334,14 @@ int main(int argc, char* argv[]) t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { - copy_to_cpu_alltoall_nonblocking(send_data_d, - s, - MPI_DOUBLE, - recv_data_d, - s, - MPI_DOUBLE, - xcomm); + MPIL_Alltoall(send_data_d, s, MPI_DOUBLE, recv_data_d, s, MPI_DOUBLE, xcomm); } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("Copy-to-CPU Nonblocking Time %e\n", t0); - + if (rank == 0) + { + printf("Copy-to-CPU Nonblocking Time %e\n", t0); + } } MPIL_Comm_free(&xcomm); diff --git a/benchmarks/microbenchmarks.cpp b/benchmarks/microbenchmarks.cpp index 3f407af76..dce1ae5b5 100644 --- a/benchmarks/microbenchmarks.cpp +++ b/benchmarks/microbenchmarks.cpp @@ -1,78 +1,110 @@ -#include "locality_aware.h" -#include +#include #include +#include #include + #include -#include -#include #include +#include + +#include "locality_aware.h" -#define NODES 2 //number of nodes -#define SPN 2 //number of sockets per node -#define PPNUMA 14 // number of processes per NUMA region -#define PPS 56 //number of processes per socket -#define PPN 112 //number of processes per node +#define NODES 2 // number of nodes +#define SPN 2 // number of sockets per node +#define PPNUMA 14 // number of processes per NUMA region +#define PPS 56 // number of processes per socket +#define PPN 112 // number of processes per node typedef void (*pingpong_ftn)(char*, char*, int, int, MPI_Comm, MPI_Request* req); -void ping(char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) +void ping( + char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) { MPI_Send(sendbuf, size, MPI_CHAR, proc, 0, comm); MPI_Recv(recvbuf, size, MPI_CHAR, proc, 0, comm, MPI_STATUS_IGNORE); } -void pong(char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) +void pong( + char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) { MPI_Recv(recvbuf, size, MPI_CHAR, proc, 0, comm, MPI_STATUS_IGNORE); MPI_Send(sendbuf, size, MPI_CHAR, proc, 0, comm); } -void multi_ping(char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) +void multi_ping( + char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) { for (int j = 0; j < size; j++) + { MPI_Isend(&(sendbuf[j]), 1, MPI_CHAR, proc, j, comm, &(req[j])); + } MPI_Waitall(size, req, MPI_STATUSES_IGNORE); for (int j = 0; j < size; j++) + { MPI_Irecv(&(recvbuf[j]), 1, MPI_CHAR, proc, j, comm, &(req[j])); + } MPI_Waitall(size, req, MPI_STATUSES_IGNORE); } -void multi_pong(char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) +void multi_pong( + char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) { for (int j = 0; j < size; j++) + { MPI_Irecv(&(recvbuf[j]), 1, MPI_CHAR, proc, j, comm, &(req[j])); + } MPI_Waitall(size, req, MPI_STATUSES_IGNORE); for (int j = 0; j < size; j++) + { MPI_Isend(&(sendbuf[j]), 1, MPI_CHAR, proc, j, comm, &(req[j])); + } MPI_Waitall(size, req, MPI_STATUSES_IGNORE); } -void matching_ping(char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) +void matching_ping( + char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) { for (int j = 0; j < size; j++) + { MPI_Isend(&(sendbuf[j]), 1, MPI_CHAR, proc, j, comm, &(req[j])); + } MPI_Waitall(size, req, MPI_STATUSES_IGNORE); for (int j = 0; j < size; j++) - MPI_Irecv(&(recvbuf[j]), 1, MPI_CHAR, proc, size-j-1, comm, &(req[j])); + { + MPI_Irecv(&(recvbuf[j]), 1, MPI_CHAR, proc, size - j - 1, comm, &(req[j])); + } MPI_Waitall(size, req, MPI_STATUSES_IGNORE); } -void matching_pong(char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) +void matching_pong( + char* sendbuf, char* recvbuf, int size, int proc, MPI_Comm comm, MPI_Request* req) { for (int j = 0; j < size; j++) - MPI_Irecv(&(recvbuf[j]), 1, MPI_CHAR, proc, size-j-1, comm, &(req[j])); + { + MPI_Irecv(&(recvbuf[j]), 1, MPI_CHAR, proc, size - j - 1, comm, &(req[j])); + } MPI_Waitall(size, req, MPI_STATUSES_IGNORE); for (int j = 0; j < size; j++) + { MPI_Isend(&(sendbuf[j]), 1, MPI_CHAR, proc, j, comm, &(req[j])); + } MPI_Waitall(size, req, MPI_STATUSES_IGNORE); } -double ping_pong(pingpong_ftn f_ping, pingpong_ftn f_pong, int rank0, int rank1, int size, char* sendbuf, char* recvbuf, int n_iters, - MPI_Comm comm, MPI_Request* req) +double ping_pong(pingpong_ftn f_ping, + pingpong_ftn f_pong, + int rank0, + int rank1, + int size, + char* sendbuf, + char* recvbuf, + int n_iters, + MPI_Comm comm, + MPI_Request* req) { int rank; MPI_Comm_rank(comm, &rank); @@ -94,28 +126,53 @@ double ping_pong(pingpong_ftn f_ping, pingpong_ftn f_pong, int rank0, int rank1, return tfinal; } -double estimate_iters(pingpong_ftn f_ping, pingpong_ftn f_pong, int rank0, int rank1, int size, char* sendbuf, - char* recvbuf, MPI_Comm comm, MPI_Request* req) +double estimate_iters(pingpong_ftn f_ping, + pingpong_ftn f_pong, + int rank0, + int rank1, + int size, + char* sendbuf, + char* recvbuf, + MPI_Comm comm, + MPI_Request* req) { - double time = ping_pong(f_ping, f_pong, rank0, rank1, size, sendbuf, recvbuf, 5, comm, req); - MPI_Allreduce(MPI_IN_PLACE, &time, 1, MPI_DOUBLE, MPI_MAX, comm); + double time = + ping_pong(f_ping, f_pong, rank0, rank1, size, sendbuf, recvbuf, 5, comm, req); + MPI_Allreduce(MPI_IN_PLACE, &time, 1, MPI_DOUBLE, MPI_MAX, comm); int n_iters = (1.0 / time) + 1; if (time > 1.0) + { n_iters = 1; + } return n_iters; } -double time_ping_pong(pingpong_ftn f_ping, pingpong_ftn f_pong, int rank0, int rank1, int size, char* sendbuf, - char* recvbuf, MPI_Comm comm, MPI_Request* req) +double time_ping_pong(pingpong_ftn f_ping, + pingpong_ftn f_pong, + int rank0, + int rank1, + int size, + char* sendbuf, + char* recvbuf, + MPI_Comm comm, + MPI_Request* req) { - int n_iters = estimate_iters(f_ping, f_pong, rank0, rank1, size, sendbuf, recvbuf, comm, req); - double time = ping_pong(f_ping, f_pong, rank0, rank1, size, sendbuf, recvbuf, n_iters, comm, req); + int n_iters = + estimate_iters(f_ping, f_pong, rank0, rank1, size, sendbuf, recvbuf, comm, req); + double time = ping_pong( + f_ping, f_pong, rank0, rank1, size, sendbuf, recvbuf, n_iters, comm, req); return time; } - -void print_ping_pong(pingpong_ftn f_ping, pingpong_ftn f_pong, int max_p, int rank0, int rank1, char* sendbuf, char* recvbuf, - MPI_Comm comm, MPI_Request* req) +void print_ping_pong(pingpong_ftn f_ping, + pingpong_ftn f_pong, + int max_p, + int rank0, + int rank1, + char* sendbuf, + char* recvbuf, + MPI_Comm comm, + MPI_Request* req) { int rank; MPI_Comm_rank(comm, &rank); @@ -124,61 +181,91 @@ void print_ping_pong(pingpong_ftn f_ping, pingpong_ftn f_pong, int max_p, int ra for (int i = 0; i < max_p; i++) { int s = pow(2, i); - if (rank == 0) printf("Size %d: ", s); - double time = time_ping_pong(f_ping, f_pong, rank0, rank1, s, sendbuf, recvbuf, comm, req); + if (rank == 0) + { + printf("Size %d: ", s); + } + double time = + time_ping_pong(f_ping, f_pong, rank0, rank1, s, sendbuf, recvbuf, comm, req); MPI_Allreduce(MPI_IN_PLACE, &time, 1, MPI_DOUBLE, MPI_MAX, comm); - if (rank == 0) printf("%e\n", time); + if (rank == 0) + { + printf("%e\n", time); + } + } + if (rank == 0) + { + printf("\n"); } - if (rank == 0) printf("\n"); } - void standard_ping_pong(int max_p, char* sendbuf, char* recvbuf) { int rank, rank0, rank1; MPI_Comm_rank(MPI_COMM_WORLD, &rank); - if (rank == 0) printf("On-NUMA Ping-Pong:\n"); + if (rank == 0) + { + printf("On-NUMA Ping-Pong: \n"); + } rank0 = 0; - rank1 = PPNUMA/2; - print_ping_pong(ping, pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, NULL); + rank1 = PPNUMA / 2; + print_ping_pong( + ping, pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, NULL); - if (rank == 0) printf("On-Socket Ping-Pong:\n"); + if (rank == 0) + { + printf("On-Socket Ping-Pong:\n"); + } rank0 = 0; - rank1 = PPS/2; - print_ping_pong(ping, pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, NULL); + rank1 = PPS / 2; + print_ping_pong( + ping, pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, NULL); - if (rank == 0) printf("On-Node, Off-Socket Ping-Pong:\n"); + if (rank == 0) + { + printf("On-Node, Off-Socket Ping-Pong:\n"); + } rank0 = 0; - rank1 = PPN/2; - print_ping_pong(ping, pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, NULL); + rank1 = PPN / 2; + print_ping_pong( + ping, pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, NULL); - if (rank == 0) printf("Off-Node Ping-Pong:\n"); + if (rank == 0) + { + printf("Off-Node Ping-Pong:\n"); + } rank0 = 0; rank1 = PPN; - print_ping_pong(ping, pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, NULL); + print_ping_pong( + ping, pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, NULL); } -void multiproc_ping_pong_step(int max_p, char* sendbuf, char* recvbuf, int max_n, int diff, - bool cond0, bool cond1) +void multiproc_ping_pong_step( + int max_p, char* sendbuf, char* recvbuf, int max_n, int diff, bool cond0, bool cond1) { int rank, rank0, rank1; MPI_Comm_rank(MPI_COMM_WORLD, &rank); rank0 = 0; rank1 = diff; - for (int i = 1; i <= 2*max_n; i*=2) + for (int i = 1; i <= 2 * max_n; i *= 2) { // Test largest size in non-power-of-two cases - if (i > max_n) + if (i > max_n) + { i = max_n; + } - if (rank == 0) printf("Active Procs: %d\n", i); + if (rank == 0) + { + printf("Active Procs: %d\n", i); + } if (rank % max_n < i) { if (cond0) { - rank0 = rank; + rank0 = rank; rank1 = rank + diff; } else if (cond1) @@ -187,11 +274,14 @@ void multiproc_ping_pong_step(int max_p, char* sendbuf, char* recvbuf, int max_n rank1 = rank; } } - print_ping_pong(ping, pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, NULL); + print_ping_pong( + ping, pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, NULL); // Only test i == size one time, then break if (i == max_n) + { break; + } } } @@ -201,119 +291,227 @@ void multiproc_ping_pong(int max_p, char* sendbuf, char* recvbuf) bool cond0, cond1; MPI_Comm_rank(MPI_COMM_WORLD, &rank); - // On-NUMA, MultiProc (Standard) - if (rank == 0) printf("On-NUMA MultiProc Ping-Pong:\n"); - size = PPNUMA/2; + if (rank == 0) + { + printf("On-NUMA MultiProc Ping-Pong:\n"); + } + size = PPNUMA / 2; cond0 = rank < size; - cond1 = rank < 2*size; + cond1 = rank < 2 * size; multiproc_ping_pong_step(max_p, sendbuf, recvbuf, size, size, cond0, cond1); // On-Socket, MultiProc (Standard) - if (rank == 0) printf("On-Socket MultiProc Ping-Pong:\n"); - size = PPS/2; + if (rank == 0) + { + printf("On-Socket MultiProc Ping-Pong:\n"); + } + size = PPS / 2; cond0 = rank < size; - cond1 = rank < 2*size; + cond1 = rank < 2 * size; multiproc_ping_pong_step(max_p, sendbuf, recvbuf, size, size, cond0, cond1); - + // On-Node, Off-Socket, MultiProc (Standard) - if (rank == 0) printf("On-Node, Off-Socket MultiProc Ping-Pong:\n"); - size = PPN/2; + if (rank == 0) + { + printf("On-Node, Off-Socket MultiProc Ping-Pong:\n"); + } + size = PPN / 2; cond0 = rank < size; - cond1 = rank < 2*size; + cond1 = rank < 2 * size; multiproc_ping_pong_step(max_p, sendbuf, recvbuf, size, size, cond0, cond1); // Off-Node, MultiProc (Standard) - if (rank == 0) printf("Off-Node MultiProc Ping-Pong:\n"); - size = PPN; + if (rank == 0) + { + printf("Off-Node MultiProc Ping-Pong:\n"); + } + size = PPN; cond0 = rank < size; - cond1 = rank < 2*size; + cond1 = rank < 2 * size; multiproc_ping_pong_step(max_p, sendbuf, recvbuf, size, size, cond0, cond1); // On-NUMA, MultiProc (All NUMAs active on 1 Node) - if (rank == 0) printf("On-NUMA MultiProc Ping-Pong, All NUMAs Active:\n"); - size = PPNUMA/2; + if (rank == 0) + { + printf("On-NUMA MultiProc Ping-Pong, All NUMAs Active:\n"); + } + size = PPNUMA / 2; cond0 = rank < PPN && (rank % PPNUMA) < size; cond1 = rank < PPN; multiproc_ping_pong_step(max_p, sendbuf, recvbuf, size, size, cond0, cond1); - // On-Socket, MultiProc (All Sockets active on 1 Node) - if (rank == 0) printf("On-Socket MultiProc Ping-Pong, All Sockets Active:\n"); - size = PPS/2; + // On-Socket, MultiProc (All Sockets active on 1 Node) + if (rank == 0) + { + printf("On-Socket MultiProc Ping-Pong, All Sockets Active:\n"); + } + size = PPS / 2; cond0 = rank < PPN && (rank % PPS) < size; cond1 = rank < PPN; multiproc_ping_pong_step(max_p, sendbuf, recvbuf, size, size, cond0, cond1); // Off-Node, MultiProc, Even NUMA Regions - if (rank == 0) printf("Off-Node MultiProc Ping-Pong, Even NUMA Regions:\n"); - size = PPNUMA; + if (rank == 0) + { + printf("Off-Node MultiProc Ping-Pong, Even NUMA Regions:\n"); + } + size = PPNUMA; cond0 = rank < PPN; - cond1 = rank < 2*PPN; + cond1 = rank < 2 * PPN; multiproc_ping_pong_step(max_p, sendbuf, recvbuf, size, PPN, cond0, cond1); // Off-Node, MultiProc, Even Sockets - if (rank == 0) printf("Off-Node MultiProc Ping-Pong, Even Sockets:\n"); - size = PPS; + if (rank == 0) + { + printf("Off-Node MultiProc Ping-Pong, Even Sockets:\n"); + } + size = PPS; cond0 = rank < PPN; - cond1 = rank < 2*PPN; + cond1 = rank < 2 * PPN; multiproc_ping_pong_step(max_p, sendbuf, recvbuf, size, PPN, cond0, cond1); } void multi_ping_pong(int max_p, char* sendbuf, char* recvbuf, MPI_Request* req) -{ +{ int rank, rank0, rank1; MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - if (rank == 0) printf("On-NUMA Multi Ping-Pong:\n"); + + if (rank == 0) + { + printf("On-NUMA Multi Ping-Pong:\n"); + } rank0 = 0; - rank1 = PPNUMA/2; - print_ping_pong(multi_ping, multi_pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, req); - - if (rank == 0) printf("On-Socket Multi Ping-Pong:\n"); + rank1 = PPNUMA / 2; + print_ping_pong(multi_ping, + multi_pong, + max_p, + rank0, + rank1, + sendbuf, + recvbuf, + MPI_COMM_WORLD, + req); + + if (rank == 0) + { + printf("On-Socket Multi Ping-Pong:\n"); + } rank0 = 0; - rank1 = PPS/2; - print_ping_pong(multi_ping, multi_pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, req); - - if (rank == 0) printf("On-Node, Off-Socket Multi Ping-Pong:\n"); + rank1 = PPS / 2; + print_ping_pong(multi_ping, + multi_pong, + max_p, + rank0, + rank1, + sendbuf, + recvbuf, + MPI_COMM_WORLD, + req); + + if (rank == 0) + { + printf("On-Node, Off-Socket Multi Ping-Pong:\n"); + } rank0 = 0; - rank1 = PPN/2; - print_ping_pong(multi_ping, multi_pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, req); - - if (rank == 0) printf("Off-Node Multi Ping-Pong:\n"); + rank1 = PPN / 2; + print_ping_pong(multi_ping, + multi_pong, + max_p, + rank0, + rank1, + sendbuf, + recvbuf, + MPI_COMM_WORLD, + req); + + if (rank == 0) + { + printf("Off-Node Multi Ping-Pong:\n"); + } rank0 = 0; rank1 = PPN; - print_ping_pong(multi_ping, multi_pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, req); + print_ping_pong(multi_ping, + multi_pong, + max_p, + rank0, + rank1, + sendbuf, + recvbuf, + MPI_COMM_WORLD, + req); } - void matching_ping_pong(int max_p, char* sendbuf, char* recvbuf, MPI_Request* req) -{ +{ int rank, rank0, rank1; MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - if (rank == 0) printf("On-NUMA Matching Ping-Pong:\n"); + if (rank == 0) + { + printf("On-NUMA Matching Ping-Pong:\n"); + } rank0 = 0; - rank1 = PPNUMA/2; - print_ping_pong(matching_ping, matching_pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, req); - - if (rank == 0) printf("On-Socket Matching Ping-Pong:\n"); + rank1 = PPNUMA / 2; + print_ping_pong(matching_ping, + matching_pong, + max_p, + rank0, + rank1, + sendbuf, + recvbuf, + MPI_COMM_WORLD, + req); + + if (rank == 0) + { + printf("On-Socket Matching Ping-Pong:\n"); + } rank0 = 0; - rank1 = PPS/2; - print_ping_pong(matching_ping, matching_pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, req); - - if (rank == 0) printf("On-Node, Off-Socket Matching Ping-Pong:\n"); + rank1 = PPS / 2; + print_ping_pong(matching_ping, + matching_pong, + max_p, + rank0, + rank1, + sendbuf, + recvbuf, + MPI_COMM_WORLD, + req); + + if (rank == 0) + { + printf("On-Node, Off-Socket Matching Ping-Pong:\n"); + } rank0 = 0; - rank1 = PPN/2; - print_ping_pong(matching_ping, matching_pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, req); - - if (rank == 0) printf("Off-Node Matching Ping-Pong:\n"); + rank1 = PPN / 2; + print_ping_pong(matching_ping, + matching_pong, + max_p, + rank0, + rank1, + sendbuf, + recvbuf, + MPI_COMM_WORLD, + req); + + if (rank == 0) + { + printf("Off-Node Matching Ping-Pong:\n"); + } rank0 = 0; rank1 = PPN; - print_ping_pong(matching_ping, matching_pong, max_p, rank0, rank1, sendbuf, recvbuf, MPI_COMM_WORLD, req); + print_ping_pong(matching_ping, + matching_pong, + max_p, + rank0, + rank1, + sendbuf, + recvbuf, + MPI_COMM_WORLD, + req); } - int main(int argc, char* argv[]) { MPI_Init(&argc, &argv); @@ -322,10 +520,10 @@ int main(int argc, char* argv[]) MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - int max_p = 25; - int max_s = pow(2, max_p); - char* sendbuf = new char[max_s]; - char* recvbuf = new char[max_s]; + int max_p = 25; + int max_s = pow(2, max_p); + char* sendbuf = new char[max_s]; + char* recvbuf = new char[max_s]; MPI_Request* req = new MPI_Request[max_s]; standard_ping_pong(max_p, sendbuf, recvbuf); @@ -334,11 +532,11 @@ int main(int argc, char* argv[]) multi_ping_pong(15, sendbuf, recvbuf, req); - matching_ping_pong(15, sendbuf, recvbuf, req); + matching_ping_pong(15, sendbuf, recvbuf, req); delete[] sendbuf; delete[] recvbuf; - delete[] req; + delete[] req; MPI_Finalize(); return 0; diff --git a/benchmarks/p2p_alltoall.cpp b/benchmarks/p2p_alltoall.cpp index 957710169..c76013f36 100644 --- a/benchmarks/p2p_alltoall.cpp +++ b/benchmarks/p2p_alltoall.cpp @@ -1,11 +1,13 @@ -#include "locality_aware.h" -#include +#include #include +#include #include + #include -#include -#include #include +#include + +#include "locality_aware.h" int main(int argc, char* argv[]) { @@ -15,14 +17,14 @@ int main(int argc, char* argv[]) MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - int max_i = 15; - int max_s = pow(2, max_i); + int max_i = 15; + int max_s = pow(2, max_i); int n_iter = 100; double t0, tfinal; srand(time(NULL)); - std::vector local_data(max_s*num_procs); - std::vector std_alltoall(max_s*num_procs); - std::vector loc_alltoall(max_s*num_procs); + std::vector local_data(max_s * num_procs); + std::vector std_alltoall(max_s * num_procs); + std::vector loc_alltoall(max_s * num_procs); MPIL_Comm* xcomm; MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); @@ -30,34 +32,37 @@ int main(int argc, char* argv[]) for (int i = 0; i < max_i; i++) { int s = pow(2, i); - if (rank == 0) printf("Testing Size %d\n", s); + if (rank == 0) + { + printf("Testing Size %d\n", s); + } - for (int j = 0; j < s*num_procs; j++) + for (int j = 0; j < s * num_procs; j++) + { local_data[j] = rand(); + } PMPI_Alltoall(local_data.data(), - s, - MPI_DOUBLE, - std_alltoall.data(), - s, - MPI_DOUBLE, - MPI_COMM_WORLD); + s, + MPI_DOUBLE, + std_alltoall.data(), + s, + MPI_DOUBLE, + MPI_COMM_WORLD); - MPIL_Alltoall(local_data.data(), - s, - MPI_DOUBLE, - loc_alltoall.data(), - s, - MPI_DOUBLE, - xcomm); + MPIL_Alltoall( + local_data.data(), s, MPI_DOUBLE, loc_alltoall.data(), s, MPI_DOUBLE, xcomm); for (int j = 0; j < s; j++) - { + { if (fabs(std_alltoall[j] - loc_alltoall[j]) > 1e-10) { - fprintf(stderr, - "Rank %d, idx %d, std %f, loc %f\n", - rank, j, std_alltoall[j], loc_alltoall[j]); + fprintf(stderr, + "Rank %d, idx %d, std %f, loc %f\n", + rank, + j, + std_alltoall[j], + loc_alltoall[j]); MPI_Abort(MPI_COMM_WORLD, 1); return 1; } @@ -67,53 +72,52 @@ int main(int argc, char* argv[]) // Time Standard Alltoall // PMPI_Alltoall(local_data.data(), - s, - MPI_DOUBLE, - std_alltoall.data(), - s, - MPI_DOUBLE, - MPI_COMM_WORLD); + s, + MPI_DOUBLE, + std_alltoall.data(), + s, + MPI_DOUBLE, + MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { PMPI_Alltoall(local_data.data(), - s, - MPI_DOUBLE, - std_alltoall.data(), - s, - MPI_DOUBLE, - MPI_COMM_WORLD); + s, + MPI_DOUBLE, + std_alltoall.data(), + s, + MPI_DOUBLE, + MPI_COMM_WORLD); } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("PMPI_Alltoall Time %e\n", t0); - + if (rank == 0) + { + printf("PMPI_Alltoall Time %e\n", t0); + } // Time Loc Alltoall - MPIL_Alltoall(local_data.data(), - s, - MPI_DOUBLE, - loc_alltoall.data(), - s, - MPI_DOUBLE, - xcomm); + MPIL_Alltoall( + local_data.data(), s, MPI_DOUBLE, loc_alltoall.data(), s, MPI_DOUBLE, xcomm); MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { MPIL_Alltoall(local_data.data(), - s, - MPI_DOUBLE, - loc_alltoall.data(), - s, - MPI_DOUBLE, - xcomm); + s, + MPI_DOUBLE, + loc_alltoall.data(), + s, + MPI_DOUBLE, + xcomm); } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIL_Alltoall Time %e\n", t0); - + if (rank == 0) + { + printf("MPIL_Alltoall Time %e\n", t0); + } } MPIL_Comm_free(&xcomm); diff --git a/benchmarks/p2p_alltoallv.cpp b/benchmarks/p2p_alltoallv.cpp index f97e2e17f..5a8d48244 100644 --- a/benchmarks/p2p_alltoallv.cpp +++ b/benchmarks/p2p_alltoallv.cpp @@ -1,11 +1,13 @@ -#include "locality_aware.h" -#include +#include #include +#include #include + #include -#include -#include #include +#include + +#include "locality_aware.h" int main(int argc, char* argv[]) { @@ -15,19 +17,19 @@ int main(int argc, char* argv[]) MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - int max_i = 15; - int max_s = pow(2, max_i); + int max_i = 15; + int max_s = pow(2, max_i); int n_iter = 100; double t0, tfinal; srand(time(NULL)); - std::vector local_data(max_s*num_procs); - std::vector std_alltoallv(max_s*num_procs); - std::vector loc_alltoallv(max_s*num_procs); + std::vector local_data(max_s * num_procs); + std::vector std_alltoallv(max_s * num_procs); + std::vector loc_alltoallv(max_s * num_procs); std::vector send_sizes(num_procs); - std::vector send_displs(num_procs+1); + std::vector send_displs(num_procs + 1); std::vector recv_sizes(num_procs); - std::vector recv_displs(num_procs+1); + std::vector recv_displs(num_procs + 1); send_displs[0] = 0; recv_displs[0] = 0; @@ -37,46 +39,54 @@ int main(int argc, char* argv[]) for (int i = 0; i < max_i; i++) { int s = pow(2, i); - if (rank == 0) printf("Testing Size %d\n", s); + if (rank == 0) + { + printf("Testing Size %d\n", s); + } for (int j = 0; j < num_procs; j++) { - send_sizes[j] = s; - send_displs[j+1] = send_displs[j] + s; - recv_sizes[j] = s; - recv_displs[j+1] = recv_displs[j] + s; + send_sizes[j] = s; + send_displs[j + 1] = send_displs[j] + s; + recv_sizes[j] = s; + recv_displs[j + 1] = recv_displs[j] + s; } - for (int j = 0; j < s*num_procs; j++) + for (int j = 0; j < s * num_procs; j++) + { local_data[j] = rand(); + } PMPI_Alltoallv(local_data.data(), - send_sizes.data(), - send_displs.data(), - MPI_DOUBLE, - std_alltoallv.data(), - recv_sizes.data(), - recv_displs.data(), - MPI_DOUBLE, - MPI_COMM_WORLD); + send_sizes.data(), + send_displs.data(), + MPI_DOUBLE, + std_alltoallv.data(), + recv_sizes.data(), + recv_displs.data(), + MPI_DOUBLE, + MPI_COMM_WORLD); MPIL_Alltoallv(local_data.data(), - send_sizes.data(), - send_displs.data(), - MPI_DOUBLE, - loc_alltoallv.data(), - recv_sizes.data(), - recv_displs.data(), - MPI_DOUBLE, - xcomm); + send_sizes.data(), + send_displs.data(), + MPI_DOUBLE, + loc_alltoallv.data(), + recv_sizes.data(), + recv_displs.data(), + MPI_DOUBLE, + xcomm); for (int j = 0; j < s; j++) - { + { if (fabs(std_alltoallv[j] - loc_alltoallv[j]) > 1e-10) { - fprintf(stderr, - "Rank %d, idx %d, std %f, loc %f\n", - rank, j, std_alltoallv[j], loc_alltoallv[j]); + fprintf(stderr, + "Rank %d, idx %d, std %f, loc %f\n", + rank, + j, + std_alltoallv[j], + loc_alltoallv[j]); MPI_Abort(MPI_COMM_WORLD, 1); return 1; } @@ -86,61 +96,65 @@ int main(int argc, char* argv[]) // Time Standard Alltoallv // PMPI_Alltoallv(local_data.data(), - send_sizes.data(), - send_displs.data(), - MPI_DOUBLE, - std_alltoallv.data(), - recv_sizes.data(), - recv_displs.data(), - MPI_DOUBLE, - MPI_COMM_WORLD); + send_sizes.data(), + send_displs.data(), + MPI_DOUBLE, + std_alltoallv.data(), + recv_sizes.data(), + recv_displs.data(), + MPI_DOUBLE, + MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { PMPI_Alltoallv(local_data.data(), - send_sizes.data(), - send_displs.data(), - MPI_DOUBLE, - std_alltoallv.data(), - recv_sizes.data(), - recv_displs.data(), - MPI_DOUBLE, - MPI_COMM_WORLD); + send_sizes.data(), + send_displs.data(), + MPI_DOUBLE, + std_alltoallv.data(), + recv_sizes.data(), + recv_displs.data(), + MPI_DOUBLE, + MPI_COMM_WORLD); } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("PMPI_Alltoallv Time %e\n", t0); - + if (rank == 0) + { + printf("PMPI_Alltoallv Time %e\n", t0); + } // Time Loc Alltoallv MPIL_Alltoallv(local_data.data(), - send_sizes.data(), - send_displs.data(), - MPI_DOUBLE, - loc_alltoallv.data(), - recv_sizes.data(), - recv_displs.data(), - MPI_DOUBLE, - xcomm); + send_sizes.data(), + send_displs.data(), + MPI_DOUBLE, + loc_alltoallv.data(), + recv_sizes.data(), + recv_displs.data(), + MPI_DOUBLE, + xcomm); MPI_Barrier(MPI_COMM_WORLD); t0 = MPI_Wtime(); for (int k = 0; k < n_iter; k++) { MPIL_Alltoallv(local_data.data(), - send_sizes.data(), - send_displs.data(), - MPI_DOUBLE, - loc_alltoallv.data(), - recv_sizes.data(), - recv_displs.data(), - MPI_DOUBLE, - xcomm); + send_sizes.data(), + send_displs.data(), + MPI_DOUBLE, + loc_alltoallv.data(), + recv_sizes.data(), + recv_displs.data(), + MPI_DOUBLE, + xcomm); } tfinal = (MPI_Wtime() - t0) / n_iter; MPI_Reduce(&tfinal, &t0, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); - if (rank == 0) printf("MPIL_Alltoallv Time %e\n", t0); - + if (rank == 0) + { + printf("MPIL_Alltoallv Time %e\n", t0); + } } MPIL_Comm_free(&xcomm); diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 000000000..89a892fa3 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,5 @@ +target_include_directories(locality_aware PUBLIC + $ + $ +) + diff --git a/include/locality_aware.h b/include/locality_aware.h new file mode 100644 index 000000000..1ddf18235 --- /dev/null +++ b/include/locality_aware.h @@ -0,0 +1,283 @@ +#ifndef MPI_ADVANCE_H +#define MPI_ADVANCE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Objects offered by this header*/ +typedef struct _MPIL_Comm MPIL_Comm; +typedef struct _MPIL_Info MPIL_Info; +typedef struct _MPIL_Topo MPIL_Topo; +typedef struct _MPIL_Request MPIL_Request; + +/* Enums for listing of implemented algorithms */ +enum AlltoallMethod +{ +#if defined(GPU) && defined(GPU_AWARE) + ALLTOALL_GPU_PAIRWISE, + ALLTOALL_GPU_NONBLOCKING, + ALLTOALL_CTC_PAIRWISE, + ALLTOALL_CTC_NONBLOCKING, +#endif + ALLTOALL_PAIRWISE, + ALLTOALL_NONBLOCKING, + ALLTOALL_HIERARCHICAL_PAIRWISE, + ALLTOALL_HIERARCHICAL_NONBLOCKING, + ALLTOALL_MULTILEADER_PAIRWISE, + ALLTOALL_MULTILEADER_NONBLOCKING, + ALLTOALL_NODE_AWARE_PAIRWISE, + ALLTOALL_NODE_AWARE_NONBLOCKING, + ALLTOALL_LOCALITY_AWARE_PAIRWISE, + ALLTOALL_LOCALITY_AWARE_NONBLOCKING, + ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE, + ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING, + ALLTOALL_PMPI + +}; + +enum AlltoallvMethod +{ +#if defined(GPU) && defined(GPU_AWARE) + ALLTOALLV_GPU_PAIRWISE, + ALLTOALLV_GPU_NONBLOCKING, + ALLTOALLV_CTC_PAIRWISE, + ALLTOALLV_CTC_NONBLOCKING, +#endif + ALLTOALLV_PAIRWISE, + ALLTOALLV_NONBLOCKING, + ALLTOALLV_BATCH, + ALLTOALLV_BATCH_ASYNC, + ALLTOALLV_PMPI +}; + +enum NeighborAlltoallvMethod +{ + NEIGHBOR_ALLTOALLV_STANDARD, + NEIGHBOR_ALLTOALLV_LOCALITY +}; + +enum NeighborAlltoallvInitMethod +{ + NEIGHBOR_ALLTOALLV_INIT_STANDARD, + NEIGHBOR_ALLTOALLV_INIT_LOCALITY +}; + +enum AlltoallCRSMethod +{ + ALLTOALL_CRS_RMA, + ALLTOALL_CRS_NONBLOCKING, + ALLTOALL_CRS_NONBLOCKING_LOC, + ALLTOALL_CRS_PERSONALIZED, + ALLTOALL_CRS_PERSONALIZED_LOC +}; + +enum AlltoallvCRSMethod +{ + ALLTOALLV_CRS_NONBLOCKING, + ALLTOALLV_CRS_NONBLOCKING_LOC, + ALLTOALLV_CRS_PERSONALIZED, + ALLTOALLV_CRS_PERSONALIZED_LOC +}; + +/* Create global variables for algorithm selection. */ +extern enum AlltoallMethod mpil_alltoall_implementation; +extern enum AlltoallvMethod mpil_alltoallv_implementation; +extern enum NeighborAlltoallvMethod mpil_neighbor_alltoallv_implementation; +extern enum NeighborAlltoallvInitMethod mpil_neighbor_alltoallv_init_implementation; +extern enum AlltoallCRSMethod mpil_alltoall_crs_implementation; +extern enum AlltoallvCRSMethod mpil_alltoallv_crs_implementation; + +/* Algorithm selection functions. */ +int MPIL_Set_alltoall_algorithm(enum AlltoallMethod algorithm); +int MPIL_Set_alltoallv_algorithm(enum AlltoallvMethod algorithm); +int MPIL_Set_alltoallv_neighbor_alogorithm(enum NeighborAlltoallvMethod algorithm); +int MPIL_Set_alltoallv_neighbor_init_alogorithm( + enum NeighborAlltoallvInitMethod algorithm); +int MPIL_Set_alltoall_crs(enum AlltoallCRSMethod algorithm); +int MPIL_Set_alltoallv_crs(enum AlltoallvCRSMethod algorithm); + +// Functions to control various versions of the MPIL_Comm object--------------------- +int MPIL_Comm_init(MPIL_Comm** xcomm_ptr, MPI_Comm global_comm); +int MPIL_Comm_free(MPIL_Comm** xcomm_ptr); + +int MPIL_Comm_topo_init(MPIL_Comm* xcomm); +int MPIL_Comm_topo_free(MPIL_Comm* xcomm); + +int MPIL_Comm_leader_init(MPIL_Comm* xcomm, int procs_per_leader); +int MPIL_Comm_leader_free(MPIL_Comm* xcomm); + +int MPIL_Comm_win_init(MPIL_Comm* xcomm, int bytes, int type_bytes); +int MPIL_Comm_win_free(MPIL_Comm* xcomm); + +int MPIL_Comm_device_init(MPIL_Comm* xcomm); +int MPIL_Comm_device_free(MPIL_Comm* xcomm); + +int MPIL_Comm_req_resize(MPIL_Comm* xcomm, int n); +int MPIL_Comm_update_locality(MPIL_Comm* xcomm, int ppn); + +/** @brief get current tag and increment tag in the comm.**/ +int MPIL_Comm_tag(MPIL_Comm* comm, int* tag); + +// Functions to initialize and free the MPI_Info object +int MPIL_Info_init(MPIL_Info** info); +int MPIL_Info_free(MPIL_Info** info); + +// Functions to control the MPIL_Topo object +int MPIL_Topo_init(int indegree, + const int sources[], + const int sourceweights[], + int outdegree, + const int destinations[], + const int destweights[], + MPIL_Info* info, + MPIL_Topo** mpil_topo_ptr); +int MPIL_Topo_from_neighbor_comm(MPIL_Comm* comm, MPIL_Topo** mpil_topo_ptr); +int MPIL_Topo_free(MPIL_Topo** topo); + +// Functions to control the MPIL_Request object +int MPIL_Start(MPIL_Request* request); +int MPIL_Wait(MPIL_Request* request, MPI_Status* status); +int MPIL_Request_free(MPIL_Request** request); +int MPIL_Request_reorder(MPIL_Request* request, int value); + +int MPIL_Dist_graph_create_adjacent(MPI_Comm comm_old, + int indegree, + const int sources[], + const int sourceweights[], + int outdegree, + const int destinations[], + const int destweights[], + MPIL_Info* info, + int reorder, + MPIL_Comm** comm_dist_graph_ptr); + +// Main MPIL Functions +int MPIL_Alltoall(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm); +int MPIL_Alltoallv(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm); + +int MPIL_Neighbor_alltoallv(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm); +int MPIL_Neighbor_alltoallv_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm); + +int MPIL_Neighbor_alltoallv_init(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); +int MPIL_Neighbor_alltoallv_init_ext(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); +int MPIL_Neighbor_alltoallv_init_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); +int MPIL_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); + +int MPIL_Alltoall_crs(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIL_Info* xinfo, + MPIL_Comm* xcomm); +int MPIL_Alltoallv_crs(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIL_Info* xinfo, + MPIL_Comm* comm); + +// Utility functions (used in some of the crs tests, may move internal +int MPIL_Alloc(void** pointer, const int bytes); +int MPIL_Free(void* pointer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt new file mode 100644 index 000000000..7ea40cf01 --- /dev/null +++ b/library/CMakeLists.txt @@ -0,0 +1,5 @@ +target_include_directories(locality_aware PUBLIC + $) +add_subdirectory(bindings) +add_subdirectory(source) +add_subdirectory(tests) diff --git a/library/bindings/CMakeLists.txt b/library/bindings/CMakeLists.txt new file mode 100644 index 000000000..510005e1d --- /dev/null +++ b/library/bindings/CMakeLists.txt @@ -0,0 +1,34 @@ +target_sources(locality_aware PRIVATE + global_defaults.c + Collective/MPIL_Alltoall.c + Collective/MPIL_Alltoallv.c + Collective/MPIL_Alltoall_crs.c + Collective/MPIL_Alltoallv_crs.c + MPIL_Alloc/MPIL_Alloc.cpp + MPIL_Alloc/MPIL_Free.cpp + MPIL_Info/MPIL_Info_init.cpp + MPIL_Info/MPIL_Info_free.cpp + MPIL_Request/MPIL_Start.c + MPIL_Request/MPIL_Wait.c + MPIL_Request/MPIL_Request_free.c + MPIL_Request/MPIL_Request_reorder.c +) + +#for directories with several files +add_subdirectory(Neighborhood) +add_subdirectory(MPIL_Comm) + +#add any sources that utilize GPU specific commands to global list for later conversion. +if(USE_GPU) + get_property(GPU_SOURCES GLOBAL PROPERTY GPU_SOURCES_GLOBAL) + set_property(GLOBAL APPEND PROPERTY GPU_SOURCES_GLOBAL + "${CMAKE_CURRENT_SOURCE_DIR}/Collective/MPIL_Alltoall.c" + "${CMAKE_CURRENT_SOURCE_DIR}/Collective/MPIL_Alltoallv.c" + "${CMAKE_CURRENT_SOURCE_DIR}/Collective/MPIL_Alltoall_crs.c" + "${CMAKE_CURRENT_SOURCE_DIR}/Collective/MPIL_Alltoallv_crs.c" + "${CMAKE_CURRENT_SOURCE_DIR}/MPIL_Comm/MPIL_Comm_device_init.c" + "${CMAKE_CURRENT_SOURCE_DIR}/MPIL_Comm/MPIL_Comm_device_free.c" + "${CMAKE_CURRENT_SOURCE_DIR}/MPIL_Request/MPIL_Request_free.c" + ) +endif() + diff --git a/library/bindings/Collective/MPIL_Alltoall.c b/library/bindings/Collective/MPIL_Alltoall.c new file mode 100644 index 000000000..1c1aaecbe --- /dev/null +++ b/library/bindings/Collective/MPIL_Alltoall.c @@ -0,0 +1,79 @@ +#include "collective/alltoall.h" +#include "locality_aware.h" +#ifdef GPU +#include "heterogeneous/gpu_alltoall.h" +#endif + +int MPIL_Alltoall(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* mpi_comm) +{ + alltoall_ftn method; + + switch (mpil_alltoall_implementation) + { +#if defined(GPU) && defined(GPU_AWARE) + + case ALLTOALL_GPU_PAIRWISE: + method = gpu_aware_alltoall_pairwise; + break; + case ALLTOALL_GPU_NONBLOCKING: + method = gpu_aware_alltoall_nonblocking; + break; + case ALLTOALL_CTC_PAIRWISE: + method = copy_to_cpu_alltoall_pairwise; + break; + case ALLTOALL_CTC_NONBLOCKING: + method = copy_to_cpu_alltoall_nonblocking; + break; +#endif + case ALLTOALL_PAIRWISE: + method = alltoall_pairwise; + break; + case ALLTOALL_NONBLOCKING: + method = alltoall_nonblocking; + break; + case ALLTOALL_HIERARCHICAL_PAIRWISE: + method = alltoall_hierarchical_pairwise; + break; + case ALLTOALL_HIERARCHICAL_NONBLOCKING: + method = alltoall_hierarchical_nonblocking; + break; + case ALLTOALL_MULTILEADER_PAIRWISE: + method = alltoall_multileader_pairwise; + break; + case ALLTOALL_MULTILEADER_NONBLOCKING: + method = alltoall_multileader_nonblocking; + break; + case ALLTOALL_NODE_AWARE_PAIRWISE: + method = alltoall_node_aware_pairwise; + break; + case ALLTOALL_NODE_AWARE_NONBLOCKING: + method = alltoall_node_aware_nonblocking; + break; + case ALLTOALL_LOCALITY_AWARE_PAIRWISE: + method = alltoall_locality_aware_pairwise; + break; + case ALLTOALL_LOCALITY_AWARE_NONBLOCKING: + method = alltoall_locality_aware_nonblocking; + break; + case ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE: + method = alltoall_multileader_locality_pairwise; + break; + case ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING: + method = alltoall_multileader_locality_nonblocking; + break; + case ALLTOALL_PMPI: + method = alltoall_pmpi; + break; + default: + method = alltoall_pmpi; + break; + } + + return method(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, mpi_comm); +} diff --git a/library/bindings/Collective/MPIL_Alltoall_crs.c b/library/bindings/Collective/MPIL_Alltoall_crs.c new file mode 100644 index 000000000..f581b6664 --- /dev/null +++ b/library/bindings/Collective/MPIL_Alltoall_crs.c @@ -0,0 +1,51 @@ +#include "locality_aware.h" +#include "neighborhood/alltoall_crs.h" + +int MPIL_Alltoall_crs(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIL_Info* xinfo, + MPIL_Comm* xcomm) +{ + alltoall_crs_ftn method; + switch (mpil_alltoall_crs_implementation) + { + case ALLTOALL_CRS_PERSONALIZED: + method = alltoall_crs_personalized; + break; + case ALLTOALL_CRS_PERSONALIZED_LOC: + method = alltoall_crs_personalized_loc; + break; + case ALLTOALL_CRS_RMA: + method = alltoall_crs_rma; + break; + case ALLTOALL_CRS_NONBLOCKING: + method = alltoall_crs_nonblocking; + break; + case ALLTOALL_CRS_NONBLOCKING_LOC: + method = alltoall_crs_nonblocking_loc; + break; + default: + method = alltoall_crs_personalized; + break; + } + return method(send_nnz, + dest, + sendcount, + sendtype, + sendvals, + recv_nnz, + src_ptr, + recvcount, + recvtype, + recvvals_ptr, + xinfo, + xcomm); +} \ No newline at end of file diff --git a/library/bindings/Collective/MPIL_Alltoallv.c b/library/bindings/Collective/MPIL_Alltoallv.c new file mode 100644 index 000000000..ecdd2a9ae --- /dev/null +++ b/library/bindings/Collective/MPIL_Alltoallv.c @@ -0,0 +1,63 @@ +#include "collective/alltoallv.h" +#include "locality_aware.h" +#ifdef GPU +#include "heterogeneous/gpu_alltoallv.h" +#endif + +int MPIL_Alltoallv(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* mpi_comm) +{ + alltoallv_ftn method; + switch (mpil_alltoallv_implementation) + { +#if defined(GPU) && defined(GPU_AWARE) + case ALLTOALLV_GPU_PAIRWISE: + method = gpu_aware_alltoallv_pairwise; + break; + case ALLTOALLV_GPU_NONBLOCKING: + method = gpu_aware_alltoallv_nonblocking; + break; + case ALLTOALLV_CTC_PAIRWISE: + method = copy_to_cpu_alltoallv_pairwise; + break; + case ALLTOALLV_CTC_NONBLOCKING: + method = copy_to_cpu_alltoallv_nonblocking; + break; +#endif + case ALLTOALLV_PAIRWISE: + method = alltoallv_pairwise; + break; + case ALLTOALLV_NONBLOCKING: + method = alltoallv_nonblocking; + break; + case ALLTOALLV_BATCH: + method = alltoallv_batch; + break; + case ALLTOALLV_BATCH_ASYNC: + method = alltoallv_batch_async; + break; + case ALLTOALLV_PMPI: + method = alltoallv_pmpi; + break; + default: + method = alltoallv_pmpi; + break; + } + + return method(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + mpi_comm); +} diff --git a/library/bindings/Collective/MPIL_Alltoallv_crs.c b/library/bindings/Collective/MPIL_Alltoallv_crs.c new file mode 100644 index 000000000..d3037682d --- /dev/null +++ b/library/bindings/Collective/MPIL_Alltoallv_crs.c @@ -0,0 +1,58 @@ +#include "locality_aware.h" +#include "neighborhood/alltoall_crs.h" + +int MPIL_Alltoallv_crs(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIL_Info* xinfo, + MPIL_Comm* xcomm) +{ + alltoallv_crs_ftn method; + + switch (mpil_alltoallv_crs_implementation) + { + case ALLTOALLV_CRS_NONBLOCKING: + method = alltoallv_crs_nonblocking; + break; + case ALLTOALLV_CRS_NONBLOCKING_LOC: + method = alltoallv_crs_nonblocking_loc; + break; + case ALLTOALLV_CRS_PERSONALIZED: + method = alltoallv_crs_personalized; + break; + case ALLTOALLV_CRS_PERSONALIZED_LOC: + method = alltoallv_crs_personalized_loc; + break; + default: + method = alltoallv_crs_personalized; + break; + } + + return method(send_nnz, + send_size, + dest, + sendcounts, + sdispls, + sendtype, + sendvals, + recv_nnz, + recv_size, + src_ptr, + recvcounts_ptr, + rdispls_ptr, + recvtype, + recvvals_ptr, + xinfo, + xcomm); +} diff --git a/library/bindings/MPIL_Alloc/MPIL_Alloc.cpp b/library/bindings/MPIL_Alloc/MPIL_Alloc.cpp new file mode 100644 index 000000000..91415d2ed --- /dev/null +++ b/library/bindings/MPIL_Alloc/MPIL_Alloc.cpp @@ -0,0 +1,15 @@ +#include "locality_aware.h" + +int MPIL_Alloc(void** pointer, const int bytes) +{ + if (bytes == 0) + { + *pointer = nullptr; + } + else + { + *pointer = new char[bytes]; + } + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Alloc/MPIL_Free.cpp b/library/bindings/MPIL_Alloc/MPIL_Free.cpp new file mode 100644 index 000000000..4c153277a --- /dev/null +++ b/library/bindings/MPIL_Alloc/MPIL_Free.cpp @@ -0,0 +1,12 @@ +#include "locality_aware.h" + +int MPIL_Free(void* pointer) +{ + if (pointer != nullptr) + { + char* char_ptr = (char*)pointer; + delete[] char_ptr; + } + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Comm/CMakeLists.txt b/library/bindings/MPIL_Comm/CMakeLists.txt new file mode 100644 index 000000000..adff2f6e5 --- /dev/null +++ b/library/bindings/MPIL_Comm/CMakeLists.txt @@ -0,0 +1,4 @@ +file(GLOB communicator_bindings CONFIGURE_DEPENDS "*.c" "*.cpp") +set(communicator_SOURCES ${communicator_bindings} CACHE INTERNAL "All source files for communicator directory.") +target_sources(locality_aware PRIVATE ${communicator_bindings}) + diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_device_free.c b/library/bindings/MPIL_Comm/MPIL_Comm_device_free.c new file mode 100644 index 000000000..46b47d210 --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_device_free.c @@ -0,0 +1,20 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +#ifdef GPU +#include "heterogeneous/gpu_utils.h" +#endif + +int MPIL_Comm_device_free(MPIL_Comm* xcomm) +{ +#ifdef GPU + int ierr = gpuSuccess; + if (xcomm->gpus_per_node) + { + ierr = gpuStreamDestroy((gpuStream_t)xcomm->proc_stream); + } + gpu_check(ierr); +#endif + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_device_init.c b/library/bindings/MPIL_Comm/MPIL_Comm_device_init.c new file mode 100644 index 000000000..ea1ff1314 --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_device_init.c @@ -0,0 +1,28 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" +#ifdef GPU +#include "heterogeneous/gpu_utils.h" +#endif + +int MPIL_Comm_device_init(MPIL_Comm* xcomm) +{ +#ifdef GPU + if (xcomm->local_comm == MPI_COMM_NULL) + { + MPIL_Comm_topo_init(xcomm); + } + + int local_rank, ierr; + MPI_Comm_rank(xcomm->local_comm, &local_rank); + ierr = gpuGetDeviceCount(&(xcomm->gpus_per_node)); + gpu_check(ierr); + if (xcomm->gpus_per_node) + { + xcomm->rank_gpu = local_rank; + ierr = gpuStreamCreate((gpuStream_t*)&(xcomm->proc_stream)); + gpu_check(ierr); + } +#endif + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_free.c b/library/bindings/MPIL_Comm/MPIL_Comm_free.c new file mode 100644 index 000000000..d4aab5a02 --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_free.c @@ -0,0 +1,28 @@ +#include + +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_free(MPIL_Comm** xcomm_ptr) +{ + MPIL_Comm* xcomm = *xcomm_ptr; + + if (xcomm->n_requests > 0) + { + free(xcomm->requests); + } + + if (xcomm->neighbor_comm != MPI_COMM_NULL) + { + MPI_Comm_free(&(xcomm->neighbor_comm)); + } + + MPIL_Comm_topo_free(xcomm); + MPIL_Comm_leader_free(xcomm); + MPIL_Comm_win_free(xcomm); + MPIL_Comm_device_free(xcomm); + + free(xcomm); + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_init.c b/library/bindings/MPIL_Comm/MPIL_Comm_init.c new file mode 100644 index 000000000..af1d12d84 --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_init.c @@ -0,0 +1,47 @@ +#include + +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_init(MPIL_Comm** xcomm_ptr, MPI_Comm global_comm) +{ + int rank, num_procs; + MPI_Comm_rank(global_comm, &rank); + MPI_Comm_size(global_comm, &num_procs); + + MPIL_Comm* xcomm = (MPIL_Comm*)malloc(sizeof(MPIL_Comm)); + xcomm->global_comm = global_comm; + + xcomm->local_comm = MPI_COMM_NULL; + xcomm->group_comm = MPI_COMM_NULL; + + xcomm->leader_comm = MPI_COMM_NULL; + xcomm->leader_group_comm = MPI_COMM_NULL; + xcomm->leader_local_comm = MPI_COMM_NULL; + + xcomm->neighbor_comm = MPI_COMM_NULL; + + xcomm->win = MPI_WIN_NULL; + xcomm->win_array = NULL; + xcomm->win_bytes = 0; + + xcomm->requests = NULL; + xcomm->statuses = NULL; + xcomm->n_requests = 0; + + int flag; + MPI_Comm_get_attr(MPI_COMM_WORLD, MPI_TAG_UB, &(xcomm->max_tag), &flag); + xcomm->tag = 126 % xcomm->max_tag; + + xcomm->global_rank_to_local = NULL; + xcomm->global_rank_to_node = NULL; + xcomm->ordered_global_ranks = NULL; + +#ifdef GPU + xcomm->gpus_per_node = 0; +#endif + + *xcomm_ptr = xcomm; + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_leader_free.c b/library/bindings/MPIL_Comm/MPIL_Comm_leader_free.c new file mode 100644 index 000000000..309ae0b8b --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_leader_free.c @@ -0,0 +1,20 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_leader_free(MPIL_Comm* xcomm) +{ + if (xcomm->leader_comm != MPI_COMM_NULL) + { + MPI_Comm_free(&(xcomm->leader_comm)); + } + if (xcomm->leader_group_comm != MPI_COMM_NULL) + { + MPI_Comm_free(&(xcomm->leader_group_comm)); + } + if (xcomm->leader_local_comm != MPI_COMM_NULL) + { + MPI_Comm_free(&(xcomm->leader_local_comm)); + } + + return MPI_SUCCESS; +} \ No newline at end of file diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_leader_init.c b/library/bindings/MPIL_Comm/MPIL_Comm_leader_init.c new file mode 100644 index 000000000..089bd9038 --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_leader_init.c @@ -0,0 +1,26 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_leader_init(MPIL_Comm* xcomm, int procs_per_leader) +{ + int rank, num_procs; + MPI_Comm_rank(xcomm->global_comm, &rank); + MPI_Comm_size(xcomm->global_comm, &num_procs); + + MPI_Comm_split( + xcomm->global_comm, rank / procs_per_leader, rank, &(xcomm->leader_comm)); + + int leader_rank; + MPI_Comm_rank(xcomm->leader_comm, &leader_rank); + + MPI_Comm_split(xcomm->global_comm, leader_rank, rank, &(xcomm->leader_group_comm)); + + if (xcomm->local_comm == MPI_COMM_NULL) + { + MPIL_Comm_topo_init(xcomm); + } + + MPI_Comm_split(xcomm->local_comm, leader_rank, rank, &(xcomm->leader_local_comm)); + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_req_resize.c b/library/bindings/MPIL_Comm/MPIL_Comm_req_resize.c new file mode 100644 index 000000000..b385db791 --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_req_resize.c @@ -0,0 +1,18 @@ +#include + +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_req_resize(MPIL_Comm* xcomm, int n) +{ + if (n <= 0) + { + return MPI_SUCCESS; + } + + xcomm->n_requests = n; + xcomm->requests = (MPI_Request*)realloc(xcomm->requests, n * sizeof(MPI_Request)); + xcomm->statuses = (MPI_Status*)realloc(xcomm->statuses, n * sizeof(MPI_Status)); + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_tag.c b/library/bindings/MPIL_Comm/MPIL_Comm_tag.c new file mode 100644 index 000000000..deb70d027 --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_tag.c @@ -0,0 +1,7 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_tag(MPIL_Comm* comm, int* tag) +{ + return get_tag(comm, tag); +} \ No newline at end of file diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_topo_free.c b/library/bindings/MPIL_Comm/MPIL_Comm_topo_free.c new file mode 100644 index 000000000..38820d18a --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_topo_free.c @@ -0,0 +1,31 @@ +#include + +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_topo_free(MPIL_Comm* xcomm) +{ + if (xcomm->local_comm != MPI_COMM_NULL) + { + MPI_Comm_free(&(xcomm->local_comm)); + } + if (xcomm->group_comm != MPI_COMM_NULL) + { + MPI_Comm_free(&(xcomm->group_comm)); + } + + if (xcomm->global_rank_to_local != NULL) + { + free(xcomm->global_rank_to_local); + } + if (xcomm->global_rank_to_node != NULL) + { + free(xcomm->global_rank_to_node); + } + if (xcomm->ordered_global_ranks != NULL) + { + free(xcomm->ordered_global_ranks); + } + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_topo_init.c b/library/bindings/MPIL_Comm/MPIL_Comm_topo_init.c new file mode 100644 index 000000000..d49cd4993 --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_topo_init.c @@ -0,0 +1,59 @@ +#include + +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_topo_init(MPIL_Comm* xcomm) +{ + int rank, num_procs; + MPI_Comm_rank(xcomm->global_comm, &rank); + MPI_Comm_size(xcomm->global_comm, &num_procs); + + // Split global comm into local (per node) communicators + MPI_Comm_split_type(xcomm->global_comm, + MPI_COMM_TYPE_SHARED, + rank, + MPI_INFO_NULL, + &(xcomm->local_comm)); + + int local_rank, ppn; + MPI_Comm_rank(xcomm->local_comm, &local_rank); + MPI_Comm_size(xcomm->local_comm, &ppn); + + // Split global comm into group (per local rank) communicators + MPI_Comm_split(xcomm->global_comm, local_rank, rank, &(xcomm->group_comm)); + + int node; + MPI_Comm_rank(xcomm->group_comm, &node); + + // Gather arrays for get_node, get_local, and get_global methods + // These arrays allow for these methods to work with any ordering + // No longer relying on SMP ordering of processes to nodes! + // Does rely on constant ppn + xcomm->global_rank_to_local = (int*)malloc(num_procs * sizeof(int)); + xcomm->global_rank_to_node = (int*)malloc(num_procs * sizeof(int)); + MPI_Allgather(&local_rank, + 1, + MPI_INT, + xcomm->global_rank_to_local, + 1, + MPI_INT, + xcomm->global_comm); + MPI_Allgather( + &node, 1, MPI_INT, xcomm->global_rank_to_node, 1, MPI_INT, xcomm->global_comm); + + xcomm->ordered_global_ranks = (int*)malloc(num_procs * sizeof(int)); + for (int i = 0; i < num_procs; i++) + { + int local = xcomm->global_rank_to_local[i]; + int node = xcomm->global_rank_to_node[i]; + xcomm->ordered_global_ranks[node * ppn + local] = i; + } + + // Set xcomm variables + MPI_Comm_size(xcomm->local_comm, &(xcomm->ppn)); + xcomm->num_nodes = ((num_procs - 1) / xcomm->ppn) + 1; + xcomm->rank_node = get_node(xcomm, rank); + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_update_locality.c b/library/bindings/MPIL_Comm/MPIL_Comm_update_locality.c new file mode 100644 index 000000000..568dd2bb1 --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_update_locality.c @@ -0,0 +1,7 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_update_locality(MPIL_Comm* xcomm, int ppn) +{ + return update_locality(xcomm, ppn); +} \ No newline at end of file diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_win_free.c b/library/bindings/MPIL_Comm/MPIL_Comm_win_free.c new file mode 100644 index 000000000..4b843e083 --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_win_free.c @@ -0,0 +1,24 @@ +#include // For NULL + +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_win_free(MPIL_Comm* xcomm) +{ + int rank, num_procs; + MPI_Comm_rank(xcomm->global_comm, &rank); + MPI_Comm_size(xcomm->global_comm, &num_procs); + + if (xcomm->win != MPI_WIN_NULL) + { + MPI_Win_free(&(xcomm->win)); + } + if (xcomm->win_array != NULL) + { + MPI_Free_mem(xcomm->win_array); + } + xcomm->win_bytes = 0; + xcomm->win_type_bytes = 0; + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Comm/MPIL_Comm_win_init.c b/library/bindings/MPIL_Comm/MPIL_Comm_win_init.c new file mode 100644 index 000000000..fe1bbd63f --- /dev/null +++ b/library/bindings/MPIL_Comm/MPIL_Comm_win_init.c @@ -0,0 +1,21 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +int MPIL_Comm_win_init(MPIL_Comm* xcomm, int bytes, int type_bytes) +{ + int rank, num_procs; + MPI_Comm_rank(xcomm->global_comm, &rank); + MPI_Comm_size(xcomm->global_comm, &num_procs); + + xcomm->win_bytes = bytes; + xcomm->win_type_bytes = type_bytes; + MPI_Alloc_mem(xcomm->win_bytes, MPI_INFO_NULL, &(xcomm->win_array)); + MPI_Win_create(xcomm->win_array, + xcomm->win_bytes, + xcomm->win_type_bytes, + MPI_INFO_NULL, + xcomm->global_comm, + &(xcomm->win)); + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Info/MPIL_Info_free.cpp b/library/bindings/MPIL_Info/MPIL_Info_free.cpp new file mode 100644 index 000000000..e0a583d36 --- /dev/null +++ b/library/bindings/MPIL_Info/MPIL_Info_free.cpp @@ -0,0 +1,12 @@ +#include + +#include "communicator/MPIL_Info.h" +#include "locality_aware.h" + +int MPIL_Info_free(MPIL_Info** info_ptr) +{ + MPIL_Info* xinfo = *info_ptr; + free(xinfo); + + return MPI_SUCCESS; +} \ No newline at end of file diff --git a/library/bindings/MPIL_Info/MPIL_Info_init.cpp b/library/bindings/MPIL_Info/MPIL_Info_init.cpp new file mode 100644 index 000000000..ac87d8877 --- /dev/null +++ b/library/bindings/MPIL_Info/MPIL_Info_init.cpp @@ -0,0 +1,16 @@ +#include + +#include "communicator/MPIL_Info.h" +#include "locality_aware.h" + +// MPIL Info Object Routines +int MPIL_Info_init(MPIL_Info** info_ptr) +{ + MPIL_Info* xinfo = (MPIL_Info*)malloc(sizeof(MPIL_Info)); + xinfo->crs_num_initialized = 0; + xinfo->crs_size_initialized = 0; + + *info_ptr = xinfo; + + return MPI_SUCCESS; +} diff --git a/library/bindings/MPIL_Request/MPIL_Request_free.c b/library/bindings/MPIL_Request/MPIL_Request_free.c new file mode 100644 index 000000000..2f3ec4cc3 --- /dev/null +++ b/library/bindings/MPIL_Request/MPIL_Request_free.c @@ -0,0 +1,70 @@ +#include + +#include "locality_aware.h" +#include "persistent/MPIL_Request.h" +#ifdef GPU +#include "heterogeneous/gpu_utils.h" +#endif + +int MPIL_Request_free(MPIL_Request** request_ptr) +{ + MPIL_Request* request = *request_ptr; + + if (request->local_L_n_msgs) + { + for (int i = 0; i < request->local_L_n_msgs; i++) + { + MPI_Request_free(&(request->local_L_requests[i])); + } + free(request->local_L_requests); + } + if (request->local_S_n_msgs) + { + for (int i = 0; i < request->local_S_n_msgs; i++) + { + MPI_Request_free(&(request->local_S_requests[i])); + } + free(request->local_S_requests); + } + if (request->local_R_n_msgs) + { + for (int i = 0; i < request->local_R_n_msgs; i++) + { + MPI_Request_free(&(request->local_R_requests[i])); + } + free(request->local_R_requests); + } + if (request->global_n_msgs) + { + for (int i = 0; i < request->global_n_msgs; i++) + { + MPI_Request_free(&(request->global_requests[i])); + } + free(request->global_requests); + } + + // If Locality-Aware + if (request->locality != NULL) + { + destroy_locality_comm(request->locality); + } + +// TODO : for safety, may want to check if allocated with malloc? +#ifdef GPU // Assuming cpu buffers allocated in pinned memory + int ierr; + if (request->cpu_sendbuf) + { + ierr = gpuFreeHost(request->cpu_sendbuf); + gpu_check(ierr); + } + if (request->cpu_recvbuf) + { + ierr = gpuFreeHost(request->cpu_recvbuf); + gpu_check(ierr); + } +#endif + + free(request); + + return 0; +} diff --git a/library/bindings/MPIL_Request/MPIL_Request_reorder.c b/library/bindings/MPIL_Request/MPIL_Request_reorder.c new file mode 100644 index 000000000..85c87f697 --- /dev/null +++ b/library/bindings/MPIL_Request/MPIL_Request_reorder.c @@ -0,0 +1,8 @@ +#include "locality_aware.h" +#include "persistent/MPIL_Request.h" + +int MPIL_Request_reorder(MPIL_Request* request, int value) +{ + request->reorder = value; + return MPI_SUCCESS; +} \ No newline at end of file diff --git a/library/bindings/MPIL_Request/MPIL_Start.c b/library/bindings/MPIL_Request/MPIL_Start.c new file mode 100644 index 000000000..b08256533 --- /dev/null +++ b/library/bindings/MPIL_Request/MPIL_Start.c @@ -0,0 +1,19 @@ +#include // For NULL + +#include "locality_aware.h" +#include "persistent/MPIL_Request.h" + +// Starting locality-aware requests +// 1. Start Local_L +// 2. Start and wait for local_S +// 3. Start global +/** @brief wrapper interface for the start function of the request object**/ +int MPIL_Start(MPIL_Request* request) +{ + if (request == NULL) + { + return 0; + } + + return (request->start_function)(request); +} diff --git a/library/bindings/MPIL_Request/MPIL_Wait.c b/library/bindings/MPIL_Request/MPIL_Wait.c new file mode 100644 index 000000000..749387d64 --- /dev/null +++ b/library/bindings/MPIL_Request/MPIL_Wait.c @@ -0,0 +1,20 @@ +#include // For NULL + +#include "locality_aware.h" +#include "persistent/MPIL_Request.h" + +// Wait for locality-aware requests +// 1. Wait for global +// 2. Start and wait for local_R +// 3. Wait for local_L +// TODO : Currently ignores the status! +/** @brief wrapper interface for the wait function of the request object**/ +int MPIL_Wait(MPIL_Request* request, MPI_Status* status) +{ + if (request == NULL) + { + return 0; + } + + return (request->wait_function)(request, status); +} diff --git a/library/bindings/Neighborhood/CMakeLists.txt b/library/bindings/Neighborhood/CMakeLists.txt new file mode 100644 index 000000000..803da4f61 --- /dev/null +++ b/library/bindings/Neighborhood/CMakeLists.txt @@ -0,0 +1,3 @@ +file(GLOB neighborhood_bindings CONFIGURE_DEPENDS "*.c" "*.cpp") +set(communicator_SOURCES ${neighborhood_bindings} CACHE INTERNAL "All source files for communicator directory.") +target_sources(locality_aware PRIVATE ${neighborhood_bindings}) \ No newline at end of file diff --git a/src/neighborhood/dist_graph.c b/library/bindings/Neighborhood/MPIL_Dist_graph_create_adjacent.c similarity index 95% rename from src/neighborhood/dist_graph.c rename to library/bindings/Neighborhood/MPIL_Dist_graph_create_adjacent.c index 305a64eda..af3087344 100644 --- a/src/neighborhood/dist_graph.c +++ b/library/bindings/Neighborhood/MPIL_Dist_graph_create_adjacent.c @@ -1,4 +1,5 @@ -#include "dist_graph.h" +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" int MPIL_Dist_graph_create_adjacent(MPI_Comm comm_old, int indegree, diff --git a/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv.c b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv.c new file mode 100644 index 000000000..e12ac0563 --- /dev/null +++ b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv.c @@ -0,0 +1,31 @@ +#include "locality_aware.h" +#include "neighborhood/MPIL_Topo.h" + +int MPIL_Neighbor_alltoallv(const void* sendbuffer, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuffer, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + MPIL_Topo* topo; + MPIL_Topo_from_neighbor_comm(comm, &topo); + + MPIL_Neighbor_alltoallv_topo(sendbuffer, + sendcounts, + sdispls, + sendtype, + recvbuffer, + recvcounts, + rdispls, + recvtype, + topo, + comm); + + MPIL_Topo_free(&topo); + + return MPI_SUCCESS; +} \ No newline at end of file diff --git a/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init.c b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init.c new file mode 100644 index 000000000..a23ad568b --- /dev/null +++ b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init.c @@ -0,0 +1,35 @@ +#include "locality_aware.h" +#include "neighborhood/MPIL_Topo.h" + +int MPIL_Neighbor_alltoallv_init(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) +{ + MPIL_Topo* topo; + MPIL_Topo_from_neighbor_comm(comm, &topo); + + MPIL_Neighbor_alltoallv_init_topo(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + topo, + comm, + info, + request_ptr); + + MPIL_Topo_free(&topo); + + return MPI_SUCCESS; +} \ No newline at end of file diff --git a/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_ext.c b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_ext.c new file mode 100644 index 000000000..22065e144 --- /dev/null +++ b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_ext.c @@ -0,0 +1,39 @@ +#include "locality_aware.h" +#include "neighborhood/MPIL_Topo.h" + +int MPIL_Neighbor_alltoallv_init_ext(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) +{ + MPIL_Topo* topo; + MPIL_Topo_from_neighbor_comm(comm, &topo); + + MPIL_Neighbor_alltoallv_init_ext_topo(sendbuf, + sendcounts, + sdispls, + global_sindices, + sendtype, + recvbuf, + recvcounts, + rdispls, + global_rindices, + recvtype, + topo, + comm, + info, + request_ptr); + + MPIL_Topo_free(&topo); + + return MPI_SUCCESS; +} diff --git a/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_ext_topo.c b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_ext_topo.c new file mode 100644 index 000000000..e2ed0ee56 --- /dev/null +++ b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_ext_topo.c @@ -0,0 +1,64 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" +#include "neighborhood/neighborhood_init.h" + +int MPIL_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) +{ + switch (mpil_neighbor_alltoallv_init_implementation) + { + case NEIGHBOR_ALLTOALLV_INIT_STANDARD: + return neighbor_alltoallv_init_standard(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + topo, + comm, + info, + request_ptr); + case NEIGHBOR_ALLTOALLV_INIT_LOCALITY: + return neighbor_alltoallv_init_locality_ext(sendbuf, + sendcounts, + sdispls, + global_sindices, + sendtype, + recvbuf, + recvcounts, + rdispls, + global_rindices, + recvtype, + topo, + comm, + info, + request_ptr); + default: + return neighbor_alltoallv_init_standard(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + topo, + comm, + info, + request_ptr); + } +} diff --git a/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_topo.c b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_topo.c new file mode 100644 index 000000000..51155b694 --- /dev/null +++ b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_init_topo.c @@ -0,0 +1,44 @@ +#include "locality_aware.h" +#include "neighborhood/neighborhood_init.h" + +int MPIL_Neighbor_alltoallv_init_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) +{ + neighbor_alltoallv_init_ftn method; + + switch (mpil_neighbor_alltoallv_init_implementation) + { + case NEIGHBOR_ALLTOALLV_INIT_STANDARD: + method = neighbor_alltoallv_init_standard; + break; + case NEIGHBOR_ALLTOALLV_INIT_LOCALITY: + method = neighbor_alltoallv_init_locality; + break; + default: + method = neighbor_alltoallv_init_standard; + break; + } + + return method(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + topo, + comm, + info, + request_ptr); +} diff --git a/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_topo.c b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_topo.c new file mode 100644 index 000000000..4cbabd9c4 --- /dev/null +++ b/library/bindings/Neighborhood/MPIL_Neighbor_alltoallv_topo.c @@ -0,0 +1,43 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" +#include "neighborhood/neighbor.h" +int MPIL_Neighbor_alltoallv_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm) +{ + int rank; + MPI_Comm_rank(comm->global_comm, &rank); + + neighbor_alltoallv_ftn method; + + switch (mpil_neighbor_alltoallv_implementation) + { + case NEIGHBOR_ALLTOALLV_STANDARD: + method = neighbor_alltoallv_standard; + break; + case NEIGHBOR_ALLTOALLV_LOCALITY: + method = neighbor_alltoallv_locality; + break; + default: + method = neighbor_alltoallv_standard; + break; + } + + return method(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + topo, + comm); +} diff --git a/library/bindings/global_defaults.c b/library/bindings/global_defaults.c new file mode 100644 index 000000000..361c30907 --- /dev/null +++ b/library/bindings/global_defaults.c @@ -0,0 +1,48 @@ +#include "locality_aware.h" + +// Default algorithms +enum AlltoallMethod mpil_alltoall_implementation = ALLTOALL_PAIRWISE; +enum AlltoallvMethod mpil_alltoallv_implementation = ALLTOALLV_PAIRWISE; +enum AlltoallCRSMethod mpil_alltoall_crs_implementation = ALLTOALL_CRS_PERSONALIZED; +enum AlltoallvCRSMethod mpil_alltoallv_crs_implementation = ALLTOALLV_CRS_PERSONALIZED; +enum NeighborAlltoallvMethod mpil_neighbor_alltoallv_implementation = + NEIGHBOR_ALLTOALLV_STANDARD; +enum NeighborAlltoallvInitMethod mpil_neighbor_alltoallv_init_implementation = + NEIGHBOR_ALLTOALLV_INIT_STANDARD; + +int MPIL_Set_alltoall_algorithm(enum AlltoallMethod algorithm) +{ + mpil_alltoall_implementation = (enum AlltoallMethod)algorithm; + return MPI_SUCCESS; +} +int MPIL_Set_alltoallv_algorithm(enum AlltoallvMethod algorithm) +{ + mpil_alltoallv_implementation = (enum AlltoallvMethod)algorithm; + return MPI_SUCCESS; +} + +int MPIL_Set_alltoallv_neighbor_alogorithm(enum NeighborAlltoallvMethod algorithm) +{ + mpil_neighbor_alltoallv_implementation = (enum NeighborAlltoallvMethod)algorithm; + return MPI_SUCCESS; +} + +int MPIL_Set_alltoallv_neighbor_init_alogorithm( + enum NeighborAlltoallvInitMethod algorithm) +{ + mpil_neighbor_alltoallv_init_implementation = + (enum NeighborAlltoallvInitMethod)algorithm; + return MPI_SUCCESS; +} + +int MPIL_Set_alltoall_crs(enum AlltoallCRSMethod algorithm) +{ + mpil_alltoall_crs_implementation = (enum AlltoallCRSMethod)algorithm; + return MPI_SUCCESS; +} + +int MPIL_Set_alltoallv_crs(enum AlltoallvCRSMethod algorithm) +{ + mpil_alltoallv_crs_implementation = (enum AlltoallvCRSMethod)algorithm; + return MPI_SUCCESS; +} \ No newline at end of file diff --git a/src/collective/alltoall.h b/library/include/collective/alltoall.h similarity index 54% rename from src/collective/alltoall.h rename to library/include/collective/alltoall.h index 3cfe1bbb2..0d8a67a6d 100644 --- a/src/collective/alltoall.h +++ b/library/include/collective/alltoall.h @@ -2,36 +2,14 @@ #define MPI_ADVANCE_ALLTOALL_H #include -#include #include -#include "collective.h" -#include "communicator/mpil_comm.h" -#include "utils/utils.h" +#include "communicator/MPIL_Comm.h" #ifdef __cplusplus extern "C" { #endif -// TODO : need to add batch/batch asynch as underlying options for Alltoall -enum AlltoallMethod -{ - ALLTOALL_PAIRWISE, - ALLTOALL_NONBLOCKING, - ALLTOALL_HIERARCHICAL_PAIRWISE, - ALLTOALL_HIERARCHICAL_NONBLOCKING, - ALLTOALL_MULTILEADER_PAIRWISE, - ALLTOALL_MULTILEADER_NONBLOCKING, - ALLTOALL_NODE_AWARE_PAIRWISE, - ALLTOALL_NODE_AWARE_NONBLOCKING, - ALLTOALL_LOCALITY_AWARE_PAIRWISE, - ALLTOALL_LOCALITY_AWARE_NONBLOCKING, - ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE, - ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING, - ALLTOALL_PMPI -}; -extern enum AlltoallMethod mpil_alltoall_implementation; - typedef int (*alltoall_ftn)( const void*, const int, MPI_Datatype, void*, const int, MPI_Datatype, MPIL_Comm*); typedef int (*alltoall_helper_ftn)(const void*, @@ -43,6 +21,9 @@ typedef int (*alltoall_helper_ftn)(const void*, MPI_Comm, int tag); +//** External Wrappers +//**//---------------------------------------------------------------------- +/** @brief set tag and call pairwise_helper **/ int alltoall_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -50,6 +31,8 @@ int alltoall_pairwise(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief set message tag and call pairwise_helper **/ int alltoall_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -58,6 +41,7 @@ int alltoall_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); +/** @brief call alltoall_hiearchical passing pairwise_helper**/ int alltoall_hierarchical_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -65,6 +49,8 @@ int alltoall_hierarchical_pairwise(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief call alltoall_hiearchical passing nonblocking_helper**/ int alltoall_hierarchical_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -72,6 +58,8 @@ int alltoall_hierarchical_nonblocking(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief call alltoall_hiearchical passing pairwise_helper, nleaders=4**/ int alltoall_multileader_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -79,6 +67,8 @@ int alltoall_multileader_pairwise(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief call alltoall_hiearchical passing nonblocking_helper, nleaders=4**/ int alltoall_multileader_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -87,6 +77,7 @@ int alltoall_multileader_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); +/** @brief call node_aware with pairwise helper **/ int alltoall_node_aware_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -94,6 +85,8 @@ int alltoall_node_aware_pairwise(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief call node_aware with nonblocking helper **/ int alltoall_node_aware_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -101,6 +94,8 @@ int alltoall_node_aware_nonblocking(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief call locality_aware with pairwise helper, groups_per_node=4**/ int alltoall_locality_aware_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -108,6 +103,8 @@ int alltoall_locality_aware_pairwise(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief call locality_aware with nonblocking helper, groups_per_node=4**/ int alltoall_locality_aware_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -116,6 +113,7 @@ int alltoall_locality_aware_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); +/** @brief calls multileader_locality with pairwise helper **/ int alltoall_multileader_locality_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -123,6 +121,8 @@ int alltoall_multileader_locality_pairwise(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief calls multileader_locality with nonblocking helper **/ int alltoall_multileader_locality_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -131,6 +131,96 @@ int alltoall_multileader_locality_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); +//** Intermediate Wrappers +//**//----------------------------------------------------------------- +/** @brief calls alltoall_locality_aware with groups_per_node=1**/ +int alltoall_node_aware(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm); + +/** @brief wrapper around alltoall_multileader, nleaders=1)**/ +int alltoall_hierarchical(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm); + +//** Core Helper functions +//**//------------------------------------------------------------------ +/** @brief Uses Sendrecv to do the alltoall**/ +int pairwise_helper(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPI_Comm comm, + int tag); + +/** @brief Uses Isend and Irecv to do the alltoall**/ +int nonblocking_helper(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPI_Comm comm, + int tag); + +/** @brief ??? \todo fill**/ +int alltoall_multileader(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm, + int n_leaders); + +/** @brief complex returns locality_helper **/ +int alltoall_locality_aware(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm, + int groups_per_node); + +/** @brief ??? \todo fill**/ +int alltoall_locality_aware_helper(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm, + int groups_per_node, + MPI_Comm local_comm, + MPI_Comm group_comm, + int tag); + +/** @brief ??? \todo fill**/ +int alltoall_multileader_locality(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm); + // Calls underlying MPI implementation int alltoall_pmpi(const void* sendbuf, const int sendcount, diff --git a/src/collective/alltoallv.h b/library/include/collective/alltoallv.h similarity index 88% rename from src/collective/alltoallv.h rename to library/include/collective/alltoallv.h index 6943c4739..587381c39 100644 --- a/src/collective/alltoallv.h +++ b/library/include/collective/alltoallv.h @@ -5,24 +5,12 @@ #include #include -#include "collective.h" -#include "communicator/mpil_comm.h" +#include "communicator/MPIL_Comm.h" #ifdef __cplusplus extern "C" { #endif -// TODO : need to add hierarchical/locality-aware methods for alltoallv -enum AlltoallvMethod -{ - ALLTOALLV_PAIRWISE, - ALLTOALLV_NONBLOCKING, - ALLTOALLV_BATCH, - ALLTOALLV_BATCH_ASYNC, - ALLTOALLV_PMPI -}; -extern enum AlltoallvMethod mpil_alltoallv_implementation; - typedef int (*alltoallv_ftn)(const void*, const int*, const int*, @@ -70,7 +58,6 @@ int alltoallv_batch_async(const void* sendbuf, const int rdispls[], MPI_Datatype recvtype, MPIL_Comm* comm); - int alltoallv_pmpi(const void* sendbuf, const int sendcounts[], const int sdispls[], diff --git a/library/include/communicator/MPIL_Comm.h b/library/include/communicator/MPIL_Comm.h new file mode 100644 index 000000000..762077556 --- /dev/null +++ b/library/include/communicator/MPIL_Comm.h @@ -0,0 +1,66 @@ +#ifndef MPIL_COMM_H +#define MPIL_COMM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _MPIL_Comm +{ + MPI_Comm global_comm; + + MPI_Comm neighbor_comm; + + // For hierarchical collectives + MPI_Comm local_comm; + MPI_Comm group_comm; + + MPI_Comm leader_comm; + MPI_Comm leader_group_comm; + MPI_Comm leader_local_comm; + + int num_nodes; + int rank_node; + int ppn; + + MPI_Win win; + char* win_array; + int win_bytes; + int win_type_bytes; + + MPI_Request* requests; + MPI_Status* statuses; + int n_requests; + + int tag; + int max_tag; + + int* global_rank_to_local; + int* global_rank_to_node; + int* ordered_global_ranks; + +#ifdef GPU + + int gpus_per_node; + int rank_gpu; + // actual type is gpuStream_t, changed to void* to assist compiling. + void* proc_stream; +#endif +} MPIL_Comm; + +int get_node(const MPIL_Comm* data, const int proc); +int get_local_proc(const MPIL_Comm* data, const int proc); +int get_global_proc(const MPIL_Comm* data, const int node, const int local_proc); + +// For testing purposes (manually set PPN) +int update_locality(MPIL_Comm* xcomm, int ppn); + +int get_tag(MPIL_Comm* xcomm, int* tag); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/library/include/communicator/MPIL_Info.h b/library/include/communicator/MPIL_Info.h new file mode 100644 index 000000000..2976a24f3 --- /dev/null +++ b/library/include/communicator/MPIL_Info.h @@ -0,0 +1,18 @@ +#ifndef MPIL_INFO_H +#define MPIL_INFO_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _MPIL_Info +{ + int crs_num_initialized; + int crs_size_initialized; +} MPIL_Info; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/communicator/comm_data.h b/library/include/communicator/comm_data.h similarity index 93% rename from src/communicator/comm_data.h rename to library/include/communicator/comm_data.h index 1b2cfb3ff..244bc14a3 100644 --- a/src/communicator/comm_data.h +++ b/library/include/communicator/comm_data.h @@ -2,8 +2,6 @@ #define MPI_ADVANCE_COMM_DATA_H #include -#include -#include #ifdef __cplusplus extern "C" { diff --git a/src/communicator/comm_pkg.h b/library/include/communicator/comm_pkg.h similarity index 100% rename from src/communicator/comm_pkg.h rename to library/include/communicator/comm_pkg.h diff --git a/src/communicator/locality_comm.h b/library/include/communicator/locality_comm.h similarity index 94% rename from src/communicator/locality_comm.h rename to library/include/communicator/locality_comm.h index 31f4cbfbf..2a8f38746 100644 --- a/src/communicator/locality_comm.h +++ b/library/include/communicator/locality_comm.h @@ -3,15 +3,15 @@ #include +#include "MPIL_Comm.h" #include "comm_pkg.h" -#include "mpil_comm.h" // Declarations of C++ methods #ifdef __cplusplus extern "C" { #endif -typedef struct _LocalityComm +typedef struct LocalityComm { CommPkg* local_L_comm; CommPkg* local_S_comm; @@ -27,7 +27,6 @@ void init_locality_comm(LocalityComm** locality_ptr, MPI_Datatype recvtype); void finalize_locality_comm(LocalityComm* locality); void destroy_locality_comm(LocalityComm* locality); - void get_local_comm_data(LocalityComm* locality, int* max_local_num, int* max_local_size, diff --git a/src/heterogeneous/gpu_alltoall.h b/library/include/heterogeneous/gpu_alltoall.h similarity index 98% rename from src/heterogeneous/gpu_alltoall.h rename to library/include/heterogeneous/gpu_alltoall.h index af824f3ae..77495b6bc 100644 --- a/src/heterogeneous/gpu_alltoall.h +++ b/library/include/heterogeneous/gpu_alltoall.h @@ -2,7 +2,7 @@ #define MPI_ADVANCE_GPU_ALLTOALL_H #include "collective/alltoall.h" -#include "collective/collective.h" +#include "gpu_utils.h" #ifdef __cplusplus extern "C" { @@ -56,6 +56,7 @@ int copy_to_cpu_alltoall_nonblocking(const void* sendbuf, #ifdef OPENMP #include + int threaded_alltoall_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, diff --git a/src/heterogeneous/gpu_alltoallv.h b/library/include/heterogeneous/gpu_alltoallv.h similarity index 99% rename from src/heterogeneous/gpu_alltoallv.h rename to library/include/heterogeneous/gpu_alltoallv.h index d25a7ebb9..04e279efe 100644 --- a/src/heterogeneous/gpu_alltoallv.h +++ b/library/include/heterogeneous/gpu_alltoallv.h @@ -2,7 +2,7 @@ #define MPI_ADVANCE_GPU_ALLTOALLV_H #include "collective/alltoallv.h" -#include "collective/collective.h" +#include "gpu_utils.h" #ifdef __cplusplus extern "C" { diff --git a/src/utils/utils.h b/library/include/heterogeneous/gpu_utils.h similarity index 62% rename from src/utils/utils.h rename to library/include/heterogeneous/gpu_utils.h index 6b7baf1ea..7e0d2cb83 100644 --- a/src/utils/utils.h +++ b/library/include/heterogeneous/gpu_utils.h @@ -1,5 +1,5 @@ -#ifndef MPI_ADVANCE_UTILS_H -#define MPI_ADVANCE_UTILS_H +#ifndef MPIL_GPU_UTILS_H +#define MPIL_GPU_UTILS_H #ifdef HIP #include "utils_hip.h" @@ -13,16 +13,6 @@ extern "C" { #endif -// MPIL Info Object -typedef struct _MPIL_Info -{ - int crs_num_initialized; - int crs_size_initialized; -} MPIL_Info; - -int MPIL_Info_init(MPIL_Info** info); -int MPIL_Info_free(MPIL_Info** info); - // If using GPU, specific gpu methods (for either NCCL or HIP) #ifdef GPU __global__ void device_repack(char* __restrict__ sendbuf, @@ -39,17 +29,10 @@ void get_memcpy_kind(gpuMemoryType send_type, gpuMemcpyKind* memcpy_kind); void gpu_repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf); void gpu_check(int ierr); -#endif -// General utility methods (that use C++ functions) -void sort(int n_objects, int* object_indices, int* object_values); -void rotate(void* ref, int new_start_byte, int end_byte); -void reverse(void* recvbuf, int n_bytes, int var_bytes); void repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf); -// Allocate Vector in MPI -int MPIL_Alloc(void** pointer, const int bytes); -int MPIL_Free(void* pointer); +#endif #ifdef __cplusplus } diff --git a/src/utils/utils_cuda.h b/library/include/heterogeneous/utils_cuda.h similarity index 98% rename from src/utils/utils_cuda.h rename to library/include/heterogeneous/utils_cuda.h index 55475caae..5bd33e038 100644 --- a/src/utils/utils_cuda.h +++ b/library/include/heterogeneous/utils_cuda.h @@ -1,5 +1,6 @@ #ifndef UTILS_CUDA_HPP #define UTILS_CUDA_HPP +#include // Devices #define gpuGetDeviceCount cudaGetDeviceCount diff --git a/src/utils/utils_hip.h b/library/include/heterogeneous/utils_hip.h similarity index 97% rename from src/utils/utils_hip.h rename to library/include/heterogeneous/utils_hip.h index a3d59bd53..7d798ce8e 100644 --- a/src/utils/utils_hip.h +++ b/library/include/heterogeneous/utils_hip.h @@ -5,7 +5,6 @@ #define __HIP_PLATFORM_AMD__ 1 #endif -// #include "hip/hip_runtime_api.h" #include "hip/hip_runtime.h" // Devices diff --git a/library/include/neighborhood/MPIL_Topo.h b/library/include/neighborhood/MPIL_Topo.h new file mode 100644 index 000000000..0c71d3c69 --- /dev/null +++ b/library/include/neighborhood/MPIL_Topo.h @@ -0,0 +1,23 @@ +#ifndef MPIL_TOPO_H +#define MPIL_TOPO_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _MPIL_Topo +{ + int indegree; + int* sources; + int* sourceweights; + int outdegree; + int* destinations; + int* destweights; + int reorder; +} MPIL_Topo; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/neighborhood/sparse_coll.h b/library/include/neighborhood/alltoall_crs.h similarity index 79% rename from src/neighborhood/sparse_coll.h rename to library/include/neighborhood/alltoall_crs.h index f7d10268b..8afb02200 100644 --- a/src/neighborhood/sparse_coll.h +++ b/library/include/neighborhood/alltoall_crs.h @@ -1,45 +1,27 @@ -#ifndef MPI_ADVANCE_SPARSE_COLL_H -#define MPI_ADVANCE_SPARSE_COLL_H +#ifndef ALLTOALL_CRS_H +#define ALLTOALL_CRS_H -#include "communicator/locality_comm.h" -#include "communicator/mpil_comm.h" -#include "mpi.h" -#include "utils/utils.h" +#include + +#include "communicator/MPIL_Comm.h" +#include "communicator/MPIL_Info.h" -// Declarations of C++ methods #ifdef __cplusplus extern "C" { #endif -int MPIL_Alltoall_crs(const int send_nnz, - const int* dest, - const int sendcount, - MPI_Datatype sendtype, - const void* sendvals, - int* recv_nnz, - int** src_ptr, - int recvcount, - MPI_Datatype recvtype, - void** recvvals_ptr, - MPIL_Info* xinfo, - MPIL_Comm* xcomm); - -int MPIL_Alltoallv_crs(const int send_nnz, - const int send_size, - const int* dest, - const int* sendcounts, - const int* sdispls, - MPI_Datatype sendtype, - const void* sendvals, - int* recv_nnz, - int* recv_size, - int** src_ptr, - int** recvcounts_ptr, - int** rdispls_ptr, - MPI_Datatype recvtype, - void** recvvals_ptr, - MPIL_Info* xinfo, - MPIL_Comm* comm); +typedef int (*alltoall_crs_ftn)(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src, + int recvcount, + MPI_Datatype recvtype, + void** recvvals, + MPIL_Info* xinfo, + MPIL_Comm* comm); int alltoall_crs_rma(const int send_nnz, const int* dest, @@ -106,6 +88,23 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, MPIL_Info* xinfo, MPIL_Comm* comm); +typedef int (*alltoallv_crs_ftn)(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIL_Info* xinfo, + MPIL_Comm* comm); + int alltoallv_crs_personalized(const int send_nnz, const int send_size, const int* dest, @@ -176,7 +175,6 @@ int alltoallv_crs_nonblocking_loc(const int send_nnz, #ifdef __cplusplus } - #endif -#endif +#endif \ No newline at end of file diff --git a/src/neighborhood/neighbor.h b/library/include/neighborhood/neighbor.h similarity index 60% rename from src/neighborhood/neighbor.h rename to library/include/neighborhood/neighbor.h index e00b95ea6..f0c751192 100644 --- a/src/neighborhood/neighbor.h +++ b/library/include/neighborhood/neighbor.h @@ -2,26 +2,15 @@ #define MPI_ADVANCE_NEIGHBOR_COLL_H #include -#include -#include "communicator/locality_comm.h" -#include "dist_graph.h" -#include "dist_topo.h" -#include "persistent/persistent.h" -#include "utils/utils.h" +#include "communicator/MPIL_Comm.h" +#include "neighborhood/MPIL_Topo.h" // Declarations of C++ methods #ifdef __cplusplus extern "C" { #endif -enum NeighborAlltoallvMethod -{ - NEIGHBOR_ALLTOALLV_STANDARD, - NEIGHBOR_ALLTOALLV_LOCALITY -}; -extern enum NeighborAlltoallvMethod mpix_neighbor_alltoallv_implementation; - typedef int (*neighbor_alltoallv_ftn)(const void* sendbuffer, const int sendcounts[], const int sdispls[], @@ -36,26 +25,6 @@ typedef int (*neighbor_alltoallv_ftn)(const void* sendbuffer, // Standard Neighbor Alltoallv // Extension takes array of requests instead of single request // 'requests' must be of size indegree+outdegree! -int MPIL_Neighbor_alltoallv_topo(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm); - -int MPIL_Neighbor_alltoallv(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* comm); int neighbor_alltoallv_standard(const void* sendbuf, const int sendcounts[], diff --git a/library/include/neighborhood/neighbor_locality.h b/library/include/neighborhood/neighbor_locality.h new file mode 100644 index 000000000..062ca38e5 --- /dev/null +++ b/library/include/neighborhood/neighbor_locality.h @@ -0,0 +1,46 @@ +#ifndef NEIGHBOR_LOCALITY_H +#define NEIGHBOR_LOCALITY_H + +#include +#include + +#include "communicator/comm_data.h" +#include "communicator/comm_pkg.h" +#include "communicator/locality_comm.h" + +void map_procs_to_nodes(LocalityComm* locality, + const int orig_num_msgs, + const int* orig_procs, + const int* orig_counts, + std::vector& msg_nodes, + std::vector& msg_node_to_local, + bool incr); +void form_local_comm(const int orig_num_sends, + const int* orig_send_procs, + const int* orig_send_ptr, + const int* orig_sendcounts, + const long* orig_send_indices, + const std::vector& nodes_to_local, + CommData* send_data, + CommData* recv_data, + CommData* local_data, + std::vector& recv_idx_nodes, + LocalityComm* locality, + const int tag); +void form_global_comm(CommData* local_data, + CommData* global_data, + std::vector& local_data_nodes, + const MPIL_Comm* mpil_comm, + int tag); +void update_global_comm(LocalityComm* locality); +void form_global_map(const CommData* map_data, std::map& global_map); +void map_indices(CommData* idx_data, std::map& global_map); +void map_indices(CommData* idx_data, const CommData* map_data); +void remove_duplicates(CommData* comm_pkg); +void remove_duplicates(CommPkg* data); +void remove_duplicates(LocalityComm* locality); +void update_indices(LocalityComm* locality, + std::map& send_global_to_local, + std::map& recv_global_to_local); + +#endif \ No newline at end of file diff --git a/library/include/neighborhood/neighborhood_init.h b/library/include/neighborhood/neighborhood_init.h new file mode 100644 index 000000000..62ca31284 --- /dev/null +++ b/library/include/neighborhood/neighborhood_init.h @@ -0,0 +1,105 @@ +#ifndef MPI_ADVANCE_NEIGHBOR_INIT_H +#define MPI_ADVANCE_NEIGHBOR_INIT_H + +#include "MPIL_Topo.h" +#include "communicator/MPIL_Comm.h" +#include "communicator/MPIL_Info.h" +#include "persistent/MPIL_Request.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int neighbor_start(MPIL_Request* request); +int neighbor_wait(MPIL_Request* request, MPI_Status* status); +void init_neighbor_request(MPIL_Request** request_ptr); + +typedef int (*neighbor_alltoallv_init_ftn)(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); + +int neighbor_alltoallv_init_standard(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); + +int neighbor_alltoallv_init_locality(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); + +int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuffer, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); + +void init_locality(const int n_sends, + const int* send_procs, + const int* send_indptr, + const int* sendcounts, + const int n_recvs, + const int* recv_procs, + const int* recv_indptr, + const int* recvcounts, + const long* global_send_indices, + const long* global_recv_indices, + const MPI_Datatype sendtype, + const MPI_Datatype recvtype, + MPIL_Comm* mpil_comm, + MPIL_Request* request); + +int init_communication(const void* sendbuffer, + int n_sends, + const int* send_procs, + const int* send_ptr, + MPI_Datatype sendtype, + void* recvbuffer, + int n_recvs, + const int* recv_procs, + const int* recv_ptr, + MPI_Datatype recvtype, + int tag, + MPI_Comm comm, + int* n_request_ptr, + MPI_Request** request_ptr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/persistent/persistent.h b/library/include/persistent/MPIL_Request.h similarity index 61% rename from src/persistent/persistent.h rename to library/include/persistent/MPIL_Request.h index f69cafc51..c5c0ad04f 100644 --- a/src/persistent/persistent.h +++ b/library/include/persistent/MPIL_Request.h @@ -1,83 +1,63 @@ -#ifndef MPI_ADVANCE_PERSISTENT_H -#define MPI_ADVANCE_PERSISTENT_H - -#include "communicator/locality_comm.h" -#include "communicator/mpil_comm.h" -#include "utils/utils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct _MPIL_Request; // forward declaration -typedef struct _MPIL_Request MPIL_Request; - -struct _MPIL_Request -{ - // Message counts - // Will only use global unless locality-aware - int local_L_n_msgs; - int local_S_n_msgs; - int local_R_n_msgs; - int global_n_msgs; - - // MPI Request arrays - // Will only use global unless locality-aware - MPI_Request* local_L_requests; - MPI_Request* local_S_requests; - MPI_Request* local_R_requests; - MPI_Request* global_requests; - - // Pointer to locality communication, only for locality-aware - LocalityComm* locality; - - // Pointer to sendbuf and recvbuf - const void* sendbuf; // pointer to sendbuf (where original data begins) - void* recvbuf; // pointer to recvbuf (where final data goes) - - // Number of bytes per receive object (for locality-aware) - int recv_size; - - // Block size : for strided/blocked communication - int block_size; - - int tag; - int reorder; - - // For allocating cpu buffers for heterogeneous communication -#ifdef GPU - void* cpu_sendbuf; // for copy-to-cpu - void* cpu_recvbuf; // for copy-to-cpu -#endif - - // Keep track of which start/wait functions to call for given request - int (*start_function)(MPIL_Request* request); - int (*wait_function)(MPIL_Request* request, MPI_Status* status); -}; - -typedef int (*mpix_start_ftn)(MPIL_Request* request); -typedef int (*mpix_wait_ftn)(MPIL_Request* request, MPI_Status* status); - -// Starting locality-aware requests -// 1. Start Local_L -// 2. Start and wait for local_S -// 3. Start global -int MPIL_Start(MPIL_Request* request); - -// Wait for locality-aware requests -// 1. Wait for global -// 2. Start and wait for local_R -// 3. Wait for local_L -int MPIL_Wait(MPIL_Request* request, MPI_Status* status); - -int MPIL_Request_free(MPIL_Request** request); - -void init_request(MPIL_Request** request_ptr); -void allocate_requests(int n_requests, MPI_Request** request_ptr); -void destroy_request(MPIL_Request* request); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef MPIL_REQUEST_H +#define MPIL_REQUEST_H + +#include + +#include "communicator/locality_comm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _MPIL_Request +{ + // Message counts + // Will only use global unless locality-aware + int local_L_n_msgs; + int local_S_n_msgs; + int local_R_n_msgs; + int global_n_msgs; + + // MPI Request arrays + // Will only use global unless locality-aware + MPI_Request* local_L_requests; + MPI_Request* local_S_requests; + MPI_Request* local_R_requests; + MPI_Request* global_requests; + + // Pointer to locality communication, only for locality-aware + LocalityComm* locality; + + // Pointer to sendbuf and recvbuf + const void* sendbuf; // pointer to sendbuf (where original data begins) + void* recvbuf; // pointer to recvbuf (where final data goes) + + // Number of bytes per receive object (for locality-aware) + int recv_size; + + // Block size : for strided/blocked communication + int block_size; + + int tag; + int reorder; + + // For allocating cpu buffers for heterogeneous communication +#ifdef GPU + void* cpu_sendbuf; // for copy-to-cpu + void* cpu_recvbuf; // for copy-to-cpu +#endif + + // Keep track of which start/wait functions to call for given request + int (*start_function)(struct _MPIL_Request* request); + int (*wait_function)(struct _MPIL_Request* request, MPI_Status* status); +} MPIL_Request; + +void init_request(MPIL_Request** request_ptr); +void allocate_requests(int n_requests, MPI_Request** request_ptr); +void destroy_request(MPIL_Request* request); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/library/include/utils/utils.h b/library/include/utils/utils.h new file mode 100644 index 000000000..20242c93a --- /dev/null +++ b/library/include/utils/utils.h @@ -0,0 +1,51 @@ +#ifndef MPIL_UTILS_H +#define MPIL_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif // General utility methods (that use C++ functions) + +/** @brief wrapper around std::sort + @param [in] n_objects number of objects to short + @param [in, out] array of indexes + @param [in] array of values +**/ +void sort(int n_objects, int* object_indices, int* object_values); + +/** @brief wrapper around std::rotate, + * @details + * Rotates such that new_first_byte is first in array + * Divides recvbuf into two parts [first, middle] and (middle, last) + * then swaps their positioning. + * Example: A = 0, 1, 2, 3, 4, 5 + * std::rotate(A*, 2, A*+6) would split into (0, 1) and (2, 3, 4, 5) + * and after running A = 2, 3, 4, 5, 0, 1 + * + * @param [in, out] recvbuf buffer of elements to rotate + * @param [in] new_first_byte index immediately after the split point. + * @param [in] index of last element in the effected range + **/ +void rotate(void* ref, int new_start_byte, int end_byte); + +/** @brief reverses order of elements in recv_buffer + * @details + * Divides recvbuf into two parts [first, middle] and (middle, last) + * then swaps their positioning. + * Example: A = 0, 1, 2, 3, 4, 5 + * std::rotate(A*, 2, A*+6) would split into (0, 1) and (2, 3, 4, 5) + * and after running A = 2, 3, 4, 5, 0, 1 + * + * @param [in, out] recvbuf buffer of elements to rotate + * @param [in] new_first_byte index immediately after the split point. + * @param [in] index of last element in the effected range + * \todo why this instead of std::reverse? + **/ +void reverse(void* recvbuf, int n_bytes, int var_bytes); + +void repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/library/source/CMakeLists.txt b/library/source/CMakeLists.txt new file mode 100644 index 000000000..cec93ae70 --- /dev/null +++ b/library/source/CMakeLists.txt @@ -0,0 +1,10 @@ +add_subdirectory(collective) +add_subdirectory(communicator) +add_subdirectory(neighborhood) +add_subdirectory(persistent) +add_subdirectory(utils) +if(USE_GPU) + add_subdirectory(heterogeneous) +endif() + + diff --git a/library/source/collective/CMakeLists.txt b/library/source/collective/CMakeLists.txt new file mode 100644 index 000000000..221a3f1be --- /dev/null +++ b/library/source/collective/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(alltoall) +add_subdirectory(alltoallv) \ No newline at end of file diff --git a/library/source/collective/alltoall/CMakeLists.txt b/library/source/collective/alltoall/CMakeLists.txt new file mode 100644 index 000000000..7e0c4f347 --- /dev/null +++ b/library/source/collective/alltoall/CMakeLists.txt @@ -0,0 +1,23 @@ +target_sources(locality_aware PRIVATE + alltoall_hierarchical.c + alltoall_hierarchical_nonblocking.c + alltoall_hierarchical_pairwise.c + alltoall_locality_aware.c + alltoall_locality_aware_helper.c + alltoall_locality_aware_nonblocking.c + alltoall_locality_aware_pairwise.c + alltoall_multileader.c + alltoall_multileader_locality.c + alltoall_multileader_locality_nonblocking.c + alltoall_multileader_locality_pairwise.c + alltoall_multileader_nonblocking.c + alltoall_multileader_pairwise.c + alltoall_node_aware.c + alltoall_node_aware_nonblocking.c + alltoall_node_aware_pairwise.c + alltoall_nonblocking.c + alltoall_pairwise.c + alltoall_pmpi.c + nonblocking_helper.c + pairwise_helper.c +) diff --git a/library/source/collective/alltoall/alltoall_hierarchical.c b/library/source/collective/alltoall/alltoall_hierarchical.c new file mode 100644 index 000000000..fe807dd45 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_hierarchical.c @@ -0,0 +1,14 @@ +#include "collective/alltoall.h" + +int alltoall_hierarchical(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_multileader( + f, sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm, 1); +} \ No newline at end of file diff --git a/library/source/collective/alltoall/alltoall_hierarchical_nonblocking.c b/library/source/collective/alltoall/alltoall_hierarchical_nonblocking.c new file mode 100644 index 000000000..8d2840589 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_hierarchical_nonblocking.c @@ -0,0 +1,19 @@ +#include "collective/alltoall.h" + +int alltoall_hierarchical_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_hierarchical(nonblocking_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); +} diff --git a/library/source/collective/alltoall/alltoall_hierarchical_pairwise.c b/library/source/collective/alltoall/alltoall_hierarchical_pairwise.c new file mode 100644 index 000000000..daf2e48a0 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_hierarchical_pairwise.c @@ -0,0 +1,19 @@ +#include "collective/alltoall.h" + +int alltoall_hierarchical_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_hierarchical(pairwise_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); +} \ No newline at end of file diff --git a/library/source/collective/alltoall/alltoall_locality_aware.c b/library/source/collective/alltoall/alltoall_locality_aware.c new file mode 100644 index 000000000..ccdc74260 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_locality_aware.c @@ -0,0 +1,71 @@ +#include "collective/alltoall.h" +#include "locality_aware.h" + +int alltoall_locality_aware(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm, + int groups_per_node) +{ + int rank, num_procs; + MPI_Comm_rank(comm->global_comm, &rank); + MPI_Comm_size(comm->global_comm, &num_procs); + + int tag; + get_tag(comm, &tag); + + if (comm->local_comm == MPI_COMM_NULL) + { + MPIL_Comm_topo_init(comm); + } + + int ppn; + MPI_Comm_size(comm->local_comm, &ppn); + + MPI_Comm local_comm = comm->local_comm; + MPI_Comm group_comm = comm->group_comm; + + if (groups_per_node > 1) + { + if (ppn < groups_per_node) + { + groups_per_node = ppn; + } + int procs_per_group = ppn / groups_per_node; + + if (comm->leader_comm != MPI_COMM_NULL) + { + int ppg; + MPI_Comm_size(comm->leader_comm, &ppg); + if (ppg != procs_per_group) + { + MPI_Comm_free(&(comm->leader_comm)); + } + } + + if (comm->leader_comm == MPI_COMM_NULL) + { + MPIL_Comm_leader_init(comm, procs_per_group); + } + + local_comm = comm->leader_comm; + group_comm = comm->leader_group_comm; + } + + return alltoall_locality_aware_helper(f, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm, + groups_per_node, + local_comm, + group_comm, + tag); +} diff --git a/library/source/collective/alltoall/alltoall_locality_aware_helper.c b/library/source/collective/alltoall/alltoall_locality_aware_helper.c new file mode 100644 index 000000000..4bf7330f4 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_locality_aware_helper.c @@ -0,0 +1,88 @@ +#include + +#include "collective/alltoall.h" +#include "locality_aware.h" + +int alltoall_locality_aware_helper(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm, + int groups_per_node, + MPI_Comm local_comm, + MPI_Comm group_comm, + int tag) +{ + int rank, num_procs; + MPI_Comm_rank(comm->global_comm, &rank); + MPI_Comm_size(comm->global_comm, &num_procs); + + int ppg; + MPI_Comm_size(local_comm, &ppg); + + char* recv_buffer = (char*)recvbuf; + + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + int n_groups = num_procs / ppg; + + char* tmpbuf = (char*)malloc(num_procs * sendcount * send_size); + + // 1. Alltoall between group_comms (all data for any process on node) + f(sendbuf, + ppg * sendcount, + sendtype, + tmpbuf, + ppg * recvcount, + recvtype, + group_comm, + tag); + + // 2. Re-pack + int ctr = 0; + for (int dest_proc = 0; dest_proc < ppg; dest_proc++) + { + int offset = dest_proc * recvcount * recv_size; + for (int origin = 0; origin < n_groups; origin++) + { + int node_offset = origin * ppg * recvcount * recv_size; + memcpy(&(recv_buffer[ctr]), + &(tmpbuf[node_offset + offset]), + recvcount * recv_size); + ctr += recvcount * recv_size; + } + } + + // 3. Local alltoall + f(recvbuf, + n_groups * recvcount, + recvtype, + tmpbuf, + n_groups * recvcount, + recvtype, + local_comm, + tag); + + // 4. Re-order + ctr = 0; + for (int node = 0; node < n_groups; node++) + { + int node_offset = node * recvcount * recv_size; + for (int dest = 0; dest < ppg; dest++) + { + int dest_offset = dest * n_groups * recvcount * recv_size; + memcpy(&(recv_buffer[ctr]), + &(tmpbuf[node_offset + dest_offset]), + recvcount * recv_size); + ctr += recvcount * recv_size; + } + } + + free(tmpbuf); + return MPI_SUCCESS; +} diff --git a/library/source/collective/alltoall/alltoall_locality_aware_nonblocking.c b/library/source/collective/alltoall/alltoall_locality_aware_nonblocking.c new file mode 100644 index 000000000..acd82dda1 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_locality_aware_nonblocking.c @@ -0,0 +1,20 @@ +#include "collective/alltoall.h" + +int alltoall_locality_aware_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_locality_aware(nonblocking_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm, + 4); +} diff --git a/library/source/collective/alltoall/alltoall_locality_aware_pairwise.c b/library/source/collective/alltoall/alltoall_locality_aware_pairwise.c new file mode 100644 index 000000000..723df05c4 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_locality_aware_pairwise.c @@ -0,0 +1,20 @@ +#include "collective/alltoall.h" + +int alltoall_locality_aware_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_locality_aware(pairwise_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm, + 4); +} diff --git a/library/source/collective/alltoall/alltoall_multileader.c b/library/source/collective/alltoall/alltoall_multileader.c new file mode 100644 index 000000000..e374250d4 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_multileader.c @@ -0,0 +1,165 @@ +#include + +#include "collective/alltoall.h" +#include "locality_aware.h" + +int alltoall_multileader(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm, + int n_leaders) +{ + int rank, num_procs; + MPI_Comm_rank(comm->global_comm, &rank); + MPI_Comm_size(comm->global_comm, &num_procs); + + int tag; + get_tag(comm, &tag); + + if (comm->local_comm == MPI_COMM_NULL) + { + MPIL_Comm_topo_init(comm); + } + + int ppn; + MPI_Comm_size(comm->local_comm, &ppn); + + MPI_Comm local_comm = comm->local_comm; + MPI_Comm group_comm = comm->group_comm; + + if (n_leaders > 1) + { + if (ppn < n_leaders) + { + n_leaders = ppn; + } + int procs_per_leader = ppn / n_leaders; + + // If leader comm exists but with wrong number of leaders per node, + // free the stale communicator + if (comm->leader_comm != MPI_COMM_NULL) + { + int ppl; + MPI_Comm_size(comm->leader_comm, &ppl); + if (ppl != procs_per_leader) + { + MPI_Comm_free(&comm->leader_comm); + } + } + + // If leader comm does not exist, create it + if (comm->leader_comm == MPI_COMM_NULL) + { + MPIL_Comm_leader_init(comm, procs_per_leader); + } + + local_comm = comm->leader_comm; + group_comm = comm->leader_group_comm; + } + + char* recv_buffer = (char*)recvbuf; + char* send_buffer = (char*)sendbuf; + + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + int local_rank, ppl; + MPI_Comm_rank(local_comm, &local_rank); + MPI_Comm_size(local_comm, &ppl); + + // TODO: currently assuming full nodes, even ppn per node + // this is common, so fair assumption for now + // likely need to fix before using in something like Trilinos + int n_nodes = num_procs / ppl; + + char* local_send_buffer = NULL; + char* local_recv_buffer = NULL; + + if (local_rank == 0) + { + local_send_buffer = (char*)malloc(ppl * num_procs * sendcount * send_size); + local_recv_buffer = (char*)malloc(ppl * num_procs * recvcount * recv_size); + } + else + { + local_send_buffer = (char*)malloc(sizeof(char)); + local_recv_buffer = (char*)malloc(sizeof(char)); + } + + // 1. Gather locally + MPI_Gather(send_buffer, + sendcount * num_procs, + sendtype, + local_recv_buffer, + sendcount * num_procs, + sendtype, + 0, + local_comm); + + // 2. Re-pack for sends + // Assumes SMP ordering + // TODO: allow for other orderings + int ctr; + + if (local_rank == 0) + { + ctr = 0; + for (int dest_node = 0; dest_node < n_nodes; dest_node++) + { + int dest_node_start = dest_node * ppl * sendcount * send_size; + for (int origin_proc = 0; origin_proc < ppl; origin_proc++) + { + int origin_proc_start = origin_proc * num_procs * sendcount * send_size; + memcpy(&(local_send_buffer[ctr]), + &(local_recv_buffer[origin_proc_start + dest_node_start]), + ppl * sendcount * send_size); + ctr += ppl * sendcount * send_size; + } + } + + // 3. MPI_Alltoall between leaders + f(local_send_buffer, + ppl * ppl * sendcount, + sendtype, + local_recv_buffer, + ppl * ppl * recvcount, + recvtype, + group_comm, + tag); + + // 4. Re-pack for local scatter + ctr = 0; + for (int dest_proc = 0; dest_proc < ppl; dest_proc++) + { + int dest_proc_start = dest_proc * recvcount * recv_size; + for (int orig_proc = 0; orig_proc < num_procs; orig_proc++) + { + int orig_proc_start = orig_proc * ppl * recvcount * recv_size; + memcpy(&(local_send_buffer[ctr]), + &(local_recv_buffer[orig_proc_start + dest_proc_start]), + recvcount * recv_size); + ctr += recvcount * recv_size; + } + } + } + + // 5. Scatter + MPI_Scatter(local_send_buffer, + recvcount * num_procs, + recvtype, + recv_buffer, + recvcount * num_procs, + recvtype, + 0, + local_comm); + + free(local_send_buffer); + free(local_recv_buffer); + + return MPI_SUCCESS; +} diff --git a/library/source/collective/alltoall/alltoall_multileader_locality.c b/library/source/collective/alltoall/alltoall_multileader_locality.c new file mode 100644 index 000000000..cd5183280 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_multileader_locality.c @@ -0,0 +1,192 @@ +#include + +#include "collective/alltoall.h" +#include "locality_aware.h" + +int alltoall_multileader_locality(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + int rank, num_procs; + MPI_Comm_rank(comm->global_comm, &rank); + MPI_Comm_size(comm->global_comm, &num_procs); + + int tag; + get_tag(comm, &tag); + + if (comm->local_comm == MPI_COMM_NULL) + { + MPIL_Comm_topo_init(comm); + } + + int local_rank, ppn; + MPI_Comm_rank(comm->local_comm, &local_rank); + MPI_Comm_size(comm->local_comm, &ppn); + + if (comm->leader_comm == MPI_COMM_NULL) + { + int num_leaders_per_node = 4; + if (ppn < num_leaders_per_node) + { + num_leaders_per_node = ppn; + } + MPIL_Comm_leader_init(comm, ppn / num_leaders_per_node); + } + + int procs_per_leader, leader_rank; + MPI_Comm_rank(comm->leader_comm, &leader_rank); + MPI_Comm_size(comm->leader_comm, &procs_per_leader); + + char* recv_buffer = (char*)recvbuf; + char* send_buffer = (char*)sendbuf; + + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + // TODO: currently assuming full nodes, even procs_per_leader per node + // this is common, so fair assumption for now + // likely need to fix before using in something like Trilinos + int n_nodes = num_procs / ppn; + int n_leaders = num_procs / procs_per_leader; + + int leaders_per_node; + MPI_Comm_size(comm->leader_local_comm, &leaders_per_node); + + char* local_send_buffer = NULL; + char* local_recv_buffer = NULL; + if (leader_rank == 0) + { + local_send_buffer = + (char*)malloc(procs_per_leader * num_procs * sendcount * send_size); + local_recv_buffer = + (char*)malloc(procs_per_leader * num_procs * recvcount * recv_size); + } + else + { + local_send_buffer = (char*)malloc(sizeof(char)); + local_recv_buffer = (char*)malloc(sizeof(char)); + } + // 1. Gather locally + MPI_Gather(send_buffer, + sendcount * num_procs, + sendtype, + local_recv_buffer, + sendcount * num_procs, + sendtype, + 0, + comm->leader_comm); + + // 2. Re-pack for sends + // Assumes SMP ordering + // TODO: allow for other orderings + int ctr; + + if (leader_rank == 0) + { + /* + alltoall_locality_aware_helper(f, sendbuf, procs_per_leader*sendcount, + sendtype, recvbuf, procs_per_leader*recvcount, recvtype, comm, groups_per_node, + comm->leader_local_comm, comm->group_comm); + */ + + ctr = 0; + for (int dest_node = 0; dest_node < n_leaders; dest_node++) + { + int dest_node_start = dest_node * procs_per_leader * sendcount * send_size; + for (int origin_proc = 0; origin_proc < procs_per_leader; origin_proc++) + { + int origin_proc_start = origin_proc * num_procs * sendcount * send_size; + memcpy(&(local_send_buffer[ctr]), + &(local_recv_buffer[origin_proc_start + dest_node_start]), + procs_per_leader * sendcount * send_size); + ctr += procs_per_leader * sendcount * send_size; + } + } + + // 3. MPI_Alltoall between nodes + f(local_send_buffer, + ppn * procs_per_leader * sendcount, + sendtype, + local_recv_buffer, + ppn * procs_per_leader * recvcount, + recvtype, + comm->group_comm, + tag); + + // Re-Pack for exchange between local leaders + ctr = 0; + for (int local_leader = 0; local_leader < leaders_per_node; local_leader++) + { + int leader_start = local_leader * procs_per_leader * procs_per_leader * + sendcount * send_size; + for (int dest_node = 0; dest_node < n_nodes; dest_node++) + { + int dest_node_start = + dest_node * ppn * procs_per_leader * sendcount * send_size; + memcpy(&(local_send_buffer[ctr]), + &(local_recv_buffer[dest_node_start + leader_start]), + procs_per_leader * procs_per_leader * sendcount * send_size); + ctr += procs_per_leader * procs_per_leader * sendcount * send_size; + } + } + + f(local_send_buffer, + n_nodes * procs_per_leader * procs_per_leader * sendcount, + sendtype, + local_recv_buffer, + n_nodes * procs_per_leader * procs_per_leader * recvcount, + recvtype, + comm->leader_local_comm, + tag); + + ctr = 0; + for (int dest_proc = 0; dest_proc < procs_per_leader; dest_proc++) + { + int dest_proc_start = dest_proc * recvcount * recv_size; + + for (int orig_node = 0; orig_node < n_nodes; orig_node++) + { + int orig_node_start = orig_node * procs_per_leader * procs_per_leader * + recvcount * recv_size; + + for (int orig_leader = 0; orig_leader < leaders_per_node; orig_leader++) + { + int orig_leader_start = orig_leader * n_nodes * procs_per_leader * + procs_per_leader * recvcount * recv_size; + for (int orig_proc = 0; orig_proc < procs_per_leader; orig_proc++) + { + int orig_proc_start = + orig_proc * procs_per_leader * recvcount * recv_size; + int idx = orig_node_start + orig_leader_start + orig_proc_start + + dest_proc_start; + memcpy(&(local_send_buffer[ctr]), + &(local_recv_buffer[idx]), + recvcount * recv_size); + ctr += recvcount * recv_size; + } + } + } + } + } + + // 5. Scatter + MPI_Scatter(local_send_buffer, + recvcount * num_procs, + recvtype, + recv_buffer, + recvcount * num_procs, + recvtype, + 0, + comm->leader_comm); + + free(local_send_buffer); + free(local_recv_buffer); + + return MPI_SUCCESS; +} diff --git a/library/source/collective/alltoall/alltoall_multileader_locality_nonblocking.c b/library/source/collective/alltoall/alltoall_multileader_locality_nonblocking.c new file mode 100644 index 000000000..ede3b5dac --- /dev/null +++ b/library/source/collective/alltoall/alltoall_multileader_locality_nonblocking.c @@ -0,0 +1,19 @@ +#include "collective/alltoall.h" + +int alltoall_multileader_locality_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_multileader_locality(nonblocking_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); +} diff --git a/library/source/collective/alltoall/alltoall_multileader_locality_pairwise.c b/library/source/collective/alltoall/alltoall_multileader_locality_pairwise.c new file mode 100644 index 000000000..a6c422138 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_multileader_locality_pairwise.c @@ -0,0 +1,19 @@ +#include "collective/alltoall.h" + +int alltoall_multileader_locality_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_multileader_locality(pairwise_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); +} diff --git a/library/source/collective/alltoall/alltoall_multileader_nonblocking.c b/library/source/collective/alltoall/alltoall_multileader_nonblocking.c new file mode 100644 index 000000000..2ce212863 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_multileader_nonblocking.c @@ -0,0 +1,20 @@ +#include "collective/alltoall.h" + +int alltoall_multileader_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_multileader(nonblocking_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm, + 4); +} diff --git a/library/source/collective/alltoall/alltoall_multileader_pairwise.c b/library/source/collective/alltoall/alltoall_multileader_pairwise.c new file mode 100644 index 000000000..dbd68ff1d --- /dev/null +++ b/library/source/collective/alltoall/alltoall_multileader_pairwise.c @@ -0,0 +1,20 @@ +#include "collective/alltoall.h" + +int alltoall_multileader_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_multileader(pairwise_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm, + 4); +} \ No newline at end of file diff --git a/library/source/collective/alltoall/alltoall_node_aware.c b/library/source/collective/alltoall/alltoall_node_aware.c new file mode 100644 index 000000000..564117168 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_node_aware.c @@ -0,0 +1,14 @@ +#include "collective/alltoall.h" + +int alltoall_node_aware(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_locality_aware( + f, sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm, 1); +} diff --git a/library/source/collective/alltoall/alltoall_node_aware_nonblocking.c b/library/source/collective/alltoall/alltoall_node_aware_nonblocking.c new file mode 100644 index 000000000..6200d51c1 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_node_aware_nonblocking.c @@ -0,0 +1,19 @@ +#include "collective/alltoall.h" + +int alltoall_node_aware_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_node_aware(nonblocking_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); +} diff --git a/library/source/collective/alltoall/alltoall_node_aware_pairwise.c b/library/source/collective/alltoall/alltoall_node_aware_pairwise.c new file mode 100644 index 000000000..cd430df1c --- /dev/null +++ b/library/source/collective/alltoall/alltoall_node_aware_pairwise.c @@ -0,0 +1,19 @@ +#include "collective/alltoall.h" + +int alltoall_node_aware_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return alltoall_node_aware(pairwise_helper, + sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm); +} diff --git a/library/source/collective/alltoall/alltoall_nonblocking.c b/library/source/collective/alltoall/alltoall_nonblocking.c new file mode 100644 index 000000000..69045a6dc --- /dev/null +++ b/library/source/collective/alltoall/alltoall_nonblocking.c @@ -0,0 +1,23 @@ +#include "collective/alltoall.h" +#include "locality_aware.h" + +int alltoall_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + int tag; + get_tag(comm, &tag); + + return nonblocking_helper(sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm->global_comm, + tag); +} diff --git a/library/source/collective/alltoall/alltoall_pairwise.c b/library/source/collective/alltoall/alltoall_pairwise.c new file mode 100644 index 000000000..56e30b729 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_pairwise.c @@ -0,0 +1,23 @@ +#include "collective/alltoall.h" +#include "locality_aware.h" + +int alltoall_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + int tag; + get_tag(comm, &tag); + + return pairwise_helper(sendbuf, + sendcount, + sendtype, + recvbuf, + recvcount, + recvtype, + comm->global_comm, + tag); +} \ No newline at end of file diff --git a/library/source/collective/alltoall/alltoall_pmpi.c b/library/source/collective/alltoall/alltoall_pmpi.c new file mode 100644 index 000000000..c3392ac29 --- /dev/null +++ b/library/source/collective/alltoall/alltoall_pmpi.c @@ -0,0 +1,13 @@ +#include "collective/alltoall.h" +// Calls underlying MPI implementation +int alltoall_pmpi(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return PMPI_Alltoall( + sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm->global_comm); +} diff --git a/library/source/collective/alltoall/nonblocking_helper.c b/library/source/collective/alltoall/nonblocking_helper.c new file mode 100644 index 000000000..a095b60d4 --- /dev/null +++ b/library/source/collective/alltoall/nonblocking_helper.c @@ -0,0 +1,65 @@ +#include "collective/alltoall.h" + +int nonblocking_helper(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPI_Comm comm, + int tag) +{ + int rank, num_procs; + MPI_Comm_rank(comm, &rank); + MPI_Comm_size(comm, &num_procs); + + int send_proc, recv_proc; + int send_pos, recv_pos; + + char* recv_buffer = (char*)recvbuf; + char* send_buffer = (char*)sendbuf; + + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + MPI_Request* requests = (MPI_Request*)malloc(2 * num_procs * sizeof(MPI_Request)); + + // Send to rank + i + // Recv from rank - i + for (int i = 0; i < num_procs; i++) + { + send_proc = rank + i; + if (send_proc >= num_procs) + { + send_proc -= num_procs; + } + recv_proc = rank - i; + if (recv_proc < 0) + { + recv_proc += num_procs; + } + send_pos = send_proc * sendcount * send_size; + recv_pos = recv_proc * recvcount * recv_size; + + MPI_Isend(send_buffer + send_pos, + sendcount, + sendtype, + send_proc, + tag, + comm, + &(requests[i])); + MPI_Irecv(recv_buffer + recv_pos, + recvcount, + recvtype, + recv_proc, + tag, + comm, + &(requests[num_procs + i])); + } + + MPI_Waitall(2 * num_procs, requests, MPI_STATUSES_IGNORE); + + free(requests); + return MPI_SUCCESS; +} diff --git a/library/source/collective/alltoall/pairwise_helper.c b/library/source/collective/alltoall/pairwise_helper.c new file mode 100644 index 000000000..bca62c171 --- /dev/null +++ b/library/source/collective/alltoall/pairwise_helper.c @@ -0,0 +1,58 @@ +#include "collective/alltoall.h" + +int pairwise_helper(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPI_Comm comm, + int tag) +{ + int rank, num_procs; + MPI_Comm_rank(comm, &rank); + MPI_Comm_size(comm, &num_procs); + + int send_proc, recv_proc; + int send_pos, recv_pos; + MPI_Status status; + + char* recv_buffer = (char*)recvbuf; + char* send_buffer = (char*)sendbuf; + + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + // Send to rank + i + // Recv from rank - i + for (int i = 0; i < num_procs; i++) + { + send_proc = rank + i; + if (send_proc >= num_procs) + { + send_proc -= num_procs; + } + recv_proc = rank - i; + if (recv_proc < 0) + { + recv_proc += num_procs; + } + send_pos = send_proc * sendcount * send_size; + recv_pos = recv_proc * recvcount * recv_size; + + MPI_Sendrecv(send_buffer + send_pos, + sendcount, + sendtype, + send_proc, + tag, + recv_buffer + recv_pos, + recvcount, + recvtype, + recv_proc, + tag, + comm, + &status); + } + return MPI_SUCCESS; +} diff --git a/library/source/collective/alltoallv/CMakeLists.txt b/library/source/collective/alltoallv/CMakeLists.txt new file mode 100644 index 000000000..8f4a7d31b --- /dev/null +++ b/library/source/collective/alltoallv/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(locality_aware PRIVATE + alltoallv_batch_async.c + alltoallv_pairwise.c + alltoallv_batch.c + alltoallv_nonblocking.c + alltoallv_pmpi.c +) diff --git a/library/source/collective/alltoallv/alltoallv_batch.c b/library/source/collective/alltoallv/alltoallv_batch.c new file mode 100644 index 000000000..34dad7876 --- /dev/null +++ b/library/source/collective/alltoallv/alltoallv_batch.c @@ -0,0 +1,98 @@ +#include "collective/alltoallv.h" +#include "locality_aware.h" + +int alltoallv_batch(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + int rank, num_procs; + MPI_Comm_rank(comm->global_comm, &rank); + MPI_Comm_size(comm->global_comm, &num_procs); + + // Tuning Parameter : number of non-blocking messages between waits + int nb_stride = 5; + if (nb_stride >= num_procs) + { + alltoallv_nonblocking(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); + } + + int tag; + get_tag(comm, &tag); + + int ctr; + int send_proc, recv_proc; + int send_pos, recv_pos; + + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + MPI_Request* requests = (MPI_Request*)malloc(2 * nb_stride * sizeof(MPI_Request)); + + char* send_buffer = (char*)sendbuf; + char* recv_buffer = (char*)recvbuf; + + // For each step i + // exchange among procs stride (i+1) apart + ctr = 0; + for (int i = 0; i < num_procs; i++) + { + send_proc = rank + i; + if (send_proc >= num_procs) + { + send_proc -= num_procs; + } + recv_proc = rank - i; + if (recv_proc < 0) + { + recv_proc += num_procs; + } + + send_pos = sdispls[send_proc] * send_size; + recv_pos = rdispls[recv_proc] * recv_size; + + MPI_Isend(send_buffer + send_pos, + sendcounts[send_proc], + sendtype, + send_proc, + tag, + comm->global_comm, + &(requests[ctr++])); + MPI_Irecv(recv_buffer + recv_pos, + recvcounts[recv_proc], + recvtype, + recv_proc, + tag, + comm->global_comm, + &(requests[ctr++])); + + if ((i + 1) % nb_stride == 0) + { + MPI_Waitall(2 * nb_stride, requests, MPI_STATUSES_IGNORE); + ctr = 0; + } + } + + if (ctr) + { + MPI_Waitall(ctr, requests, MPI_STATUSES_IGNORE); + } + + free(requests); + + return 0; +} diff --git a/library/source/collective/alltoallv/alltoallv_batch_async.c b/library/source/collective/alltoallv/alltoallv_batch_async.c new file mode 100644 index 000000000..fba96e3d0 --- /dev/null +++ b/library/source/collective/alltoallv/alltoallv_batch_async.c @@ -0,0 +1,96 @@ +#include "collective/alltoallv.h" +#include "locality_aware.h" + +int alltoallv_batch_async(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + int rank, num_procs; + MPI_Comm_rank(comm->global_comm, &rank); + MPI_Comm_size(comm->global_comm, &num_procs); + + // Tuning Parameter : number of non-blocking messages between waits + int nb_stride = 5; + if (nb_stride >= num_procs) + { + return alltoallv_nonblocking(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); + } + + int tag; + get_tag(comm, &tag); + + int send_proc, recv_proc; + int send_pos, recv_pos; + + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + MPI_Request* requests = (MPI_Request*)malloc(2 * nb_stride * sizeof(MPI_Request)); + + char* send_buffer = (char*)sendbuf; + char* recv_buffer = (char*)recvbuf; + + // For each step i + // exchange among procs stride (i+1) apart + int send_idx = 0; + int recv_idx = 0; + for (int i = 0; i < num_procs; i++) + { + send_proc = rank + i; + if (send_proc >= num_procs) + { + send_proc -= num_procs; + } + recv_proc = rank - i; + if (recv_proc < 0) + { + recv_proc += num_procs; + } + + send_pos = sdispls[send_proc] * send_size; + recv_pos = rdispls[recv_proc] * recv_size; + + MPI_Isend(send_buffer + send_pos, + sendcounts[send_proc], + sendtype, + send_proc, + tag, + comm->global_comm, + &(requests[send_idx++])); + MPI_Irecv(recv_buffer + recv_pos, + recvcounts[recv_proc], + recvtype, + recv_proc, + tag, + comm->global_comm, + &(requests[nb_stride + recv_idx++])); + + if ((i + 1) >= nb_stride) + { + MPI_Waitany(nb_stride, requests, &send_idx, MPI_STATUSES_IGNORE); + MPI_Waitany( + nb_stride, &(requests[nb_stride]), &recv_idx, MPI_STATUSES_IGNORE); + } + } + + MPI_Waitall(2 * nb_stride, requests, MPI_STATUSES_IGNORE); + + free(requests); + + return 0; +} diff --git a/library/source/collective/alltoallv/alltoallv_nonblocking.c b/library/source/collective/alltoallv/alltoallv_nonblocking.c new file mode 100644 index 000000000..0ca8c15af --- /dev/null +++ b/library/source/collective/alltoallv/alltoallv_nonblocking.c @@ -0,0 +1,85 @@ +#include "collective/alltoallv.h" +#include "locality_aware.h" + +int alltoallv_nonblocking(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + int rank, num_procs; + MPI_Comm_rank(comm->global_comm, &rank); + MPI_Comm_size(comm->global_comm, &num_procs); + + if (num_procs <= 1) + { + alltoallv_pairwise(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); + } + + int tag; + get_tag(comm, &tag); + + int send_proc, recv_proc; + int send_pos, recv_pos; + + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + MPI_Request* requests = (MPI_Request*)malloc(2 * num_procs * sizeof(MPI_Request)); + + char* send_buffer = (char*)sendbuf; + char* recv_buffer = (char*)recvbuf; + + // For each step i + // exchange among procs stride (i+1) apart + for (int i = 0; i < num_procs; i++) + { + send_proc = rank + i; + if (send_proc >= num_procs) + { + send_proc -= num_procs; + } + recv_proc = rank - i; + if (recv_proc < 0) + { + recv_proc += num_procs; + } + + send_pos = sdispls[send_proc] * send_size; + recv_pos = rdispls[recv_proc] * recv_size; + + MPI_Isend(send_buffer + send_pos, + sendcounts[send_proc], + sendtype, + send_proc, + tag, + comm->global_comm, + &(requests[i])); + MPI_Irecv(recv_buffer + recv_pos, + recvcounts[recv_proc], + recvtype, + recv_proc, + tag, + comm->global_comm, + &(requests[num_procs + i])); + } + + MPI_Waitall(2 * num_procs, requests, MPI_STATUSES_IGNORE); + + free(requests); + + return 0; +} diff --git a/library/source/collective/alltoallv/alltoallv_pairwise.c b/library/source/collective/alltoallv/alltoallv_pairwise.c new file mode 100644 index 000000000..3774d824f --- /dev/null +++ b/library/source/collective/alltoallv/alltoallv_pairwise.c @@ -0,0 +1,65 @@ +#include "collective/alltoallv.h" +#include "locality_aware.h" + +int alltoallv_pairwise(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + int rank, num_procs; + MPI_Comm_rank(comm->global_comm, &rank); + MPI_Comm_size(comm->global_comm, &num_procs); + + int tag; + get_tag(comm, &tag); + + int send_proc, recv_proc; + int send_pos, recv_pos; + MPI_Status status; + + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + char* send_buffer = (char*)sendbuf; + char* recv_buffer = (char*)recvbuf; + + // Send to rank + i + // Recv from rank - i + for (int i = 0; i < num_procs; i++) + { + send_proc = rank + i; + if (send_proc >= num_procs) + { + send_proc -= num_procs; + } + recv_proc = rank - i; + if (recv_proc < 0) + { + recv_proc += num_procs; + } + + send_pos = sdispls[send_proc] * send_size; + recv_pos = rdispls[recv_proc] * recv_size; + + MPI_Sendrecv(send_buffer + send_pos, + sendcounts[send_proc], + sendtype, + send_proc, + tag, + recv_buffer + recv_pos, + recvcounts[recv_proc], + recvtype, + recv_proc, + tag, + comm->global_comm, + &status); + } + + return 0; +} diff --git a/library/source/collective/alltoallv/alltoallv_pmpi.c b/library/source/collective/alltoallv/alltoallv_pmpi.c new file mode 100644 index 000000000..58e6dfbb2 --- /dev/null +++ b/library/source/collective/alltoallv/alltoallv_pmpi.c @@ -0,0 +1,23 @@ +#include "collective/alltoallv.h" + +// Calls underlying MPI implementation +int alltoallv_pmpi(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm) +{ + return PMPI_Alltoallv(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm->global_comm); +} diff --git a/library/source/communicator/CMakeLists.txt b/library/source/communicator/CMakeLists.txt new file mode 100644 index 000000000..5c3bce741 --- /dev/null +++ b/library/source/communicator/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(locality_aware PRIVATE + update_locality.c + topology_functions.c + comm_data.c + comm_pkg.c + locality_comm.c + get_tag.c +) \ No newline at end of file diff --git a/src/communicator/comm_data.c b/library/source/communicator/comm_data.c similarity index 95% rename from src/communicator/comm_data.c rename to library/source/communicator/comm_data.c index 08e122895..c31c2249e 100644 --- a/src/communicator/comm_data.c +++ b/library/source/communicator/comm_data.c @@ -1,4 +1,6 @@ -#include "comm_data.h" +#include "communicator/comm_data.h" + +#include void init_comm_data(CommData** comm_data_ptr, MPI_Datatype datatype) { diff --git a/src/communicator/comm_pkg.c b/library/source/communicator/comm_pkg.c similarity index 91% rename from src/communicator/comm_pkg.c rename to library/source/communicator/comm_pkg.c index 144f930fc..bcc2a4140 100644 --- a/src/communicator/comm_pkg.c +++ b/library/source/communicator/comm_pkg.c @@ -1,4 +1,6 @@ -#include "comm_pkg.h" +#include "communicator/comm_pkg.h" + +#include void init_comm_pkg(CommPkg** comm_ptr, MPI_Datatype sendtype, diff --git a/library/source/communicator/get_tag.c b/library/source/communicator/get_tag.c new file mode 100644 index 000000000..a970fa7aa --- /dev/null +++ b/library/source/communicator/get_tag.c @@ -0,0 +1,9 @@ +#include "communicator/MPIL_Comm.h" + +int get_tag(MPIL_Comm* xcomm, int* tag) +{ + *tag = xcomm->tag; + xcomm->tag = ((xcomm->tag + 1) % xcomm->max_tag); + + return MPI_SUCCESS; +} diff --git a/src/communicator/locality_comm.c b/library/source/communicator/locality_comm.c similarity index 88% rename from src/communicator/locality_comm.c rename to library/source/communicator/locality_comm.c index d881efb0c..2e169cc57 100644 --- a/src/communicator/locality_comm.c +++ b/library/source/communicator/locality_comm.c @@ -1,26 +1,30 @@ -#include "locality_comm.h" +#include "communicator/locality_comm.h" + +#include + +#include "locality_aware.h" void init_locality_comm(LocalityComm** locality_ptr, - MPIL_Comm* mpix_comm, + MPIL_Comm* mpil_comm, MPI_Datatype sendtype, MPI_Datatype recvtype) { LocalityComm* locality = (LocalityComm*)malloc(sizeof(LocalityComm)); int tag; - MPIL_Comm_tag(mpix_comm, &tag); + get_tag(mpil_comm, &tag); init_comm_pkg(&(locality->local_L_comm), sendtype, recvtype, tag); - MPIL_Comm_tag(mpix_comm, &tag); + get_tag(mpil_comm, &tag); init_comm_pkg(&(locality->local_S_comm), sendtype, recvtype, tag); - MPIL_Comm_tag(mpix_comm, &tag); + get_tag(mpil_comm, &tag); init_comm_pkg(&(locality->local_R_comm), recvtype, recvtype, tag); - MPIL_Comm_tag(mpix_comm, &tag); + get_tag(mpil_comm, &tag); init_comm_pkg(&(locality->global_comm), recvtype, recvtype, tag); - locality->communicators = mpix_comm; + locality->communicators = mpil_comm; *locality_ptr = locality; } diff --git a/library/source/communicator/topology_functions.c b/library/source/communicator/topology_functions.c new file mode 100644 index 000000000..95f128874 --- /dev/null +++ b/library/source/communicator/topology_functions.c @@ -0,0 +1,17 @@ +#include "communicator/MPIL_Comm.h" + +/**** Topology Functions ****/ +int get_node(const MPIL_Comm* data, const int proc) +{ + return data->global_rank_to_node[proc]; +} + +int get_local_proc(const MPIL_Comm* data, const int proc) +{ + return data->global_rank_to_local[proc]; +} + +int get_global_proc(const MPIL_Comm* data, const int node, const int local_proc) +{ + return data->ordered_global_ranks[local_proc + (node * data->ppn)]; +} diff --git a/library/source/communicator/update_locality.c b/library/source/communicator/update_locality.c new file mode 100644 index 000000000..ca2b74f9a --- /dev/null +++ b/library/source/communicator/update_locality.c @@ -0,0 +1,68 @@ +#include + +#include "communicator/MPIL_Comm.h" + +// For testing purposes +// Manually update aggregation size (ppn) +int update_locality(MPIL_Comm* xcomm, int ppn) +{ + int rank, num_procs; + MPI_Comm_rank(xcomm->global_comm, &rank); + MPI_Comm_size(xcomm->global_comm, &num_procs); + + if (xcomm->local_comm != MPI_COMM_NULL) + { + MPI_Comm_free(&(xcomm->local_comm)); + } + if (xcomm->group_comm != MPI_COMM_NULL) + { + MPI_Comm_free(&(xcomm->group_comm)); + } + + MPI_Comm_split(xcomm->global_comm, rank / ppn, rank, &(xcomm->local_comm)); + + int local_rank; + MPI_Comm_rank(xcomm->local_comm, &local_rank); + MPI_Comm_split(xcomm->global_comm, local_rank, rank, &(xcomm->group_comm)); + + int node; + MPI_Comm_rank(xcomm->group_comm, &node); + + if (xcomm->global_rank_to_local == NULL) + { + xcomm->global_rank_to_local = (int*)malloc(num_procs * sizeof(int)); + } + + if (xcomm->global_rank_to_node == NULL) + { + xcomm->global_rank_to_node = (int*)malloc(num_procs * sizeof(int)); + } + + MPI_Allgather(&local_rank, + 1, + MPI_INT, + xcomm->global_rank_to_local, + 1, + MPI_INT, + xcomm->global_comm); + MPI_Allgather( + &node, 1, MPI_INT, xcomm->global_rank_to_node, 1, MPI_INT, xcomm->global_comm); + + if (xcomm->ordered_global_ranks == NULL) + { + xcomm->ordered_global_ranks = (int*)malloc(num_procs * sizeof(int)); + } + + for (int i = 0; i < num_procs; i++) + { + int local = xcomm->global_rank_to_local[i]; + int node = xcomm->global_rank_to_node[i]; + xcomm->ordered_global_ranks[node * ppn + local] = i; + } + + MPI_Comm_size(xcomm->local_comm, &(xcomm->ppn)); + xcomm->num_nodes = ((num_procs - 1) / xcomm->ppn) + 1; + xcomm->rank_node = get_node(xcomm, rank); + + return MPI_SUCCESS; +} diff --git a/library/source/heterogeneous/CMakeLists.txt b/library/source/heterogeneous/CMakeLists.txt new file mode 100644 index 000000000..1bef5ef2e --- /dev/null +++ b/library/source/heterogeneous/CMakeLists.txt @@ -0,0 +1,23 @@ +#always add to gpu language conversion list. +get_property(GPU_SOURCES GLOBAL PROPERTY GPU_SOURCES_GLOBAL) +set_property(GLOBAL APPEND PROPERTY GPU_SOURCES_GLOBAL + ${CMAKE_CURRENT_SOURCE_DIR}/get_mem_types.c + ${CMAKE_CURRENT_SOURCE_DIR}/gpu_alltoall.c + ${CMAKE_CURRENT_SOURCE_DIR}/gpu_check.c + ${CMAKE_CURRENT_SOURCE_DIR}/repack.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/device_repack.c + ${CMAKE_CURRENT_SOURCE_DIR}/get_memcpy_kind.c + ${CMAKE_CURRENT_SOURCE_DIR}/gpu_alltoallv.c + ${CMAKE_CURRENT_SOURCE_DIR}/gpu_repack.c +) + +target_sources(locality_aware PRIVATE + get_mem_types.c + gpu_alltoall.c + gpu_check.c + repack.cpp + device_repack.c + get_memcpy_kind.c + gpu_alltoallv.c + gpu_repack.c +) diff --git a/library/source/heterogeneous/device_repack.c b/library/source/heterogeneous/device_repack.c new file mode 100644 index 000000000..9bde9b357 --- /dev/null +++ b/library/source/heterogeneous/device_repack.c @@ -0,0 +1,23 @@ +#include "heterogeneous/gpu_utils.h" + +// Repack Data on Device +#ifdef GPU +__global__ void device_repack(char* __restrict__ sendbuf, + char* __restrict__ recvbuf, + int size_x, + int size_y, + int size_z) +{ + const int tid_x = threadIdx.x + blockIdx.x * blockDim.x; + const int tid_y = threadIdx.y + blockIdx.y * blockDim.y; + const int tid_z = threadIdx.z + blockIdx.z * blockDim.z; + + if (tid_x >= size_x || tid_y >= size_y || tid_z >= size_z) + { + return; + } + + recvbuf[(tid_y * size_x + tid_x) * size_z + tid_z] = + sendbuf[(tid_x * size_y + tid_y) * size_z + tid_z]; +} +#endif \ No newline at end of file diff --git a/library/source/heterogeneous/get_mem_types.c b/library/source/heterogeneous/get_mem_types.c new file mode 100644 index 000000000..459b0f0c1 --- /dev/null +++ b/library/source/heterogeneous/get_mem_types.c @@ -0,0 +1,37 @@ +#include "heterogeneous/gpu_utils.h" +// GPU Method to find where memory was allocated +#ifdef GPU +void get_mem_types(const void* sendbuf, + const void* recvbuf, + gpuMemoryType* send_ptr, + gpuMemoryType* recv_ptr) +{ + gpuMemoryType send_type, recv_type; + + gpuPointerAttributes mem; + gpuPointerGetAttributes(&mem, sendbuf); + int ierr = gpuGetLastError(); + if (ierr == gpuErrorInvalidValue) + { + send_type = gpuMemoryTypeHost; + } + else + { + send_type = mem.type; + } + + gpuPointerGetAttributes(&mem, recvbuf); + ierr = gpuGetLastError(); + if (ierr == gpuErrorInvalidValue) + { + recv_type = gpuMemoryTypeHost; + } + else + { + recv_type = mem.type; + } + + *send_ptr = send_type; + *recv_ptr = recv_type; +} +#endif \ No newline at end of file diff --git a/library/source/heterogeneous/get_memcpy_kind.c b/library/source/heterogeneous/get_memcpy_kind.c new file mode 100644 index 000000000..53cc07072 --- /dev/null +++ b/library/source/heterogeneous/get_memcpy_kind.c @@ -0,0 +1,27 @@ + +#include "heterogeneous/gpu_utils.h" +// GPU Method to find where memory was allocated +#ifdef GPU + +void get_memcpy_kind(gpuMemoryType send_type, + gpuMemoryType recv_type, + gpuMemcpyKind* memcpy_kind) +{ + if (send_type == gpuMemoryTypeDevice && recv_type == gpuMemoryTypeDevice) + { + *memcpy_kind = gpuMemcpyDeviceToDevice; + } + else if (send_type == gpuMemoryTypeDevice) + { + *memcpy_kind = gpuMemcpyDeviceToHost; + } + else if (recv_type == gpuMemoryTypeDevice) + { + *memcpy_kind = gpuMemcpyHostToDevice; + } + else + { + *memcpy_kind = gpuMemcpyHostToHost; + } +} +#endif \ No newline at end of file diff --git a/src/heterogeneous/gpu_alltoall.c b/library/source/heterogeneous/gpu_alltoall.c similarity index 98% rename from src/heterogeneous/gpu_alltoall.c rename to library/source/heterogeneous/gpu_alltoall.c index dceb9b214..14f1fdbd4 100644 --- a/src/heterogeneous/gpu_alltoall.c +++ b/library/source/heterogeneous/gpu_alltoall.c @@ -1,7 +1,8 @@ -#include "gpu_alltoall.h" +#include "heterogeneous/gpu_alltoall.h" #include "collective/alltoall.h" -#include "collective/collective.h" +#include "communicator/MPIL_Comm.h" +#include "heterogeneous/gpu_alltoallv.h" // ASSUMES 1 CPU CORE PER GPU (Standard for applications) int gpu_aware_alltoall(alltoall_ftn f, @@ -36,15 +37,15 @@ int gpu_aware_alltoall(alltoall_ftn f, return ierr; } -int gpu_aware_alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) +int gpu_aware_alltoall_nonblocking(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) { - return gpu_aware_alltoall(alltoall_pairwise, + return gpu_aware_alltoall(alltoall_nonblocking, sendbuf, sendcount, sendtype, @@ -54,15 +55,15 @@ int gpu_aware_alltoall_pairwise(const void* sendbuf, comm); } -int gpu_aware_alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) +int gpu_aware_alltoall_pairwise(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm) { - return gpu_aware_alltoall(alltoall_nonblocking, + return gpu_aware_alltoall(alltoall_pairwise, sendbuf, sendcount, sendtype, @@ -187,7 +188,7 @@ int threaded_alltoall_pairwise(const void* sendbuf, { MPI_Status status; int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; @@ -281,7 +282,7 @@ int threaded_alltoall_nonblocking(const void* sendbuf, #pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) { int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; diff --git a/src/heterogeneous/gpu_alltoallv.c b/library/source/heterogeneous/gpu_alltoallv.c similarity index 99% rename from src/heterogeneous/gpu_alltoallv.c rename to library/source/heterogeneous/gpu_alltoallv.c index 936ac9a03..219c552f4 100644 --- a/src/heterogeneous/gpu_alltoallv.c +++ b/library/source/heterogeneous/gpu_alltoallv.c @@ -1,7 +1,6 @@ -#include "gpu_alltoallv.h" +#include "heterogeneous/gpu_alltoallv.h" #include "collective/alltoallv.h" -#include "collective/collective.h" // ASSUMES 1 CPU CORE PER GPU (Standard for applications) int gpu_aware_alltoallv(alltoallv_ftn f, @@ -309,7 +308,7 @@ int threaded_alltoallv_pairwise(const void* sendbuf, { MPI_Status status; int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; @@ -415,7 +414,7 @@ int threaded_alltoallv_nonblocking(const void* sendbuf, #pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) { int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); int send_proc, recv_proc; int send_pos, recv_pos; diff --git a/library/source/heterogeneous/gpu_check.c b/library/source/heterogeneous/gpu_check.c new file mode 100644 index 000000000..bef62037b --- /dev/null +++ b/library/source/heterogeneous/gpu_check.c @@ -0,0 +1,11 @@ +#include "heterogeneous/gpu_utils.h" +#include "stdio.h" + +// Repack Data on Device +void gpu_check(int ierr) +{ + if (ierr != gpuSuccess) + { + printf("Error in Device Function!\n"); + } +} diff --git a/library/source/heterogeneous/gpu_repack.c b/library/source/heterogeneous/gpu_repack.c new file mode 100644 index 000000000..51f0a5f7c --- /dev/null +++ b/library/source/heterogeneous/gpu_repack.c @@ -0,0 +1,13 @@ +#include "heterogeneous/gpu_utils.h" + +#ifdef GPU +void gpu_repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf) +{ + dim3 dimBlock(8, 8, 8); + int grid_x = ((size_i - 1) / 8) + 1; + int grid_y = ((size_j - 1) / 8) + 1; + int grid_z = ((size_k - 1) / 8) + 1; + dim3 dimGrid(grid_x, grid_y, grid_z); + device_repack<<>>(sendbuf, recvbuf, size_i, size_j, size_k); +} +#endif \ No newline at end of file diff --git a/library/source/heterogeneous/repack.cpp b/library/source/heterogeneous/repack.cpp new file mode 100644 index 000000000..f21cbb861 --- /dev/null +++ b/library/source/heterogeneous/repack.cpp @@ -0,0 +1,63 @@ +#include + +#include "heterogeneous/gpu_utils.h" + +// Repack Method (calls device if on GPU) +void repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf) +{ +#ifdef GPU + gpuMemoryType send_type, recv_type; + get_mem_types(sendbuf, recvbuf, &send_type, &recv_type); + + if (send_type == gpuMemoryTypeDevice && recv_type == gpuMemoryTypeDevice) + { + // gpu_repack(size_i, size_j, size_k, sendbuf, recvbuf); + for (int i = 0; i < size_i; i++) + { + for (int j = 0; j < size_j; j++) + { + gpuMemcpy(recvbuf + (j * size_i + i) * size_k, + sendbuf + (i * size_j + j) * size_k, + size_k, + gpuMemcpyDeviceToDevice); + } + } + } + else if (send_type == gpuMemoryTypeDevice) + { + for (int i = 0; i < size_i; i++) + { + for (int j = 0; j < size_j; j++) + { + gpuMemcpy(recvbuf + (j * size_i + i) * size_k, + sendbuf + (i * size_j + j) * size_k, + size_k, + gpuMemcpyHostToDevice); + } + } + } + else if (recv_type == gpuMemoryTypeDevice) + { + for (int i = 0; i < size_i; i++) + { + for (int j = 0; j < size_j; j++) + { + gpuMemcpy(recvbuf + (j * size_i + i) * size_k, + sendbuf + (i * size_j + j) * size_k, + size_k, + gpuMemcpyDeviceToHost); + } + } + } + else +#endif + for (int i = 0; i < size_i; i++) + { + for (int j = 0; j < size_j; j++) + { + memcpy(recvbuf + (j * size_i + i) * size_k, + sendbuf + (i * size_j + j) * size_k, + size_k); + } + } +} \ No newline at end of file diff --git a/library/source/neighborhood/CMakeLists.txt b/library/source/neighborhood/CMakeLists.txt new file mode 100644 index 000000000..8b10f5176 --- /dev/null +++ b/library/source/neighborhood/CMakeLists.txt @@ -0,0 +1,18 @@ +target_sources(locality_aware PRIVATE + sparse_col/alltoall_crs.cpp + sparse_col/alltoallv_crs.cpp + sparse_col/sparse_coll_utils.cpp + MPIL_Topo/MPIL_Topo_free.c + MPIL_Topo/MPIL_Topo_from_neighbor_comm.c + MPIL_Topo/MPIL_Topo_init.c + locality/neighbor_locality.cpp + locality/neighbor_alltoallv_locality.c + locality/neighbor_alltoallv_init_locality.c + locality/neighbor_alltoallv_init_locality_ext.c + persistent/init_communication.c + persistent/init_neighbor_request.c + persistent/neighbor_start.c + persistent/neighbor_wait.c + standard/neighbor_alltoallv_standard.c + standard/neighbor_alltoallv_init_standard.c +) \ No newline at end of file diff --git a/library/source/neighborhood/MPIL_Topo/MPIL_Topo_free.c b/library/source/neighborhood/MPIL_Topo/MPIL_Topo_free.c new file mode 100644 index 000000000..8975868db --- /dev/null +++ b/library/source/neighborhood/MPIL_Topo/MPIL_Topo_free.c @@ -0,0 +1,29 @@ +#include +#include + +#include "neighborhood/MPIL_Topo.h" + +int MPIL_Topo_free(MPIL_Topo** mpil_topo_ptr) +{ + MPIL_Topo* mpil_topo = *mpil_topo_ptr; + + if (mpil_topo->indegree) + { + free(mpil_topo->sources); + if (mpil_topo->sourceweights != MPI_UNWEIGHTED) + { + free(mpil_topo->sourceweights); + } + } + + if (mpil_topo->outdegree) + { + free(mpil_topo->destinations); + if (mpil_topo->destweights != MPI_UNWEIGHTED) + { + free(mpil_topo->destweights); + } + } + free(mpil_topo); + return MPI_SUCCESS; +} diff --git a/library/source/neighborhood/MPIL_Topo/MPIL_Topo_from_neighbor_comm.c b/library/source/neighborhood/MPIL_Topo/MPIL_Topo_from_neighbor_comm.c new file mode 100644 index 000000000..ebbb52aeb --- /dev/null +++ b/library/source/neighborhood/MPIL_Topo/MPIL_Topo_from_neighbor_comm.c @@ -0,0 +1,55 @@ +#include + +#include "communicator/MPIL_Comm.h" +#include "neighborhood/MPIL_Topo.h" + +int MPIL_Topo_from_neighbor_comm(MPIL_Comm* comm, MPIL_Topo** mpil_topo_ptr) +{ + MPIL_Topo* mpil_topo = (MPIL_Topo*)malloc(sizeof(MPIL_Topo)); + + int weighted; + MPI_Dist_graph_neighbors_count( + comm->neighbor_comm, &(mpil_topo->indegree), &(mpil_topo->outdegree), &weighted); + + // Create copy of sources/destinations in MPIL_Topo struct + mpil_topo->sources = NULL; + mpil_topo->destinations = NULL; + + if (mpil_topo->indegree) + { + mpil_topo->sources = (int*)malloc(mpil_topo->indegree * sizeof(int)); + if (weighted) + { + mpil_topo->sourceweights = (int*)malloc(mpil_topo->indegree * sizeof(int)); + } + else + { + mpil_topo->sourceweights = MPI_UNWEIGHTED; + } + } + + if (mpil_topo->outdegree) + { + mpil_topo->destinations = (int*)malloc(mpil_topo->outdegree * sizeof(int)); + if (weighted) + { + mpil_topo->destweights = (int*)malloc(mpil_topo->outdegree * sizeof(int)); + } + else + { + mpil_topo->destweights = MPI_UNWEIGHTED; + } + } + + MPI_Dist_graph_neighbors(comm->neighbor_comm, + mpil_topo->indegree, + mpil_topo->sources, + mpil_topo->sourceweights, + mpil_topo->outdegree, + mpil_topo->destinations, + mpil_topo->destweights); + + *mpil_topo_ptr = mpil_topo; + + return MPI_SUCCESS; +} diff --git a/library/source/neighborhood/MPIL_Topo/MPIL_Topo_init.c b/library/source/neighborhood/MPIL_Topo/MPIL_Topo_init.c new file mode 100644 index 000000000..efffd8962 --- /dev/null +++ b/library/source/neighborhood/MPIL_Topo/MPIL_Topo_init.c @@ -0,0 +1,61 @@ +#include +#include +#include + +#include "communicator/MPIL_Info.h" +#include "neighborhood/MPIL_Topo.h" + +int MPIL_Topo_init(int indegree, + const int sources[], + const int sourceweights[], + int outdegree, + const int destinations[], + const int destweights[], + MPIL_Info* info, + MPIL_Topo** mpil_topo_ptr) +{ + MPIL_Topo* mpil_topo = (MPIL_Topo*)malloc(sizeof(MPIL_Topo)); + + // Copy indegree and outdegree into MPIL_Topo struct + mpil_topo->indegree = indegree; + mpil_topo->outdegree = outdegree; + + // Create copy of sources/destinations in MPIL_Topo struct + mpil_topo->sources = NULL; + mpil_topo->destinations = NULL; + + if (indegree) + { + mpil_topo->sources = (int*)malloc(indegree * sizeof(int)); + memcpy(mpil_topo->sources, sources, indegree * sizeof(int)); + if (sourceweights != MPI_UNWEIGHTED) + { + mpil_topo->sourceweights = (int*)malloc(indegree * sizeof(int)); + memcpy(mpil_topo->sourceweights, sourceweights, indegree * sizeof(int)); + } + else + { + mpil_topo->sourceweights = MPI_UNWEIGHTED; + } + } + + if (outdegree) + { + mpil_topo->destinations = (int*)malloc(outdegree * sizeof(int)); + memcpy(mpil_topo->destinations, destinations, outdegree * sizeof(int)); + + if (destweights != MPI_UNWEIGHTED) + { + mpil_topo->destweights = (int*)malloc(outdegree * sizeof(int)); + memcpy(mpil_topo->destweights, destweights, outdegree * sizeof(int)); + } + else + { + mpil_topo->destweights = MPI_UNWEIGHTED; + } + } + + *mpil_topo_ptr = mpil_topo; + + return MPI_SUCCESS; +} diff --git a/library/source/neighborhood/locality/neighbor_alltoallv_init_locality.c b/library/source/neighborhood/locality/neighbor_alltoallv_init_locality.c new file mode 100644 index 000000000..519b8428e --- /dev/null +++ b/library/source/neighborhood/locality/neighbor_alltoallv_init_locality.c @@ -0,0 +1,123 @@ +#include + +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" +#include "neighborhood/MPIL_Topo.h" +#include "neighborhood/neighborhood_init.h" + +int neighbor_alltoallv_init_locality(const void* sendbuffer, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuffer, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) +{ + int rank; + MPI_Comm_rank(comm->global_comm, &rank); + + if (comm->local_comm == MPI_COMM_NULL) + { + MPIL_Comm_topo_init(comm); + } + + int* global_sdispls = NULL; + int* global_rdispls = NULL; + + int ctr; + + if (topo->indegree) + { + global_rdispls = (int*)malloc(topo->indegree * sizeof(int)); + ctr = 0; + for (int i = 0; i < topo->indegree; i++) + { + global_rdispls[i] = ctr; + ctr += recvcounts[i]; + } + } + + if (topo->outdegree) + { + global_sdispls = (int*)malloc(topo->outdegree * sizeof(int)); + ctr = 0; + for (int i = 0; i < topo->outdegree; i++) + { + global_sdispls[i] = ctr; + ctr += sendcounts[i]; + } + } + + long send_size = 0; + long recv_size = 0; + for (int i = 0; i < topo->indegree; i++) + { + recv_size += recvcounts[i]; + } + for (int i = 0; i < topo->outdegree; i++) + { + send_size += sendcounts[i]; + } + + long first_send; + MPI_Exscan(&send_size, &first_send, 1, MPI_LONG, MPI_SUM, comm->global_comm); + if (rank == 0) + { + first_send = 0; + } + + long* global_send_indices = NULL; + long* global_recv_indices = NULL; + + if (recv_size) + { + global_recv_indices = (long*)malloc(recv_size * sizeof(long)); + } + if (send_size) + { + global_send_indices = (long*)malloc(send_size * sizeof(long)); + } + for (int i = 0; i < send_size; i++) + { + global_send_indices[i] = first_send + i; + } + + MPIL_Neighbor_alltoallv_topo(global_send_indices, + sendcounts, + global_sdispls, + MPI_LONG, + global_recv_indices, + recvcounts, + global_rdispls, + MPI_LONG, + topo, + comm); + + int err = neighbor_alltoallv_init_locality_ext(sendbuffer, + sendcounts, + sdispls, + global_send_indices, + sendtype, + recvbuffer, + recvcounts, + rdispls, + global_recv_indices, + recvtype, + topo, + comm, + info, + request_ptr); + + free(global_send_indices); + free(global_recv_indices); + + free(global_sdispls); + free(global_rdispls); + + return err; +} diff --git a/library/source/neighborhood/locality/neighbor_alltoallv_init_locality_ext.c b/library/source/neighborhood/locality/neighbor_alltoallv_init_locality_ext.c new file mode 100644 index 000000000..923960453 --- /dev/null +++ b/library/source/neighborhood/locality/neighbor_alltoallv_init_locality_ext.c @@ -0,0 +1,123 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" +#include "neighborhood/MPIL_Topo.h" +#include "neighborhood/neighborhood_init.h" +#include "persistent/MPIL_Request.h" + +// Locality-Aware Extension to Persistent Neighbor Alltoallv +// Needs global indices for each send and receive +int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuffer, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) +{ + if (comm->local_comm == MPI_COMM_NULL) + { + MPIL_Comm_topo_init(comm); + } + + MPIL_Request* request; + init_neighbor_request(&request); + + // Initialize Locality-Aware Communication Strategy (3-Step) + // E.G. Determine which processes talk to each other at every step + // TODO : instead of mpi_comm, use comm + // - will need to create local_comm in dist_graph_create_adjacent... + init_locality(topo->outdegree, + topo->destinations, + sdispls, + sendcounts, + topo->indegree, + topo->sources, + rdispls, + recvcounts, + global_sindices, + global_rindices, + sendtype, + recvtype, + comm, // communicator used in dist_graph_create_adjacent + request); + + request->sendbuf = sendbuffer; + request->recvbuf = recvbuffer; + MPI_Type_size(recvtype, &(request->recv_size)); + + // Local L Communication + // init_communication(sendbuffer, + init_communication(request->locality->local_L_comm->send_data->buffer, + request->locality->local_L_comm->send_data->num_msgs, + request->locality->local_L_comm->send_data->procs, + request->locality->local_L_comm->send_data->indptr, + sendtype, + request->locality->local_L_comm->recv_data->buffer, + request->locality->local_L_comm->recv_data->num_msgs, + request->locality->local_L_comm->recv_data->procs, + request->locality->local_L_comm->recv_data->indptr, + recvtype, + request->locality->local_L_comm->tag, + comm->local_comm, + &(request->local_L_n_msgs), + &(request->local_L_requests)); + + // Local S Communication + init_communication(request->locality->local_S_comm->send_data->buffer, + request->locality->local_S_comm->send_data->num_msgs, + request->locality->local_S_comm->send_data->procs, + request->locality->local_S_comm->send_data->indptr, + sendtype, + request->locality->local_S_comm->recv_data->buffer, + request->locality->local_S_comm->recv_data->num_msgs, + request->locality->local_S_comm->recv_data->procs, + request->locality->local_S_comm->recv_data->indptr, + recvtype, + request->locality->local_S_comm->tag, + comm->local_comm, + &(request->local_S_n_msgs), + &(request->local_S_requests)); + + // Global Communication + init_communication(request->locality->global_comm->send_data->buffer, + request->locality->global_comm->send_data->num_msgs, + request->locality->global_comm->send_data->procs, + request->locality->global_comm->send_data->indptr, + sendtype, + request->locality->global_comm->recv_data->buffer, + request->locality->global_comm->recv_data->num_msgs, + request->locality->global_comm->recv_data->procs, + request->locality->global_comm->recv_data->indptr, + recvtype, + request->locality->global_comm->tag, + comm->global_comm, + &(request->global_n_msgs), + &(request->global_requests)); + + // Local R Communication + init_communication(request->locality->local_R_comm->send_data->buffer, + request->locality->local_R_comm->send_data->num_msgs, + request->locality->local_R_comm->send_data->procs, + request->locality->local_R_comm->send_data->indptr, + sendtype, + request->locality->local_R_comm->recv_data->buffer, + request->locality->local_R_comm->recv_data->num_msgs, + request->locality->local_R_comm->recv_data->procs, + request->locality->local_R_comm->recv_data->indptr, + recvtype, + request->locality->local_R_comm->tag, + comm->local_comm, + &(request->local_R_n_msgs), + &(request->local_R_requests)); + + *request_ptr = request; + + return MPI_SUCCESS; +} diff --git a/library/source/neighborhood/locality/neighbor_alltoallv_locality.c b/library/source/neighborhood/locality/neighbor_alltoallv_locality.c new file mode 100644 index 000000000..6faaaa431 --- /dev/null +++ b/library/source/neighborhood/locality/neighbor_alltoallv_locality.c @@ -0,0 +1,86 @@ +#include +#include + +#include "communicator/MPIL_Comm.h" +#include "communicator/MPIL_Info.h" +#include "locality_aware.h" +#include "neighborhood/MPIL_Topo.h" +#include "neighborhood/alltoall_crs.h" + +// Non-persistent, locality-aware == call dynamic version +int neighbor_alltoallv_locality(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm) +{ + int rank, num_procs; + MPI_Comm_rank(comm->global_comm, &rank); + MPI_Comm_size(comm->global_comm, &num_procs); + + int send_nnz = topo->outdegree; + int send_size = 0; + for (int i = 0; i < send_nnz; i++) + { + send_size += sendcounts[i]; + } + + int recv_nnz, recv_size; + int *src_tmp, *recvcounts_tmp, *rdispls_tmp; + char* recvvals_tmp; + + int recv_bytes; + MPI_Type_size(recvtype, &recv_bytes); + + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); + + alltoallv_crs_personalized(send_nnz, + send_size, + topo->destinations, + sendcounts, + sdispls, + sendtype, + sendbuf, + &recv_nnz, + &recv_size, + &src_tmp, + &recvcounts_tmp, + &rdispls_tmp, + recvtype, + (void**)&recvvals_tmp, + xinfo, + comm); + + char* recvvals = (char*)recvbuf; + + int idx, proc; + int* new_proc_idx = (int*)malloc(num_procs * sizeof(int)); + for (int i = 0; i < recv_nnz; i++) + { + new_proc_idx[src_tmp[i]] = i; + } + for (int i = 0; i < recv_nnz; i++) + { + proc = topo->sources[i]; + idx = new_proc_idx[proc]; + memcpy(&(recvvals[rdispls[i] * recv_bytes]), + &(recvvals_tmp[rdispls_tmp[idx] * recv_bytes]), + recvcounts[i] * recv_bytes); + } + free(new_proc_idx); + + MPIL_Info_free(&xinfo); + + MPIL_Free(src_tmp); + MPIL_Free(recvcounts_tmp); + MPIL_Free(rdispls_tmp); + MPIL_Free(recvvals_tmp); + + return MPI_SUCCESS; +} diff --git a/src/neighborhood/neighbor_locality.cpp b/library/source/neighborhood/locality/neighbor_locality.cpp similarity index 90% rename from src/neighborhood/neighbor_locality.cpp rename to library/source/neighborhood/locality/neighbor_locality.cpp index ca3c85175..994db8e4f 100644 --- a/src/neighborhood/neighbor_locality.cpp +++ b/library/source/neighborhood/locality/neighbor_locality.cpp @@ -1,58 +1,19 @@ +#include "neighborhood/neighbor_locality.h" + #include -#include -#include -#include "neighbor.h" -#include "neighbor_init.h" -#include "persistent/neighbor_persistent.h" - -/****************************************** - **** - **** Helper Methods - **** - ******************************************/ - -void map_procs_to_nodes(LocalityComm* locality, - const int orig_num_msgs, - const int* orig_procs, - const int* orig_counts, - std::vector& msg_nodes, - std::vector& msg_node_to_local, - bool incr); -void form_local_comm(const int orig_num_sends, - const int* orig_send_procs, - const int* orig_send_ptr, - const int* orig_sendcounts, - const long* orig_send_indices, - const std::vector& nodes_to_local, - CommData* send_data, - CommData* recv_data, - CommData* local_data, - std::vector& recv_idx_nodes, - LocalityComm* locality, - const int tag); -void form_global_comm(CommData* local_data, - CommData* global_data, - std::vector& local_data_nodes, - const MPIL_Comm* mpix_comm, - int tag); -void update_global_comm(LocalityComm* locality); -void form_global_map(const CommData* map_data, std::map& global_map); -void map_indices(CommData* idx_data, std::map& global_map); -void map_indices(CommData* idx_data, const CommData* map_data); -void remove_duplicates(CommData* comm_pkg); -void remove_duplicates(CommPkg* data); -void remove_duplicates(LocalityComm* locality); -void update_indices(LocalityComm* locality, - std::map& send_global_to_local, - std::map& recv_global_to_local); +#include "communicator/MPIL_Comm.h" +#include "persistent/MPIL_Request.h" /****************************************** **** **** Main Methods **** ******************************************/ - +// Declarations of C++ methods +#ifdef __cplusplus +extern "C" { +#endif // Initialize NAPComm* structure, to be used for any number of // instances of communication void init_locality(const int n_sends, @@ -67,17 +28,17 @@ void init_locality(const int n_sends, const long* global_recv_indices, const MPI_Datatype sendtype, const MPI_Datatype recvtype, - MPIL_Comm* mpix_comm, + MPIL_Comm* mpil_comm, MPIL_Request* request) { // Get MPI Information int rank, num_procs; - MPI_Comm_rank(mpix_comm->global_comm, &rank); - MPI_Comm_size(mpix_comm->global_comm, &num_procs); + MPI_Comm_rank(mpil_comm->global_comm, &rank); + MPI_Comm_size(mpil_comm->global_comm, &num_procs); // Initialize structure LocalityComm* locality_comm; - init_locality_comm(&locality_comm, mpix_comm, sendtype, recvtype); + init_locality_comm(&locality_comm, mpil_comm, sendtype, recvtype); // Find global send nodes std::vector send_nodes; @@ -109,7 +70,7 @@ void init_locality(const int n_sends, form_global_comm(locality_comm->local_S_comm->recv_data, locality_comm->global_comm->send_data, recv_idx_nodes, - mpix_comm, + mpil_comm, 93284); // Find global recv nodes @@ -183,7 +144,9 @@ void init_locality(const int n_sends, request->locality = locality_comm; request->tag = locality_comm->global_comm->tag; } - +#ifdef __cplusplus +} +#endif // Destroy NAPComm* structure void destroy_locality(MPIL_Request* request) { @@ -506,7 +469,7 @@ void form_local_comm(const int orig_num_sends, void form_global_comm(CommData* local_data, CommData* global_data, std::vector& local_data_nodes, - const MPIL_Comm* mpix_comm, + const MPIL_Comm* mpil_comm, int tag) { std::vector tmp_send_indices; @@ -516,11 +479,11 @@ void form_global_comm(CommData* local_data, // Get MPI Information int rank, num_procs; int local_rank, local_num_procs; - MPI_Comm_rank(mpix_comm->global_comm, &rank); - MPI_Comm_size(mpix_comm->global_comm, &num_procs); - MPI_Comm_rank(mpix_comm->local_comm, &local_rank); - MPI_Comm_size(mpix_comm->local_comm, &local_num_procs); - int num_nodes = mpix_comm->num_nodes; + MPI_Comm_rank(mpil_comm->global_comm, &rank); + MPI_Comm_size(mpil_comm->global_comm, &num_procs); + MPI_Comm_rank(mpil_comm->local_comm, &local_rank); + MPI_Comm_size(mpil_comm->local_comm, &local_num_procs); + int num_nodes = mpil_comm->num_nodes; int node_idx, node; int start, end, idx; @@ -585,8 +548,8 @@ void update_global_comm(LocalityComm* locality) MPI_Request* requests = NULL; int* send_buffer = NULL; int send_tag, recv_tag; - MPIL_Comm_tag(locality->communicators, &send_tag); - MPIL_Comm_tag(locality->communicators, &recv_tag); + get_tag(locality->communicators, &send_tag); + get_tag(locality->communicators, &recv_tag); int node, global_proc; int num_to_recv; MPI_Status recv_status; diff --git a/library/source/neighborhood/persistent/init_communication.c b/library/source/neighborhood/persistent/init_communication.c new file mode 100644 index 000000000..866f98829 --- /dev/null +++ b/library/source/neighborhood/persistent/init_communication.c @@ -0,0 +1,63 @@ +#include "neighborhood/neighborhood_init.h" +#include "persistent/MPIL_Request.h" + +int init_communication(const void* sendbuffer, + int n_sends, + const int* send_procs, + const int* send_ptr, + MPI_Datatype sendtype, + void* recvbuffer, + int n_recvs, + const int* recv_procs, + const int* recv_ptr, + MPI_Datatype recvtype, + int tag, + MPI_Comm comm, + int* n_request_ptr, + MPI_Request** request_ptr) +{ + int ierr = 0; + int start, size; + int send_size, recv_size; + + char* send_buffer = (char*)sendbuffer; + char* recv_buffer = (char*)recvbuffer; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + MPI_Request* requests; + *n_request_ptr = n_recvs + n_sends; + allocate_requests(*n_request_ptr, &requests); + + for (int i = 0; i < n_recvs; i++) + { + start = recv_ptr[i]; + size = recv_ptr[i + 1] - start; + + ierr += MPI_Recv_init(&(recv_buffer[start * recv_size]), + size, + recvtype, + recv_procs[i], + tag, + comm, + &(requests[i])); + } + + for (int i = 0; i < n_sends; i++) + { + start = send_ptr[i]; + size = send_ptr[i + 1] - start; + + ierr += MPI_Send_init(&(send_buffer[start * send_size]), + size, + sendtype, + send_procs[i], + tag, + comm, + &(requests[n_recvs + i])); + } + + *request_ptr = requests; + + return ierr; +} diff --git a/library/source/neighborhood/persistent/init_neighbor_request.c b/library/source/neighborhood/persistent/init_neighbor_request.c new file mode 100644 index 000000000..4fd049591 --- /dev/null +++ b/library/source/neighborhood/persistent/init_neighbor_request.c @@ -0,0 +1,10 @@ +#include "neighborhood/neighborhood_init.h" + +void init_neighbor_request(MPIL_Request** request_ptr) +{ + init_request(request_ptr); + MPIL_Request* request = *request_ptr; + + request->start_function = neighbor_start; + request->wait_function = neighbor_wait; +} \ No newline at end of file diff --git a/library/source/neighborhood/persistent/neighbor_start.c b/library/source/neighborhood/persistent/neighbor_start.c new file mode 100644 index 000000000..6260736a9 --- /dev/null +++ b/library/source/neighborhood/persistent/neighbor_start.c @@ -0,0 +1,77 @@ +#include // For NULL + +#include "neighborhood/neighborhood_init.h" +#include "persistent/MPIL_Request.h" + +int neighbor_start(MPIL_Request* request) +{ + if (request == NULL) + { + return 0; + } + + int ierr = 0; + int idx; + + char* send_buffer = NULL; + int recv_size = 0; + if (request->recv_size) + { + send_buffer = (char*)(request->sendbuf); + recv_size = request->recv_size; + } + + // Local L sends sendbuf + if (request->local_L_n_msgs) + { + for (int i = 0; i < request->locality->local_L_comm->send_data->size_msgs; i++) + { + idx = request->locality->local_L_comm->send_data->indices[i]; + for (int j = 0; j < recv_size; j++) + { + request->locality->local_L_comm->send_data->buffer[i * recv_size + j] = + send_buffer[idx * recv_size + j]; + } + } + ierr += MPI_Startall(request->local_L_n_msgs, request->local_L_requests); + } + + // Local S sends sendbuf + if (request->local_S_n_msgs) + { + for (int i = 0; i < request->locality->local_S_comm->send_data->size_msgs; i++) + { + idx = request->locality->local_S_comm->send_data->indices[i]; + + for (int j = 0; j < recv_size; j++) + { + request->locality->local_S_comm->send_data->buffer[i * recv_size + j] = + send_buffer[idx * recv_size + j]; + } + } + + ierr += MPI_Startall(request->local_S_n_msgs, request->local_S_requests); + ierr += MPI_Waitall( + request->local_S_n_msgs, request->local_S_requests, MPI_STATUSES_IGNORE); + + // Copy into global->send_data->buffer + for (int i = 0; i < request->locality->global_comm->send_data->size_msgs; i++) + { + idx = request->locality->global_comm->send_data->indices[i]; + for (int j = 0; j < recv_size; j++) + { + request->locality->global_comm->send_data->buffer[i * recv_size + j] = + request->locality->local_S_comm->recv_data + ->buffer[idx * recv_size + j]; + } + } + } + + // Global sends buffer in locality, sendbuf in standard + if (request->global_n_msgs) + { + ierr += MPI_Startall(request->global_n_msgs, request->global_requests); + } + + return ierr; +} diff --git a/src/persistent/neighbor_persistent.c b/library/source/neighborhood/persistent/neighbor_wait.c similarity index 52% rename from src/persistent/neighbor_persistent.c rename to library/source/neighborhood/persistent/neighbor_wait.c index f236771af..2df44c364 100644 --- a/src/persistent/neighbor_persistent.c +++ b/library/source/neighborhood/persistent/neighbor_wait.c @@ -1,158 +1,88 @@ -#include "neighbor_persistent.h" - -int neighbor_start(MPIL_Request* request) -{ - if (request == NULL) - { - return 0; - } - - int ierr = 0; - int idx; - - char* send_buffer = NULL; - int recv_size = 0; - if (request->recv_size) - { - send_buffer = (char*)(request->sendbuf); - recv_size = request->recv_size; - } - - // Local L sends sendbuf - if (request->local_L_n_msgs) - { - for (int i = 0; i < request->locality->local_L_comm->send_data->size_msgs; i++) - { - idx = request->locality->local_L_comm->send_data->indices[i]; - for (int j = 0; j < recv_size; j++) - { - request->locality->local_L_comm->send_data->buffer[i * recv_size + j] = - send_buffer[idx * recv_size + j]; - } - } - ierr += MPI_Startall(request->local_L_n_msgs, request->local_L_requests); - } - - // Local S sends sendbuf - if (request->local_S_n_msgs) - { - for (int i = 0; i < request->locality->local_S_comm->send_data->size_msgs; i++) - { - idx = request->locality->local_S_comm->send_data->indices[i]; - - for (int j = 0; j < recv_size; j++) - { - request->locality->local_S_comm->send_data->buffer[i * recv_size + j] = - send_buffer[idx * recv_size + j]; - } - } - - ierr += MPI_Startall(request->local_S_n_msgs, request->local_S_requests); - ierr += MPI_Waitall( - request->local_S_n_msgs, request->local_S_requests, MPI_STATUSES_IGNORE); - - // Copy into global->send_data->buffer - for (int i = 0; i < request->locality->global_comm->send_data->size_msgs; i++) - { - idx = request->locality->global_comm->send_data->indices[i]; - for (int j = 0; j < recv_size; j++) - { - request->locality->global_comm->send_data->buffer[i * recv_size + j] = - request->locality->local_S_comm->recv_data - ->buffer[idx * recv_size + j]; - } - } - } - - // Global sends buffer in locality, sendbuf in standard - if (request->global_n_msgs) - { - ierr += MPI_Startall(request->global_n_msgs, request->global_requests); - } - - return ierr; -} - -// Wait for locality-aware requests -// 1. Wait for global -// 2. Start and wait for local_R -// 3. Wait for local_L -// TODO : Currently ignores the status! -int neighbor_wait(MPIL_Request* request, MPI_Status* status) -{ - if (request == NULL) - { - return 0; - } - - int ierr = 0; - int idx; - - char* recv_buffer = NULL; - int recv_size = 0; - if (request->recv_size) - { - recv_buffer = (char*)(request->recvbuf); - recv_size = request->recv_size; - } - - // Global waits for recvs - if (request->global_n_msgs) - { - ierr += MPI_Waitall( - request->global_n_msgs, request->global_requests, MPI_STATUSES_IGNORE); - - if (request->local_R_n_msgs) - { - for (int i = 0; i < request->locality->local_R_comm->send_data->size_msgs; - i++) - { - idx = request->locality->local_R_comm->send_data->indices[i]; - for (int j = 0; j < recv_size; j++) - { - request->locality->local_R_comm->send_data - ->buffer[i * recv_size + j] = - request->locality->global_comm->recv_data - ->buffer[idx * recv_size + j]; - } - } - } - } - - // Wait for local_R recvs - if (request->local_R_n_msgs) - { - ierr += MPI_Startall(request->local_R_n_msgs, request->local_R_requests); - ierr += MPI_Waitall( - request->local_R_n_msgs, request->local_R_requests, MPI_STATUSES_IGNORE); - - for (int i = 0; i < request->locality->local_R_comm->recv_data->size_msgs; i++) - { - idx = request->locality->local_R_comm->recv_data->indices[i]; - for (int j = 0; j < recv_size; j++) - { - recv_buffer[idx * recv_size + j] = - request->locality->local_R_comm->recv_data->buffer[i * recv_size + j]; - } - } - } - - // Wait for local_L recvs - if (request->local_L_n_msgs) - { - ierr += MPI_Waitall( - request->local_L_n_msgs, request->local_L_requests, MPI_STATUSES_IGNORE); - - for (int i = 0; i < request->locality->local_L_comm->recv_data->size_msgs; i++) - { - idx = request->locality->local_L_comm->recv_data->indices[i]; - for (int j = 0; j < recv_size; j++) - { - recv_buffer[idx * recv_size + j] = - request->locality->local_L_comm->recv_data->buffer[i * recv_size + j]; - } - } - } - - return ierr; -} +#include // For NULL + +#include "neighborhood/neighborhood_init.h" +#include "persistent/MPIL_Request.h" + +// Wait for locality-aware requests +// 1. Wait for global +// 2. Start and wait for local_R +// 3. Wait for local_L +// TODO : Currently ignores the status! +int neighbor_wait(MPIL_Request* request, MPI_Status* status) +{ + if (request == NULL) + { + return 0; + } + + int ierr = 0; + int idx; + + char* recv_buffer = NULL; + int recv_size = 0; + if (request->recv_size) + { + recv_buffer = (char*)(request->recvbuf); + recv_size = request->recv_size; + } + + // Global waits for recvs + if (request->global_n_msgs) + { + ierr += MPI_Waitall( + request->global_n_msgs, request->global_requests, MPI_STATUSES_IGNORE); + + if (request->local_R_n_msgs) + { + for (int i = 0; i < request->locality->local_R_comm->send_data->size_msgs; + i++) + { + idx = request->locality->local_R_comm->send_data->indices[i]; + for (int j = 0; j < recv_size; j++) + { + request->locality->local_R_comm->send_data + ->buffer[i * recv_size + j] = + request->locality->global_comm->recv_data + ->buffer[idx * recv_size + j]; + } + } + } + } + + // Wait for local_R recvs + if (request->local_R_n_msgs) + { + ierr += MPI_Startall(request->local_R_n_msgs, request->local_R_requests); + ierr += MPI_Waitall( + request->local_R_n_msgs, request->local_R_requests, MPI_STATUSES_IGNORE); + + for (int i = 0; i < request->locality->local_R_comm->recv_data->size_msgs; i++) + { + idx = request->locality->local_R_comm->recv_data->indices[i]; + for (int j = 0; j < recv_size; j++) + { + recv_buffer[idx * recv_size + j] = + request->locality->local_R_comm->recv_data->buffer[i * recv_size + j]; + } + } + } + + // Wait for local_L recvs + if (request->local_L_n_msgs) + { + ierr += MPI_Waitall( + request->local_L_n_msgs, request->local_L_requests, MPI_STATUSES_IGNORE); + + for (int i = 0; i < request->locality->local_L_comm->recv_data->size_msgs; i++) + { + idx = request->locality->local_L_comm->recv_data->indices[i]; + for (int j = 0; j < recv_size; j++) + { + recv_buffer[idx * recv_size + j] = + request->locality->local_L_comm->recv_data->buffer[i * recv_size + j]; + } + } + } + + return ierr; +} diff --git a/src/neighborhood/alltoall_crs.cpp b/library/source/neighborhood/sparse_col/alltoall_crs.cpp similarity index 98% rename from src/neighborhood/alltoall_crs.cpp rename to library/source/neighborhood/sparse_col/alltoall_crs.cpp index fd1561963..d481b7b19 100644 --- a/src/neighborhood/alltoall_crs.cpp +++ b/library/source/neighborhood/sparse_col/alltoall_crs.cpp @@ -1,7 +1,14 @@ +#include #include #include -#include "sparse_coll.h" +#include "communicator/MPIL_Comm.h" +#include "communicator/MPIL_Info.h" +#include "locality_aware.h" + +#ifdef __cplusplus +extern "C" { +#endif int alltoall_crs_rma(const int send_nnz, const int* dest, @@ -90,7 +97,7 @@ int alltoall_crs_rma(const int send_nnz, *recv_nnz_ptr = src.size(); MPIL_Alloc((void**)src_ptr, src.size() * sizeof(int)); MPIL_Alloc(recvvals_ptr, recv_buffer.size()); - + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvvals_ptr), recv_buffer.data(), recv_buffer.size()); @@ -117,7 +124,7 @@ int alltoall_crs_personalized(const int send_nnz, MPI_Status recv_status; int proc, ctr; int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); char* send_buffer; if (send_nnz) @@ -149,7 +156,7 @@ int alltoall_crs_personalized(const int send_nnz, char* recvvals; MPIL_Alloc((void**)&src, *recv_nnz * sizeof(int)); MPIL_Alloc((void**)&recvvals, *recv_nnz * recv_bytes); - + if (comm->n_requests < send_nnz) { MPIL_Comm_req_resize(comm, send_nnz); @@ -227,7 +234,7 @@ int alltoall_crs_nonblocking(const int send_nnz, MPI_Status recv_status; MPI_Request bar_req; int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); std::vector src; std::vector recv_buffer; @@ -290,7 +297,7 @@ int alltoall_crs_nonblocking(const int send_nnz, *recv_nnz = src.size(); MPIL_Alloc((void**)src_ptr, src.size() * sizeof(int)); MPIL_Alloc(recvvals_ptr, recv_buffer.size()); - + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvvals_ptr), recv_buffer.data(), recv_buffer.size()); @@ -380,7 +387,7 @@ void local_redistribute(int node_recv_size, } int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); MPI_Allreduce( MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); @@ -521,7 +528,7 @@ int alltoall_crs_personalized_loc(const int send_nnz, int proc, ctr, start, end; int count, n_msgs, n_sends; int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); std::vector node_send_buffer; std::vector local_send_buffer; @@ -706,7 +713,7 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, int proc, ctr, flag, ibar, start, end; int count, n_msgs, n_sends; int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); std::vector node_send_buffer; std::vector local_send_buffer; @@ -860,3 +867,6 @@ int alltoall_crs_nonblocking_loc(const int send_nnz, return MPI_SUCCESS; } +#ifdef __cplusplus +} +#endif diff --git a/src/neighborhood/alltoallv_crs.cpp b/library/source/neighborhood/sparse_col/alltoallv_crs.cpp similarity index 93% rename from src/neighborhood/alltoallv_crs.cpp rename to library/source/neighborhood/sparse_col/alltoallv_crs.cpp index e17190cd9..c5ef35e36 100644 --- a/src/neighborhood/alltoallv_crs.cpp +++ b/library/source/neighborhood/sparse_col/alltoallv_crs.cpp @@ -1,7 +1,13 @@ #include #include -#include "sparse_coll.h" +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +// Declarations of C++ methods +#ifdef __cplusplus +extern "C" { +#endif int alltoallv_crs_personalized(const int send_nnz, const int send_size, @@ -27,7 +33,7 @@ int alltoallv_crs_personalized(const int send_nnz, MPI_Status recv_status; int proc, ctr, count; int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); char* send_buffer = (char*)sendvals; int send_bytes, recv_bytes; @@ -144,7 +150,7 @@ int alltoallv_crs_nonblocking(const int send_nnz, MPI_Status recv_status; MPI_Request bar_req; int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); if (comm->n_requests < send_nnz) { @@ -222,7 +228,7 @@ int alltoallv_crs_nonblocking(const int send_nnz, MPIL_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); MPIL_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); MPIL_Alloc((void**)recvvals_ptr, recvvals.size()); - + memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size() * sizeof(int)); memcpy((*rdispls_ptr), rdispls.data(), rdispls.size() * sizeof(int)); @@ -231,19 +237,19 @@ int alltoallv_crs_nonblocking(const int send_nnz, return MPI_SUCCESS; } -void local_redistribute(int n_recvs, - std::vector& origins, - std::vector& origin_displs, - std::vector& recv_buf, - int* recv_nnz, - int* recv_size, - int** src_ptr, - int** recvcounts_ptr, - int** rdispls_ptr, - MPI_Datatype recvtype, - void** recvvals_ptr, - MPIL_Info* xinfo, - MPIL_Comm* comm) +void local_redistributev(int n_recvs, + std::vector& origins, + std::vector& origin_displs, + std::vector& recv_buf, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIL_Info* xinfo, + MPIL_Comm* comm) { int rank, num_procs, local_rank, PPN; MPI_Comm_rank(comm->global_comm, &rank); @@ -372,7 +378,7 @@ void local_redistribute(int n_recvs, // Tell them which global indices I need from them std::vector local_req(PPN); - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); n_sends = 0; for (int i = 0; i < PPN; i++) @@ -469,7 +475,6 @@ void local_redistribute(int n_recvs, MPIL_Alloc((void**)recvcounts_ptr, recvcounts.size() * sizeof(int)); MPIL_Alloc((void**)rdispls_ptr, rdispls.size() * sizeof(int)); MPIL_Alloc(recvvals_ptr, recvvals.size()); - memcpy((*src_ptr), src.data(), src.size() * sizeof(int)); memcpy((*recvcounts_ptr), recvcounts.data(), recvcounts.size() * sizeof(int)); @@ -511,7 +516,7 @@ int alltoallv_crs_personalized_loc(const int send_nnz, } int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); char* send_buffer = (char*)sendvals; int send_bytes, recv_bytes, int_bytes; @@ -647,19 +652,19 @@ int alltoallv_crs_personalized_loc(const int send_nnz, MPI_Waitall(n_sends, comm->requests, MPI_STATUSES_IGNORE); - local_redistribute(n_recvs, - origins, - origin_displs, - recv_buf, - recv_nnz, - recv_size, - src_ptr, - recvcounts_ptr, - rdispls_ptr, - recvtype, - recvvals_ptr, - xinfo, - comm); + local_redistributev(n_recvs, + origins, + origin_displs, + recv_buf, + recv_nnz, + recv_size, + src_ptr, + recvcounts_ptr, + rdispls_ptr, + recvtype, + recvvals_ptr, + xinfo, + comm); return MPI_SUCCESS; } @@ -698,7 +703,7 @@ int alltoallv_crs_nonblocking_loc(const int send_nnz, } int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); char* send_buffer = (char*)sendvals; int send_bytes, recv_bytes, int_bytes; @@ -855,19 +860,22 @@ int alltoallv_crs_nonblocking_loc(const int send_nnz, } } - local_redistribute(n_recvs, - origins, - origin_displs, - recv_buf, - recv_nnz, - recv_size, - src_ptr, - recvcounts_ptr, - rdispls_ptr, - recvtype, - recvvals_ptr, - xinfo, - comm); + local_redistributev(n_recvs, + origins, + origin_displs, + recv_buf, + recv_nnz, + recv_size, + src_ptr, + recvcounts_ptr, + rdispls_ptr, + recvtype, + recvvals_ptr, + xinfo, + comm); return MPI_SUCCESS; } +#ifdef __cplusplus +} +#endif diff --git a/src/neighborhood/sparse_coll_utils.cpp b/library/source/neighborhood/sparse_col/sparse_coll_utils.cpp similarity index 98% rename from src/neighborhood/sparse_coll_utils.cpp rename to library/source/neighborhood/sparse_col/sparse_coll_utils.cpp index 9df7c55cd..45dd50cd3 100644 --- a/src/neighborhood/sparse_coll_utils.cpp +++ b/library/source/neighborhood/sparse_col/sparse_coll_utils.cpp @@ -1,9 +1,9 @@ #include #include -#include "sparse_coll.h" - -/* Assumes SMP Ordering of ranks across nodes (aggregates ranks 0-PPN) */ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" +// Assumes SMP Ordering of ranks across nodes (aggregates ranks 0-PPN) int alltoall_crs_personalized_loc(int send_nnz, int* dest, int sendcount, @@ -47,7 +47,7 @@ int alltoall_crs_personalized_loc(int send_nnz, int proc, ctr, start, end; int count, n_msgs, n_sends, n_recvs, idx, new_idx; int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); std::vector node_send_buffer; std::vector local_send_buffer; @@ -227,7 +227,7 @@ int alltoall_crs_personalized_loc(int send_nnz, displs[i + 1] = displs[i] + msg_counts[i]; } - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); MPI_Allreduce( MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); @@ -317,7 +317,7 @@ int alltoall_crs_personalized_loc(int send_nnz, return MPI_SUCCESS; } -/* Assumes SMP Ordering of ranks across nodes (aggregates ranks 0-PPN) */ +// Assumes SMP Ordering of ranks across nodes (aggregates ranks 0-PPN) int alltoall_crs_nonblocking_loc(int send_nnz, int* dest, int sendcount, @@ -362,7 +362,7 @@ int alltoall_crs_nonblocking_loc(int send_nnz, int proc, ctr, flag, ibar, start, end; int count, n_msgs, n_sends, n_recvs, idx, new_idx; int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); std::vector node_send_buffer; std::vector local_send_buffer; @@ -558,7 +558,7 @@ int alltoall_crs_nonblocking_loc(int send_nnz, displs[i + 1] = displs[i] + msg_counts[i]; } - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); MPI_Allreduce( MPI_IN_PLACE, msg_counts.data(), PPN, MPI_INT, MPI_SUM, comm->local_comm); @@ -682,7 +682,7 @@ int alltoallv_crs_personalized_loc(int send_nnz, } int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); char* send_buffer = (char*)sendvals; char* recv_buffer = (char*)recvvals; @@ -934,7 +934,7 @@ int alltoallv_crs_personalized_loc(int send_nnz, // Tell them which global indices I need from them std::vector local_req(PPN); - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); n_sends = 0; for (int i = 0; i < PPN; i++) @@ -1056,7 +1056,7 @@ int alltoallv_crs_nonblocking_loc(int send_nnz, } int tag; - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); char* send_buffer = (char*)sendvals; char* recv_buffer = (char*)recvvals; @@ -1329,7 +1329,7 @@ int alltoallv_crs_nonblocking_loc(int send_nnz, // Tell them which global indices I need from them std::vector local_req(PPN); - MPIL_Comm_tag(comm, &tag); + get_tag(comm, &tag); n_sends = 0; for (int i = 0; i < PPN; i++) diff --git a/library/source/neighborhood/standard/neighbor_alltoallv_init_standard.c b/library/source/neighborhood/standard/neighbor_alltoallv_init_standard.c new file mode 100644 index 000000000..4b24d8e63 --- /dev/null +++ b/library/source/neighborhood/standard/neighbor_alltoallv_init_standard.c @@ -0,0 +1,62 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" +#include "neighborhood/MPIL_Topo.h" +#include "neighborhood/neighborhood_init.h" +#include "persistent/MPIL_Request.h" + +int neighbor_alltoallv_init_standard(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr) +{ + MPIL_Request* request; + init_neighbor_request(&request); + + int tag; + MPIL_Comm_tag(comm, &tag); + + request->global_n_msgs = topo->indegree + topo->outdegree; + allocate_requests(request->global_n_msgs, &(request->global_requests)); + + const char* send_buffer = (const char*)(sendbuf); + char* recv_buffer = (char*)(recvbuf); + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + int ierr = 0; + + for (int i = 0; i < topo->indegree; i++) + { + ierr += MPI_Recv_init(&(recv_buffer[rdispls[i] * recv_size]), + recvcounts[i], + recvtype, + topo->sources[i], + tag, + comm->global_comm, + &(request->global_requests[i])); + } + + for (int i = 0; i < topo->outdegree; i++) + { + ierr += MPI_Send_init(&(send_buffer[sdispls[i] * send_size]), + sendcounts[i], + sendtype, + topo->destinations[i], + tag, + comm->global_comm, + &(request->global_requests[topo->indegree + i])); + } + + *request_ptr = request; + + return ierr; +} diff --git a/library/source/neighborhood/standard/neighbor_alltoallv_standard.c b/library/source/neighborhood/standard/neighbor_alltoallv_standard.c new file mode 100644 index 000000000..8fb582661 --- /dev/null +++ b/library/source/neighborhood/standard/neighbor_alltoallv_standard.c @@ -0,0 +1,83 @@ +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" +#include "neighborhood/neighbor.h" +#include "string.h" + +// Standard, non-persistent neighbor collective +int neighbor_alltoallv_standard(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm) +{ + int tag; + MPIL_Comm_tag(comm, &tag); + + if (topo->indegree + topo->outdegree == 0) + { + return MPI_SUCCESS; + } + + if (comm->n_requests < topo->indegree + topo->outdegree) + { + MPIL_Comm_req_resize(comm, topo->indegree + topo->outdegree); + } + + const char* send_buffer = NULL; + char* recv_buffer = NULL; + + if (topo->indegree) + { + recv_buffer = (char*)recvbuf; + } + if (topo->outdegree) + { + send_buffer = (char*)sendbuf; + } + + int send_size, recv_size; + MPI_Type_size(sendtype, &send_size); + MPI_Type_size(recvtype, &recv_size); + + int count = 0; + for (int i = 0; i < topo->indegree; i++) + { + if (recvcounts[i]) + { + MPI_Irecv(&(recv_buffer[rdispls[i] * recv_size]), + recvcounts[i], + recvtype, + topo->sources[i], + tag, + comm->global_comm, + &(comm->requests[count++])); + } + } + + for (int i = 0; i < topo->outdegree; i++) + { + if (sendcounts[i]) + { + MPI_Isend(&(send_buffer[sdispls[i] * send_size]), + sendcounts[i], + sendtype, + topo->destinations[i], + tag, + comm->global_comm, + &(comm->requests[count++])); + } + } + + MPI_Waitall(count, comm->requests, comm->statuses); + + return MPI_SUCCESS; +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/library/source/persistent/CMakeLists.txt b/library/source/persistent/CMakeLists.txt new file mode 100644 index 000000000..020cc3ea1 --- /dev/null +++ b/library/source/persistent/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(locality_aware PRIVATE + init_request.c + allocate_requests.c +) \ No newline at end of file diff --git a/library/source/persistent/allocate_requests.c b/library/source/persistent/allocate_requests.c new file mode 100644 index 000000000..f09e7bcaa --- /dev/null +++ b/library/source/persistent/allocate_requests.c @@ -0,0 +1,16 @@ +#include + +#include "persistent/MPIL_Request.h" + +void allocate_requests(int n_requests, MPI_Request** request_ptr) +{ + if (n_requests) + { + MPI_Request* request = (MPI_Request*)malloc(sizeof(MPI_Request) * n_requests); + *request_ptr = request; + } + else + { + *request_ptr = NULL; + } +} \ No newline at end of file diff --git a/library/source/persistent/init_request.c b/library/source/persistent/init_request.c new file mode 100644 index 000000000..88cb1d032 --- /dev/null +++ b/library/source/persistent/init_request.c @@ -0,0 +1,31 @@ +#include + +#include "persistent/MPIL_Request.h" + +/** @brief constuctor for MPIL_Request Object**/ +void init_request(MPIL_Request** request_ptr) +{ + MPIL_Request* request = (MPIL_Request*)malloc(sizeof(MPIL_Request)); + + request->locality = NULL; + + request->local_L_n_msgs = 0; + request->local_S_n_msgs = 0; + request->local_R_n_msgs = 0; + request->global_n_msgs = 0; + + request->local_L_requests = NULL; + request->local_S_requests = NULL; + request->local_R_requests = NULL; + request->global_requests = NULL; + + request->recv_size = 0; + request->block_size = 1; + +#ifdef GPU + request->cpu_sendbuf = NULL; + request->cpu_recvbuf = NULL; +#endif + + *request_ptr = request; +} \ No newline at end of file diff --git a/library/source/utils/CMakeLists.txt b/library/source/utils/CMakeLists.txt new file mode 100644 index 000000000..97f3ba835 --- /dev/null +++ b/library/source/utils/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(locality_aware PRIVATE utils.cpp) \ No newline at end of file diff --git a/library/source/utils/utils.cpp b/library/source/utils/utils.cpp new file mode 100644 index 000000000..017781d65 --- /dev/null +++ b/library/source/utils/utils.cpp @@ -0,0 +1,30 @@ +#include "utils/utils.h" + +#include + +void reverse(void* recvbuf, int n_bytes, int var_bytes) +{ + char* recv_buffer = (char*)(recvbuf); + int n_vars = n_bytes / var_bytes; + for (int i = 0; i < n_vars / 2; i++) + { + for (int j = 0; j < var_bytes; j++) + { + std::swap(recv_buffer[i * var_bytes + j], + recv_buffer[(n_vars - i - 1) * var_bytes + j]); + } + } +} + +void rotate(void* recvbuf, int new_first_byte, int last_byte) +{ + char* recv_buffer = (char*)(recvbuf); + std::rotate(recv_buffer, &(recv_buffer[new_first_byte]), &(recv_buffer[last_byte])); +} + +void sort(int n_objects, int* object_indices, int* object_values) +{ + std::sort(object_indices, object_indices + n_objects, [&](const int i, const int j) { + return object_values[i] > object_values[j]; + }); +} \ No newline at end of file diff --git a/library/tests/CMakeLists.txt b/library/tests/CMakeLists.txt new file mode 100644 index 000000000..139aa723e --- /dev/null +++ b/library/tests/CMakeLists.txt @@ -0,0 +1,14 @@ +## Grab all .cpp files in test directory +file(GLOB CPP_SOURCES CONFIGURE_DEPENDS "*.cpp" ".c") + +foreach(src ${CPP_SOURCES}) + make_test(${src}) +endforeach() + +target_include_directories(locality_aware PUBLIC + $) + + +if(USE_GPU) + add_subdirectory(gpu_tests) +endif() diff --git a/library/tests/alltoall_c.c b/library/tests/alltoall_c.c new file mode 100644 index 000000000..d36ffa710 --- /dev/null +++ b/library/tests/alltoall_c.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include + +#include "locality_aware.h" + +void compare_alltoall_results(int* pmpi, int* mpil, int s) +{ + int num_procs; + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + for (int j = 0; j < s * num_procs; j++) + { + if (pmpi[j] != mpil[j]) + { + fprintf(stderr, + "MPIL Alltoall != PMPI, position %d, pmpi %d, mpil %d\n", + j, + pmpi[j], + mpil[j]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Test Integer Alltoall + int max_i = 10; + int max_s = pow(2, max_i); + int elements = max_s * num_procs; + int* local_data = malloc(elements * sizeof(int)); + int* pmpi_alltoall = malloc(elements * sizeof(int)); + int* mpil_alltoall = malloc(elements * sizeof(int)); + + MPIL_Comm* locality_comm; + MPIL_Comm_init(&locality_comm, MPI_COMM_WORLD); + update_locality(locality_comm, 4); + + for (int i = 0; i < max_i; i++) + { + int s = pow(2, i); + + // Will only be clean for up to double digit process counts + for (int j = 0; j < num_procs; j++) + { + for (int k = 0; k < s; k++) + { + local_data[j * s + k] = rank * 10000 + j * 100 + k; + } + } + + // Standard Alltoall + PMPI_Alltoall(local_data, s, MPI_INT, pmpi_alltoall, s, MPI_INT, MPI_COMM_WORLD); + + // Locality-Aware Pairwise Alltoall + for (int i = 0; i < elements; i++) + { + mpil_alltoall[i] = 0; + } + + MPIL_Alltoall(local_data, s, MPI_INT, mpil_alltoall, s, MPI_INT, locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + } + + MPIL_Comm_free(&locality_comm); + + free(local_data); + free(pmpi_alltoall); + free(mpil_alltoall); + + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/gpu_tests/CMakeLists.txt b/library/tests/gpu_tests/CMakeLists.txt new file mode 100644 index 000000000..da6eb8786 --- /dev/null +++ b/library/tests/gpu_tests/CMakeLists.txt @@ -0,0 +1,12 @@ +## Grab all .cpp files in test directory +file(GLOB CPP_SOURCES CONFIGURE_DEPENDS "*.cpp") + +## All should be compiled appropriately (cxx vs nvcc vs hipcc) +set_source_files_properties(${CPP_SOURCES} PROPERTIES LANGUAGE ${locality_aware_LANG}) + +## Go through each CPP file +foreach(src ${CPP_SOURCES}) + make_test(${src}) +endforeach() + + diff --git a/library/tests/gpu_tests/test_gpu_alltoall.cpp b/library/tests/gpu_tests/test_gpu_alltoall.cpp new file mode 100644 index 000000000..c9e0a06e2 --- /dev/null +++ b/library/tests/gpu_tests/test_gpu_alltoall.cpp @@ -0,0 +1,154 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "heterogeneous/gpu_utils.h" +#include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +void compare_alltoall_results(std::vector& pmpi_alltoall, + std::vector& mpix_alltoall, + int s) +{ + int num_procs; + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + for (int i = 0; i < s * num_procs; i++) + { + if (pmpi_alltoall[i] != mpix_alltoall[i]) + { + fprintf(stderr, + "Alltoall ERROR: position %d, pmpi %d, mpix %d\n", + i, + pmpi_alltoall[i], + mpix_alltoall[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Test Integer Alltoall + int max_i = 10; + int max_s = pow(2, max_i); + srand(time(NULL)); + std::vector local_data(max_s * num_procs); + std::vector pmpi_alltoall(max_s * num_procs); + std::vector mpix_alltoall(max_s * num_procs); + std::vector device_data(max_s * num_procs); + + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_device_init(xcomm); + + int n_gpus; + gpuGetDeviceCount(&n_gpus); + gpuSetDevice(xcomm->rank_gpu); + + int* local_data_d; + int* alltoall_d; + gpuMalloc((void**)&local_data_d, max_s * num_procs * sizeof(int)); + gpuMalloc((void**)&alltoall_d, max_s * num_procs * sizeof(int)); + + for (int i = 0; i < max_i; i++) + { + int s = pow(2, i); + + // Will only be clean for up to double digit process counts + for (int i = 0; i < num_procs; i++) + { + for (int j = 0; j < s; j++) + { + local_data[i * s + j] = rank * 10000 + i * 100 + j; + } + } + gpuMemcpy(local_data_d, + local_data.data(), + s * num_procs * sizeof(int), + gpuMemcpyHostToDevice); + + // Standard Alltoall + PMPI_Alltoall(local_data.data(), + s, + MPI_INT, + pmpi_alltoall.data(), + s, + MPI_INT, + MPI_COMM_WORLD); + + // Pairwise Alltoall + MPIL_Set_alltoall_algorithm(ALLTOALL_PAIRWISE); + MPIL_Alltoall( + local_data.data(), s, MPI_INT, mpix_alltoall.data(), s, MPI_INT, xcomm); + compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); + + // Standard GPU Alltoall + PMPI_Alltoall(local_data_d, s, MPI_INT, alltoall_d, s, MPI_INT, MPI_COMM_WORLD); + gpuMemcpy(device_data.data(), + alltoall_d, + s * num_procs * sizeof(int), + gpuMemcpyDeviceToHost); + compare_alltoall_results(pmpi_alltoall, device_data, s); + gpuMemset(alltoall_d, 0, s * num_procs * sizeof(int)); + + // GPU-Aware Pairwise Alltoall + MPIL_Set_alltoall_algorithm(ALLTOALL_GPU_PAIRWISE); + MPIL_Alltoall(local_data_d, s, MPI_INT, alltoall_d, s, MPI_INT, xcomm); + gpuMemcpy(device_data.data(), + alltoall_d, + s * num_procs * sizeof(int), + gpuMemcpyDeviceToHost); + compare_alltoall_results(pmpi_alltoall, device_data, s); + gpuMemset(alltoall_d, 0, s * num_procs * sizeof(int)); + + // GPU-Aware Nonblocking Alltoall + MPIL_Set_alltoall_algorithm(ALLTOALL_GPU_NONBLOCKING); + MPIL_Alltoall(local_data_d, s, MPI_INT, alltoall_d, s, MPI_INT, xcomm); + gpuMemcpy(device_data.data(), + alltoall_d, + s * num_procs * sizeof(int), + gpuMemcpyDeviceToHost); + compare_alltoall_results(pmpi_alltoall, device_data, s); + gpuMemset(alltoall_d, 0, s * num_procs * sizeof(int)); + + // Copy-to-CPU Pairwise Alltoall + MPIL_Set_alltoall_algorithm(ALLTOALL_CTC_PAIRWISE); + MPIL_Alltoall(local_data_d, s, MPI_INT, alltoall_d, s, MPI_INT, xcomm); + gpuMemcpy(device_data.data(), + alltoall_d, + s * num_procs * sizeof(int), + gpuMemcpyDeviceToHost); + compare_alltoall_results(pmpi_alltoall, device_data, s); + gpuMemset(alltoall_d, 0, s * num_procs * sizeof(int)); + + // Copy-to-CPU Nonblocking Alltoall + MPIL_Set_alltoall_algorithm(ALLTOALL_CTC_NONBLOCKING); + MPIL_Alltoall(local_data_d, s, MPI_INT, alltoall_d, s, MPI_INT, xcomm); + gpuMemcpy(device_data.data(), + alltoall_d, + s * num_procs * sizeof(int), + gpuMemcpyDeviceToHost); + compare_alltoall_results(pmpi_alltoall, device_data, s); + gpuMemset(alltoall_d, 0, s * num_procs * sizeof(int)); + } + + gpuFree(local_data_d); + gpuFree(alltoall_d); + + MPIL_Comm_free(&xcomm); + + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/gpu_tests/test_gpu_alltoallv.cpp b/library/tests/gpu_tests/test_gpu_alltoallv.cpp new file mode 100644 index 000000000..d0c027233 --- /dev/null +++ b/library/tests/gpu_tests/test_gpu_alltoallv.cpp @@ -0,0 +1,283 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "par_binary_IO.hpp" +#include "sparse_mat.hpp" +#include "communicator/MPIL_Comm.h" +#include "heterogeneous/gpu_utils.h" +#include "locality_aware.h" + +void compare_alltoallv_results(std::vector& pmpi_alltoall, + std::vector& mpix_alltoall, + int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi_alltoall[i] != mpix_alltoall[i]) + { + fprintf(stderr, + "Alltoallv ERROR: position s=%d, %d, pmpi %d, mpix %d\n", + s, + i, + pmpi_alltoall[i], + mpix_alltoall[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +void test_matrix(const char* filename) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + if (rank == 0) + { + std::cout << "1 SIZE: " << rank << std::endl; + } + + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_device_init(xcomm); + + // Read suitesparse matrix + ParMat A; + readParMatrix(filename, A); + form_comm(A); + + std::vector send_vals(A.on_proc.n_rows); + std::iota(send_vals.begin(), send_vals.end(), 0); + for (int i = 0; i < A.on_proc.n_rows; i++) + { + send_vals[i] += (rank * 1000); + } + + // Alltoallv_send_vals must be ordered (dest 0 to num_procs-1) + std::vector proc_pos(num_procs, -1); + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + proc_pos[A.send_comm.procs[i]] = i; + } + + std::vector alltoallv_send_vals(A.send_comm.size_msgs); + int start, end, idx; + int ctr = 0; + for (int i = 0; i < num_procs; i++) + { + idx = proc_pos[i]; + if (proc_pos[i] < 0) + { + continue; + } + + start = A.send_comm.ptr[idx]; + end = A.send_comm.ptr[idx + 1]; + for (int j = start; j < end; j++) + { + alltoallv_send_vals[ctr++] = send_vals[A.send_comm.idx[j]]; + } + } + + std::vector sendcounts(num_procs, 0); + std::vector sdispls(num_procs + 1); + std::vector recvcounts(num_procs, 0); + std::vector rdispls(num_procs + 1); + + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + sendcounts[A.send_comm.procs[i]] = A.send_comm.ptr[i + 1] - A.send_comm.ptr[i]; + } + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + recvcounts[A.recv_comm.procs[i]] = A.recv_comm.ptr[i + 1] - A.recv_comm.ptr[i]; + } + + sdispls[0] = 0; + rdispls[0] = 0; + for (int i = 0; i < num_procs; i++) + { + sdispls[i + 1] = sdispls[i] + sendcounts[i]; + rdispls[i + 1] = rdispls[i] + recvcounts[i]; + } + + int n_gpus; + gpuGetDeviceCount(&n_gpus); + gpuSetDevice(xcomm->rank_gpu); + + MPI_Barrier(MPI_COMM_WORLD); + int *sendbuf_d, *recvbuf_d; + gpuMalloc((void**)&sendbuf_d, A.send_comm.size_msgs * sizeof(int)); + gpuMalloc((void**)&recvbuf_d, A.recv_comm.size_msgs * sizeof(int)); + gpuMemcpy(sendbuf_d, + alltoallv_send_vals.data(), + A.send_comm.size_msgs * sizeof(int), + gpuMemcpyHostToDevice); + + std::vector pmpi_recv_vals(A.recv_comm.size_msgs); + std::vector gpu_recv_vals(A.recv_comm.size_msgs); + + if (rank == 0) + { + std::cout << "GPU" << std::endl; + for (int msg : gpu_recv_vals) + { + std::cout << msg << " , "; + } + std::cout << std::endl; + //////////////////////////////////////// + std::cout << "RECV" << std::endl; + for (int msg : pmpi_recv_vals) + { + std::cout << msg << " , "; + } + std::cout << std::endl; + } + MPI_Barrier(MPI_COMM_WORLD); + + // Inter-CPU Alltoallv + PMPI_Alltoallv(alltoallv_send_vals.data(), + sendcounts.data(), + sdispls.data(), + MPI_INT, + pmpi_recv_vals.data(), + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm->global_comm); + + // Inter-GPU Alltoallv + PMPI_Alltoallv(sendbuf_d, + sendcounts.data(), + sdispls.data(), + MPI_INT, + recvbuf_d, + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm->global_comm); + + if (rank == 0) + { + std::cout << "buf_d 2" << std::endl; + for (int i = 0; i < *recvcounts.data(); i++) + { + std::cout << recvbuf_d[i] << " , "; + } + std::cout << std::endl; + //////////////////////////////////////// + std::cout << "RECV 2" << std::endl; + for (int msg : pmpi_recv_vals) + { + std::cout << msg << " , "; + } + std::cout << std::endl; + } + MPI_Barrier(MPI_COMM_WORLD); + + gpuMemcpy(gpu_recv_vals.data(), + recvbuf_d, + A.recv_comm.size_msgs * sizeof(int), + gpuMemcpyDeviceToHost); + compare_alltoallv_results(pmpi_recv_vals, gpu_recv_vals, A.recv_comm.size_msgs); + gpuMemset(recvbuf_d, 0, A.recv_comm.size_msgs * sizeof(int)); + + std::cout << "1 RANK: " << rank << std::endl; + MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_GPU_PAIRWISE); + MPIL_Alltoallv(sendbuf_d, + sendcounts.data(), + sdispls.data(), + MPI_INT, + recvbuf_d, + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm); + gpuMemcpy(gpu_recv_vals.data(), + recvbuf_d, + A.recv_comm.size_msgs * sizeof(int), + gpuMemcpyDeviceToHost); + compare_alltoallv_results(pmpi_recv_vals, gpu_recv_vals, A.recv_comm.size_msgs); + gpuMemset(recvbuf_d, 0, A.recv_comm.size_msgs * sizeof(int)); + + std::cout << "2 RANK: " << rank << std::endl; + MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_GPU_NONBLOCKING); + MPIL_Alltoallv(sendbuf_d, + sendcounts.data(), + sdispls.data(), + MPI_INT, + recvbuf_d, + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm); + gpuMemcpy(gpu_recv_vals.data(), + recvbuf_d, + A.recv_comm.size_msgs * sizeof(int), + gpuMemcpyDeviceToHost); + compare_alltoallv_results(pmpi_recv_vals, gpu_recv_vals, A.recv_comm.size_msgs); + gpuMemset(recvbuf_d, 0, A.recv_comm.size_msgs * sizeof(int)); + + std::cout << "3 RANK: " << rank << std::endl; + MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_CTC_PAIRWISE); + MPIL_Alltoallv(sendbuf_d, + sendcounts.data(), + sdispls.data(), + MPI_INT, + recvbuf_d, + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm); + gpuMemcpy(gpu_recv_vals.data(), + recvbuf_d, + A.recv_comm.size_msgs * sizeof(int), + gpuMemcpyDeviceToHost); + compare_alltoallv_results(pmpi_recv_vals, gpu_recv_vals, A.recv_comm.size_msgs); + gpuMemset(recvbuf_d, 0, A.recv_comm.size_msgs * sizeof(int)); + + std::cout << "4 RANK: " << rank << std::endl; + MPI_Barrier(MPI_COMM_WORLD); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_CTC_NONBLOCKING); + MPIL_Alltoallv(sendbuf_d, + sendcounts.data(), + sdispls.data(), + MPI_INT, + recvbuf_d, + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm); + gpuMemcpy(gpu_recv_vals.data(), + recvbuf_d, + A.recv_comm.size_msgs * sizeof(int), + gpuMemcpyDeviceToHost); + compare_alltoallv_results(pmpi_recv_vals, gpu_recv_vals, A.recv_comm.size_msgs); + gpuMemset(recvbuf_d, 0, A.recv_comm.size_msgs * sizeof(int)); + + gpuFree(sendbuf_d); + gpuFree(recvbuf_d); + + MPIL_Comm_free(&xcomm); +} + +int main(int argc, char** argv) +{ + int provided; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + test_matrix("../../../../test_data/dwt_162.pm"); + test_matrix("../../../../test_data/odepa400.pm"); + test_matrix("../../../../test_data/ww_36_pmec_36.pm"); + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_alltoall.cpp b/library/tests/test_alltoall.cpp new file mode 100644 index 000000000..672442038 --- /dev/null +++ b/library/tests/test_alltoall.cpp @@ -0,0 +1,230 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "locality_aware.h" +// #include "collective/alltoall.h" + +void compare_alltoall_results(std::vector& pmpi, std::vector& mpil, int s) +{ + int num_procs; + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + for (int j = 0; j < s * num_procs; j++) + { + if (pmpi[j] != mpil[j]) + { + fprintf(stderr, + "MPIL Alltoall != PMPI, position %d, pmpi %d, mpil %d\n", + j, + pmpi[j], + mpil[j]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Test Integer Alltoall + int max_i = 10; + int max_s = pow(2, max_i); + srand(time(NULL)); + std::vector local_data(max_s * num_procs); + + std::vector pmpi_alltoall(max_s * num_procs); + std::vector mpil_alltoall(max_s * num_procs); + + MPIL_Comm* locality_comm; + MPIL_Comm_init(&locality_comm, MPI_COMM_WORLD); + MPIL_Comm_update_locality(locality_comm, 4); + + for (int i = 0; i < max_i; i++) + { + int s = pow(2, i); + + // Will only be clean for up to double digit process counts + for (int j = 0; j < num_procs; j++) + { + for (int k = 0; k < s; k++) + { + local_data[j * s + k] = rank * 10000 + j * 100 + k; + } + } + + // Standard Alltoall + PMPI_Alltoall(local_data.data(), + s, + MPI_INT, + pmpi_alltoall.data(), + s, + MPI_INT, + MPI_COMM_WORLD); + + // Locality-Aware Pairwise Alltoall + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Standard Pairwise + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Set_alltoall_algorithm(ALLTOALL_PAIRWISE); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Standard Nonblocking + MPIL_Set_alltoall_algorithm(ALLTOALL_NONBLOCKING); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Hierarchical + Pairwise + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Set_alltoall_algorithm(ALLTOALL_HIERARCHICAL_PAIRWISE); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Hierarchical + Nonblocking + MPIL_Set_alltoall_algorithm(ALLTOALL_HIERARCHICAL_NONBLOCKING); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Multileader + Pairwise + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Set_alltoall_algorithm(ALLTOALL_MULTILEADER_PAIRWISE); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Multileader + Nonblocking + MPIL_Set_alltoall_algorithm(ALLTOALL_MULTILEADER_NONBLOCKING); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Node Aware + Pairwise + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Set_alltoall_algorithm(ALLTOALL_NODE_AWARE_PAIRWISE); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Node Aware + Nonblocking + MPIL_Set_alltoall_algorithm(ALLTOALL_NODE_AWARE_NONBLOCKING); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Locality Aware + Pairwise + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Set_alltoall_algorithm(ALLTOALL_LOCALITY_AWARE_PAIRWISE); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Locality Aware + Nonblocking + MPIL_Set_alltoall_algorithm(ALLTOALL_LOCALITY_AWARE_NONBLOCKING); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Multileader + Locality Aware + Pairwise + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Set_alltoall_algorithm(ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + // Test Multileader + Locality Aware + Nonblocking + MPIL_Set_alltoall_algorithm(ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING); + + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + } + + MPIL_Comm_free(&locality_comm); + + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_alltoall_enum.cpp b/library/tests/test_alltoall_enum.cpp new file mode 100644 index 000000000..07c0b762a --- /dev/null +++ b/library/tests/test_alltoall_enum.cpp @@ -0,0 +1,223 @@ +#include +#include +#include +#include + +#include +#include +#include + +// #include "communicator/MPIL_Comm.h" +#include "locality_aware.h" + +void compare_alltoall_results(std::vector& pmpi, std::vector& mpil, int s) +{ + int num_procs; + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + for (int j = 0; j < s * num_procs; j++) + { + if (pmpi[j] != mpil[j]) + { + fprintf(stderr, + "MPIL Alltoall != PMPI, position %d, pmpi %d, mpil %d\n", + j, + pmpi[j], + mpil[j]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Test Integer Alltoall + int max_i = 10; + int max_s = pow(2, max_i); + srand(time(NULL)); + std::vector local_data(max_s * num_procs); + + std::vector pmpi_alltoall(max_s * num_procs); + std::vector mpil_alltoall(max_s * num_procs); + + MPIL_Comm* locality_comm; + MPIL_Comm_init(&locality_comm, MPI_COMM_WORLD); + MPIL_Comm_update_locality(locality_comm, 4); + + for (int i = 0; i < max_i; i++) + { + int s = pow(2, i); + + // Will only be clean for up to double digit process counts + for (int j = 0; j < num_procs; j++) + { + for (int k = 0; k < s; k++) + { + local_data[j * s + k] = rank * 10000 + j * 100 + k; + } + } + + // Standard Alltoall + PMPI_Alltoall(local_data.data(), + s, + MPI_INT, + pmpi_alltoall.data(), + s, + MPI_INT, + MPI_COMM_WORLD); + + mpil_alltoall_implementation = ALLTOALL_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_HIERARCHICAL_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_HIERARCHICAL_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_MULTILEADER_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_MULTILEADER_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_NODE_AWARE_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_NODE_AWARE_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_LOCALITY_AWARE_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_LOCALITY_AWARE_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + + mpil_alltoall_implementation = ALLTOALL_PMPI; + std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); + MPIL_Alltoall(local_data.data(), + s, + MPI_INT, + mpil_alltoall.data(), + s, + MPI_INT, + locality_comm); + compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); + } + + MPIL_Comm_free(&locality_comm); + + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_alltoallv.cpp b/library/tests/test_alltoallv.cpp new file mode 100644 index 000000000..4fb3da346 --- /dev/null +++ b/library/tests/test_alltoallv.cpp @@ -0,0 +1,150 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "locality_aware.h" + +void compare_alltoallv_results(std::vector& pmpi, std::vector& mpil, int s) +{ + int num_procs; + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + for (int j = 0; j < s * num_procs; j++) + { + if (pmpi[j] != mpil[j]) + { + fprintf(stderr, + "MPIL Alltoallv != PMPI, position %d, pmpi %d, mpil %d\n", + j, + pmpi[j], + mpil[j]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Test Integer Alltoall + int max_i = 10; + int max_s = pow(2, max_i); + srand(time(NULL)); + std::vector local_data(max_s * num_procs); + + std::vector pmpi_alltoallv(max_s * num_procs); + std::vector mpil_alltoallv(max_s * num_procs); + + std::vector sizes(num_procs); + std::vector displs(num_procs + 1); + + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_update_locality(xcomm, 4); + + for (int i = 0; i < max_i; i++) + { + int s = pow(2, i); + + // Will only be clean for up to double digit process counts + displs[0] = 0; + for (int j = 0; j < num_procs; j++) + { + for (int k = 0; k < s; k++) + { + local_data[j * s + k] = rank * 10000 + j * 100 + k; + } + sizes[j] = s; + displs[j + 1] = displs[j] + s; + } + + PMPI_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + pmpi_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + MPI_COMM_WORLD); + + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + mpil_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); + + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_PAIRWISE); + MPIL_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + mpil_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); + + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_NONBLOCKING); + MPIL_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + mpil_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); + + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_BATCH); + MPIL_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + mpil_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); + + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_BATCH_ASYNC); + MPIL_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + mpil_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); + } + + MPIL_Comm_free(&xcomm); + + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_alltoallv_enum.cpp b/library/tests/test_alltoallv_enum.cpp new file mode 100644 index 000000000..01a674127 --- /dev/null +++ b/library/tests/test_alltoallv_enum.cpp @@ -0,0 +1,151 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "locality_aware.h" + +void compare_alltoallv_results(std::vector& pmpi, std::vector& mpil, int s) +{ + int num_procs; + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + for (int j = 0; j < s * num_procs; j++) + { + if (pmpi[j] != mpil[j]) + { + fprintf(stderr, + "MPIL Alltoallv != PMPI, position %d, pmpi %d, mpil %d\n", + j, + pmpi[j], + mpil[j]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Test Integer Alltoall + int max_i = 10; + int max_s = pow(2, max_i); + srand(time(NULL)); + std::vector local_data(max_s * num_procs); + + std::vector pmpi_alltoallv(max_s * num_procs); + std::vector mpil_alltoallv(max_s * num_procs); + + std::vector sizes(num_procs); + std::vector displs(num_procs + 1); + + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_update_locality(xcomm, 4); + + for (int i = 0; i < max_i; i++) + { + int s = pow(2, i); + + // Will only be clean for up to double digit process counts + displs[0] = 0; + for (int j = 0; j < num_procs; j++) + { + for (int k = 0; k < s; k++) + { + local_data[j * s + k] = rank * 10000 + j * 100 + k; + } + sizes[j] = s; + displs[j + 1] = displs[j] + s; + } + + PMPI_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + pmpi_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + MPI_COMM_WORLD); + + mpil_alltoallv_implementation = ALLTOALLV_PAIRWISE; + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + mpil_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); + + mpil_alltoallv_implementation = ALLTOALLV_NONBLOCKING; + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + mpil_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); + + mpil_alltoallv_implementation = ALLTOALLV_BATCH; + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + mpil_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); + + mpil_alltoallv_implementation = ALLTOALLV_BATCH_ASYNC; + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + mpil_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); + + mpil_alltoallv_implementation = ALLTOALLV_PMPI; + std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); + MPIL_Alltoallv(local_data.data(), + sizes.data(), + displs.data(), + MPI_INT, + mpil_alltoallv.data(), + sizes.data(), + displs.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); + } + + MPIL_Comm_free(&xcomm); + + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_neighbor_alltoallv_init.cpp b/library/tests/test_neighbor_alltoallv_init.cpp new file mode 100644 index 000000000..0072d17ce --- /dev/null +++ b/library/tests/test_neighbor_alltoallv_init.cpp @@ -0,0 +1,219 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "locality_aware.h" +#include "neighbor_data.hpp" + +void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, + std::vector& mpix_recv_vals, + int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi_recv_vals[i] != mpix_recv_vals[i]) + { + fprintf(stderr, + "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", + i, + pmpi_recv_vals[i], + mpix_recv_vals[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + + // Get MPI Information + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Initial communication info (standard) + int local_size = 10000; // Number of variables each rank stores + MPIL_Data send_data; + MPIL_Data recv_data; + form_initial_communicator(local_size, &send_data, &recv_data); + std::vector global_send_idx(send_data.size_msgs); + std::vector global_recv_idx(recv_data.size_msgs); + form_global_indices( + local_size, send_data, recv_data, global_send_idx, global_recv_idx); + + std::vector pmpi_recv_vals(recv_data.size_msgs); + std::vector mpix_recv_vals(recv_data.size_msgs); + + std::vector send_vals(local_size); + int val = local_size * rank; + for (int i = 0; i < local_size; i++) + { + send_vals[i] = val++; + } + std::vector alltoallv_send_vals(send_data.size_msgs); + for (int i = 0; i < send_data.size_msgs; i++) + { + alltoallv_send_vals[i] = send_vals[send_data.indices[i]]; + } + + // Some MPI versions require sendcounts and recvcounts + // to be non-NULL + int* send_counts = send_data.counts.data(); + if (send_data.counts.data() == NULL) + { + send_counts = new int[1]; + } + int* recv_counts = recv_data.counts.data(); + if (recv_data.counts.data() == NULL) + { + recv_counts = new int[1]; + } + + MPI_Comm std_comm; + MPI_Status status; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; + + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); + + // Standard MPI Dist Graph Create + MPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, + recv_data.num_msgs, + recv_data.procs.data(), + recv_data.counts.data(), + send_data.num_msgs, + send_data.procs.data(), + send_data.counts.data(), + MPI_INFO_NULL, + 0, + &std_comm); + + // MPI Advance Dist Graph Create + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, + recv_data.num_msgs, + recv_data.procs.data(), + recv_data.counts.data(), + send_data.num_msgs, + send_data.procs.data(), + send_data.counts.data(), + xinfo, + 0, + &xcomm); + // Update Locality : 4 PPN (for single-node tests) + MPIL_Comm_update_locality(xcomm, 4); + + // Standard MPI Implementation of Alltoallv + MPI_Neighbor_alltoallv(alltoallv_send_vals.data(), + send_counts, + send_data.indptr.data(), + MPI_INT, + pmpi_recv_vals.data(), + recv_counts, + recv_data.indptr.data(), + MPI_INT, + std_comm); + + // Simple Persistent MPI Advance Implementation + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_STANDARD); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + send_data.counts.data(), + send_data.indptr.data(), + MPI_INT, + mpix_recv_vals.data(), + recv_data.counts.data(), + recv_data.indptr.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); + + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_LOCALITY); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + send_data.counts.data(), + send_data.indptr.data(), + MPI_INT, + mpix_recv_vals.data(), + recv_data.counts.data(), + recv_data.indptr.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); + + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_STANDARD); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), + send_data.counts.data(), + send_data.indptr.data(), + global_send_idx.data(), + MPI_INT, + mpix_recv_vals.data(), + recv_data.counts.data(), + recv_data.indptr.data(), + global_recv_idx.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); + + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_LOCALITY); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), + send_data.counts.data(), + send_data.indptr.data(), + global_send_idx.data(), + MPI_INT, + mpix_recv_vals.data(), + recv_data.counts.data(), + recv_data.indptr.data(), + global_recv_idx.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); + + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); + MPI_Comm_free(&std_comm); + + if (send_data.counts.data() == NULL) + { + delete[] send_counts; + } + if (recv_data.counts.data() == NULL) + { + delete[] recv_counts; + } + + MPI_Finalize(); + return 0; +} // end of main() // \ No newline at end of file diff --git a/library/tests/test_neighbor_reorder.cpp b/library/tests/test_neighbor_reorder.cpp new file mode 100644 index 000000000..09f41b576 --- /dev/null +++ b/library/tests/test_neighbor_reorder.cpp @@ -0,0 +1,166 @@ +#include +#include +#include +#include + +#include +#include +#include + +// #include "persistent/MPIL_Request.h" +#include "locality_aware.h" +#include "neighbor_data.hpp" + +void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, + std::vector& mpix_recv_vals, + int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi_recv_vals[i] != mpix_recv_vals[i]) + { + fprintf(stderr, + "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", + i, + pmpi_recv_vals[i], + mpix_recv_vals[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + // Get MPI Information + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Initial communication info (standard) + int local_size = 10000; // Number of variables each rank stores + MPIL_Data send_data; + MPIL_Data recv_data; + form_initial_communicator(local_size, &send_data, &recv_data); + std::vector global_send_idx(send_data.size_msgs); + std::vector global_recv_idx(recv_data.size_msgs); + form_global_indices( + local_size, send_data, recv_data, global_send_idx, global_recv_idx); + + // Test correctness of communication + std::vector mpix_recv_vals(recv_data.size_msgs); + std::vector pmpi_recv_vals(recv_data.size_msgs); + + std::vector send_vals(local_size); + int val = local_size * rank; + for (int i = 0; i < local_size; i++) + { + send_vals[i] = val++; + } + + std::vector alltoallv_send_vals(send_data.size_msgs); + for (int i = 0; i < send_data.size_msgs; i++) + { + alltoallv_send_vals[i] = send_vals[send_data.indices[i]]; + } + + MPI_Comm std_comm; + MPI_Status status; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; + + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); + + // Standard MPI Dist Graph Create + MPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, + recv_data.num_msgs, + recv_data.procs.data(), + recv_data.counts.data(), + send_data.num_msgs, + send_data.procs.data(), + send_data.counts.data(), + MPI_INFO_NULL, + 0, + &std_comm); + + // MPI Advance Dist Graph Create + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, + recv_data.num_msgs, + recv_data.procs.data(), + recv_data.counts.data(), + send_data.num_msgs, + send_data.procs.data(), + send_data.counts.data(), + xinfo, + 0, + &xcomm); + + // Update Locality : 4 PPN (for single-node tests) + MPIL_Comm_update_locality(xcomm, 4); + + // Standard MPI Implementation of Alltoallv + int* send_counts = send_data.counts.data(); + if (send_data.counts.data() == NULL) + { + send_counts = new int[1]; + } + int* recv_counts = recv_data.counts.data(); + if (recv_data.counts.data() == NULL) + { + recv_counts = new int[1]; + } + MPI_Neighbor_alltoallv(alltoallv_send_vals.data(), + send_counts, + send_data.indptr.data(), + MPI_INT, + pmpi_recv_vals.data(), + recv_counts, + recv_data.indptr.data(), + MPI_INT, + std_comm); + if (send_data.counts.data() == NULL) + { + delete[] send_counts; + } + if (recv_data.counts.data() == NULL) + { + delete[] recv_counts; + } + + // Simple Persistent MPI Advance Implementation + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + send_data.counts.data(), + send_data.indptr.data(), + MPI_INT, + mpix_recv_vals.data(), + recv_data.counts.data(), + recv_data.indptr.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + + // Reorder during first send/recv + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Request_reorder(xrequest, 1); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); + + // Standard send/recv with reordered recvs + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); + + MPIL_Request_free(&xrequest); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); + MPI_Comm_free(&std_comm); + + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_neighbor_topo_alltoallv_init.cpp b/library/tests/test_neighbor_topo_alltoallv_init.cpp new file mode 100644 index 000000000..597437729 --- /dev/null +++ b/library/tests/test_neighbor_topo_alltoallv_init.cpp @@ -0,0 +1,203 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "locality_aware.h" +#include "neighbor_data.hpp" + +void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, + std::vector& mpix_recv_vals, + int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi_recv_vals[i] != mpix_recv_vals[i]) + { + fprintf(stderr, + "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", + i, + pmpi_recv_vals[i], + mpix_recv_vals[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + + // Get MPI Information + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Initial communication info (standard) + int local_size = 10000; // Number of variables each rank stores + MPIL_Data send_data; + MPIL_Data recv_data; + form_initial_communicator(local_size, &send_data, &recv_data); + std::vector global_send_idx(send_data.size_msgs); + std::vector global_recv_idx(recv_data.size_msgs); + form_global_indices( + local_size, send_data, recv_data, global_send_idx, global_recv_idx); + + std::vector pmpi_recv_vals(recv_data.size_msgs); + std::vector mpix_recv_vals(recv_data.size_msgs); + + std::vector send_vals(local_size); + int val = local_size * rank; + for (int i = 0; i < local_size; i++) + { + send_vals[i] = val++; + } + std::vector alltoallv_send_vals(send_data.size_msgs); + for (int i = 0; i < send_data.size_msgs; i++) + { + alltoallv_send_vals[i] = send_vals[send_data.indices[i]]; + } + + // Some MPI versions require sendcounts and recvcounts + // to be non-NULL + int* send_counts = send_data.counts.data(); + if (send_data.counts.data() == NULL) + { + send_counts = new int[1]; + } + int* recv_counts = recv_data.counts.data(); + if (recv_data.counts.data() == NULL) + { + recv_counts = new int[1]; + } + + MPI_Comm std_comm; + MPI_Status status; + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_update_locality(xcomm, 4); + MPIL_Request* xrequest; + + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); + + MPIL_Topo* topo; + MPIL_Topo_init(recv_data.num_msgs, + recv_data.procs.data(), + recv_data.counts.data(), + send_data.num_msgs, + send_data.procs.data(), + send_data.counts.data(), + xinfo, + &topo); + + // Standard MPI Dist Graph Create + MPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, + recv_data.num_msgs, + recv_data.procs.data(), + recv_data.counts.data(), + send_data.num_msgs, + send_data.procs.data(), + send_data.counts.data(), + MPI_INFO_NULL, + 0, + &std_comm); + + // Standard MPI Implementation of Alltoallv + MPI_Neighbor_alltoallv(alltoallv_send_vals.data(), + send_counts, + send_data.indptr.data(), + MPI_INT, + pmpi_recv_vals.data(), + recv_counts, + recv_data.indptr.data(), + MPI_INT, + std_comm); + + // Simple Persistent MPI Advance Implementation + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_STANDARD); + MPIL_Neighbor_alltoallv_init_topo(alltoallv_send_vals.data(), + send_data.counts.data(), + send_data.indptr.data(), + MPI_INT, + mpix_recv_vals.data(), + recv_data.counts.data(), + recv_data.indptr.data(), + MPI_INT, + topo, + xcomm, + xinfo, + &xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); + + // Locality-Aware MPI Advance Implementation + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_LOCALITY); + // neighbor_alltoallv_init_locality_ext(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init_ext_topo(alltoallv_send_vals.data(), + send_data.counts.data(), + send_data.indptr.data(), + global_send_idx.data(), + MPI_INT, + mpix_recv_vals.data(), + recv_data.counts.data(), + recv_data.indptr.data(), + global_recv_idx.data(), + MPI_INT, + topo, + xcomm, + xinfo, + &xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); + + // Partial Locality-Aware MPI Advance Implementation + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_LOCALITY); + // neighbor_alltoallv_init_locality(alltoallv_send_vals.data(), + MPIL_Neighbor_alltoallv_init_topo(alltoallv_send_vals.data(), + send_data.counts.data(), + send_data.indptr.data(), + MPI_INT, + mpix_recv_vals.data(), + recv_data.counts.data(), + recv_data.indptr.data(), + MPI_INT, + topo, + xcomm, + xinfo, + &xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); + + MPIL_Topo_free(&topo); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); + MPI_Comm_free(&std_comm); + + if (send_data.counts.data() == NULL) + { + delete[] send_counts; + } + if (recv_data.counts.data() == NULL) + { + delete[] recv_counts; + } + + MPI_Finalize(); + return 0; +} // end of main() // \ No newline at end of file diff --git a/library/tests/test_suitesparse_alltoall_crs.cpp b/library/tests/test_suitesparse_alltoall_crs.cpp new file mode 100644 index 000000000..7d0a726f3 --- /dev/null +++ b/library/tests/test_suitesparse_alltoall_crs.cpp @@ -0,0 +1,193 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "locality_aware.h" +#include "tests/par_binary_IO.hpp" +#include "tests/sparse_mat.hpp" + +void compare_alltoall_crs_results( + int n_recvs, int send_msgs, int* recvvals, int* src, std::vector& proc_counts) +{ + if (n_recvs != send_msgs) + { + fprintf(stderr, "NRecvs incorrect (%d), should be %d\n", n_recvs, send_msgs); + MPI_Abort(MPI_COMM_WORLD, -1); + } + for (int i = 0; i < n_recvs; i++) + { + if (recvvals[i] != proc_counts[src[i]]) + { + fprintf( + stderr, + "RecvVals incorrect: position %d from %d, recvvals %d, should be %d\n", + i, + src[i], + recvvals[i], + proc_counts[src[i]]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +void test_matrix(const char* filename) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + MPIL_Comm* xcomm; + MPIL_Info* xinfo; + + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_topo_init(xcomm); + + MPIL_Info_init(&xinfo); + + // Update so there are 4 PPN rather than what MPI_Comm_split returns + MPIL_Comm_update_locality(xcomm, 4); + + // Read suitesparse matrix + ParMat A; + readParMatrix(filename, A); + form_comm(A); + std::vector proc_counts(num_procs, 0); + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + proc_counts[A.send_comm.procs[i]] = A.send_comm.counts[i]; + } + + int n_recvs; + int* src; + int* recvvals; + + /* TEST RMA VERSION */ + n_recvs = -1; + MPIL_Set_alltoall_crs(ALLTOALL_CRS_RMA); + MPIL_Alltoall_crs(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + 1, + MPI_INT, + A.recv_comm.counts.data(), + &n_recvs, + &src, + 1, + MPI_INT, + (void**)&recvvals, + xinfo, + xcomm); + compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); + MPIL_Free(src); + MPIL_Free(recvvals); + + /* TEST PERSONALIZED VERSION */ + n_recvs = -1; + MPIL_Set_alltoall_crs(ALLTOALL_CRS_PERSONALIZED); + MPIL_Alltoall_crs(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + 1, + MPI_INT, + A.recv_comm.counts.data(), + &n_recvs, + &src, + 1, + MPI_INT, + (void**)&recvvals, + xinfo, + xcomm); + compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); + MPIL_Free(src); + MPIL_Free(recvvals); + + /* TEST PERSONALIZED LOCALITY VERSION */ + n_recvs = -1; + MPIL_Set_alltoall_crs(ALLTOALL_CRS_PERSONALIZED_LOC); + MPIL_Alltoall_crs(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + 1, + MPI_INT, + A.recv_comm.counts.data(), + &n_recvs, + &src, + 1, + MPI_INT, + (void**)&recvvals, + xinfo, + xcomm); + compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); + MPIL_Free(src); + MPIL_Free(recvvals); + + /* TEST NONBLOCKING VERSION */ + n_recvs = -1; + MPIL_Set_alltoall_crs(ALLTOALL_CRS_NONBLOCKING); + MPIL_Alltoall_crs(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + 1, + MPI_INT, + A.recv_comm.counts.data(), + &n_recvs, + &src, + 1, + MPI_INT, + (void**)&recvvals, + xinfo, + xcomm); + compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); + MPIL_Free(src); + MPIL_Free(recvvals); + + /* TEST NONBLOCKING LOCALITY VERSION */ + n_recvs = -1; + MPIL_Set_alltoall_crs(ALLTOALL_CRS_NONBLOCKING_LOC); + MPIL_Alltoall_crs(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + 1, + MPI_INT, + A.recv_comm.counts.data(), + &n_recvs, + &src, + 1, + MPI_INT, + (void**)&recvvals, + xinfo, + xcomm); + compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); + MPIL_Free(src); + MPIL_Free(recvvals); + + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + test_matrix("../../../test_data/dwt_162.pm"); + test_matrix("../../../test_data/odepa400.pm"); + test_matrix("../../../test_data/ww_36_pmec_36.pm"); + test_matrix("../../../test_data/bcsstk01.pm"); + test_matrix("../../../test_data/west0132.pm"); + test_matrix("../../../test_data/gams10a.pm"); + test_matrix("../../../test_data/gams10am.pm"); + test_matrix("../../../test_data/D_10.pm"); + test_matrix("../../../test_data/oscil_dcop_11.pm"); + test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); + test_matrix("../../../test_data/ch5-5-b1.pm"); + test_matrix("../../../test_data/msc01050.pm"); + test_matrix("../../../test_data/SmaGri.pm"); + test_matrix("../../../test_data/radfr1.pm"); + test_matrix("../../../test_data/bibd_49_3.pm"); + test_matrix("../../../test_data/can_1054.pm"); + test_matrix("../../../test_data/can_1072.pm"); + test_matrix("../../../test_data/lp_woodw.pm"); + test_matrix("../../../test_data/lp_sctap2.pm"); + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_suitesparse_alltoallv.cpp b/library/tests/test_suitesparse_alltoallv.cpp new file mode 100644 index 000000000..d071eb587 --- /dev/null +++ b/library/tests/test_suitesparse_alltoallv.cpp @@ -0,0 +1,194 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "locality_aware.h" +#include "tests/par_binary_IO.hpp" +#include "tests/sparse_mat.hpp" + +void compare_alltoallv_results(std::vector& pmpi, std::vector& mpil, int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi[i] != mpil[i]) + { + fprintf(stderr, + "MPIL Alltoallv != PMPI, position %d, pmpi %d, mpil %d\n", + i, + pmpi[i], + mpil[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } +} + +void test_matrix(const char* filename) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_update_locality(xcomm, 4); + + // Read suitesparse matrix + ParMat A; + readParMatrix(filename, A); + form_comm(A); + + std::vector send_vals(A.on_proc.n_rows); + std::iota(send_vals.begin(), send_vals.end(), 0); + for (int i = 0; i < A.on_proc.n_rows; i++) + { + send_vals[i] += (rank * 1000); + } + + // Alltoallv_send_vals must be ordered (dest 0 to num_procs-1) + std::vector proc_pos(num_procs, -1); + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + proc_pos[A.send_comm.procs[i]] = i; + } + + std::vector alltoallv_send_vals(A.send_comm.size_msgs); + int start, end, idx; + int ctr = 0; + for (int i = 0; i < num_procs; i++) + { + idx = proc_pos[i]; + if (proc_pos[i] < 0) + { + continue; + } + + start = A.send_comm.ptr[idx]; + end = A.send_comm.ptr[idx + 1]; + for (int j = start; j < end; j++) + { + alltoallv_send_vals[ctr++] = send_vals[A.send_comm.idx[j]]; + } + } + + std::vector sendcounts(num_procs, 0); + std::vector sdispls(num_procs + 1); + std::vector recvcounts(num_procs, 0); + std::vector rdispls(num_procs + 1); + + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + sendcounts[A.send_comm.procs[i]] = A.send_comm.ptr[i + 1] - A.send_comm.ptr[i]; + } + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + recvcounts[A.recv_comm.procs[i]] = A.recv_comm.ptr[i + 1] - A.recv_comm.ptr[i]; + } + + sdispls[0] = 0; + rdispls[0] = 0; + for (int i = 0; i < num_procs; i++) + { + sdispls[i + 1] = sdispls[i] + sendcounts[i]; + rdispls[i + 1] = rdispls[i] + recvcounts[i]; + } + + std::vector pmpi_recv_vals(A.recv_comm.size_msgs); + std::vector mpil_recv_vals(A.recv_comm.size_msgs); + + communicate(A, send_vals, mpil_recv_vals, MPI_INT); + + PMPI_Alltoallv(alltoallv_send_vals.data(), + sendcounts.data(), + sdispls.data(), + MPI_INT, + pmpi_recv_vals.data(), + recvcounts.data(), + rdispls.data(), + MPI_INT, + MPI_COMM_WORLD); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Alltoallv(alltoallv_send_vals.data(), + sendcounts.data(), + sdispls.data(), + MPI_INT, + mpil_recv_vals.data(), + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_PAIRWISE); + MPIL_Alltoallv(alltoallv_send_vals.data(), + sendcounts.data(), + sdispls.data(), + MPI_INT, + mpil_recv_vals.data(), + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_NONBLOCKING); + MPIL_Alltoallv(alltoallv_send_vals.data(), + sendcounts.data(), + sdispls.data(), + MPI_INT, + mpil_recv_vals.data(), + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_BATCH); + MPIL_Alltoallv(alltoallv_send_vals.data(), + sendcounts.data(), + sdispls.data(), + MPI_INT, + mpil_recv_vals.data(), + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Set_alltoallv_algorithm(ALLTOALLV_BATCH_ASYNC); + MPIL_Alltoallv(alltoallv_send_vals.data(), + sendcounts.data(), + sdispls.data(), + MPI_INT, + mpil_recv_vals.data(), + recvcounts.data(), + rdispls.data(), + MPI_INT, + xcomm); + compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + MPIL_Comm_free(&xcomm); +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + + test_matrix("../../../test_data/dwt_162.pm"); + test_matrix("../../../test_data/odepa400.pm"); + test_matrix("../../../test_data/ww_36_pmec_36.pm"); + + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_suitesparse_alltoallv_crs.cpp b/library/tests/test_suitesparse_alltoallv_crs.cpp new file mode 100644 index 000000000..d7fdac4b3 --- /dev/null +++ b/library/tests/test_suitesparse_alltoallv_crs.cpp @@ -0,0 +1,274 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "locality_aware.h" +#include "tests/par_binary_IO.hpp" +#include "tests/sparse_mat.hpp" + +void compare_alltoallv_crs_results(int n_recvs, + int send_msgs, + int s_recvs, + int send_size, + int* src, + std::vector& proc_counts, + int* recvcounts, + std::vector& proc_displs, + std::vector& send_idx, + int* rdispls, + long* recvvals, + int first_col) +{ + if (n_recvs != send_msgs) + { + fprintf(stderr, "NRecvs incorrect (%d), should be %d\n", n_recvs, send_msgs); + MPI_Abort(MPI_COMM_WORLD, -1); + } + if (s_recvs != send_size) + { + fprintf(stderr, "SRecvs incorrect (%d), should be %d\n", s_recvs, send_size); + MPI_Abort(MPI_COMM_WORLD, -1); + } + for (int i = 0; i < n_recvs; i++) + { + int proc = src[i]; + if (recvcounts[i] != proc_counts[proc]) + { + fprintf(stderr, + "Incorrect count at position %d, process %d, recvcounts %d, should " + "be %d\n", + i, + proc, + recvcounts[i], + proc_counts[proc]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + for (int j = 0; j < recvcounts[i]; j++) + { + if (recvvals[rdispls[i] + j] - first_col != send_idx[proc_displs[proc] + j]) + { + fprintf(stderr, + "Incorrect recvval from proc %d, position %d, getting %ld, " + "should be %d\n", + proc, + j, + recvvals[rdispls[i] + j] - first_col, + send_idx[proc_displs[proc] + j]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } + } +} + +void test_matrix(const char* filename) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_topo_init(xcomm); + + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); + + // Update so there are 4 PPN rather than what MPI_Comm_split returns + MPIL_Comm_update_locality(xcomm, 4); + + // Read suitesparse matrix + ParMat A; + readParMatrix(filename, A); + form_comm(A); + std::vector proc_counts(num_procs, 0); + std::vector proc_displs(num_procs, 0); + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + int proc = A.send_comm.procs[i]; + proc_counts[proc] = A.send_comm.counts[i]; + proc_displs[proc] = A.send_comm.ptr[i]; + } + + int n_recvs, s_recvs; + int *src, *recvcounts, *rdispls; + long* recvvals; + + /* TEST PERSONALIZED VERSION */ + s_recvs = -1; + MPIL_Set_alltoallv_crs(ALLTOALLV_CRS_PERSONALIZED); + MPIL_Alltoallv_crs(A.recv_comm.n_msgs, + A.recv_comm.size_msgs, + A.recv_comm.procs.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_LONG, + A.off_proc_columns.data(), + &n_recvs, + &s_recvs, + &src, + &recvcounts, + &rdispls, + MPI_LONG, + (void**)&recvvals, + xinfo, + xcomm); + compare_alltoallv_crs_results(n_recvs, + A.send_comm.n_msgs, + s_recvs, + A.send_comm.size_msgs, + src, + proc_counts, + recvcounts, + proc_displs, + A.send_comm.idx, + rdispls, + recvvals, + A.first_col); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); + + /* TEST NONBLOCKING VERSION */ + s_recvs = -1; + MPIL_Set_alltoallv_crs(ALLTOALLV_CRS_NONBLOCKING); + MPIL_Alltoallv_crs(A.recv_comm.n_msgs, + A.recv_comm.size_msgs, + A.recv_comm.procs.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_LONG, + A.off_proc_columns.data(), + &n_recvs, + &s_recvs, + &src, + &recvcounts, + &rdispls, + MPI_LONG, + (void**)&recvvals, + xinfo, + xcomm); + compare_alltoallv_crs_results(n_recvs, + A.send_comm.n_msgs, + s_recvs, + A.send_comm.size_msgs, + src, + proc_counts, + recvcounts, + proc_displs, + A.send_comm.idx, + rdispls, + recvvals, + A.first_col); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); + + /* TEST PERSONALIZED LOCALITY VERSION */ + s_recvs = -1; + MPIL_Set_alltoallv_crs(ALLTOALLV_CRS_PERSONALIZED_LOC); + MPIL_Alltoallv_crs(A.recv_comm.n_msgs, + A.recv_comm.size_msgs, + A.recv_comm.procs.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_LONG, + A.off_proc_columns.data(), + &n_recvs, + &s_recvs, + &src, + &recvcounts, + &rdispls, + MPI_LONG, + (void**)&recvvals, + xinfo, + xcomm); + compare_alltoallv_crs_results(n_recvs, + A.send_comm.n_msgs, + s_recvs, + A.send_comm.size_msgs, + src, + proc_counts, + recvcounts, + proc_displs, + A.send_comm.idx, + rdispls, + recvvals, + A.first_col); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); + + /* TEST PERSONALIZED LOCALITY VERSION */ + s_recvs = -1; + MPIL_Set_alltoallv_crs(ALLTOALLV_CRS_NONBLOCKING_LOC); + MPIL_Alltoallv_crs(A.recv_comm.n_msgs, + A.recv_comm.size_msgs, + A.recv_comm.procs.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_LONG, + A.off_proc_columns.data(), + &n_recvs, + &s_recvs, + &src, + &recvcounts, + &rdispls, + MPI_LONG, + (void**)&recvvals, + xinfo, + xcomm); + compare_alltoallv_crs_results(n_recvs, + A.send_comm.n_msgs, + s_recvs, + A.send_comm.size_msgs, + src, + proc_counts, + recvcounts, + proc_displs, + A.send_comm.idx, + rdispls, + recvvals, + A.first_col); + MPIL_Free(src); + MPIL_Free(recvcounts); + MPIL_Free(rdispls); + MPIL_Free(recvvals); + + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + test_matrix("../../../test_data/dwt_162.pm"); + test_matrix("../../../test_data/odepa400.pm"); + test_matrix("../../../test_data/ww_36_pmec_36.pm"); + test_matrix("../../../test_data/bcsstk01.pm"); + test_matrix("../../../test_data/west0132.pm"); + test_matrix("../../../test_data/gams10a.pm"); + test_matrix("../../../test_data/gams10am.pm"); + test_matrix("../../../test_data/D_10.pm"); + test_matrix("../../../test_data/oscil_dcop_11.pm"); + test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); + test_matrix("../../../test_data/ch5-5-b1.pm"); + test_matrix("../../../test_data/msc01050.pm"); + test_matrix("../../../test_data/SmaGri.pm"); + test_matrix("../../../test_data/radfr1.pm"); + test_matrix("../../../test_data/bibd_49_3.pm"); + test_matrix("../../../test_data/can_1054.pm"); + test_matrix("../../../test_data/can_1072.pm"); + test_matrix("../../../test_data/lp_woodw.pm"); + test_matrix("../../../test_data/lp_sctap2.pm"); + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_suitesparse_neighbor_alltoallv.cpp b/library/tests/test_suitesparse_neighbor_alltoallv.cpp new file mode 100644 index 000000000..fe1c4cb54 --- /dev/null +++ b/library/tests/test_suitesparse_neighbor_alltoallv.cpp @@ -0,0 +1,227 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "locality_aware.h" +#include "tests/par_binary_IO.hpp" +#include "tests/sparse_mat.hpp" + +void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, + std::vector& mpix_recv_vals, + int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi_recv_vals[i] != mpix_recv_vals[i]) + { + fprintf(stderr, + "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", + i, + pmpi_recv_vals[i], + mpix_recv_vals[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +void test_matrix(const char* filename) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Read suitesparse matrix + ParMat A; + int idx; + readParMatrix(filename, A); + form_comm(A); + + std::vector pmpi_recv_vals, mpix_recv_vals; + std::vector send_vals, alltoallv_send_vals; + std::vector send_indices; + + if (A.on_proc.n_cols) + { + send_vals.resize(A.on_proc.n_cols); + std::iota(send_vals.begin(), send_vals.end(), 0); + for (int i = 0; i < A.on_proc.n_cols; i++) + { + send_vals[i] += (rank * 1000); + } + } + + if (A.recv_comm.size_msgs) + { + pmpi_recv_vals.resize(A.recv_comm.size_msgs); + mpix_recv_vals.resize(A.recv_comm.size_msgs); + } + + if (A.send_comm.size_msgs) + { + alltoallv_send_vals.resize(A.send_comm.size_msgs); + send_indices.resize(A.send_comm.size_msgs); + for (int i = 0; i < A.send_comm.size_msgs; i++) + { + idx = A.send_comm.idx[i]; + alltoallv_send_vals[i] = send_vals[idx]; + send_indices[i] = A.send_comm.idx[i] + A.first_col; + } + } + + communicate(A, send_vals, mpix_recv_vals, MPI_INT); + + MPI_Comm std_comm; + MPI_Status status; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; + MPIL_Info* xinfo; + + MPIL_Info_init(&xinfo); + + int* s = A.recv_comm.procs.data(); + if (A.recv_comm.n_msgs == 0) + { + s = MPI_WEIGHTS_EMPTY; + } + int* d = A.send_comm.procs.data(); + if (A.send_comm.n_msgs == 0) + { + d = MPI_WEIGHTS_EMPTY; + } + + PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, + A.recv_comm.n_msgs, + s, + MPI_UNWEIGHTED, + A.send_comm.n_msgs, + d, + MPI_UNWEIGHTED, + MPI_INFO_NULL, + 0, + &std_comm); + + int* send_counts = A.send_comm.counts.data(); + if (A.send_comm.counts.data() == NULL) + { + send_counts = new int[1]; + } + int* recv_counts = A.recv_comm.counts.data(); + if (A.recv_comm.counts.data() == NULL) + { + recv_counts = new int[1]; + } + PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), + send_counts, + A.send_comm.ptr.data(), + MPI_INT, + pmpi_recv_vals.data(), + recv_counts, + A.recv_comm.ptr.data(), + MPI_INT, + std_comm); + if (A.send_comm.counts.data() == NULL) + { + delete[] send_counts; + } + if (A.recv_comm.counts.data() == NULL) + { + delete[] recv_counts; + } + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, + A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + MPI_UNWEIGHTED, + A.send_comm.n_msgs, + A.send_comm.procs.data(), + MPI_UNWEIGHTED, + xinfo, + 0, + &xcomm); + + MPIL_Comm_update_locality(xcomm, 4); + + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + xcomm); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + // 2. Node-Aware Communication + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Set_alltoallv_neighbor_alogorithm(NEIGHBOR_ALLTOALLV_STANDARD); + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + xcomm); + + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + // 3. MPI Advance - Optimized Communication + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Set_alltoallv_neighbor_alogorithm(NEIGHBOR_ALLTOALLV_LOCALITY); + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + xcomm); + + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + // MPIL_Topo_free(&topo); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); + PMPI_Comm_free(&std_comm); +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + test_matrix("../../../test_data/dwt_162.pm"); + test_matrix("../../../test_data/odepa400.pm"); + test_matrix("../../../test_data/ww_36_pmec_36.pm"); + test_matrix("../../../test_data/bcsstk01.pm"); + test_matrix("../../../test_data/west0132.pm"); + test_matrix("../../../test_data/gams10a.pm"); + test_matrix("../../../test_data/gams10am.pm"); + test_matrix("../../../test_data/D_10.pm"); + test_matrix("../../../test_data/oscil_dcop_11.pm"); + test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); + test_matrix("../../../test_data/ch5-5-b1.pm"); + test_matrix("../../../test_data/msc01050.pm"); + test_matrix("../../../test_data/SmaGri.pm"); + test_matrix("../../../test_data/radfr1.pm"); + test_matrix("../../../test_data/bibd_49_3.pm"); + test_matrix("../../../test_data/can_1054.pm"); + test_matrix("../../../test_data/can_1072.pm"); + test_matrix("../../../test_data/lp_sctap2.pm"); + test_matrix("../../../test_data/lp_woodw.pm"); + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_suitesparse_neighbor_alltoallv_enum.cpp b/library/tests/test_suitesparse_neighbor_alltoallv_enum.cpp new file mode 100644 index 000000000..6b26b1290 --- /dev/null +++ b/library/tests/test_suitesparse_neighbor_alltoallv_enum.cpp @@ -0,0 +1,208 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "locality_aware.h" +#include "tests/par_binary_IO.hpp" + +void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, + std::vector& mpix_recv_vals, + int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi_recv_vals[i] != mpix_recv_vals[i]) + { + fprintf(stderr, + "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", + i, + pmpi_recv_vals[i], + mpix_recv_vals[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +void test_matrix(const char* filename) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Read suitesparse matrix + ParMat A; + int idx; + readParMatrix(filename, A); + form_comm(A); + + std::vector pmpi_recv_vals, mpix_recv_vals; + std::vector send_vals, alltoallv_send_vals; + std::vector send_indices; + + if (A.on_proc.n_cols) + { + send_vals.resize(A.on_proc.n_cols); + std::iota(send_vals.begin(), send_vals.end(), 0); + for (int i = 0; i < A.on_proc.n_cols; i++) + { + send_vals[i] += (rank * 1000); + } + } + + if (A.recv_comm.size_msgs) + { + pmpi_recv_vals.resize(A.recv_comm.size_msgs); + mpix_recv_vals.resize(A.recv_comm.size_msgs); + } + + if (A.send_comm.size_msgs) + { + alltoallv_send_vals.resize(A.send_comm.size_msgs); + send_indices.resize(A.send_comm.size_msgs); + for (int i = 0; i < A.send_comm.size_msgs; i++) + { + idx = A.send_comm.idx[i]; + alltoallv_send_vals[i] = send_vals[idx]; + send_indices[i] = A.send_comm.idx[i] + A.first_col; + } + } + + communicate(A, send_vals, mpix_recv_vals, MPI_INT); + + MPI_Comm std_comm; + MPI_Status status; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; + MPIL_Info* xinfo; + + MPIL_Info_init(&xinfo); + + int* s = A.recv_comm.procs.data(); + if (A.recv_comm.n_msgs == 0) + { + s = MPI_WEIGHTS_EMPTY; + } + int* d = A.send_comm.procs.data(); + if (A.send_comm.n_msgs == 0) + { + d = MPI_WEIGHTS_EMPTY; + } + + PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, + A.recv_comm.n_msgs, + s, + MPI_UNWEIGHTED, + A.send_comm.n_msgs, + d, + MPI_UNWEIGHTED, + MPI_INFO_NULL, + 0, + &std_comm); + + int* send_counts = A.send_comm.counts.data(); + if (A.send_comm.counts.data() == NULL) + { + send_counts = new int[1]; + } + int* recv_counts = A.recv_comm.counts.data(); + if (A.recv_comm.counts.data() == NULL) + { + recv_counts = new int[1]; + } + PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), + send_counts, + A.send_comm.ptr.data(), + MPI_INT, + pmpi_recv_vals.data(), + recv_counts, + A.recv_comm.ptr.data(), + MPI_INT, + std_comm); + if (A.send_comm.counts.data() == NULL) + { + delete[] send_counts; + } + if (A.recv_comm.counts.data() == NULL) + { + delete[] recv_counts; + } + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, + A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + MPI_UNWEIGHTED, + A.send_comm.n_msgs, + A.send_comm.procs.data(), + MPI_UNWEIGHTED, + xinfo, + 0, + &xcomm); + + MPIL_Comm_update_locality(xcomm, 4); + + MPIL_Set_alltoallv_neighbor_alogorithm(NEIGHBOR_ALLTOALLV_STANDARD); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + xcomm); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Set_alltoallv_neighbor_alogorithm(NEIGHBOR_ALLTOALLV_LOCALITY); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + xcomm); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); + PMPI_Comm_free(&std_comm); +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + test_matrix("../../../test_data/dwt_162.pm"); + test_matrix("../../../test_data/odepa400.pm"); + test_matrix("../../../test_data/ww_36_pmec_36.pm"); + test_matrix("../../../test_data/bcsstk01.pm"); + test_matrix("../../../test_data/west0132.pm"); + test_matrix("../../../test_data/gams10a.pm"); + test_matrix("../../../test_data/gams10am.pm"); + test_matrix("../../../test_data/D_10.pm"); + test_matrix("../../../test_data/oscil_dcop_11.pm"); + test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); + test_matrix("../../../test_data/ch5-5-b1.pm"); + test_matrix("../../../test_data/msc01050.pm"); + test_matrix("../../../test_data/SmaGri.pm"); + test_matrix("../../../test_data/radfr1.pm"); + test_matrix("../../../test_data/bibd_49_3.pm"); + test_matrix("../../../test_data/can_1054.pm"); + test_matrix("../../../test_data/can_1072.pm"); + test_matrix("../../../test_data/lp_sctap2.pm"); + test_matrix("../../../test_data/lp_woodw.pm"); + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_suitesparse_neighbor_alltoallv_init.cpp b/library/tests/test_suitesparse_neighbor_alltoallv_init.cpp new file mode 100644 index 000000000..83c2faf4f --- /dev/null +++ b/library/tests/test_suitesparse_neighbor_alltoallv_init.cpp @@ -0,0 +1,277 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "locality_aware.h" +#include "tests/par_binary_IO.hpp" +#include "tests/sparse_mat.hpp" + +void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, + std::vector& mpix_recv_vals, + int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi_recv_vals[i] != mpix_recv_vals[i]) + { + fprintf(stderr, + "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", + i, + pmpi_recv_vals[i], + mpix_recv_vals[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +void test_matrix(const char* filename) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Read suitesparse matrix + ParMat A; + int idx; + readParMatrix(filename, A); + form_comm(A); + + std::vector pmpi_recv_vals, mpix_recv_vals; + std::vector send_vals, alltoallv_send_vals; + std::vector send_indices; + + if (A.on_proc.n_cols) + { + send_vals.resize(A.on_proc.n_cols); + std::iota(send_vals.begin(), send_vals.end(), 0); + for (int i = 0; i < A.on_proc.n_cols; i++) + { + send_vals[i] += (rank * 1000); + } + } + + if (A.recv_comm.size_msgs) + { + pmpi_recv_vals.resize(A.recv_comm.size_msgs); + mpix_recv_vals.resize(A.recv_comm.size_msgs); + } + + if (A.send_comm.size_msgs) + { + alltoallv_send_vals.resize(A.send_comm.size_msgs); + send_indices.resize(A.send_comm.size_msgs); + for (int i = 0; i < A.send_comm.size_msgs; i++) + { + idx = A.send_comm.idx[i]; + alltoallv_send_vals[i] = send_vals[idx]; + send_indices[i] = A.send_comm.idx[i] + A.first_col; + } + } + + communicate(A, send_vals, mpix_recv_vals, MPI_INT); + + MPI_Comm std_comm; + MPI_Status status; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; + MPIL_Info* xinfo; + + MPIL_Info_init(&xinfo); + + int* s = A.recv_comm.procs.data(); + if (A.recv_comm.n_msgs == 0) + { + s = MPI_WEIGHTS_EMPTY; + } + int* d = A.send_comm.procs.data(); + if (A.send_comm.n_msgs == 0) + { + d = MPI_WEIGHTS_EMPTY; + } + + PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, + A.recv_comm.n_msgs, + s, + MPI_UNWEIGHTED, + A.send_comm.n_msgs, + d, + MPI_UNWEIGHTED, + MPI_INFO_NULL, + 0, + &std_comm); + + int* send_counts = A.send_comm.counts.data(); + if (A.send_comm.counts.data() == NULL) + { + send_counts = new int[1]; + } + int* recv_counts = A.recv_comm.counts.data(); + if (A.recv_comm.counts.data() == NULL) + { + recv_counts = new int[1]; + } + PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), + send_counts, + A.send_comm.ptr.data(), + MPI_INT, + pmpi_recv_vals.data(), + recv_counts, + A.recv_comm.ptr.data(), + MPI_INT, + std_comm); + if (A.send_comm.counts.data() == NULL) + { + delete[] send_counts; + } + if (A.recv_comm.counts.data() == NULL) + { + delete[] recv_counts; + } + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, + A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + MPI_UNWEIGHTED, + A.send_comm.n_msgs, + A.send_comm.procs.data(), + MPI_UNWEIGHTED, + xinfo, + 0, + &xcomm); + + MPIL_Comm_update_locality(xcomm, 4); + + MPIL_Set_alltoallv_neighbor_alogorithm(NEIGHBOR_ALLTOALLV_STANDARD); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + xcomm); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + // 2. Node-Aware Communication + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_STANDARD); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + // 3. MPI Advance - Optimized Communication + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_LOCALITY); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + // Standard from Extended Interface + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_STANDARD); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + send_indices.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + A.off_proc_columns.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + + // Full Locality + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_LOCALITY); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + send_indices.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + A.off_proc_columns.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); + PMPI_Comm_free(&std_comm); +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + test_matrix("../../../test_data/dwt_162.pm"); + test_matrix("../../../test_data/odepa400.pm"); + test_matrix("../../../test_data/ww_36_pmec_36.pm"); + test_matrix("../../../test_data/bcsstk01.pm"); + test_matrix("../../../test_data/west0132.pm"); + test_matrix("../../../test_data/gams10a.pm"); + test_matrix("../../../test_data/gams10am.pm"); + test_matrix("../../../test_data/D_10.pm"); + test_matrix("../../../test_data/oscil_dcop_11.pm"); + test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); + test_matrix("../../../test_data/ch5-5-b1.pm"); + test_matrix("../../../test_data/msc01050.pm"); + test_matrix("../../../test_data/SmaGri.pm"); + test_matrix("../../../test_data/radfr1.pm"); + test_matrix("../../../test_data/bibd_49_3.pm"); + test_matrix("../../../test_data/can_1054.pm"); + test_matrix("../../../test_data/can_1072.pm"); + test_matrix("../../../test_data/lp_sctap2.pm"); + test_matrix("../../../test_data/lp_woodw.pm"); + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_suitesparse_neighbor_reorder.cpp b/library/tests/test_suitesparse_neighbor_reorder.cpp new file mode 100644 index 000000000..949a7b3f4 --- /dev/null +++ b/library/tests/test_suitesparse_neighbor_reorder.cpp @@ -0,0 +1,208 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "locality_aware.h" +#include "tests/par_binary_IO.hpp" + +void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, + std::vector& mpix_recv_vals, + int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi_recv_vals[i] != mpix_recv_vals[i]) + { + fprintf(stderr, + "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", + i, + pmpi_recv_vals[i], + mpix_recv_vals[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +void test_matrix(const char* filename) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Read suitesparse matrix + ParMat A; + int idx; + readParMatrix(filename, A); + form_comm(A); + + std::vector pmpi_recv_vals, mpix_recv_vals; + std::vector send_vals, alltoallv_send_vals; + std::vector send_indices; + + if (A.on_proc.n_cols) + { + send_vals.resize(A.on_proc.n_cols); + std::iota(send_vals.begin(), send_vals.end(), 0); + for (int i = 0; i < A.on_proc.n_cols; i++) + { + send_vals[i] += (rank * 1000); + } + } + + if (A.recv_comm.size_msgs) + { + pmpi_recv_vals.resize(A.recv_comm.size_msgs); + mpix_recv_vals.resize(A.recv_comm.size_msgs); + } + + if (A.send_comm.size_msgs) + { + alltoallv_send_vals.resize(A.send_comm.size_msgs); + send_indices.resize(A.send_comm.size_msgs); + for (int i = 0; i < A.send_comm.size_msgs; i++) + { + idx = A.send_comm.idx[i]; + alltoallv_send_vals[i] = send_vals[idx]; + send_indices[i] = A.send_comm.idx[i] + A.first_col; + } + } + + communicate(A, send_vals, mpix_recv_vals, MPI_INT); + + MPI_Comm std_comm; + MPI_Status status; + MPIL_Comm* neighbor_comm; + MPIL_Request* neighbor_request; + MPIL_Info* xinfo; + + MPIL_Info_init(&xinfo); + + int* s = A.recv_comm.procs.data(); + if (A.recv_comm.n_msgs == 0) + { + s = MPI_WEIGHTS_EMPTY; + } + int* d = A.send_comm.procs.data(); + if (A.send_comm.n_msgs == 0) + { + d = MPI_WEIGHTS_EMPTY; + } + + PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, + A.recv_comm.n_msgs, + s, + MPI_UNWEIGHTED, + A.send_comm.n_msgs, + d, + MPI_UNWEIGHTED, + MPI_INFO_NULL, + 0, + &std_comm); + + int* send_counts = A.send_comm.counts.data(); + if (A.send_comm.counts.data() == NULL) + { + send_counts = new int[1]; + } + int* recv_counts = A.recv_comm.counts.data(); + if (A.recv_comm.counts.data() == NULL) + { + recv_counts = new int[1]; + } + PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), + send_counts, + A.send_comm.ptr.data(), + MPI_INT, + pmpi_recv_vals.data(), + recv_counts, + A.recv_comm.ptr.data(), + MPI_INT, + std_comm); + if (A.send_comm.counts.data() == NULL) + { + delete[] send_counts; + } + if (A.recv_comm.counts.data() == NULL) + { + delete[] recv_counts; + } + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, + A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + MPI_UNWEIGHTED, + A.send_comm.n_msgs, + A.send_comm.procs.data(), + MPI_UNWEIGHTED, + xinfo, + 0, + &neighbor_comm); + + MPIL_Comm_update_locality(neighbor_comm, 4); + + // 2. Node-Aware Communication - reorder during first send/recv + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + neighbor_comm, + xinfo, + &neighbor_request); + + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Request_reorder(neighbor_request, 1); + MPIL_Start(neighbor_request); + MPIL_Wait(neighbor_request, &status); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + // Standard send/recv with reordered recvs + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Start(neighbor_request); + MPIL_Wait(neighbor_request, &status); + MPIL_Request_free(&neighbor_request); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&neighbor_comm); + PMPI_Comm_free(&std_comm); +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + test_matrix("../../../test_data/dwt_162.pm"); + test_matrix("../../../test_data/odepa400.pm"); + test_matrix("../../../test_data/ww_36_pmec_36.pm"); + test_matrix("../../../test_data/bcsstk01.pm"); + test_matrix("../../../test_data/west0132.pm"); + test_matrix("../../../test_data/gams10a.pm"); + test_matrix("../../../test_data/gams10am.pm"); + test_matrix("../../../test_data/D_10.pm"); + test_matrix("../../../test_data/oscil_dcop_11.pm"); + test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); + test_matrix("../../../test_data/ch5-5-b1.pm"); + test_matrix("../../../test_data/msc01050.pm"); + test_matrix("../../../test_data/SmaGri.pm"); + test_matrix("../../../test_data/radfr1.pm"); + test_matrix("../../../test_data/bibd_49_3.pm"); + test_matrix("../../../test_data/can_1054.pm"); + test_matrix("../../../test_data/can_1072.pm"); + test_matrix("../../../test_data/lp_sctap2.pm"); + test_matrix("../../../test_data/lp_woodw.pm"); + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp b/library/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp new file mode 100644 index 000000000..217a46658 --- /dev/null +++ b/library/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp @@ -0,0 +1,285 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "locality_aware.h" +#include "tests/par_binary_IO.hpp" +#include "tests/sparse_mat.hpp" + +void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, + std::vector& mpix_recv_vals, + int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi_recv_vals[i] != mpix_recv_vals[i]) + { + fprintf(stderr, + "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", + i, + pmpi_recv_vals[i], + mpix_recv_vals[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +void test_matrix(const char* filename) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Read suitesparse matrix + ParMat A; + int idx; + readParMatrix(filename, A); + form_comm(A); + + std::vector pmpi_recv_vals, mpix_recv_vals; + std::vector send_vals, alltoallv_send_vals; + std::vector send_indices; + + if (A.on_proc.n_cols) + { + send_vals.resize(A.on_proc.n_cols); + std::iota(send_vals.begin(), send_vals.end(), 0); + for (int i = 0; i < A.on_proc.n_cols; i++) + { + send_vals[i] += (rank * 1000); + } + } + + if (A.recv_comm.size_msgs) + { + pmpi_recv_vals.resize(A.recv_comm.size_msgs); + mpix_recv_vals.resize(A.recv_comm.size_msgs); + } + + if (A.send_comm.size_msgs) + { + alltoallv_send_vals.resize(A.send_comm.size_msgs); + send_indices.resize(A.send_comm.size_msgs); + for (int i = 0; i < A.send_comm.size_msgs; i++) + { + idx = A.send_comm.idx[i]; + alltoallv_send_vals[i] = send_vals[idx]; + send_indices[i] = A.send_comm.idx[i] + A.first_col; + } + } + + communicate(A, send_vals, mpix_recv_vals, MPI_INT); + + MPI_Comm std_comm; + MPI_Status status; + + MPIL_Request* xrequest; + MPIL_Comm* xcomm; + MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); + MPIL_Comm_update_locality(xcomm, 4); + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); + + MPIL_Topo* topo; + MPIL_Topo_init(A.recv_comm.n_msgs, + A.recv_comm.procs.data(), + A.recv_comm.counts.data(), + A.send_comm.n_msgs, + A.send_comm.procs.data(), + A.send_comm.counts.data(), + xinfo, + &topo); + + int* s = A.recv_comm.procs.data(); + if (A.recv_comm.n_msgs == 0) + { + s = MPI_WEIGHTS_EMPTY; + } + int* d = A.send_comm.procs.data(); + if (A.send_comm.n_msgs == 0) + { + d = MPI_WEIGHTS_EMPTY; + } + + PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, + A.recv_comm.n_msgs, + s, + MPI_UNWEIGHTED, + A.send_comm.n_msgs, + d, + MPI_UNWEIGHTED, + MPI_INFO_NULL, + 0, + &std_comm); + + int* send_counts = A.send_comm.counts.data(); + if (A.send_comm.counts.data() == NULL) + { + send_counts = new int[1]; + } + int* recv_counts = A.recv_comm.counts.data(); + if (A.recv_comm.counts.data() == NULL) + { + recv_counts = new int[1]; + } + PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), + send_counts, + A.send_comm.ptr.data(), + MPI_INT, + pmpi_recv_vals.data(), + recv_counts, + A.recv_comm.ptr.data(), + MPI_INT, + std_comm); + if (A.send_comm.counts.data() == NULL) + { + delete[] send_counts; + } + if (A.recv_comm.counts.data() == NULL) + { + delete[] recv_counts; + } + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Set_alltoallv_neighbor_alogorithm(NEIGHBOR_ALLTOALLV_STANDARD); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_topo(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + topo, + xcomm); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_STANDARD); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init_topo(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + topo, + xcomm, + xinfo, + &xrequest); + + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_LOCALITY); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init_topo(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + MPI_INT, + topo, + xcomm, + xinfo, + &xrequest); + + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + // 3. MPI Advance - Optimized Communication + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_STANDARD); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init_ext_topo(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + send_indices.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + A.off_proc_columns.data(), + MPI_INT, + topo, + xcomm, + xinfo, + &xrequest); + + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Set_alltoallv_neighbor_init_alogorithm(NEIGHBOR_ALLTOALLV_INIT_LOCALITY); + std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init_ext_topo(alltoallv_send_vals.data(), + A.send_comm.counts.data(), + A.send_comm.ptr.data(), + send_indices.data(), + MPI_INT, + mpix_recv_vals.data(), + A.recv_comm.counts.data(), + A.recv_comm.ptr.data(), + A.off_proc_columns.data(), + MPI_INT, + topo, + xcomm, + xinfo, + &xrequest); + + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); + + MPIL_Topo_free(&topo); + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); + PMPI_Comm_free(&std_comm); +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + test_matrix("../../../test_data/dwt_162.pm"); + test_matrix("../../../test_data/odepa400.pm"); + test_matrix("../../../test_data/ww_36_pmec_36.pm"); + test_matrix("../../../test_data/bcsstk01.pm"); + test_matrix("../../../test_data/west0132.pm"); + test_matrix("../../../test_data/gams10a.pm"); + test_matrix("../../../test_data/gams10am.pm"); + test_matrix("../../../test_data/D_10.pm"); + test_matrix("../../../test_data/oscil_dcop_11.pm"); + test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); + test_matrix("../../../test_data/ch5-5-b1.pm"); + test_matrix("../../../test_data/msc01050.pm"); + test_matrix("../../../test_data/SmaGri.pm"); + test_matrix("../../../test_data/radfr1.pm"); + test_matrix("../../../test_data/bibd_49_3.pm"); + test_matrix("../../../test_data/can_1054.pm"); + test_matrix("../../../test_data/can_1072.pm"); + test_matrix("../../../test_data/lp_sctap2.pm"); + test_matrix("../../../test_data/lp_woodw.pm"); + MPI_Finalize(); + return 0; +} // end of main() // diff --git a/library/tests/tests/compare.hpp b/library/tests/tests/compare.hpp new file mode 100644 index 000000000..eaf881c39 --- /dev/null +++ b/library/tests/tests/compare.hpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "locality_aware.h" + +void compare(int n_recvs, + int std_n_recvs, + std::vector& src, + std::vector& recvvals, + std::vector& std_recvvals) +{ + if (n_recvs != std_n_recvs) + { + fprintf(stderr, "Incorrect NRecvs! New %d, Std %d\n", n_recvs, std_n_recvs); + MPI_Abort(MPI_COMM_WORLD, -1); + } + for (int i = 0; i < n_recvs; i++) + { + if (recvvals[i] != std_recvvals[src[i]]) + { + fprintf(stderr, + "Recv %d, RecvVals New %d, Std %d\n", + i, + recvvals[i], + std_recvvals[src[i]]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +void compare(int n_recvs, + int std_n_recvs, + int s_recvs, + int std_s_recvs, + std::vector& src, + std::vector& recvcounts, + std::vector& rdispls, + std::vector& recvvals, + int first_col, + std::vector& proc_counts, + std::vector& proc_displs, + std::vector& indices) +{ + int proc; + if (n_recvs != std_n_recvs) + { + fprintf(stderr, "Incorrect NRecvs! New %d, Std %d\n", n_recvs, std_n_recvs); + MPI_Abort(MPI_COMM_WORLD, -1); + } + if (s_recvs != std_s_recvs) + { + fprintf(stderr, "Incorrect SRecvs! New %d, Std %d\n", s_recvs, std_s_recvs); + MPI_Abort(MPI_COMM_WORLD, -1); + } + for (int i = 0; i < n_recvs; i++) + { + proc = src[i]; + if (recvcounts[i] != proc_counts[proc]) + { + fprintf(stderr, + "Recv %d, Recvcounts %d, ProcCounts[%d] %d\n", + i, + recvcounts[i], + proc, + proc_counts[proc]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + for (int j = 0; j < recvcounts[i]; j++) + { + if (recvvals[rdispls[i] + j] - first_col != indices[proc_displs[proc] + j]) + { + fprintf(stderr, + "Recv %d, Position %d, Recvvals %d, Indices %d\n", + i, + j, + recvvals[rdispls[i] + j] - first_col, + indices[proc_displs[proc] + j]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } + } +} \ No newline at end of file diff --git a/src/neighborhood/tests/neighbor_data.hpp b/library/tests/tests/neighbor_data.hpp similarity index 55% rename from src/neighborhood/tests/neighbor_data.hpp rename to library/tests/tests/neighbor_data.hpp index bd5bf7fe9..043eb1891 100644 --- a/src/neighborhood/tests/neighbor_data.hpp +++ b/library/tests/tests/neighbor_data.hpp @@ -5,7 +5,7 @@ template struct MPIL_Data { int num_msgs; - int size_msgs; + int size_msgs; std::vector procs; std::vector counts; std::vector indptr; @@ -16,7 +16,9 @@ struct MPIL_Data // Form random communication template -void form_initial_communicator(int local_size, MPIL_Data* send_data, MPIL_Data* recv_data) +void form_initial_communicator(int local_size, + MPIL_Data* send_data, + MPIL_Data* recv_data) { // Get MPI Information int rank, num_procs; @@ -29,13 +31,13 @@ void form_initial_communicator(int local_size, MPIL_Data* send_data, MPIL_Dat // Declare Variables srand(49352034 + rank); - int n_sends = (rand() % max_n) + 1; // Between 1 and max_n msgs sent - int tag = 4935; + int n_sends = (rand() % max_n) + 1; // Between 1 and max_n msgs sent + int tag = 4935; int start, end, proc; int size, ctr; std::vector comm_procs(num_procs, 0); MPI_Status recv_status; - + // Create standard communication // Random send procs / data for (int i = 0; i < n_sends; i++) @@ -43,7 +45,7 @@ void form_initial_communicator(int local_size, MPIL_Data* send_data, MPIL_Dat proc = rand() % num_procs; while (proc == rank || comm_procs[proc] == 1) { - proc = rand() % num_procs; + proc = rand() % num_procs; } comm_procs[proc] = 1; } @@ -59,7 +61,7 @@ void form_initial_communicator(int local_size, MPIL_Data* send_data, MPIL_Dat send_data->indptr.resize(send_data->num_msgs + 1); send_data->requests.resize(send_data->num_msgs); - ctr = 0; + ctr = 0; send_data->indptr[0] = 0; for (int i = 0; i < send_data->num_msgs; i++) { @@ -67,17 +69,20 @@ void form_initial_communicator(int local_size, MPIL_Data* send_data, MPIL_Dat for (int j = 0; j < size; j++) { send_data->indices.push_back(ctr++); - if (ctr >= local_size) ctr = 0; + if (ctr >= local_size) + { + ctr = 0; + } } - send_data->indptr[i+1] = (U)(send_data->indices.size()); - send_data->counts[i] = (int)(send_data->indptr[i+1] - send_data->indptr[i]); + send_data->indptr[i + 1] = (U)(send_data->indices.size()); + send_data->counts[i] = (int)(send_data->indptr[i + 1] - send_data->indptr[i]); } send_data->size_msgs = send_data->indices.size(); send_data->buffer.resize(send_data->size_msgs); // Form recv_data (first gather number of messages sent to rank) - MPI_Allreduce(MPI_IN_PLACE, comm_procs.data(), num_procs, MPI_INT, - MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce( + MPI_IN_PLACE, comm_procs.data(), num_procs, MPI_INT, MPI_SUM, MPI_COMM_WORLD); recv_data->num_msgs = comm_procs[rank]; recv_data->procs.resize(recv_data->num_msgs); recv_data->counts.resize(recv_data->num_msgs); @@ -86,12 +91,17 @@ void form_initial_communicator(int local_size, MPIL_Data* send_data, MPIL_Dat for (int i = 0; i < send_data->num_msgs; i++) { - proc = send_data->procs[i]; - start = (int)send_data->indptr[i]; - end = (int)send_data->indptr[i+1]; + proc = send_data->procs[i]; + start = (int)send_data->indptr[i]; + end = (int)send_data->indptr[i + 1]; send_data->buffer[i] = end - start; - MPI_Isend(&(send_data->buffer[i]), 1, MPI_INT, proc, tag, - MPI_COMM_WORLD, &send_data->requests[i]); + MPI_Isend(&(send_data->buffer[i]), + 1, + MPI_INT, + proc, + tag, + MPI_COMM_WORLD, + &send_data->requests[i]); } recv_data->indptr[0] = 0; @@ -100,57 +110,71 @@ void form_initial_communicator(int local_size, MPIL_Data* send_data, MPIL_Dat MPI_Probe(MPI_ANY_SOURCE, tag, MPI_COMM_WORLD, &recv_status); proc = recv_status.MPI_SOURCE; MPI_Recv(&size, 1, MPI_INT, proc, tag, MPI_COMM_WORLD, &recv_status); - recv_data->procs[i] = proc; - recv_data->indptr[i+1] = recv_data->indptr[i] + (U)(size); - recv_data->counts[i] = size; + recv_data->procs[i] = proc; + recv_data->indptr[i + 1] = recv_data->indptr[i] + (U)(size); + recv_data->counts[i] = size; } recv_data->size_msgs = (int)(recv_data->indptr[recv_data->num_msgs]); recv_data->buffer.resize(recv_data->size_msgs); if (send_data->num_msgs) { - MPI_Waitall(send_data->num_msgs, send_data->requests.data(), - MPI_STATUSES_IGNORE); + MPI_Waitall(send_data->num_msgs, send_data->requests.data(), MPI_STATUSES_IGNORE); } } - template -void form_global_indices(int local_size, MPIL_Data send_data, MPIL_Data recv_data, - std::vector& global_send_idx, std::vector& global_recv_idx) +void form_global_indices(int local_size, + MPIL_Data send_data, + MPIL_Data recv_data, + std::vector& global_send_idx, + std::vector& global_recv_idx) { int rank, num_procs; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); int first = local_size * rank; - int tag = 29354; + int tag = 29354; for (int i = 0; i < send_data.num_msgs; i++) { int proc = send_data.procs[i]; - U start = send_data.indptr[i]; - U end = send_data.indptr[i+1]; + U start = send_data.indptr[i]; + U end = send_data.indptr[i + 1]; for (U j = start; j < end; j++) { - int idx = send_data.indices[j]; + int idx = send_data.indices[j]; global_send_idx[j] = first + idx; } - MPI_Isend(&(global_send_idx[start]), (int)(end - start), MPI_LONG, proc, - tag, MPI_COMM_WORLD, &(send_data.requests[i])); + MPI_Isend(&(global_send_idx[start]), + (int)(end - start), + MPI_LONG, + proc, + tag, + MPI_COMM_WORLD, + &(send_data.requests[i])); } for (int i = 0; i < recv_data.num_msgs; i++) { int proc = recv_data.procs[i]; - U start = recv_data.indptr[i]; - U end = recv_data.indptr[i+1]; - MPI_Irecv(&(global_recv_idx[start]), (int)(end - start), MPI_LONG, proc, - tag, MPI_COMM_WORLD, &(recv_data.requests[i])); + U start = recv_data.indptr[i]; + U end = recv_data.indptr[i + 1]; + MPI_Irecv(&(global_recv_idx[start]), + (int)(end - start), + MPI_LONG, + proc, + tag, + MPI_COMM_WORLD, + &(recv_data.requests[i])); + } + if (send_data.num_msgs) + { + MPI_Waitall(send_data.num_msgs, send_data.requests.data(), MPI_STATUSES_IGNORE); + } + if (recv_data.num_msgs) + { + MPI_Waitall(recv_data.num_msgs, recv_data.requests.data(), MPI_STATUSES_IGNORE); } - if (send_data.num_msgs) MPI_Waitall(send_data.num_msgs, - send_data.requests.data(), MPI_STATUSES_IGNORE); - if (recv_data.num_msgs) MPI_Waitall(recv_data.num_msgs, - recv_data.requests.data(), MPI_STATUSES_IGNORE); } - #endif diff --git a/src/tests/par_binary_IO.hpp b/library/tests/tests/par_binary_IO.hpp old mode 100755 new mode 100644 similarity index 51% rename from src/tests/par_binary_IO.hpp rename to library/tests/tests/par_binary_IO.hpp index 783d2941e..109da9f7e --- a/src/tests/par_binary_IO.hpp +++ b/library/tests/tests/par_binary_IO.hpp @@ -1,289 +1,376 @@ -#ifndef MATRIX_IO_H -#define MATRIX_IO_H - -#define PETSC_MAT_CODE 1211216 - -#include "mpi.h" -#include -#include -#include -#include -#include "sparse_mat.hpp" -#include -#include -#include -#include -#include - -#include -#include "limits.h" - -bool little_endian() -{ - int num = 1; - return (*(char *)&num == 1); -} - -template -void endian_swap(T *objp) -{ - unsigned char *memp = reinterpret_cast(objp); - std::reverse(memp, memp + sizeof(T)); -} - -template -void readParMatrix(const char* filename, ParMat& A) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - - int64_t pos; - int32_t code; - int32_t global_num_rows; - int32_t global_num_cols; - int32_t global_nnz; - int32_t idx; - int n_items_read; - double val; - - int extra; - bool is_little_endian = false; - - int ctr, size; - - int sizeof_dbl = sizeof(val); - int sizeof_int32 = sizeof(code); - - FILE* ifile = fopen(filename, "rb"); - if (fseek(ifile, 0, SEEK_SET)) printf("Error seeking beginning of file\n"); - - n_items_read = fread(&code, sizeof_int32, 1, ifile); - if (n_items_read == EOF) printf("EOF reading code\n"); - if (ferror(ifile)) printf("Error reading code\n"); - if (code != PETSC_MAT_CODE) - { - endian_swap(&code); - is_little_endian = true; - } - - - n_items_read = fread(&global_num_rows, sizeof_int32, 1, ifile); - if (n_items_read == EOF) printf("EOF reading code\n"); - if (ferror(ifile)) printf("Error reading N\n"); - n_items_read = fread(&global_num_cols, sizeof_int32, 1, ifile); - if (n_items_read == EOF) printf("EOF reading code\n"); - if (ferror(ifile)) printf("Error reading M\n"); - n_items_read = fread(&global_nnz, sizeof_int32, 1, ifile); - if (n_items_read == EOF) printf("EOF reading code\n"); - if (ferror(ifile)) printf("Error reading nnz\n"); - - if (is_little_endian) - { - endian_swap(&global_num_rows); - endian_swap(&global_num_cols); - endian_swap(&global_nnz); - } - - A.global_rows = global_num_rows; - A.global_cols = global_num_cols; - if (A.global_rows < num_procs || A.global_cols < num_procs) - { - if (A.global_rows < A.global_cols) - { - A.local_rows = 0; - extra = A.global_rows; - if (extra > rank) - { - A.local_rows = 1; - A.first_row = rank; - } - else A.first_row = extra; - - if (A.local_rows) - { - A.local_cols = A.global_cols / extra; - extra = A.global_cols % extra; - A.first_col = A.local_cols * rank; - if (extra > rank) - { - A.local_cols++; - A.first_col += rank; - } - else A.first_col += extra; - } - else - { - A.local_cols = 0; - A.first_col = A.global_cols; - } - } - else - { - A.local_cols = 0; - extra = A.global_cols; - if (extra > rank) - { - A.local_cols = 1; - A.first_col = rank; - } - else A.first_col = extra; - - if (A.local_cols) - { - A.local_rows = A.global_rows / extra; - extra = A.global_rows % extra; - A.first_row = A.local_rows * rank; - if (extra > rank) - { - A.local_rows++; - A.first_row += rank; - } - else A.first_row += extra; - } - else - { - A.local_rows = 0; - A.first_row = A.global_rows; - } - } - } - else - { - A.local_rows = A.global_rows / num_procs; - extra = A.global_rows % num_procs; - A.first_row = A.local_rows * rank; - if (extra > rank) - { - A.local_rows++; - A.first_row += rank; - } - else A.first_row += extra; - - A.local_cols = A.global_cols / num_procs; - extra = A.global_cols % num_procs; - A.first_col = A.local_cols * rank; - if (extra > rank) - { - A.local_cols++; - A.first_col += rank; - } - else A.first_col += extra; - } - - A.on_proc.n_rows = A.local_rows; - A.on_proc.n_cols = A.local_cols; - A.off_proc.n_rows = A.local_rows; - - - std::vector row_sizes(A.local_rows); - std::vector col_indices; - std::vector vals; - std::vector proc_nnz(num_procs); - long nnz = 0; - - pos = (4 + A.first_row) * sizeof_int32; - if (fseek(ifile, pos, SEEK_SET)) printf("Error seeking pos\n"); - for (int i = 0; i < A.local_rows; i++) - { - n_items_read = fread(&idx, sizeof_int32, 1, ifile); - if (n_items_read == EOF) printf("EOF reading code\n"); - if (ferror(ifile)) printf("Error reading row_size\n"); - if (is_little_endian) endian_swap(&idx); - row_sizes[i] = idx; - nnz += idx; - } - - long first_nnz = 0; - MPI_Exscan(&nnz, &first_nnz, 1, MPI_LONG, MPI_SUM, MPI_COMM_WORLD); - - col_indices.resize(nnz); - vals.resize(nnz); - - pos = (4 + A.global_rows + first_nnz) * sizeof_int32; - if (fseek(ifile, pos, SEEK_SET)) printf("Error seeking pos\n"); - for (int i = 0; i < nnz; i++) - { - n_items_read = fread(&idx, sizeof_int32, 1, ifile); - if (n_items_read == EOF) printf("EOF reading code\n"); - if (ferror(ifile)) printf("Error reading col idx\n"); - if (is_little_endian) endian_swap(&idx); - col_indices[i] = idx; - } - - pos = (4 + A.global_rows + global_nnz) * sizeof_int32 + (first_nnz * sizeof_dbl); - if (fseek(ifile, pos, SEEK_SET)) printf("Error seeking pos\n"); - for (int i = 0; i < nnz; i++) - { - n_items_read = fread(&val, sizeof_dbl, 1, ifile); - if (n_items_read == EOF) printf("EOF reading code\n"); - if (ferror(ifile)) printf("Error reading value\n"); - if (is_little_endian) endian_swap(&val); - vals[i] = val; - } - fclose(ifile); - - int last_col = A.first_col + A.local_cols - 1; - A.on_proc.rowptr.resize(A.local_rows+1); - A.off_proc.rowptr.resize(A.local_rows+1); - A.on_proc.rowptr[0] = 0; - A.off_proc.rowptr[0] = 0; - ctr = 0; - for (int i = 0; i < A.local_rows; i++) - { - size = row_sizes[i]; - for (int j = 0; j < size; j++) - { - idx = col_indices[ctr]; - val = vals[ctr++]; - if ((int)idx >= A.first_col && idx <= last_col) - { - A.on_proc.col_idx.push_back(idx - A.first_col); - A.on_proc.data.push_back(val); - } - else - { - A.off_proc.col_idx.push_back(idx); - A.off_proc.data.push_back(val); - } - } - A.on_proc.rowptr[i+1] = A.on_proc.col_idx.size(); - A.off_proc.rowptr[i+1] = A.off_proc.col_idx.size(); - } - A.on_proc.nnz = A.on_proc.col_idx.size(); - A.off_proc.nnz = A.off_proc.col_idx.size(); - - std::map orig_to_new; - //for (int i = 0; i < A.off_proc.col_idx.size(); i++) - // A.off_proc_columns.push_back(A.off_proc.col_idx[i]); - std::copy(A.off_proc.col_idx.begin(), A.off_proc.col_idx.end(), - std::back_inserter(A.off_proc_columns)); - std::sort(A.off_proc_columns.begin(), A.off_proc_columns.end()); - - int prev_col = -1; - A.off_proc_num_cols = 0; - for (std::vector::iterator it = A.off_proc_columns.begin(); - it != A.off_proc_columns.end(); ++it) - { - //if (rank == 0) printf("*it, prev_col %d, %d\n", *it, prev_col); - if (*it != prev_col) - { - orig_to_new[*it] = A.off_proc_num_cols; - A.off_proc_columns[A.off_proc_num_cols++] = *it; - prev_col = *it; - } - } - A.off_proc_columns.resize(A.off_proc_num_cols); - - for (std::vector::iterator it = A.off_proc.col_idx.begin(); - it != A.off_proc.col_idx.end(); ++it) - { - *it = orig_to_new[*it]; - } - - A.off_proc.n_cols = A.off_proc_num_cols; -} - -#endif - +#ifndef MATRIX_IO_H +#define MATRIX_IO_H + +#define PETSC_MAT_CODE 1211216 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "limits.h" +#include "sparse_mat.hpp" + +bool little_endian() +{ + int num = 1; + return (*(char*)&num == 1); +} + +template +void endian_swap(T* objp) +{ + unsigned char* memp = reinterpret_cast(objp); + std::reverse(memp, memp + sizeof(T)); +} + +template +int readParMatrix(const char* filename, ParMat& A) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + int64_t pos; + int32_t code; + int32_t global_num_rows; + int32_t global_num_cols; + int32_t global_nnz; + int32_t idx; + int n_items_read; + double val; + + int extra; + bool is_little_endian = false; + + int ctr, size; + + int sizeof_dbl = sizeof(val); + int sizeof_int32 = sizeof(code); + + FILE* ifile = fopen(filename, "rb"); + if (ifile == NULL) + { + printf("Error openning file\n"); + return 1; + } + if (fseek(ifile, 0, SEEK_SET)) + { + printf("Error seeking beginning of file\n"); + } + + n_items_read = fread(&code, sizeof_int32, 1, ifile); + if (n_items_read == EOF) + { + printf("EOF reading code\n"); + } + if (ferror(ifile)) + { + printf("Error reading code\n"); + } + if (code != PETSC_MAT_CODE) + { + endian_swap(&code); + is_little_endian = true; + } + + n_items_read = fread(&global_num_rows, sizeof_int32, 1, ifile); + if (n_items_read == EOF) + { + printf("EOF reading code\n"); + } + if (ferror(ifile)) + { + printf("Error reading N\n"); + } + n_items_read = fread(&global_num_cols, sizeof_int32, 1, ifile); + if (n_items_read == EOF) + { + printf("EOF reading code\n"); + } + if (ferror(ifile)) + { + printf("Error reading M\n"); + } + n_items_read = fread(&global_nnz, sizeof_int32, 1, ifile); + if (n_items_read == EOF) + { + printf("EOF reading code\n"); + } + if (ferror(ifile)) + { + printf("Error reading nnz\n"); + } + + if (is_little_endian) + { + endian_swap(&global_num_rows); + endian_swap(&global_num_cols); + endian_swap(&global_nnz); + } + + A.global_rows = global_num_rows; + A.global_cols = global_num_cols; + if (A.global_rows < num_procs || A.global_cols < num_procs) + { + if (A.global_rows < A.global_cols) + { + A.local_rows = 0; + extra = A.global_rows; + if (extra > rank) + { + A.local_rows = 1; + A.first_row = rank; + } + else + { + A.first_row = extra; + } + + if (A.local_rows) + { + A.local_cols = A.global_cols / extra; + extra = A.global_cols % extra; + A.first_col = A.local_cols * rank; + if (extra > rank) + { + A.local_cols++; + A.first_col += rank; + } + else + { + A.first_col += extra; + } + } + else + { + A.local_cols = 0; + A.first_col = A.global_cols; + } + } + else + { + A.local_cols = 0; + extra = A.global_cols; + if (extra > rank) + { + A.local_cols = 1; + A.first_col = rank; + } + else + { + A.first_col = extra; + } + + if (A.local_cols) + { + A.local_rows = A.global_rows / extra; + extra = A.global_rows % extra; + A.first_row = A.local_rows * rank; + if (extra > rank) + { + A.local_rows++; + A.first_row += rank; + } + else + { + A.first_row += extra; + } + } + else + { + A.local_rows = 0; + A.first_row = A.global_rows; + } + } + } + else + { + A.local_rows = A.global_rows / num_procs; + extra = A.global_rows % num_procs; + A.first_row = A.local_rows * rank; + if (extra > rank) + { + A.local_rows++; + A.first_row += rank; + } + else + { + A.first_row += extra; + } + + A.local_cols = A.global_cols / num_procs; + extra = A.global_cols % num_procs; + A.first_col = A.local_cols * rank; + if (extra > rank) + { + A.local_cols++; + A.first_col += rank; + } + else + { + A.first_col += extra; + } + } + + A.on_proc.n_rows = A.local_rows; + A.on_proc.n_cols = A.local_cols; + A.off_proc.n_rows = A.local_rows; + + std::vector row_sizes(A.local_rows); + std::vector col_indices; + std::vector vals; + std::vector proc_nnz(num_procs); + long nnz = 0; + + pos = (4 + A.first_row) * sizeof_int32; + if (fseek(ifile, pos, SEEK_SET)) + { + printf("Error seeking pos\n"); + } + for (int i = 0; i < A.local_rows; i++) + { + n_items_read = fread(&idx, sizeof_int32, 1, ifile); + if (n_items_read == EOF) + { + printf("EOF reading code\n"); + } + if (ferror(ifile)) + { + printf("Error reading row_size\n"); + } + if (is_little_endian) + { + endian_swap(&idx); + } + row_sizes[i] = idx; + nnz += idx; + } + + long first_nnz = 0; + MPI_Exscan(&nnz, &first_nnz, 1, MPI_LONG, MPI_SUM, MPI_COMM_WORLD); + + col_indices.resize(nnz); + vals.resize(nnz); + + pos = (4 + A.global_rows + first_nnz) * sizeof_int32; + if (fseek(ifile, pos, SEEK_SET)) + { + printf("Error seeking pos\n"); + } + for (int i = 0; i < nnz; i++) + { + n_items_read = fread(&idx, sizeof_int32, 1, ifile); + if (n_items_read == EOF) + { + printf("EOF reading code\n"); + } + if (ferror(ifile)) + { + printf("Error reading col idx\n"); + } + if (is_little_endian) + { + endian_swap(&idx); + } + col_indices[i] = idx; + } + + pos = (4 + A.global_rows + global_nnz) * sizeof_int32 + (first_nnz * sizeof_dbl); + if (fseek(ifile, pos, SEEK_SET)) + { + printf("Error seeking pos\n"); + } + for (int i = 0; i < nnz; i++) + { + n_items_read = fread(&val, sizeof_dbl, 1, ifile); + if (n_items_read == EOF) + { + printf("EOF reading code\n"); + } + if (ferror(ifile)) + { + printf("Error reading value\n"); + } + if (is_little_endian) + { + endian_swap(&val); + } + vals[i] = val; + } + fclose(ifile); + + int last_col = A.first_col + A.local_cols - 1; + A.on_proc.rowptr.resize(A.local_rows + 1); + A.off_proc.rowptr.resize(A.local_rows + 1); + A.on_proc.rowptr[0] = 0; + A.off_proc.rowptr[0] = 0; + ctr = 0; + for (int i = 0; i < A.local_rows; i++) + { + size = row_sizes[i]; + for (int j = 0; j < size; j++) + { + idx = col_indices[ctr]; + val = vals[ctr++]; + if ((int)idx >= A.first_col && idx <= last_col) + { + A.on_proc.col_idx.push_back(idx - A.first_col); + A.on_proc.data.push_back(val); + } + else + { + A.off_proc.col_idx.push_back(idx); + A.off_proc.data.push_back(val); + } + } + A.on_proc.rowptr[i + 1] = A.on_proc.col_idx.size(); + A.off_proc.rowptr[i + 1] = A.off_proc.col_idx.size(); + } + A.on_proc.nnz = A.on_proc.col_idx.size(); + A.off_proc.nnz = A.off_proc.col_idx.size(); + + std::map orig_to_new; + // for (int i = 0; i < A.off_proc.col_idx.size(); i++) + // A.off_proc_columns.push_back(A.off_proc.col_idx[i]); + std::copy(A.off_proc.col_idx.begin(), + A.off_proc.col_idx.end(), + std::back_inserter(A.off_proc_columns)); + std::sort(A.off_proc_columns.begin(), A.off_proc_columns.end()); + + int prev_col = -1; + A.off_proc_num_cols = 0; + for (std::vector::iterator it = A.off_proc_columns.begin(); + it != A.off_proc_columns.end(); + ++it) + { + // if (rank == 0) printf("*it, prev_col %d, %d\n", *it, prev_col); + if (*it != prev_col) + { + orig_to_new[*it] = A.off_proc_num_cols; + A.off_proc_columns[A.off_proc_num_cols++] = *it; + prev_col = *it; + } + } + A.off_proc_columns.resize(A.off_proc_num_cols); + + for (std::vector::iterator it = A.off_proc.col_idx.begin(); + it != A.off_proc.col_idx.end(); + ++it) + { + *it = orig_to_new[*it]; + } + + A.off_proc.n_cols = A.off_proc_num_cols; + + return 0; +} + +#endif \ No newline at end of file diff --git a/src/tests/sparse_mat.hpp b/library/tests/tests/sparse_mat.hpp old mode 100755 new mode 100644 similarity index 52% rename from src/tests/sparse_mat.hpp rename to library/tests/tests/sparse_mat.hpp index 5a82ba33e..0b65cfc62 --- a/src/tests/sparse_mat.hpp +++ b/library/tests/tests/sparse_mat.hpp @@ -1,337 +1,433 @@ -#ifndef MPI_SPARSE_MAT_HPP -#define MPI_SPARSE_MAT_HPP - -#include "mpi.h" -#include - -struct Mat -{ - std::vector rowptr; - std::vector col_idx; - std::vector data; - int n_rows; - int n_cols; - int nnz; -}; - - -template -struct Comm -{ - int n_msgs; - int size_msgs; - std::vector procs; - std::vector ptr; - std::vector counts; - std::vector idx; - std::vector req; -}; - -template -struct ParMat -{ - Mat on_proc; - Mat off_proc; - int global_rows; - int global_cols; - int local_rows; - int local_cols; - int first_row; - int first_col; - int off_proc_num_cols; - std::vector off_proc_columns; - Comm send_comm; - Comm recv_comm; - MPI_Comm dist_graph_comm; -}; - -template -void form_recv_comm(ParMat& A) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Gather first col for all processes into list - std::vector first_cols(num_procs+1); - MPI_Allgather(&A.first_col, 1, MPI_INT, first_cols.data(), 1, MPI_INT, MPI_COMM_WORLD); - first_cols[num_procs] = A.global_cols; - - // Map Columns to Processes - int proc = 0; - int prev_proc = -1; - for (int i = 0; i < A.off_proc_num_cols; i++) - { - int global_col = A.off_proc_columns[i]; - while (first_cols[proc+1] <= global_col) - proc++; - if (proc != prev_proc) - { - A.recv_comm.procs.push_back(proc); - A.recv_comm.ptr.push_back((U)(i)); - prev_proc = proc; - } - } - - // Set Recv Sizes - A.recv_comm.ptr.push_back((U)(A.off_proc_num_cols)); - A.recv_comm.n_msgs = A.recv_comm.procs.size(); - A.recv_comm.size_msgs = A.off_proc_num_cols; - if (A.recv_comm.n_msgs == 0) - return; - - A.recv_comm.req.resize(A.recv_comm.n_msgs); - A.recv_comm.counts.resize(A.recv_comm.n_msgs); - for (int i = 0; i < A.recv_comm.n_msgs; i++) - A.recv_comm.counts[i] = A.recv_comm.ptr[i+1] - A.recv_comm.ptr[i]; -} - -// Must Form Recv Comm before Send! -template -void form_send_comm_standard(ParMat& A) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - std::vector recv_buf; - std::vector sizes(num_procs, 0); - int proc, count, ctr; - MPI_Status recv_status; - - // Allreduce to find size of data I will receive - for (int i = 0; i < A.recv_comm.n_msgs; i++) - sizes[A.recv_comm.procs[i]] = A.recv_comm.ptr[i+1] - A.recv_comm.ptr[i]; - MPI_Allreduce(MPI_IN_PLACE, sizes.data(), num_procs, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - A.send_comm.size_msgs = sizes[rank]; - - // Send a message to every process that I will need data from - // Tell them which global indices I need from them - int msg_tag = 1234; - for (int i = 0; i < A.recv_comm.n_msgs; i++) - { - proc = A.recv_comm.procs[i]; - MPI_Isend(&(A.off_proc_columns[A.recv_comm.ptr[i]]), A.recv_comm.counts[i], MPI_LONG, proc, msg_tag, - MPI_COMM_WORLD, &(A.recv_comm.req[i])); - } - - // Wait to receive values - // until I have received fewer than the number of global indices I am waiting on - if (A.send_comm.size_msgs) - { - A.send_comm.idx.resize(A.send_comm.size_msgs); - recv_buf.resize(A.send_comm.size_msgs); - } - ctr = 0; - A.send_comm.ptr.push_back(0); - while (ctr < A.send_comm.size_msgs) - { - // Wait for a message - MPI_Probe(MPI_ANY_SOURCE, msg_tag, MPI_COMM_WORLD, &recv_status); - - // Get the source process and message size - proc = recv_status.MPI_SOURCE; - A.send_comm.procs.push_back(proc); - MPI_Get_count(&recv_status, MPI_LONG, &count); - A.send_comm.counts.push_back(count); - - // Receive the message, and add local indices to send_comm - MPI_Recv(&(recv_buf[ctr]), count, MPI_LONG, proc, msg_tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - for (int i = 0; i < count; i++) - { - A.send_comm.idx[ctr+i] = (recv_buf[ctr+i] - A.first_col); - } - ctr += count; - A.send_comm.ptr.push_back((U)(ctr)); - } - - // Set send sizes - A.send_comm.n_msgs = A.send_comm.procs.size(); - - if (A.send_comm.n_msgs) - A.send_comm.req.resize(A.send_comm.n_msgs); - - if (A.recv_comm.n_msgs) - MPI_Waitall(A.recv_comm.n_msgs, A.recv_comm.req.data(), MPI_STATUSES_IGNORE); -} - - - -template -void form_comm(ParMat& A) -{ - // Form Recv Side - form_recv_comm(A); - - // Form Send Side (Algorithm Options Here!) - form_send_comm_standard(A); - - // TODO: Make MPI_Advance Sparse Alltoallv dynamically allocated MPI buffers - // Can call it here - -} - -template -void communicate3_flip(ParMat& A, std::vector& sendbuf, std::vector& recvbuf, MPI_Datatype type) -{ - int proc; - T start, end; - int tag = 2948; - - for (int i = 0; i < A.send_comm.n_msgs; i++) - { - proc = A.send_comm.procs[i]; - start = A.send_comm.ptr[i]; - end = A.send_comm.ptr[i+1]; - MPI_Recv_init(&(sendbuf[start]), (int)(end - start), type, proc, tag, - MPI_COMM_WORLD, &(A.send_comm.req[i])); - } - - for (int i = 0; i < A.recv_comm.n_msgs; i++) - { - proc = A.recv_comm.procs[i]; - start = A.recv_comm.ptr[i]; - end = A.recv_comm.ptr[i+1]; - MPI_Send_init(&(recvbuf[start]), (int)(end - start), type, proc, tag, - MPI_COMM_WORLD, &(A.recv_comm.req[i])); - } - - if (A.send_comm.n_msgs) - MPI_Startall(A.send_comm.n_msgs, A.send_comm.req.data()); - if (A.recv_comm.n_msgs) - MPI_Startall(A.recv_comm.n_msgs, A.recv_comm.req.data()); - - if (A.send_comm.n_msgs) - { - MPI_Waitall(A.send_comm.n_msgs, A.send_comm.req.data(), MPI_STATUSES_IGNORE); - for (int i = 0; i < A.send_comm.n_msgs; i++) - MPI_Request_free(&(A.send_comm.req[i])); - } - if (A.recv_comm.n_msgs) - { - MPI_Waitall(A.recv_comm.n_msgs, A.recv_comm.req.data(), MPI_STATUSES_IGNORE); - for (int i = 0; i < A.recv_comm.n_msgs; i++) - MPI_Request_free(&(A.recv_comm.req[i])); - } -} - -template -void communicate3(ParMat& A, std::vector& sendbuf, std::vector& recvbuf, MPI_Datatype type) -{ - int proc; - T start, end; - int tag = 2948; - - for (int i = 0; i < A.recv_comm.n_msgs; i++) - { - proc = A.recv_comm.procs[i]; - start = A.recv_comm.ptr[i]; - end = A.recv_comm.ptr[i+1]; - MPI_Recv_init(&(recvbuf[start]), (int)(end - start), type, proc, tag, - MPI_COMM_WORLD, &(A.recv_comm.req[i])); - } - - for (int i = 0; i < A.send_comm.n_msgs; i++) - { - proc = A.send_comm.procs[i]; - start = A.send_comm.ptr[i]; - end = A.send_comm.ptr[i+1]; - MPI_Send_init(&(sendbuf[start]), (int)(end - start), type, proc, tag, - MPI_COMM_WORLD, &(A.send_comm.req[i])); - } - - if (A.recv_comm.n_msgs) - MPI_Startall(A.recv_comm.n_msgs, A.recv_comm.req.data()); - - if (A.send_comm.n_msgs) - MPI_Startall(A.send_comm.n_msgs, A.send_comm.req.data()); - - if (A.recv_comm.n_msgs) - { - MPI_Waitall(A.recv_comm.n_msgs, A.recv_comm.req.data(), MPI_STATUSES_IGNORE); - for (int i = 0; i < A.recv_comm.n_msgs; i++) - MPI_Request_free(&(A.recv_comm.req[i])); - } - - if (A.send_comm.n_msgs) - { - MPI_Waitall(A.send_comm.n_msgs, A.send_comm.req.data(), MPI_STATUSES_IGNORE); - for (int i = 0; i < A.send_comm.n_msgs; i++) - MPI_Request_free(&(A.send_comm.req[i])); - } - -} - -template -void communicate2(ParMat& A, std::vector& sendbuf, std::vector& recvbuf, MPI_Datatype type) -{ - int proc; - T start, end; - int tag = 2948; - - for (int i = 0; i < A.recv_comm.n_msgs; i++) - { - proc = A.recv_comm.procs[i]; - start = A.recv_comm.ptr[i]; - end = A.recv_comm.ptr[i+1]; - MPI_Irecv(&(recvbuf[start]), (int)(end - start), type, proc, tag, - MPI_COMM_WORLD, &(A.recv_comm.req[i])); - } - - for (int i = 0; i < A.send_comm.n_msgs; i++) - { - proc = A.send_comm.procs[i]; - start = A.send_comm.ptr[i]; - end = A.send_comm.ptr[i+1]; - MPI_Isend(&(sendbuf[start]), (int)(end - start), type, proc, tag, - MPI_COMM_WORLD, &(A.send_comm.req[i])); - } - - if (A.recv_comm.n_msgs) - MPI_Waitall(A.recv_comm.n_msgs, A.recv_comm.req.data(), MPI_STATUSES_IGNORE); - if (A.send_comm.n_msgs) - MPI_Waitall(A.send_comm.n_msgs, A.send_comm.req.data(), MPI_STATUSES_IGNORE); -} - -template -void communicate(ParMat& A, std::vector& data, std::vector& recvbuf, MPI_Datatype type) -{ - int proc; - T start, end; - int tag = 2948; - - for (int i = 0; i < A.recv_comm.n_msgs; i++) - { - proc = A.recv_comm.procs[i]; - start = A.recv_comm.ptr[i]; - end = A.recv_comm.ptr[i+1]; - MPI_Irecv(&(recvbuf[start]), (int)(end - start), type, proc, tag, - MPI_COMM_WORLD, &(A.recv_comm.req[i])); - } - - std::vector sendbuf; - if (A.send_comm.size_msgs) - sendbuf.resize(A.send_comm.size_msgs); - for (int i = 0; i < A.send_comm.n_msgs; i++) - { - proc = A.send_comm.procs[i]; - start = A.send_comm.ptr[i]; - end = A.send_comm.ptr[i+1]; - for (T j = start; j < end; j++) - { - sendbuf[j] = data[A.send_comm.idx[j]]; - } - MPI_Isend(&(sendbuf[start]), (int)(end - start), type, proc, tag, - MPI_COMM_WORLD, &(A.send_comm.req[i])); - } - - - if (A.send_comm.n_msgs) - MPI_Waitall(A.send_comm.n_msgs, A.send_comm.req.data(), MPI_STATUSES_IGNORE); - if (A.recv_comm.n_msgs) - MPI_Waitall(A.recv_comm.n_msgs, A.recv_comm.req.data(), MPI_STATUSES_IGNORE); -} - - - -#endif +#ifndef MPI_SPARSE_MAT_HPP +#define MPI_SPARSE_MAT_HPP + +#include + +#include + +struct Mat +{ + std::vector rowptr; + std::vector col_idx; + std::vector data; + int n_rows; + int n_cols; + int nnz; +}; + +template +struct Comm +{ + int n_msgs; + int size_msgs; + std::vector procs; + std::vector ptr; + std::vector counts; + std::vector idx; + std::vector req; +}; + +template +struct ParMat +{ + Mat on_proc; + Mat off_proc; + int global_rows; + int global_cols; + int local_rows; + int local_cols; + int first_row; + int first_col; + int off_proc_num_cols; + std::vector off_proc_columns; + Comm send_comm; + Comm recv_comm; + MPI_Comm dist_graph_comm; +}; + +template +void form_recv_comm(ParMat& A) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Gather first col for all processes into list + std::vector first_cols(num_procs + 1); + MPI_Allgather( + &A.first_col, 1, MPI_INT, first_cols.data(), 1, MPI_INT, MPI_COMM_WORLD); + first_cols[num_procs] = A.global_cols; + + // Map Columns to Processes + int proc = 0; + int prev_proc = -1; + for (int i = 0; i < A.off_proc_num_cols; i++) + { + int global_col = A.off_proc_columns[i]; + while (first_cols[proc + 1] <= global_col) + { + proc++; + } + if (proc != prev_proc) + { + A.recv_comm.procs.push_back(proc); + A.recv_comm.ptr.push_back((U)(i)); + prev_proc = proc; + } + } + + // Set Recv Sizes + A.recv_comm.ptr.push_back((U)(A.off_proc_num_cols)); + A.recv_comm.n_msgs = A.recv_comm.procs.size(); + A.recv_comm.size_msgs = A.off_proc_num_cols; + if (A.recv_comm.n_msgs == 0) + { + return; + } + + A.recv_comm.req.resize(A.recv_comm.n_msgs); + A.recv_comm.counts.resize(A.recv_comm.n_msgs); + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + A.recv_comm.counts[i] = A.recv_comm.ptr[i + 1] - A.recv_comm.ptr[i]; + } +} + +// Must Form Recv Comm before Send! +template +void form_send_comm_standard(ParMat& A) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + std::vector recv_buf; + std::vector sizes(num_procs, 0); + int proc, count, ctr; + MPI_Status recv_status; + + // Allreduce to find size of data I will receive + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + sizes[A.recv_comm.procs[i]] = A.recv_comm.ptr[i + 1] - A.recv_comm.ptr[i]; + } + MPI_Allreduce( + MPI_IN_PLACE, sizes.data(), num_procs, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + A.send_comm.size_msgs = sizes[rank]; + + // Send a message to every process that I will need data from + // Tell them which global indices I need from them + int msg_tag = 1234; + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + proc = A.recv_comm.procs[i]; + MPI_Isend(&(A.off_proc_columns[A.recv_comm.ptr[i]]), + A.recv_comm.counts[i], + MPI_LONG, + proc, + msg_tag, + MPI_COMM_WORLD, + &(A.recv_comm.req[i])); + } + + // Wait to receive values + // until I have received fewer than the number of global indices I am waiting on + if (A.send_comm.size_msgs) + { + A.send_comm.idx.resize(A.send_comm.size_msgs); + recv_buf.resize(A.send_comm.size_msgs); + } + ctr = 0; + A.send_comm.ptr.push_back(0); + while (ctr < A.send_comm.size_msgs) + { + // Wait for a message + MPI_Probe(MPI_ANY_SOURCE, msg_tag, MPI_COMM_WORLD, &recv_status); + + // Get the source process and message size + proc = recv_status.MPI_SOURCE; + A.send_comm.procs.push_back(proc); + MPI_Get_count(&recv_status, MPI_LONG, &count); + A.send_comm.counts.push_back(count); + + // Receive the message, and add local indices to send_comm + MPI_Recv(&(recv_buf[ctr]), + count, + MPI_LONG, + proc, + msg_tag, + MPI_COMM_WORLD, + MPI_STATUS_IGNORE); + for (int i = 0; i < count; i++) + { + A.send_comm.idx[ctr + i] = (recv_buf[ctr + i] - A.first_col); + } + ctr += count; + A.send_comm.ptr.push_back((U)(ctr)); + } + + // Set send sizes + A.send_comm.n_msgs = A.send_comm.procs.size(); + + if (A.send_comm.n_msgs) + { + A.send_comm.req.resize(A.send_comm.n_msgs); + } + + if (A.recv_comm.n_msgs) + { + MPI_Waitall(A.recv_comm.n_msgs, A.recv_comm.req.data(), MPI_STATUSES_IGNORE); + } +} + +template +void form_comm(ParMat& A) +{ + // Form Recv Side + form_recv_comm(A); + + // Form Send Side (Algorithm Options Here!) + form_send_comm_standard(A); + + // TODO: Make MPI_Advance Sparse Alltoallv dynamically allocated MPI buffers + // Can call it here +} + +template +void communicate3_flip(ParMat& A, + std::vector& sendbuf, + std::vector& recvbuf, + MPI_Datatype type) +{ + int proc; + T start, end; + int tag = 2948; + + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + proc = A.send_comm.procs[i]; + start = A.send_comm.ptr[i]; + end = A.send_comm.ptr[i + 1]; + MPI_Recv_init(&(sendbuf[start]), + (int)(end - start), + type, + proc, + tag, + MPI_COMM_WORLD, + &(A.send_comm.req[i])); + } + + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + proc = A.recv_comm.procs[i]; + start = A.recv_comm.ptr[i]; + end = A.recv_comm.ptr[i + 1]; + MPI_Send_init(&(recvbuf[start]), + (int)(end - start), + type, + proc, + tag, + MPI_COMM_WORLD, + &(A.recv_comm.req[i])); + } + + if (A.send_comm.n_msgs) + { + MPI_Startall(A.send_comm.n_msgs, A.send_comm.req.data()); + } + if (A.recv_comm.n_msgs) + { + MPI_Startall(A.recv_comm.n_msgs, A.recv_comm.req.data()); + } + + if (A.send_comm.n_msgs) + { + MPI_Waitall(A.send_comm.n_msgs, A.send_comm.req.data(), MPI_STATUSES_IGNORE); + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + MPI_Request_free(&(A.send_comm.req[i])); + } + } + if (A.recv_comm.n_msgs) + { + MPI_Waitall(A.recv_comm.n_msgs, A.recv_comm.req.data(), MPI_STATUSES_IGNORE); + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + MPI_Request_free(&(A.recv_comm.req[i])); + } + } +} + +template +void communicate3(ParMat& A, + std::vector& sendbuf, + std::vector& recvbuf, + MPI_Datatype type) +{ + int proc; + T start, end; + int tag = 2948; + + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + proc = A.recv_comm.procs[i]; + start = A.recv_comm.ptr[i]; + end = A.recv_comm.ptr[i + 1]; + MPI_Recv_init(&(recvbuf[start]), + (int)(end - start), + type, + proc, + tag, + MPI_COMM_WORLD, + &(A.recv_comm.req[i])); + } + + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + proc = A.send_comm.procs[i]; + start = A.send_comm.ptr[i]; + end = A.send_comm.ptr[i + 1]; + MPI_Send_init(&(sendbuf[start]), + (int)(end - start), + type, + proc, + tag, + MPI_COMM_WORLD, + &(A.send_comm.req[i])); + } + + if (A.recv_comm.n_msgs) + { + MPI_Startall(A.recv_comm.n_msgs, A.recv_comm.req.data()); + } + + if (A.send_comm.n_msgs) + { + MPI_Startall(A.send_comm.n_msgs, A.send_comm.req.data()); + } + + if (A.recv_comm.n_msgs) + { + MPI_Waitall(A.recv_comm.n_msgs, A.recv_comm.req.data(), MPI_STATUSES_IGNORE); + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + MPI_Request_free(&(A.recv_comm.req[i])); + } + } + + if (A.send_comm.n_msgs) + { + MPI_Waitall(A.send_comm.n_msgs, A.send_comm.req.data(), MPI_STATUSES_IGNORE); + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + MPI_Request_free(&(A.send_comm.req[i])); + } + } +} + +template +void communicate2(ParMat& A, + std::vector& sendbuf, + std::vector& recvbuf, + MPI_Datatype type) +{ + int proc; + T start, end; + int tag = 2948; + + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + proc = A.recv_comm.procs[i]; + start = A.recv_comm.ptr[i]; + end = A.recv_comm.ptr[i + 1]; + MPI_Irecv(&(recvbuf[start]), + (int)(end - start), + type, + proc, + tag, + MPI_COMM_WORLD, + &(A.recv_comm.req[i])); + } + + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + proc = A.send_comm.procs[i]; + start = A.send_comm.ptr[i]; + end = A.send_comm.ptr[i + 1]; + MPI_Isend(&(sendbuf[start]), + (int)(end - start), + type, + proc, + tag, + MPI_COMM_WORLD, + &(A.send_comm.req[i])); + } + + if (A.recv_comm.n_msgs) + { + MPI_Waitall(A.recv_comm.n_msgs, A.recv_comm.req.data(), MPI_STATUSES_IGNORE); + } + if (A.send_comm.n_msgs) + { + MPI_Waitall(A.send_comm.n_msgs, A.send_comm.req.data(), MPI_STATUSES_IGNORE); + } +} + +template +void communicate(ParMat& A, + std::vector& data, + std::vector& recvbuf, + MPI_Datatype type) +{ + int proc; + T start, end; + int tag = 2948; + + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + proc = A.recv_comm.procs[i]; + start = A.recv_comm.ptr[i]; + end = A.recv_comm.ptr[i + 1]; + MPI_Irecv(&(recvbuf[start]), + (int)(end - start), + type, + proc, + tag, + MPI_COMM_WORLD, + &(A.recv_comm.req[i])); + } + + std::vector sendbuf; + if (A.send_comm.size_msgs) + { + sendbuf.resize(A.send_comm.size_msgs); + } + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + proc = A.send_comm.procs[i]; + start = A.send_comm.ptr[i]; + end = A.send_comm.ptr[i + 1]; + for (T j = start; j < end; j++) + { + sendbuf[j] = data[A.send_comm.idx[j]]; + } + MPI_Isend(&(sendbuf[start]), + (int)(end - start), + type, + proc, + tag, + MPI_COMM_WORLD, + &(A.send_comm.req[i])); + } + + if (A.send_comm.n_msgs) + { + MPI_Waitall(A.send_comm.n_msgs, A.send_comm.req.data(), MPI_STATUSES_IGNORE); + } + if (A.recv_comm.n_msgs) + { + MPI_Waitall(A.recv_comm.n_msgs, A.recv_comm.req.data(), MPI_STATUSES_IGNORE); + } +} + +#endif \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 9fd6dd3fc..000000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -add_subdirectory(utils) -add_subdirectory(communicator) -add_subdirectory(persistent) -add_subdirectory(collective) -add_subdirectory(neighborhood) -if(USE_CUDA OR USE_HIP) - add_subdirectory(heterogeneous) -endif() - -if (ENABLE_UNIT_TESTS) - add_subdirectory(collective/tests) - add_subdirectory(neighborhood/tests) - if (USE_GPU) - add_subdirectory(heterogeneous/tests) - endif(USE_GPU) -endif() - diff --git a/src/collective/CMakeLists.txt b/src/collective/CMakeLists.txt deleted file mode 100644 index 34878cb6c..000000000 --- a/src/collective/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -file(GLOB collective_HEADERS CONFIGURE_DEPENDS "*.h") -file(GLOB collective_SOURCES CONFIGURE_DEPENDS "*.c") - -set(collective_HEADERS ${collective_HEADERS} CACHE INTERNAL "All headers for collective files.") -set(collective_SOURCES ${collective_SOURCES} CACHE INTERNAL "All source files for collective directory.") - diff --git a/src/collective/alltoall.c b/src/collective/alltoall.c deleted file mode 100644 index efadcd813..000000000 --- a/src/collective/alltoall.c +++ /dev/null @@ -1,984 +0,0 @@ -#include "alltoall.h" - -#include -#include - -#ifdef GPU -#include "heterogeneous/gpu_alltoall.h" -#endif - -// Default alltoall is pairwise -AlltoallMethod mpil_alltoall_implementation = ALLTOALL_PAIRWISE; - -/************************************************** - * Locality-Aware Point-to-Point Alltoall - * - Aggregates messages locally to reduce - * non-local communication - * - First redistributes on-node so that each - * process holds all data for a subset - * of other nodes - * - Then, performs inter-node communication - * during which each process exchanges - * data with their assigned subset of nodes - * - Finally, redistribute received data - * on-node so that each process holds - * the correct final data - *************************************************/ -int MPIL_Alltoall(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* mpi_comm) -{ -#ifdef GPU -#ifdef GPU_AWARE - return gpu_aware_alltoall(alltoall_pairwise, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - mpi_comm); -#endif -#endif - alltoall_ftn method; - - switch (mpil_alltoall_implementation) - { - case ALLTOALL_PAIRWISE: - method = alltoall_pairwise; - break; - case ALLTOALL_NONBLOCKING: - method = alltoall_nonblocking; - break; - case ALLTOALL_HIERARCHICAL_PAIRWISE: - method = alltoall_hierarchical_pairwise; - break; - case ALLTOALL_HIERARCHICAL_NONBLOCKING: - method = alltoall_hierarchical_nonblocking; - break; - case ALLTOALL_MULTILEADER_PAIRWISE: - method = alltoall_multileader_pairwise; - break; - case ALLTOALL_MULTILEADER_NONBLOCKING: - method = alltoall_multileader_nonblocking; - break; - case ALLTOALL_NODE_AWARE_PAIRWISE: - method = alltoall_node_aware_pairwise; - break; - case ALLTOALL_NODE_AWARE_NONBLOCKING: - method = alltoall_node_aware_nonblocking; - break; - case ALLTOALL_LOCALITY_AWARE_PAIRWISE: - method = alltoall_locality_aware_pairwise; - break; - case ALLTOALL_LOCALITY_AWARE_NONBLOCKING: - method = alltoall_locality_aware_nonblocking; - break; - case ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE: - method = alltoall_multileader_locality_pairwise; - break; - case ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING: - method = alltoall_multileader_locality_nonblocking; - break; - case ALLTOALL_PMPI: - method = alltoall_pmpi; - break; - default: - method = alltoall_pmpi; - break; - } - - return method(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, mpi_comm); -} - -int pairwise_helper(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPI_Comm comm, - int tag) -{ - int rank, num_procs; - MPI_Comm_rank(comm, &rank); - MPI_Comm_size(comm, &num_procs); - - int send_proc, recv_proc; - int send_pos, recv_pos; - MPI_Status status; - - char* recv_buffer = (char*)recvbuf; - char* send_buffer = (char*)sendbuf; - - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - // Send to rank + i - // Recv from rank - i - for (int i = 0; i < num_procs; i++) - { - send_proc = rank + i; - if (send_proc >= num_procs) - { - send_proc -= num_procs; - } - recv_proc = rank - i; - if (recv_proc < 0) - { - recv_proc += num_procs; - } - send_pos = send_proc * sendcount * send_size; - recv_pos = recv_proc * recvcount * recv_size; - - MPI_Sendrecv(send_buffer + send_pos, - sendcount, - sendtype, - send_proc, - tag, - recv_buffer + recv_pos, - recvcount, - recvtype, - recv_proc, - tag, - comm, - &status); - } - return MPI_SUCCESS; -} - -int alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - int tag; - MPIL_Comm_tag(comm, &tag); - - return pairwise_helper(sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm->global_comm, - tag); -} - -int nonblocking_helper(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPI_Comm comm, - int tag) -{ - int rank, num_procs; - MPI_Comm_rank(comm, &rank); - MPI_Comm_size(comm, &num_procs); - - int send_proc, recv_proc; - int send_pos, recv_pos; - - char* recv_buffer = (char*)recvbuf; - char* send_buffer = (char*)sendbuf; - - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - MPI_Request* requests = (MPI_Request*)malloc(2 * num_procs * sizeof(MPI_Request)); - - // Send to rank + i - // Recv from rank - i - for (int i = 0; i < num_procs; i++) - { - send_proc = rank + i; - if (send_proc >= num_procs) - { - send_proc -= num_procs; - } - recv_proc = rank - i; - if (recv_proc < 0) - { - recv_proc += num_procs; - } - send_pos = send_proc * sendcount * send_size; - recv_pos = recv_proc * recvcount * recv_size; - - MPI_Isend(send_buffer + send_pos, - sendcount, - sendtype, - send_proc, - tag, - comm, - &(requests[i])); - MPI_Irecv(recv_buffer + recv_pos, - recvcount, - recvtype, - recv_proc, - tag, - comm, - &(requests[num_procs + i])); - } - - MPI_Waitall(2 * num_procs, requests, MPI_STATUSES_IGNORE); - - free(requests); - return MPI_SUCCESS; -} - -int alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - int tag; - MPIL_Comm_tag(comm, &tag); - - return nonblocking_helper(sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm->global_comm, - tag); -} - -int alltoall_multileader(alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm, - int n_leaders) -{ - int rank, num_procs; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - int tag; - MPIL_Comm_tag(comm, &tag); - - if (comm->local_comm == MPI_COMM_NULL) - { - MPIL_Comm_topo_init(comm); - } - - int ppn; - MPI_Comm_size(comm->local_comm, &ppn); - - MPI_Comm local_comm = comm->local_comm; - MPI_Comm group_comm = comm->group_comm; - - if (n_leaders > 1) - { - if (ppn < n_leaders) - { - n_leaders = ppn; - } - int procs_per_leader = ppn / n_leaders; - - // If leader comm exists but with wrong number of leaders per node, - // free the stale communicator - if (comm->leader_comm != MPI_COMM_NULL) - { - int ppl; - MPI_Comm_size(comm->leader_comm, &ppl); - if (ppl != procs_per_leader) - { - MPI_Comm_free(&comm->leader_comm); - } - } - - // If leader comm does not exist, create it - if (comm->leader_comm == MPI_COMM_NULL) - { - MPIL_Comm_leader_init(comm, procs_per_leader); - } - - local_comm = comm->leader_comm; - group_comm = comm->leader_group_comm; - } - - char* recv_buffer = (char*)recvbuf; - char* send_buffer = (char*)sendbuf; - - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - int local_rank, ppl; - MPI_Comm_rank(local_comm, &local_rank); - MPI_Comm_size(local_comm, &ppl); - - // TODO: currently assuming full nodes, even ppn per node - // this is common, so fair assumption for now - // likely need to fix before using in something like Trilinos - int n_nodes = num_procs / ppl; - - char* local_send_buffer = NULL; - char* local_recv_buffer = NULL; - - if (local_rank == 0) - { - local_send_buffer = (char*)malloc(ppl * num_procs * sendcount * send_size); - local_recv_buffer = (char*)malloc(ppl * num_procs * recvcount * recv_size); - } - else - { - local_send_buffer = (char*)malloc(sizeof(char)); - local_recv_buffer = (char*)malloc(sizeof(char)); - } - - // 1. Gather locally - MPI_Gather(send_buffer, - sendcount * num_procs, - sendtype, - local_recv_buffer, - sendcount * num_procs, - sendtype, - 0, - local_comm); - - // 2. Re-pack for sends - // Assumes SMP ordering - // TODO: allow for other orderings - int ctr; - - if (local_rank == 0) - { - ctr = 0; - for (int dest_node = 0; dest_node < n_nodes; dest_node++) - { - int dest_node_start = dest_node * ppl * sendcount * send_size; - for (int origin_proc = 0; origin_proc < ppl; origin_proc++) - { - int origin_proc_start = origin_proc * num_procs * sendcount * send_size; - memcpy(&(local_send_buffer[ctr]), - &(local_recv_buffer[origin_proc_start + dest_node_start]), - ppl * sendcount * send_size); - ctr += ppl * sendcount * send_size; - } - } - - // 3. MPI_Alltoall between leaders - f(local_send_buffer, - ppl * ppl * sendcount, - sendtype, - local_recv_buffer, - ppl * ppl * recvcount, - recvtype, - group_comm, - tag); - - // 4. Re-pack for local scatter - ctr = 0; - for (int dest_proc = 0; dest_proc < ppl; dest_proc++) - { - int dest_proc_start = dest_proc * recvcount * recv_size; - for (int orig_proc = 0; orig_proc < num_procs; orig_proc++) - { - int orig_proc_start = orig_proc * ppl * recvcount * recv_size; - memcpy(&(local_send_buffer[ctr]), - &(local_recv_buffer[orig_proc_start + dest_proc_start]), - recvcount * recv_size); - ctr += recvcount * recv_size; - } - } - } - - // 5. Scatter - MPI_Scatter(local_send_buffer, - recvcount * num_procs, - recvtype, - recv_buffer, - recvcount * num_procs, - recvtype, - 0, - local_comm); - - free(local_send_buffer); - free(local_recv_buffer); - - return MPI_SUCCESS; -} - -int alltoall_hierarchical(alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_multileader( - f, sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm, 1); -} - -int alltoall_hierarchical_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_hierarchical(pairwise_helper, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm); -} - -int alltoall_hierarchical_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_hierarchical(nonblocking_helper, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm); -} - -int alltoall_multileader_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_multileader(pairwise_helper, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm, - 4); -} - -int alltoall_multileader_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_multileader(nonblocking_helper, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm, - 4); -} - -int alltoall_locality_aware_helper(alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm, - int groups_per_node, - MPI_Comm local_comm, - MPI_Comm group_comm, - int tag) -{ - int rank, num_procs; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - int ppg; - MPI_Comm_size(local_comm, &ppg); - - char* recv_buffer = (char*)recvbuf; - - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - int n_groups = num_procs / ppg; - - char* tmpbuf = (char*)malloc(num_procs * sendcount * send_size); - - // 1. Alltoall between group_comms (all data for any process on node) - f(sendbuf, - ppg * sendcount, - sendtype, - tmpbuf, - ppg * recvcount, - recvtype, - group_comm, - tag); - - // 2. Re-pack - int ctr = 0; - for (int dest_proc = 0; dest_proc < ppg; dest_proc++) - { - int offset = dest_proc * recvcount * recv_size; - for (int origin = 0; origin < n_groups; origin++) - { - int node_offset = origin * ppg * recvcount * recv_size; - memcpy(&(recv_buffer[ctr]), - &(tmpbuf[node_offset + offset]), - recvcount * recv_size); - ctr += recvcount * recv_size; - } - } - - // 3. Local alltoall - f(recvbuf, - n_groups * recvcount, - recvtype, - tmpbuf, - n_groups * recvcount, - recvtype, - local_comm, - tag); - - // 4. Re-order - ctr = 0; - for (int node = 0; node < n_groups; node++) - { - int node_offset = node * recvcount * recv_size; - for (int dest = 0; dest < ppg; dest++) - { - int dest_offset = dest * n_groups * recvcount * recv_size; - memcpy(&(recv_buffer[ctr]), - &(tmpbuf[node_offset + dest_offset]), - recvcount * recv_size); - ctr += recvcount * recv_size; - } - } - - free(tmpbuf); - return MPI_SUCCESS; -} - -int alltoall_locality_aware(alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm, - int groups_per_node) -{ - int rank, num_procs; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - int tag; - MPIL_Comm_tag(comm, &tag); - - if (comm->local_comm == MPI_COMM_NULL) - { - MPIL_Comm_topo_init(comm); - } - - int ppn; - MPI_Comm_size(comm->local_comm, &ppn); - - MPI_Comm local_comm = comm->local_comm; - MPI_Comm group_comm = comm->group_comm; - - if (groups_per_node > 1) - { - if (ppn < groups_per_node) - { - groups_per_node = ppn; - } - int procs_per_group = ppn / groups_per_node; - - if (comm->leader_comm != MPI_COMM_NULL) - { - int ppg; - MPI_Comm_size(comm->leader_comm, &ppg); - if (ppg != procs_per_group) - { - MPI_Comm_free(&(comm->leader_comm)); - } - } - - if (comm->leader_comm == MPI_COMM_NULL) - { - MPIL_Comm_leader_init(comm, procs_per_group); - } - - local_comm = comm->leader_comm; - group_comm = comm->leader_group_comm; - } - - return alltoall_locality_aware_helper(f, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm, - groups_per_node, - local_comm, - group_comm, - tag); -} - -int alltoall_node_aware(alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_locality_aware( - f, sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm, 1); -} - -int alltoall_node_aware_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_node_aware(pairwise_helper, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm); -} - -int alltoall_node_aware_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_node_aware(nonblocking_helper, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm); -} - -int alltoall_locality_aware_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_locality_aware(pairwise_helper, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm, - 4); -} - -int alltoall_locality_aware_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_locality_aware(nonblocking_helper, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm, - 4); -} - -int alltoall_multileader_locality(alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - int rank, num_procs; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - int tag; - MPIL_Comm_tag(comm, &tag); - - if (comm->local_comm == MPI_COMM_NULL) - { - MPIL_Comm_topo_init(comm); - } - - int local_rank, ppn; - MPI_Comm_rank(comm->local_comm, &local_rank); - MPI_Comm_size(comm->local_comm, &ppn); - - if (comm->leader_comm == MPI_COMM_NULL) - { - int num_leaders_per_node = 4; - if (ppn < num_leaders_per_node) - { - num_leaders_per_node = ppn; - } - MPIL_Comm_leader_init(comm, ppn / num_leaders_per_node); - } - - int procs_per_leader, leader_rank; - MPI_Comm_rank(comm->leader_comm, &leader_rank); - MPI_Comm_size(comm->leader_comm, &procs_per_leader); - - char* recv_buffer = (char*)recvbuf; - char* send_buffer = (char*)sendbuf; - - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - // TODO: currently assuming full nodes, even procs_per_leader per node - // this is common, so fair assumption for now - // likely need to fix before using in something like Trilinos - int n_nodes = num_procs / ppn; - int n_leaders = num_procs / procs_per_leader; - - int leaders_per_node; - MPI_Comm_size(comm->leader_local_comm, &leaders_per_node); - - char* local_send_buffer = NULL; - char* local_recv_buffer = NULL; - if (leader_rank == 0) - { - local_send_buffer = - (char*)malloc(procs_per_leader * num_procs * sendcount * send_size); - local_recv_buffer = - (char*)malloc(procs_per_leader * num_procs * recvcount * recv_size); - } - else - { - local_send_buffer = (char*)malloc(sizeof(char)); - local_recv_buffer = (char*)malloc(sizeof(char)); - } - // 1. Gather locally - MPI_Gather(send_buffer, - sendcount * num_procs, - sendtype, - local_recv_buffer, - sendcount * num_procs, - sendtype, - 0, - comm->leader_comm); - - // 2. Re-pack for sends - // Assumes SMP ordering - // TODO: allow for other orderings - int ctr; - - if (leader_rank == 0) - { - /* - alltoall_locality_aware_helper(f, sendbuf, procs_per_leader*sendcount, - sendtype, recvbuf, procs_per_leader*recvcount, recvtype, comm, groups_per_node, - comm->leader_local_comm, comm->group_comm); - */ - - ctr = 0; - for (int dest_node = 0; dest_node < n_leaders; dest_node++) - { - int dest_node_start = dest_node * procs_per_leader * sendcount * send_size; - for (int origin_proc = 0; origin_proc < procs_per_leader; origin_proc++) - { - int origin_proc_start = origin_proc * num_procs * sendcount * send_size; - memcpy(&(local_send_buffer[ctr]), - &(local_recv_buffer[origin_proc_start + dest_node_start]), - procs_per_leader * sendcount * send_size); - ctr += procs_per_leader * sendcount * send_size; - } - } - - // 3. MPI_Alltoall between nodes - f(local_send_buffer, - ppn * procs_per_leader * sendcount, - sendtype, - local_recv_buffer, - ppn * procs_per_leader * recvcount, - recvtype, - comm->group_comm, - tag); - - // Re-Pack for exchange between local leaders - ctr = 0; - for (int local_leader = 0; local_leader < leaders_per_node; local_leader++) - { - int leader_start = local_leader * procs_per_leader * procs_per_leader * - sendcount * send_size; - for (int dest_node = 0; dest_node < n_nodes; dest_node++) - { - int dest_node_start = - dest_node * ppn * procs_per_leader * sendcount * send_size; - memcpy(&(local_send_buffer[ctr]), - &(local_recv_buffer[dest_node_start + leader_start]), - procs_per_leader * procs_per_leader * sendcount * send_size); - ctr += procs_per_leader * procs_per_leader * sendcount * send_size; - } - } - - f(local_send_buffer, - n_nodes * procs_per_leader * procs_per_leader * sendcount, - sendtype, - local_recv_buffer, - n_nodes * procs_per_leader * procs_per_leader * recvcount, - recvtype, - comm->leader_local_comm, - tag); - - ctr = 0; - for (int dest_proc = 0; dest_proc < procs_per_leader; dest_proc++) - { - int dest_proc_start = dest_proc * recvcount * recv_size; - - for (int orig_node = 0; orig_node < n_nodes; orig_node++) - { - int orig_node_start = orig_node * procs_per_leader * procs_per_leader * - recvcount * recv_size; - - for (int orig_leader = 0; orig_leader < leaders_per_node; orig_leader++) - { - int orig_leader_start = orig_leader * n_nodes * procs_per_leader * - procs_per_leader * recvcount * recv_size; - for (int orig_proc = 0; orig_proc < procs_per_leader; orig_proc++) - { - int orig_proc_start = - orig_proc * procs_per_leader * recvcount * recv_size; - int idx = orig_node_start + orig_leader_start + orig_proc_start + - dest_proc_start; - memcpy(&(local_send_buffer[ctr]), - &(local_recv_buffer[idx]), - recvcount * recv_size); - ctr += recvcount * recv_size; - } - } - } - } - } - - // 5. Scatter - MPI_Scatter(local_send_buffer, - recvcount * num_procs, - recvtype, - recv_buffer, - recvcount * num_procs, - recvtype, - 0, - comm->leader_comm); - - free(local_send_buffer); - free(local_recv_buffer); - - return MPI_SUCCESS; -} - -int alltoall_multileader_locality_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_multileader_locality(pairwise_helper, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm); -} - -int alltoall_multileader_locality_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return alltoall_multileader_locality(nonblocking_helper, - sendbuf, - sendcount, - sendtype, - recvbuf, - recvcount, - recvtype, - comm); -} - -// Calls underlying MPI implementation -int alltoall_pmpi(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return PMPI_Alltoall( - sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm->global_comm); -} diff --git a/src/collective/alltoallv.c b/src/collective/alltoallv.c deleted file mode 100644 index 086874f6e..000000000 --- a/src/collective/alltoallv.c +++ /dev/null @@ -1,446 +0,0 @@ -#include "alltoallv.h" - -#include -#include - -#ifdef GPU -#include "heterogeneous/gpu_alltoallv.h" -#endif - -// Default alltoallv is pairwise -AlltoallvMethod mpil_alltoallv_implementation = ALLTOALLV_PAIRWISE; - -/************************************************** - * Locality-Aware Point-to-Point Alltoallv - * Same as PMPI_Alltoall (no load balancing) - * - Aggregates messages locally to reduce - * non-local communciation - * - First redistributes on-node so that each - * process holds all data for a subset - * of other nodes - * - Then, performs inter-node communication - * during which each process exchanges - * data with their assigned subset of nodes - * - Finally, redistribute received data - * on-node so that each process holds - * the correct final data - * - To be used when sizes are relatively balanced - * - For load balacing, use persistent version - * - Load balacing is too expensive for - * non-persistent Alltoallv - *************************************************/ -int MPIL_Alltoallv(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* mpi_comm) -{ -#ifdef GPU -#ifdef GPU_AWARE - return gpu_aware_alltoallv_pairwise(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - mpi_comm); -#endif -#endif - alltoallv_ftn method; - - switch (mpil_alltoallv_implementation) - { - case ALLTOALLV_PAIRWISE: - method = alltoallv_pairwise; - break; - case ALLTOALLV_NONBLOCKING: - method = alltoallv_nonblocking; - break; - case ALLTOALLV_BATCH: - method = alltoallv_batch; - break; - case ALLTOALLV_BATCH_ASYNC: - method = alltoallv_batch_async; - break; - case ALLTOALLV_PMPI: - method = alltoallv_pmpi; - break; - default: - method = alltoallv_pmpi; - break; - } - - return method(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - mpi_comm); -} - -int alltoallv_pairwise(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - int rank, num_procs; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - int tag; - MPIL_Comm_tag(comm, &tag); - - int send_proc, recv_proc; - int send_pos, recv_pos; - MPI_Status status; - - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - char* send_buffer = (char*)sendbuf; - char* recv_buffer = (char*)recvbuf; - - // Send to rank + i - // Recv from rank - i - for (int i = 0; i < num_procs; i++) - { - send_proc = rank + i; - if (send_proc >= num_procs) - { - send_proc -= num_procs; - } - recv_proc = rank - i; - if (recv_proc < 0) - { - recv_proc += num_procs; - } - - send_pos = sdispls[send_proc] * send_size; - recv_pos = rdispls[recv_proc] * recv_size; - - MPI_Sendrecv(send_buffer + send_pos, - sendcounts[send_proc], - sendtype, - send_proc, - tag, - recv_buffer + recv_pos, - recvcounts[recv_proc], - recvtype, - recv_proc, - tag, - comm->global_comm, - &status); - } - - return 0; -} - -int alltoallv_nonblocking(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - int rank, num_procs; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - if (num_procs <= 1) - { - alltoallv_pairwise(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); - } - - int tag; - MPIL_Comm_tag(comm, &tag); - - int send_proc, recv_proc; - int send_pos, recv_pos; - - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - MPI_Request* requests = (MPI_Request*)malloc(2 * num_procs * sizeof(MPI_Request)); - - char* send_buffer = (char*)sendbuf; - char* recv_buffer = (char*)recvbuf; - - // For each step i - // exchange among procs stride (i+1) apart - for (int i = 0; i < num_procs; i++) - { - send_proc = rank + i; - if (send_proc >= num_procs) - { - send_proc -= num_procs; - } - recv_proc = rank - i; - if (recv_proc < 0) - { - recv_proc += num_procs; - } - - send_pos = sdispls[send_proc] * send_size; - recv_pos = rdispls[recv_proc] * recv_size; - - MPI_Isend(send_buffer + send_pos, - sendcounts[send_proc], - sendtype, - send_proc, - tag, - comm->global_comm, - &(requests[i])); - MPI_Irecv(recv_buffer + recv_pos, - recvcounts[recv_proc], - recvtype, - recv_proc, - tag, - comm->global_comm, - &(requests[num_procs + i])); - } - - MPI_Waitall(2 * num_procs, requests, MPI_STATUSES_IGNORE); - - free(requests); - - return 0; -} - -int alltoallv_batch(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - int rank, num_procs; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - // Tuning Parameter : number of non-blocking messages between waits - int nb_stride = 5; - if (nb_stride >= num_procs) - { - alltoallv_nonblocking(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); - } - - int tag; - MPIL_Comm_tag(comm, &tag); - - int ctr; - int send_proc, recv_proc; - int send_pos, recv_pos; - - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - MPI_Request* requests = (MPI_Request*)malloc(2 * nb_stride * sizeof(MPI_Request)); - - char* send_buffer = (char*)sendbuf; - char* recv_buffer = (char*)recvbuf; - - // For each step i - // exchange among procs stride (i+1) apart - ctr = 0; - for (int i = 0; i < num_procs; i++) - { - send_proc = rank + i; - if (send_proc >= num_procs) - { - send_proc -= num_procs; - } - recv_proc = rank - i; - if (recv_proc < 0) - { - recv_proc += num_procs; - } - - send_pos = sdispls[send_proc] * send_size; - recv_pos = rdispls[recv_proc] * recv_size; - - MPI_Isend(send_buffer + send_pos, - sendcounts[send_proc], - sendtype, - send_proc, - tag, - comm->global_comm, - &(requests[ctr++])); - MPI_Irecv(recv_buffer + recv_pos, - recvcounts[recv_proc], - recvtype, - recv_proc, - tag, - comm->global_comm, - &(requests[ctr++])); - - if ((i + 1) % nb_stride == 0) - { - MPI_Waitall(2 * nb_stride, requests, MPI_STATUSES_IGNORE); - ctr = 0; - } - } - - if (ctr) - { - MPI_Waitall(ctr, requests, MPI_STATUSES_IGNORE); - } - - free(requests); - - return 0; -} - -int alltoallv_batch_async(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - int rank, num_procs; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - // Tuning Parameter : number of non-blocking messages between waits - int nb_stride = 5; - if (nb_stride >= num_procs) - { - return alltoallv_nonblocking(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); - } - - int tag; - MPIL_Comm_tag(comm, &tag); - - int send_proc, recv_proc; - int send_pos, recv_pos; - - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - MPI_Request* requests = (MPI_Request*)malloc(2 * nb_stride * sizeof(MPI_Request)); - - char* send_buffer = (char*)sendbuf; - char* recv_buffer = (char*)recvbuf; - - // For each step i - // exchange among procs stride (i+1) apart - int send_idx = 0; - int recv_idx = 0; - for (int i = 0; i < num_procs; i++) - { - send_proc = rank + i; - if (send_proc >= num_procs) - { - send_proc -= num_procs; - } - recv_proc = rank - i; - if (recv_proc < 0) - { - recv_proc += num_procs; - } - - send_pos = sdispls[send_proc] * send_size; - recv_pos = rdispls[recv_proc] * recv_size; - - MPI_Isend(send_buffer + send_pos, - sendcounts[send_proc], - sendtype, - send_proc, - tag, - comm->global_comm, - &(requests[send_idx++])); - MPI_Irecv(recv_buffer + recv_pos, - recvcounts[recv_proc], - recvtype, - recv_proc, - tag, - comm->global_comm, - &(requests[nb_stride + recv_idx++])); - - if ((i + 1) >= nb_stride) - { - MPI_Waitany(nb_stride, requests, &send_idx, MPI_STATUSES_IGNORE); - MPI_Waitany( - nb_stride, &(requests[nb_stride]), &recv_idx, MPI_STATUSES_IGNORE); - } - } - - MPI_Waitall(2 * nb_stride, requests, MPI_STATUSES_IGNORE); - - free(requests); - - return 0; -} - -// Calls underlying MPI implementation -int alltoallv_pmpi(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - return PMPI_Alltoallv(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm->global_comm); -} diff --git a/src/collective/collective.h b/src/collective/collective.h deleted file mode 100644 index 61692fd43..000000000 --- a/src/collective/collective.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef MPI_ADVANCE_COLLECTIVES_H -#define MPI_ADVANCE_COLLECTIVES_H - -#include -#include -#include -// #include -#include "alltoall.h" -#include "alltoallv.h" -#include "utils/utils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int MPIL_Alltoall(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm); - -int MPI_Alltoallv(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPI_Comm comm); -int MPIL_Alltoallv(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* comm); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/collective/tests/CMakeLists.txt b/src/collective/tests/CMakeLists.txt deleted file mode 100644 index 308faa42d..000000000 --- a/src/collective/tests/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -## Grab all .cpp files in test directory -file(GLOB CPP_SOURCES CONFIGURE_DEPENDS "*.cpp") - -## All should be compiled appropriately (cxx vs nvcc vs hipcc) -set_source_files_properties(${CPP_SOURCES} PROPERTIES LANGUAGE ${locality_aware_LANG}) - -## Go through each CPP file -foreach(src ${CPP_SOURCES}) - - ## Grab the name without full path or extension - get_filename_component(exec_name ${src} NAME_WE) - - ## Create executable - add_executable(${exec_name} ${src}) - - # Link with MPI - target_link_libraries(${exec_name} locality_aware ${MPI_LIBRARIES}) - - # Add to CTEST - add_test(NAME ${exec_name}_Test COMMAND ${MPIRUN} -n 16 ./${exec_name}) -endforeach() - - diff --git a/src/collective/tests/test_alltoall.cpp b/src/collective/tests/test_alltoall.cpp deleted file mode 100644 index 9b47ab38f..000000000 --- a/src/collective/tests/test_alltoall.cpp +++ /dev/null @@ -1,216 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include - -void compare_alltoall_results(std::vector& pmpi, std::vector& mpil, int s) -{ - int num_procs; - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - for (int j = 0; j < s*num_procs; j++) - { - if (pmpi[j] != mpil[j]) - { - fprintf(stderr, "MPIL Alltoall != PMPI, position %d, pmpi %d, mpil %d\n", - j, pmpi[j], mpil[j]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Test Integer Alltoall - int max_i = 10; - int max_s = pow(2, max_i); - srand(time(NULL)); - std::vector local_data(max_s*num_procs); - - std::vector pmpi_alltoall(max_s*num_procs); - std::vector mpil_alltoall(max_s*num_procs); - - MPIL_Comm* locality_comm; - MPIL_Comm_init(&locality_comm, MPI_COMM_WORLD); - update_locality(locality_comm, 4); - - for (int i = 0; i < max_i; i++) - { - int s = pow(2, i); - - // Will only be clean for up to double digit process counts - for (int j = 0; j < num_procs; j++) - for (int k = 0; k < s; k++) - local_data[j*s + k] = rank*10000 + j*100 + k; - - // Standard Alltoall - PMPI_Alltoall(local_data.data(), - s, - MPI_INT, - pmpi_alltoall.data(), - s, - MPI_INT, - MPI_COMM_WORLD); - - // Locality-Aware Pairwise Alltoall - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - // Test Standard Pairwise - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - alltoall_pairwise(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - // Test Standard Nonblocking - alltoall_nonblocking(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - // Test Hierarchical + Pairwise - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - alltoall_hierarchical_pairwise(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - // Test Hierarchical + Nonblocking - alltoall_hierarchical_nonblocking(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - // Test Multileader + Pairwise - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - alltoall_multileader_pairwise(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - // Test Multileader + Nonblocking - alltoall_multileader_nonblocking(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - // Test Node Aware + Pairwise - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - alltoall_node_aware_pairwise(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - // Test Node Aware + Nonblocking - alltoall_node_aware_nonblocking(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - // Test Locality Aware + Pairwise - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - alltoall_locality_aware_pairwise(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - // Test Locality Aware + Nonblocking - alltoall_locality_aware_nonblocking(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - // Test Multileader + Locality Aware + Pairwise - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - alltoall_multileader_locality_pairwise(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - // Test Multileader + Locality Aware + Nonblocking - alltoall_multileader_locality_nonblocking(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - } - - MPIL_Comm_free(&locality_comm); - - MPI_Finalize(); - return 0; -} // end of main() // - - diff --git a/src/collective/tests/test_alltoall_enum.cpp b/src/collective/tests/test_alltoall_enum.cpp deleted file mode 100644 index 7822a5c15..000000000 --- a/src/collective/tests/test_alltoall_enum.cpp +++ /dev/null @@ -1,230 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include - -void compare_alltoall_results(std::vector& pmpi, std::vector& mpil, int s) -{ - int num_procs; - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - for (int j = 0; j < s*num_procs; j++) - { - if (pmpi[j] != mpil[j]) - { - fprintf(stderr, "MPIL Alltoall != PMPI, position %d, pmpi %d, mpil %d\n", - j, pmpi[j], mpil[j]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Test Integer Alltoall - int max_i = 10; - int max_s = pow(2, max_i); - srand(time(NULL)); - std::vector local_data(max_s*num_procs); - - std::vector pmpi_alltoall(max_s*num_procs); - std::vector mpil_alltoall(max_s*num_procs); - - MPIL_Comm* locality_comm; - MPIL_Comm_init(&locality_comm, MPI_COMM_WORLD); - update_locality(locality_comm, 4); - - for (int i = 0; i < max_i; i++) - { - int s = pow(2, i); - - // Will only be clean for up to double digit process counts - for (int j = 0; j < num_procs; j++) - for (int k = 0; k < s; k++) - local_data[j*s + k] = rank*10000 + j*100 + k; - - // Standard Alltoall - PMPI_Alltoall(local_data.data(), - s, - MPI_INT, - pmpi_alltoall.data(), - s, - MPI_INT, - MPI_COMM_WORLD); - - mpil_alltoall_implementation = ALLTOALL_PAIRWISE; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_NONBLOCKING; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_HIERARCHICAL_PAIRWISE; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_HIERARCHICAL_NONBLOCKING; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_MULTILEADER_PAIRWISE; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_MULTILEADER_NONBLOCKING; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_NODE_AWARE_PAIRWISE; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_NODE_AWARE_NONBLOCKING; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_LOCALITY_AWARE_PAIRWISE; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_LOCALITY_AWARE_NONBLOCKING; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - mpil_alltoall_implementation = ALLTOALL_PMPI; - std::fill(mpil_alltoall.begin(), mpil_alltoall.end(), 0); - MPIL_Alltoall(local_data.data(), - s, - MPI_INT, - mpil_alltoall.data(), - s, - MPI_INT, - locality_comm); - compare_alltoall_results(pmpi_alltoall, mpil_alltoall, s); - - - } - - MPIL_Comm_free(&locality_comm); - - - MPI_Finalize(); - return 0; -} // end of main() // - - diff --git a/src/collective/tests/test_alltoallv.cpp b/src/collective/tests/test_alltoallv.cpp deleted file mode 100644 index 5f9964d68..000000000 --- a/src/collective/tests/test_alltoallv.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include - -void compare_alltoallv_results(std::vector& pmpi, std::vector& mpil, int s) -{ - int num_procs; - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - for (int j = 0; j < s*num_procs; j++) - { - if (pmpi[j] != mpil[j]) - { - fprintf(stderr, "MPIL Alltoallv != PMPI, position %d, pmpi %d, mpil %d\n", - j, pmpi[j], mpil[j]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Test Integer Alltoall - int max_i = 10; - int max_s = pow(2, max_i); - srand(time(NULL)); - std::vector local_data(max_s*num_procs); - - std::vector pmpi_alltoallv(max_s*num_procs); - std::vector mpil_alltoallv(max_s*num_procs); - - std::vector sizes(num_procs); - std::vector displs(num_procs+1); - - MPIL_Comm* xcomm; - MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - update_locality(xcomm, 4); - - for (int i = 0; i < max_i; i++) - { - int s = pow(2, i); - - // Will only be clean for up to double digit process counts - displs[0] = 0; - for (int j = 0; j < num_procs; j++) - { - for (int k = 0; k < s; k++) - local_data[j*s + k] = rank*10000 + j*100 + k; - sizes[j] = s; - displs[j+1] = displs[j] + s; - } - - - PMPI_Alltoallv(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - pmpi_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - MPI_COMM_WORLD); - - std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); - MPIL_Alltoallv(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - mpil_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - - std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); - alltoallv_pairwise(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - mpil_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - - std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); - alltoallv_nonblocking(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - mpil_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - - std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); - alltoallv_batch(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - mpil_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - - std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); - alltoallv_batch_async(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - mpil_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - - } - - MPIL_Comm_free(&xcomm); - - - MPI_Finalize(); - return 0; -} // end of main() // - - - diff --git a/src/collective/tests/test_alltoallv_enum.cpp b/src/collective/tests/test_alltoallv_enum.cpp deleted file mode 100644 index 6d797077f..000000000 --- a/src/collective/tests/test_alltoallv_enum.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include - -void compare_alltoallv_results(std::vector& pmpi, std::vector& mpil, int s) -{ - int num_procs; - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - for (int j = 0; j < s*num_procs; j++) - { - if (pmpi[j] != mpil[j]) - { - fprintf(stderr, "MPIL Alltoallv != PMPI, position %d, pmpi %d, mpil %d\n", - j, pmpi[j], mpil[j]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Test Integer Alltoall - int max_i = 10; - int max_s = pow(2, max_i); - srand(time(NULL)); - std::vector local_data(max_s*num_procs); - - std::vector pmpi_alltoallv(max_s*num_procs); - std::vector mpil_alltoallv(max_s*num_procs); - - std::vector sizes(num_procs); - std::vector displs(num_procs+1); - - MPIL_Comm* xcomm; - MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - update_locality(xcomm, 4); - - for (int i = 0; i < max_i; i++) - { - int s = pow(2, i); - - // Will only be clean for up to double digit process counts - displs[0] = 0; - for (int j = 0; j < num_procs; j++) - { - for (int k = 0; k < s; k++) - local_data[j*s + k] = rank*10000 + j*100 + k; - sizes[j] = s; - displs[j+1] = displs[j] + s; - } - - - PMPI_Alltoallv(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - pmpi_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - MPI_COMM_WORLD); - - mpil_alltoallv_implementation = ALLTOALLV_PAIRWISE; - std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); - MPIL_Alltoallv(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - mpil_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - - mpil_alltoallv_implementation = ALLTOALLV_NONBLOCKING; - std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); - MPIL_Alltoallv(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - mpil_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - - mpil_alltoallv_implementation = ALLTOALLV_BATCH; - std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); - MPIL_Alltoallv(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - mpil_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - - mpil_alltoallv_implementation = ALLTOALLV_BATCH_ASYNC; - std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); - MPIL_Alltoallv(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - mpil_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - - mpil_alltoallv_implementation = ALLTOALLV_PMPI; - std::fill(mpil_alltoallv.begin(), mpil_alltoallv.end(), 0); - MPIL_Alltoallv(local_data.data(), - sizes.data(), - displs.data(), - MPI_INT, - mpil_alltoallv.data(), - sizes.data(), - displs.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_alltoallv, mpil_alltoallv, s); - - } - - - MPIL_Comm_free(&xcomm); - - - MPI_Finalize(); - return 0; -} // end of main() // - - - diff --git a/src/collective/tests/test_suitesparse_alltoallv.cpp b/src/collective/tests/test_suitesparse_alltoallv.cpp deleted file mode 100644 index 0465aaa61..000000000 --- a/src/collective/tests/test_suitesparse_alltoallv.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - -void compare_alltoallv_results(std::vector& pmpi, std::vector& mpil, int s) -{ - for (int i = 0; i < s; i++) - { - if (pmpi[i] != mpil[i]) - { - fprintf(stderr, "MPIL Alltoallv != PMPI, position %d, pmpi %d, mpil %d\n", - i, pmpi[i], mpil[i]); - MPI_Abort(MPI_COMM_WORLD, 1); - } - } - -} - -void test_matrix(const char* filename) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - MPIL_Comm* xcomm; - MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - update_locality(xcomm, 4); - - // Read suitesparse matrix - ParMat A; - readParMatrix(filename, A); - form_comm(A); - - std::vector send_vals(A.on_proc.n_rows); - std::iota(send_vals.begin(), send_vals.end(), 0); - for (int i = 0; i < A.on_proc.n_rows; i++) - send_vals[i] += (rank*1000); - - // Alltoallv_send_vals must be ordered (dest 0 to num_procs-1) - std::vector proc_pos(num_procs, -1); - for (int i = 0; i < A.send_comm.n_msgs; i++) - proc_pos[A.send_comm.procs[i]] = i; - - std::vector alltoallv_send_vals(A.send_comm.size_msgs); - int start, end, idx; - int ctr = 0; - for (int i = 0; i < num_procs; i++) - { - idx = proc_pos[i]; - if (proc_pos[i] < 0) continue; - - start = A.send_comm.ptr[idx]; - end = A.send_comm.ptr[idx+1]; - for (int j = start; j < end; j++) - { - alltoallv_send_vals[ctr++] = send_vals[A.send_comm.idx[j]]; - } - } - - std::vector sendcounts(num_procs, 0); - std::vector sdispls(num_procs+1); - std::vector recvcounts(num_procs, 0); - std::vector rdispls(num_procs+1); - - for (int i = 0; i < A.send_comm.n_msgs; i++) - sendcounts[A.send_comm.procs[i]] = A.send_comm.ptr[i+1] - A.send_comm.ptr[i]; - for (int i = 0; i < A.recv_comm.n_msgs; i++) - recvcounts[A.recv_comm.procs[i]] = A.recv_comm.ptr[i+1] - A.recv_comm.ptr[i]; - - sdispls[0] = 0; - rdispls[0] = 0; - for (int i = 0; i < num_procs; i++) - { - sdispls[i+1] = sdispls[i] + sendcounts[i]; - rdispls[i+1] = rdispls[i] + recvcounts[i]; - } - - std::vector pmpi_recv_vals(A.recv_comm.size_msgs); - std::vector mpil_recv_vals(A.recv_comm.size_msgs); - - communicate(A, send_vals, mpil_recv_vals, MPI_INT); - - PMPI_Alltoallv(alltoallv_send_vals.data(), - sendcounts.data(), - sdispls.data(), - MPI_INT, - pmpi_recv_vals.data(), - recvcounts.data(), - rdispls.data(), - MPI_INT, - MPI_COMM_WORLD); - compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - - std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); - MPIL_Alltoallv(alltoallv_send_vals.data(), - sendcounts.data(), - sdispls.data(), - MPI_INT, - mpil_recv_vals.data(), - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - - - std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); - alltoallv_pairwise(alltoallv_send_vals.data(), - sendcounts.data(), - sdispls.data(), - MPI_INT, - mpil_recv_vals.data(), - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - - std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); - alltoallv_nonblocking(alltoallv_send_vals.data(), - sendcounts.data(), - sdispls.data(), - MPI_INT, - mpil_recv_vals.data(), - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - - std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); - alltoallv_batch(alltoallv_send_vals.data(), - sendcounts.data(), - sdispls.data(), - MPI_INT, - mpil_recv_vals.data(), - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - - std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); - alltoallv_batch_async(alltoallv_send_vals.data(), - sendcounts.data(), - sdispls.data(), - MPI_INT, - mpil_recv_vals.data(), - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm); - compare_alltoallv_results(pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); - - MPIL_Comm_free(&xcomm); -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - - test_matrix("../../../../test_data/dwt_162.pm"); - test_matrix("../../../../test_data/odepa400.pm"); - test_matrix("../../../../test_data/ww_36_pmec_36.pm"); - - MPI_Finalize(); - return 0; -} // end of main() // - - diff --git a/src/communicator/CMakeLists.txt b/src/communicator/CMakeLists.txt deleted file mode 100644 index 17e5f970e..000000000 --- a/src/communicator/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -file(GLOB communicator_HEADERS CONFIGURE_DEPENDS "*.h") -file(GLOB communicator_SOURCES CONFIGURE_DEPENDS "*.c") - -set(communicator_HEADERS ${communicator_HEADERS} CACHE INTERNAL "All headers for communicator files.") -set(communicator_SOURCES ${communicator_SOURCES} CACHE INTERNAL "All source files for communicator directory.") - diff --git a/src/communicator/mpil_comm.c b/src/communicator/mpil_comm.c deleted file mode 100644 index 0f516db85..000000000 --- a/src/communicator/mpil_comm.c +++ /dev/null @@ -1,369 +0,0 @@ -#include "mpil_comm.h" - -int MPIL_Comm_init(MPIL_Comm** xcomm_ptr, MPI_Comm global_comm) -{ - int rank, num_procs; - MPI_Comm_rank(global_comm, &rank); - MPI_Comm_size(global_comm, &num_procs); - - MPIL_Comm* xcomm = (MPIL_Comm*)malloc(sizeof(MPIL_Comm)); - xcomm->global_comm = global_comm; - - xcomm->local_comm = MPI_COMM_NULL; - xcomm->group_comm = MPI_COMM_NULL; - - xcomm->leader_comm = MPI_COMM_NULL; - xcomm->leader_group_comm = MPI_COMM_NULL; - xcomm->leader_local_comm = MPI_COMM_NULL; - - xcomm->neighbor_comm = MPI_COMM_NULL; - - xcomm->win = MPI_WIN_NULL; - xcomm->win_array = NULL; - xcomm->win_bytes = 0; - - xcomm->requests = NULL; - xcomm->statuses = NULL; - xcomm->n_requests = 0; - - int flag; - MPI_Comm_get_attr(MPI_COMM_WORLD, MPI_TAG_UB, &(xcomm->max_tag), &flag); - xcomm->tag = 126 % xcomm->max_tag; - - xcomm->global_rank_to_local = NULL; - xcomm->global_rank_to_node = NULL; - xcomm->ordered_global_ranks = NULL; - -#ifdef GPU - xcomm->gpus_per_node = 0; -#endif - - *xcomm_ptr = xcomm; - - return MPI_SUCCESS; -} - -int MPIL_Comm_topo_init(MPIL_Comm* xcomm) -{ - int rank, num_procs; - MPI_Comm_rank(xcomm->global_comm, &rank); - MPI_Comm_size(xcomm->global_comm, &num_procs); - - // Split global comm into local (per node) communicators - MPI_Comm_split_type(xcomm->global_comm, - MPI_COMM_TYPE_SHARED, - rank, - MPI_INFO_NULL, - &(xcomm->local_comm)); - - int local_rank, ppn; - MPI_Comm_rank(xcomm->local_comm, &local_rank); - MPI_Comm_size(xcomm->local_comm, &ppn); - - // Split global comm into group (per local rank) communicators - MPI_Comm_split(xcomm->global_comm, local_rank, rank, &(xcomm->group_comm)); - - int node; - MPI_Comm_rank(xcomm->group_comm, &node); - - // Gather arrays for get_node, get_local, and get_global methods - // These arrays allow for these methods to work with any ordering - // No longer relying on SMP ordering of processes to nodes! - // Does rely on constant ppn - xcomm->global_rank_to_local = (int*)malloc(num_procs * sizeof(int)); - xcomm->global_rank_to_node = (int*)malloc(num_procs * sizeof(int)); - MPI_Allgather(&local_rank, - 1, - MPI_INT, - xcomm->global_rank_to_local, - 1, - MPI_INT, - xcomm->global_comm); - MPI_Allgather( - &node, 1, MPI_INT, xcomm->global_rank_to_node, 1, MPI_INT, xcomm->global_comm); - - xcomm->ordered_global_ranks = (int*)malloc(num_procs * sizeof(int)); - for (int i = 0; i < num_procs; i++) - { - int local = xcomm->global_rank_to_local[i]; - int node = xcomm->global_rank_to_node[i]; - xcomm->ordered_global_ranks[node * ppn + local] = i; - } - - // Set xcomm variables - MPI_Comm_size(xcomm->local_comm, &(xcomm->ppn)); - xcomm->num_nodes = ((num_procs - 1) / xcomm->ppn) + 1; - xcomm->rank_node = get_node(xcomm, rank); - - return MPI_SUCCESS; -} - -int MPIL_Comm_leader_init(MPIL_Comm* xcomm, int procs_per_leader) -{ - int rank, num_procs; - MPI_Comm_rank(xcomm->global_comm, &rank); - MPI_Comm_size(xcomm->global_comm, &num_procs); - - MPI_Comm_split( - xcomm->global_comm, rank / procs_per_leader, rank, &(xcomm->leader_comm)); - - int leader_rank; - MPI_Comm_rank(xcomm->leader_comm, &leader_rank); - - MPI_Comm_split(xcomm->global_comm, leader_rank, rank, &(xcomm->leader_group_comm)); - - if (xcomm->local_comm == MPI_COMM_NULL) - { - MPIL_Comm_topo_init(xcomm); - } - - MPI_Comm_split(xcomm->local_comm, leader_rank, rank, &(xcomm->leader_local_comm)); - - return MPI_SUCCESS; -} - -int MPIL_Comm_device_init(MPIL_Comm* xcomm) -{ -#ifdef GPU - if (xcomm->local_comm == MPI_COMM_NULL) - { - MPIL_Comm_topo_init(xcomm); - } - - int local_rank, ierr; - MPI_Comm_rank(xcomm->local_comm, &local_rank); - ierr = gpuGetDeviceCount(&(xcomm->gpus_per_node)); - gpu_check(ierr); - if (xcomm->gpus_per_node) - { - xcomm->rank_gpu = local_rank; - ierr = gpuStreamCreate(&(xcomm->proc_stream)); - gpu_check(ierr); - } -#endif - - return MPI_SUCCESS; -} - -int MPIL_Comm_win_init(MPIL_Comm* xcomm, int bytes, int type_bytes) -{ - int rank, num_procs; - MPI_Comm_rank(xcomm->global_comm, &rank); - MPI_Comm_size(xcomm->global_comm, &num_procs); - - xcomm->win_bytes = bytes; - xcomm->win_type_bytes = type_bytes; - MPI_Alloc_mem(xcomm->win_bytes, MPI_INFO_NULL, &(xcomm->win_array)); - MPI_Win_create(xcomm->win_array, - xcomm->win_bytes, - xcomm->win_type_bytes, - MPI_INFO_NULL, - xcomm->global_comm, - &(xcomm->win)); - - return MPI_SUCCESS; -} - -int MPIL_Comm_req_resize(MPIL_Comm* xcomm, int n) -{ - if (n <= 0) - { - return MPI_SUCCESS; - } - - xcomm->n_requests = n; - xcomm->requests = (MPI_Request*)realloc(xcomm->requests, n * sizeof(MPI_Request)); - xcomm->statuses = (MPI_Status*)realloc(xcomm->statuses, n * sizeof(MPI_Status)); - - return MPI_SUCCESS; -} - -int MPIL_Comm_tag(MPIL_Comm* xcomm, int* tag) -{ - *tag = xcomm->tag; - xcomm->tag = ((xcomm->tag + 1) % xcomm->max_tag); - - return MPI_SUCCESS; -} - -int MPIL_Comm_free(MPIL_Comm** xcomm_ptr) -{ - MPIL_Comm* xcomm = *xcomm_ptr; - - if (xcomm->n_requests > 0) - { - free(xcomm->requests); - } - - if (xcomm->neighbor_comm != MPI_COMM_NULL) - { - MPI_Comm_free(&(xcomm->neighbor_comm)); - } - - MPIL_Comm_topo_free(xcomm); - MPIL_Comm_leader_free(xcomm); - MPIL_Comm_win_free(xcomm); - MPIL_Comm_device_free(xcomm); - - free(xcomm); - - return MPI_SUCCESS; -} - -int MPIL_Comm_topo_free(MPIL_Comm* xcomm) -{ - if (xcomm->local_comm != MPI_COMM_NULL) - { - MPI_Comm_free(&(xcomm->local_comm)); - } - if (xcomm->group_comm != MPI_COMM_NULL) - { - MPI_Comm_free(&(xcomm->group_comm)); - } - - if (xcomm->global_rank_to_local != NULL) - { - free(xcomm->global_rank_to_local); - } - if (xcomm->global_rank_to_node != NULL) - { - free(xcomm->global_rank_to_node); - } - if (xcomm->ordered_global_ranks != NULL) - { - free(xcomm->ordered_global_ranks); - } - - return MPI_SUCCESS; -} - -int MPIL_Comm_leader_free(MPIL_Comm* xcomm) -{ - if (xcomm->leader_comm != MPI_COMM_NULL) - { - MPI_Comm_free(&(xcomm->leader_comm)); - } - if (xcomm->leader_group_comm != MPI_COMM_NULL) - { - MPI_Comm_free(&(xcomm->leader_group_comm)); - } - if (xcomm->leader_local_comm != MPI_COMM_NULL) - { - MPI_Comm_free(&(xcomm->leader_local_comm)); - } - - return MPI_SUCCESS; -} - -int MPIL_Comm_win_free(MPIL_Comm* xcomm) -{ - int rank, num_procs; - MPI_Comm_rank(xcomm->global_comm, &rank); - MPI_Comm_size(xcomm->global_comm, &num_procs); - - if (xcomm->win != MPI_WIN_NULL) - { - MPI_Win_free(&(xcomm->win)); - } - if (xcomm->win_array != NULL) - { - MPI_Free_mem(xcomm->win_array); - } - xcomm->win_bytes = 0; - xcomm->win_type_bytes = 0; - - return MPI_SUCCESS; -} - -int MPIL_Comm_device_free(MPIL_Comm* xcomm) -{ -#ifdef GPU - int ierr = gpuSuccess; - if (xcomm->gpus_per_node) - { - ierr = gpuStreamDestroy(xcomm->proc_stream); - } - gpu_check(ierr); -#endif - - return MPI_SUCCESS; -} - -/**** Topology Functions ****/ -int get_node(const MPIL_Comm* data, const int proc) -{ - return data->global_rank_to_node[proc]; -} - -int get_local_proc(const MPIL_Comm* data, const int proc) -{ - return data->global_rank_to_local[proc]; -} - -int get_global_proc(const MPIL_Comm* data, const int node, const int local_proc) -{ - return data->ordered_global_ranks[local_proc + (node * data->ppn)]; -} - -// For testing purposes -// Manually update aggregation size (ppn) -void update_locality(MPIL_Comm* xcomm, int ppn) -{ - int rank, num_procs; - MPI_Comm_rank(xcomm->global_comm, &rank); - MPI_Comm_size(xcomm->global_comm, &num_procs); - - if (xcomm->local_comm != MPI_COMM_NULL) - { - MPI_Comm_free(&(xcomm->local_comm)); - } - if (xcomm->group_comm != MPI_COMM_NULL) - { - MPI_Comm_free(&(xcomm->group_comm)); - } - - MPI_Comm_split(xcomm->global_comm, rank / ppn, rank, &(xcomm->local_comm)); - - int local_rank; - MPI_Comm_rank(xcomm->local_comm, &local_rank); - MPI_Comm_split(xcomm->global_comm, local_rank, rank, &(xcomm->group_comm)); - - int node; - MPI_Comm_rank(xcomm->group_comm, &node); - - if (xcomm->global_rank_to_local == NULL) - { - xcomm->global_rank_to_local = (int*)malloc(num_procs * sizeof(int)); - } - - if (xcomm->global_rank_to_node == NULL) - { - xcomm->global_rank_to_node = (int*)malloc(num_procs * sizeof(int)); - } - - MPI_Allgather(&local_rank, - 1, - MPI_INT, - xcomm->global_rank_to_local, - 1, - MPI_INT, - xcomm->global_comm); - MPI_Allgather( - &node, 1, MPI_INT, xcomm->global_rank_to_node, 1, MPI_INT, xcomm->global_comm); - - if (xcomm->ordered_global_ranks == NULL) - { - xcomm->ordered_global_ranks = (int*)malloc(num_procs * sizeof(int)); - } - - for (int i = 0; i < num_procs; i++) - { - int local = xcomm->global_rank_to_local[i]; - int node = xcomm->global_rank_to_node[i]; - xcomm->ordered_global_ranks[node * ppn + local] = i; - } - - MPI_Comm_size(xcomm->local_comm, &(xcomm->ppn)); - xcomm->num_nodes = ((num_procs - 1) / xcomm->ppn) + 1; - xcomm->rank_node = get_node(xcomm, rank); -} diff --git a/src/communicator/mpil_comm.h b/src/communicator/mpil_comm.h deleted file mode 100644 index 8937c632f..000000000 --- a/src/communicator/mpil_comm.h +++ /dev/null @@ -1,90 +0,0 @@ -// TODO Currently Assumes SMP Ordering -// And equal number of processes per node - -#ifndef MPI_ADVANCE_TOPOLOGY_H -#define MPI_ADVANCE_TOPOLOGY_H - -#include -#include -#include - -#include "utils/utils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _MPIL_Comm -{ - MPI_Comm global_comm; - - // For persistent neighborhood collectives - MPI_Comm neighbor_comm; - - // For hierarchical collectives - MPI_Comm local_comm; - MPI_Comm group_comm; - - // For multileader hierarchical collectives - MPI_Comm leader_comm; - MPI_Comm leader_group_comm; - MPI_Comm leader_local_comm; - - int num_nodes; - int rank_node; - int ppn; - - MPI_Win win; - char* win_array; - int win_bytes; - int win_type_bytes; - - MPI_Request* requests; - MPI_Status* statuses; - int n_requests; - - int tag; - int max_tag; - - int* global_rank_to_local; - int* global_rank_to_node; - int* ordered_global_ranks; - -#ifdef GPU - int gpus_per_node; - int rank_gpu; - gpuStream_t proc_stream; -#endif -} MPIL_Comm; - -int MPIL_Comm_init(MPIL_Comm** xcomm_ptr, MPI_Comm global_comm); -int MPIL_Comm_free(MPIL_Comm** xcomm_ptr); - -int MPIL_Comm_topo_init(MPIL_Comm* xcomm); -int MPIL_Comm_topo_free(MPIL_Comm* xcomm); - -int MPIL_Comm_leader_init(MPIL_Comm* xcomm, int procs_per_leader); -int MPIL_Comm_leader_free(MPIL_Comm* xcomm); - -int MPIL_Comm_win_init(MPIL_Comm* xcomm, int bytes, int type_bytes); -int MPIL_Comm_win_free(MPIL_Comm* xcomm); - -int MPIL_Comm_device_init(MPIL_Comm* xcomm); -int MPIL_Comm_device_free(MPIL_Comm* xcomm); - -int MPIL_Comm_req_resize(MPIL_Comm* xcomm, int n); - -int MPIL_Comm_tag(MPIL_Comm* comm, int* tag); - -int get_node(const MPIL_Comm* data, const int proc); -int get_local_proc(const MPIL_Comm* data, const int proc); -int get_global_proc(const MPIL_Comm* data, const int node, const int local_proc); - -// For testing purposes (manually set PPN) -void update_locality(MPIL_Comm* xcomm, int ppn); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/communicator/tests/CMakeLists.txt b/src/communicator/tests/CMakeLists.txt deleted file mode 100644 index 8b1378917..000000000 --- a/src/communicator/tests/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/heterogeneous/CMakeLists.txt b/src/heterogeneous/CMakeLists.txt deleted file mode 100644 index 0f0c764e9..000000000 --- a/src/heterogeneous/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -file(GLOB heterogeneous_HEADERS CONFIGURE_DEPENDS "*.h") -file(GLOB heterogeneous_SOURCES CONFIGURE_DEPENDS "*.c") - -set(heterogeneous_HEADERS ${heterogeneous_HEADERS} CACHE INTERNAL "All headers for heterogeneous files.") -set(heterogeneous_SOURCES ${heterogeneous_SOURCES} CACHE INTERNAL "All source files for heterogeneous directory.") - -message(STATUS ${heterogeneous_HEADERS} ${heterogeneous_SOURCES}) diff --git a/src/heterogeneous/tests/CMakeLists.txt b/src/heterogeneous/tests/CMakeLists.txt deleted file mode 100644 index 13363b538..000000000 --- a/src/heterogeneous/tests/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -## Grab all .cpp files in test directory -file(GLOB CPP_SOURCES CONFIGURE_DEPENDS "*.cpp") - -## All should be compiled appropriately (cxx vs nvcc vs hipcc) -set_source_files_properties(${CPP_SOURCES} PROPERTIES LANGUAGE ${locality_aware_LANG}) - -## Go through each CPP file -foreach(src ${CPP_SOURCES}) - - ## Grab the name without full path or extension - get_filename_component(exec_name ${src} NAME_WE) - - ## Create executable - add_executable(${exec_name} ${src}) - - # Link with MPI - target_link_libraries(${exec_name} locality_aware ${MPI_LIBRARIES} ${OpenMP_CXX_LIBRARIES}) - - # Add to CTEST - add_test(NAME ${exec_name}_Test COMMAND ${MPIRUN} -n 16 ./${exec_name}) -endforeach() - - diff --git a/src/heterogeneous/tests/test_gpu_alltoall.cpp b/src/heterogeneous/tests/test_gpu_alltoall.cpp deleted file mode 100644 index ea7ed5be9..000000000 --- a/src/heterogeneous/tests/test_gpu_alltoall.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include - -void compare_alltoall_results(std::vector& pmpi_alltoall, std::vector& mpix_alltoall, int s) -{ - int num_procs; - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - for (int i = 0; i < s*num_procs; i++) - { - if (pmpi_alltoall[i] != mpix_alltoall[i]) - { - fprintf(stderr, "Alltoall ERROR: position %d, pmpi %d, mpix %d\n", - i, pmpi_alltoall[i], mpix_alltoall[i]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Test Integer Alltoall - int max_i = 10; - int max_s = pow(2, max_i); - srand(time(NULL)); - std::vector local_data(max_s*num_procs); - std::vector pmpi_alltoall(max_s*num_procs); - std::vector mpix_alltoall(max_s*num_procs); - std::vector device_data(max_s*num_procs); - - MPIL_Comm* xcomm; - MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIL_Comm_device_init(xcomm); - - int n_gpus; - gpuGetDeviceCount(&n_gpus); - gpuSetDevice(xcomm->rank_gpu); - - int* local_data_d; - int* alltoall_d; - gpuMalloc((void**)&local_data_d, - max_s*num_procs*sizeof(int)); - gpuMalloc((void**)&alltoall_d, - max_s*num_procs*sizeof(int)); - - for (int i = 0; i < max_i; i++) - { - int s = pow(2, i); - - // Will only be clean for up to double digit process counts - for (int i = 0; i < num_procs; i++) - for (int j = 0; j < s; j++) - local_data[i*s + j] = rank*10000 + i*100 + j; - gpuMemcpy(local_data_d, - local_data.data(), - s*num_procs*sizeof(int), - gpuMemcpyHostToDevice); - - // Standard Alltoall - PMPI_Alltoall(local_data.data(), - s, - MPI_INT, - pmpi_alltoall.data(), - s, - MPI_INT, - MPI_COMM_WORLD); - - - // Pairwise Alltoall - alltoall_pairwise(local_data.data(), - s, - MPI_INT, - mpix_alltoall.data(), - s, - MPI_INT, - xcomm); - compare_alltoall_results(pmpi_alltoall, mpix_alltoall, s); - - - // Standard GPU Alltoall - PMPI_Alltoall(local_data_d, - s, - MPI_INT, - alltoall_d, - s, - MPI_INT, - MPI_COMM_WORLD); - gpuMemcpy(device_data.data(), - alltoall_d, - s*num_procs*sizeof(int), - gpuMemcpyDeviceToHost); - compare_alltoall_results(pmpi_alltoall, device_data, s); - gpuMemset(alltoall_d, 0, s*num_procs*sizeof(int)); - - // GPU-Aware Pairwise Alltoall - gpu_aware_alltoall_pairwise( - local_data_d, - s, - MPI_INT, - alltoall_d, - s, - MPI_INT, - xcomm); - gpuMemcpy(device_data.data(), - alltoall_d, - s*num_procs*sizeof(int), - gpuMemcpyDeviceToHost); - compare_alltoall_results(pmpi_alltoall, device_data, s); - gpuMemset(alltoall_d, 0, s*num_procs*sizeof(int)); - - - // GPU-Aware Nonblocking Alltoall - gpu_aware_alltoall_nonblocking( - local_data_d, - s, - MPI_INT, - alltoall_d, - s, - MPI_INT, - xcomm); - gpuMemcpy(device_data.data(), - alltoall_d, - s*num_procs*sizeof(int), - gpuMemcpyDeviceToHost); - compare_alltoall_results(pmpi_alltoall, device_data, s); - gpuMemset(alltoall_d, 0, s*num_procs*sizeof(int)); - - - // Copy-to-CPU Pairwise Alltoall - copy_to_cpu_alltoall_pairwise( - local_data_d, - s, - MPI_INT, - alltoall_d, - s, - MPI_INT, - xcomm); - gpuMemcpy(device_data.data(), - alltoall_d, - s*num_procs*sizeof(int), - gpuMemcpyDeviceToHost); - compare_alltoall_results(pmpi_alltoall, device_data, s); - gpuMemset(alltoall_d, 0, s*num_procs*sizeof(int)); - - // Copy-to-CPU Nonblocking Alltoall - copy_to_cpu_alltoall_nonblocking( - local_data_d, - s, - MPI_INT, - alltoall_d, - s, - MPI_INT, - xcomm); - gpuMemcpy(device_data.data(), - alltoall_d, - s*num_procs*sizeof(int), - gpuMemcpyDeviceToHost); - compare_alltoall_results(pmpi_alltoall, device_data, s); - gpuMemset(alltoall_d, 0, s*num_procs*sizeof(int)); - } - - gpuFree(local_data_d); - gpuFree(alltoall_d); - - MPIL_Comm_free(&xcomm); - - MPI_Finalize(); - return 0; -} // end of main() // - - - diff --git a/src/heterogeneous/tests/test_gpu_alltoallv.cpp b/src/heterogeneous/tests/test_gpu_alltoallv.cpp deleted file mode 100644 index 25a5fdcef..000000000 --- a/src/heterogeneous/tests/test_gpu_alltoallv.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - -void compare_alltoallv_results(std::vector& pmpi_alltoall, std::vector& mpix_alltoall, int s) -{ - for (int i = 0; i < s; i++) - { - if (pmpi_alltoall[i] != mpix_alltoall[i]) - { - fprintf(stderr, "Alltoallv ERROR: position %d, pmpi %d, mpix %d\n", - i, pmpi_alltoall[i], mpix_alltoall[i]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -void test_matrix(const char* filename) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - MPIL_Comm* xcomm; - MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIL_Comm_device_init(xcomm); - - // Read suitesparse matrix - ParMat A; - readParMatrix(filename, A); - form_comm(A); - - std::vector send_vals(A.on_proc.n_rows); - std::iota(send_vals.begin(), send_vals.end(), 0); - for (int i = 0; i < A.on_proc.n_rows; i++) - send_vals[i] += (rank*1000); - - // Alltoallv_send_vals must be ordered (dest 0 to num_procs-1) - std::vector proc_pos(num_procs, -1); - for (int i = 0; i < A.send_comm.n_msgs; i++) - proc_pos[A.send_comm.procs[i]] = i; - - std::vector alltoallv_send_vals(A.send_comm.size_msgs); - int start, end, idx; - int ctr = 0; - for (int i = 0; i < num_procs; i++) - { - idx = proc_pos[i]; - if (proc_pos[i] < 0) continue; - - start = A.send_comm.ptr[idx]; - end = A.send_comm.ptr[idx+1]; - for (int j = start; j < end; j++) - { - alltoallv_send_vals[ctr++] = send_vals[A.send_comm.idx[j]]; - } - } - - std::vector sendcounts(num_procs, 0); - std::vector sdispls(num_procs+1); - std::vector recvcounts(num_procs, 0); - std::vector rdispls(num_procs+1); - - for (int i = 0; i < A.send_comm.n_msgs; i++) - sendcounts[A.send_comm.procs[i]] = A.send_comm.ptr[i+1] - A.send_comm.ptr[i]; - for (int i = 0; i < A.recv_comm.n_msgs; i++) - recvcounts[A.recv_comm.procs[i]] = A.recv_comm.ptr[i+1] - A.recv_comm.ptr[i]; - - sdispls[0] = 0; - rdispls[0] = 0; - for (int i = 0; i < num_procs; i++) - { - sdispls[i+1] = sdispls[i] + sendcounts[i]; - rdispls[i+1] = rdispls[i] + recvcounts[i]; - } - - int n_gpus; - gpuGetDeviceCount(&n_gpus); - gpuSetDevice(xcomm->rank_gpu); - - int *sendbuf_d, *recvbuf_d; - gpuMalloc((void**)&sendbuf_d,A.send_comm.size_msgs*sizeof(int)); - gpuMalloc((void**)&recvbuf_d,A.recv_comm.size_msgs*sizeof(int)); - gpuMemcpy(sendbuf_d, alltoallv_send_vals.data(), A.send_comm.size_msgs*sizeof(int), - gpuMemcpyHostToDevice); - - std::vector pmpi_recv_vals(A.recv_comm.size_msgs); - std::vector gpu_recv_vals(A.recv_comm.size_msgs); - - // Inter-CPU Alltoallv - PMPI_Alltoallv(alltoallv_send_vals.data(), - sendcounts.data(), - sdispls.data(), - MPI_INT, - pmpi_recv_vals.data(), - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm->global_comm); - - // Inter-GPU Alltoallv - PMPI_Alltoallv(sendbuf_d, - sendcounts.data(), - sdispls.data(), - MPI_INT, - recvbuf_d, - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm->global_comm); - compare_alltoallv_results(pmpi_recv_vals, gpu_recv_vals, A.recv_comm.size_msgs); - gpuMemset(recvbuf_d, 0, A.recv_comm.size_msgs*sizeof(int)); - - gpu_aware_alltoallv_pairwise(sendbuf_d, - sendcounts.data(), - sdispls.data(), - MPI_INT, - recvbuf_d, - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm); - gpuMemcpy(gpu_recv_vals.data(), recvbuf_d, A.recv_comm.size_msgs*sizeof(int), gpuMemcpyDeviceToHost); - compare_alltoallv_results(pmpi_recv_vals, gpu_recv_vals, A.recv_comm.size_msgs); - gpuMemset(recvbuf_d, 0, A.recv_comm.size_msgs*sizeof(int)); - - gpu_aware_alltoallv_nonblocking(sendbuf_d, - sendcounts.data(), - sdispls.data(), - MPI_INT, - recvbuf_d, - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm); - gpuMemcpy(gpu_recv_vals.data(), recvbuf_d, A.recv_comm.size_msgs*sizeof(int), gpuMemcpyDeviceToHost); - compare_alltoallv_results(pmpi_recv_vals, gpu_recv_vals, A.recv_comm.size_msgs); - gpuMemset(recvbuf_d, 0, A.recv_comm.size_msgs*sizeof(int)); - - copy_to_cpu_alltoallv_pairwise(sendbuf_d, - sendcounts.data(), - sdispls.data(), - MPI_INT, - recvbuf_d, - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm); - gpuMemcpy(gpu_recv_vals.data(), recvbuf_d, A.recv_comm.size_msgs*sizeof(int), gpuMemcpyDeviceToHost); - compare_alltoallv_results(pmpi_recv_vals, gpu_recv_vals, A.recv_comm.size_msgs); - gpuMemset(recvbuf_d, 0, A.recv_comm.size_msgs*sizeof(int)); - - copy_to_cpu_alltoallv_nonblocking(sendbuf_d, - sendcounts.data(), - sdispls.data(), - MPI_INT, - recvbuf_d, - recvcounts.data(), - rdispls.data(), - MPI_INT, - xcomm); - gpuMemcpy(gpu_recv_vals.data(), recvbuf_d, A.recv_comm.size_msgs*sizeof(int), gpuMemcpyDeviceToHost); - compare_alltoallv_results(pmpi_recv_vals, gpu_recv_vals, A.recv_comm.size_msgs); - gpuMemset(recvbuf_d, 0, A.recv_comm.size_msgs*sizeof(int)); - - gpuFree(sendbuf_d); - gpuFree(recvbuf_d); - - MPIL_Comm_free(&xcomm); -} - -int main(int argc, char** argv) -{ - int provided; - MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); - test_matrix("../../../../test_data/dwt_162.pm"); - test_matrix("../../../../test_data/odepa400.pm"); - test_matrix("../../../../test_data/ww_36_pmec_36.pm"); - MPI_Finalize(); - return 0; -} // end of main() // - - - diff --git a/src/locality_aware.h b/src/locality_aware.h deleted file mode 100644 index 95f84e206..000000000 --- a/src/locality_aware.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef MPI_ADVANCE_H -#define MPI_ADVANCE_H - -#include "collective/alltoall.h" -#include "collective/alltoallv.h" -#include "collective/collective.h" -#include "communicator/comm_data.h" -#include "communicator/comm_pkg.h" -#include "communicator/locality_comm.h" -#include "communicator/mpil_comm.h" -#include "neighborhood/dist_graph.h" -#include "neighborhood/dist_topo.h" -#include "neighborhood/neighbor.h" -#include "neighborhood/neighbor_init.h" -#include "neighborhood/sparse_coll.h" -#include "persistent/neighbor_persistent.h" -#include "persistent/persistent.h" -#include "utils/utils.h" - -#ifdef GPU -#include "heterogeneous/gpu_alltoall.h" -#include "heterogeneous/gpu_alltoallv.h" -#endif - -#endif diff --git a/src/neighborhood/CMakeLists.txt b/src/neighborhood/CMakeLists.txt deleted file mode 100644 index 35bdb6792..000000000 --- a/src/neighborhood/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -file(GLOB neighborhood_HEADERS CONFIGURE_DEPENDS "*.h") -file(GLOB neighborhood_SOURCES CONFIGURE_DEPENDS "*.c" "*.cpp") - -set(neighborhood_HEADERS ${neighborhood_HEADERS} CACHE INTERNAL "All headers for neighborhood files.") -set(neighborhood_SOURCES ${neighborhood_SOURCES} CACHE INTERNAL "All source files for neighborhood directory.") - diff --git a/src/neighborhood/dist_graph.h b/src/neighborhood/dist_graph.h deleted file mode 100644 index e4db4b30b..000000000 --- a/src/neighborhood/dist_graph.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MPI_ADVANCE_DIST_GRAPH_H -#define MPI_ADVANCE_DIST_GRAPH_H - -#include "communicator/locality_comm.h" -#include "communicator/mpil_comm.h" -#include "mpi.h" - -// Declarations of C++ methods -#ifdef __cplusplus -extern "C" { -#endif - -int MPIL_Dist_graph_create_adjacent(MPI_Comm comm_old, - int indegree, - const int sources[], - const int sourceweights[], - int outdegree, - const int destinations[], - const int destweights[], - MPIL_Info* info, - int reorder, - MPIL_Comm** comm_dist_graph_ptr); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/neighborhood/dist_topo.c b/src/neighborhood/dist_topo.c deleted file mode 100644 index dea879252..000000000 --- a/src/neighborhood/dist_topo.c +++ /dev/null @@ -1,134 +0,0 @@ -#include "dist_topo.h" - -#include - -int MPIL_Topo_init(int indegree, - const int sources[], - const int sourceweights[], - int outdegree, - const int destinations[], - const int destweights[], - MPIL_Info* info, - MPIL_Topo** mpix_topo_ptr) -{ - MPIL_Topo* mpix_topo = (MPIL_Topo*)malloc(sizeof(MPIL_Topo)); - - // Copy indegree and outdegree into MPIL_Topo struct - mpix_topo->indegree = indegree; - mpix_topo->outdegree = outdegree; - - // Create copy of sources/destinations in MPIL_Topo struct - mpix_topo->sources = NULL; - mpix_topo->destinations = NULL; - - if (indegree) - { - mpix_topo->sources = (int*)malloc(indegree * sizeof(int)); - memcpy(mpix_topo->sources, sources, indegree * sizeof(int)); - if (sourceweights != MPI_UNWEIGHTED) - { - mpix_topo->sourceweights = (int*)malloc(indegree * sizeof(int)); - memcpy(mpix_topo->sourceweights, sourceweights, indegree * sizeof(int)); - } - else - { - mpix_topo->sourceweights = MPI_UNWEIGHTED; - } - } - - if (outdegree) - { - mpix_topo->destinations = (int*)malloc(outdegree * sizeof(int)); - memcpy(mpix_topo->destinations, destinations, outdegree * sizeof(int)); - - if (destweights != MPI_UNWEIGHTED) - { - mpix_topo->destweights = (int*)malloc(outdegree * sizeof(int)); - memcpy(mpix_topo->destweights, destweights, outdegree * sizeof(int)); - } - else - { - mpix_topo->destweights = MPI_UNWEIGHTED; - } - } - - *mpix_topo_ptr = mpix_topo; - - return MPI_SUCCESS; -} - -int MPIL_Topo_from_neighbor_comm(MPIL_Comm* comm, MPIL_Topo** mpix_topo_ptr) -{ - MPIL_Topo* mpix_topo = (MPIL_Topo*)malloc(sizeof(MPIL_Topo)); - - int weighted; - MPI_Dist_graph_neighbors_count( - comm->neighbor_comm, &(mpix_topo->indegree), &(mpix_topo->outdegree), &weighted); - - // Create copy of sources/destinations in MPIL_Topo struct - mpix_topo->sources = NULL; - mpix_topo->destinations = NULL; - - if (mpix_topo->indegree) - { - mpix_topo->sources = (int*)malloc(mpix_topo->indegree * sizeof(int)); - if (weighted) - { - mpix_topo->sourceweights = (int*)malloc(mpix_topo->indegree * sizeof(int)); - } - else - { - mpix_topo->sourceweights = MPI_UNWEIGHTED; - } - } - - if (mpix_topo->outdegree) - { - mpix_topo->destinations = (int*)malloc(mpix_topo->outdegree * sizeof(int)); - if (weighted) - { - mpix_topo->destweights = (int*)malloc(mpix_topo->outdegree * sizeof(int)); - } - else - { - mpix_topo->destweights = MPI_UNWEIGHTED; - } - } - - MPI_Dist_graph_neighbors(comm->neighbor_comm, - mpix_topo->indegree, - mpix_topo->sources, - mpix_topo->sourceweights, - mpix_topo->outdegree, - mpix_topo->destinations, - mpix_topo->destweights); - - *mpix_topo_ptr = mpix_topo; - - return MPI_SUCCESS; -} - -int MPIL_Topo_free(MPIL_Topo** mpix_topo_ptr) -{ - MPIL_Topo* mpix_topo = *mpix_topo_ptr; - - if (mpix_topo->indegree) - { - free(mpix_topo->sources); - if (mpix_topo->sourceweights != MPI_UNWEIGHTED) - { - free(mpix_topo->sourceweights); - } - } - - if (mpix_topo->outdegree) - { - free(mpix_topo->destinations); - if (mpix_topo->destweights != MPI_UNWEIGHTED) - { - free(mpix_topo->destweights); - } - } - free(mpix_topo); - return MPI_SUCCESS; -} diff --git a/src/neighborhood/dist_topo.h b/src/neighborhood/dist_topo.h deleted file mode 100644 index 7e16dad1d..000000000 --- a/src/neighborhood/dist_topo.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef MPI_ADVANCE_DIST_TOPO_H -#define MPI_ADVANCE_DIST_TOPO_H - -#include "communicator/locality_comm.h" -#include "communicator/mpil_comm.h" -#include "mpi.h" - -// Declarations of C++ methods -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _MPIL_Topo -{ - int indegree; - int* sources; - int* sourceweights; - int outdegree; - int* destinations; - int* destweights; - int reorder; -} MPIL_Topo; - -int MPIL_Topo_init(int indegree, - const int sources[], - const int sourceweights[], - int outdegree, - const int destinations[], - const int destweights[], - MPIL_Info* info, - MPIL_Topo** mpix_topo_ptr); - -int MPIL_Topo_from_neighbor_comm(MPIL_Comm* comm, MPIL_Topo** mpix_topo_ptr); - -int MPIL_Topo_free(MPIL_Topo** topo); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/neighborhood/neighbor.c b/src/neighborhood/neighbor.c deleted file mode 100644 index 7709bdc2f..000000000 --- a/src/neighborhood/neighbor.c +++ /dev/null @@ -1,233 +0,0 @@ -#include "neighbor.h" - -#include - -#include "sparse_coll.h" - -// Standard Method is default -NeighborAlltoallvMethod mpix_neighbor_alltoallv_implementation = - NEIGHBOR_ALLTOALLV_STANDARD; - -// Topology object based neighbor alltoallv -int MPIL_Neighbor_alltoallv_topo(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm) -{ - int rank; - MPI_Comm_rank(comm->global_comm, &rank); - - neighbor_alltoallv_ftn method; - - switch (mpix_neighbor_alltoallv_implementation) - { - case NEIGHBOR_ALLTOALLV_STANDARD: - method = neighbor_alltoallv_standard; - break; - case NEIGHBOR_ALLTOALLV_LOCALITY: - method = neighbor_alltoallv_locality; - break; - default: - method = neighbor_alltoallv_standard; - break; - } - - return method(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - topo, - comm); -} - -int MPIL_Neighbor_alltoallv(const void* sendbuffer, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuffer, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - MPIL_Topo* topo; - MPIL_Topo_from_neighbor_comm(comm, &topo); - - MPIL_Neighbor_alltoallv_topo(sendbuffer, - sendcounts, - sdispls, - sendtype, - recvbuffer, - recvcounts, - rdispls, - recvtype, - topo, - comm); - - MPIL_Topo_free(&topo); - - return MPI_SUCCESS; -} - -// Standard, non-persistent neighbor collective -int neighbor_alltoallv_standard(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm) -{ - int tag; - MPIL_Comm_tag(comm, &tag); - - if (topo->indegree + topo->outdegree == 0) - { - return MPI_SUCCESS; - } - - if (comm->n_requests < topo->indegree + topo->outdegree) - { - MPIL_Comm_req_resize(comm, topo->indegree + topo->outdegree); - } - - const char* send_buffer = NULL; - char* recv_buffer = NULL; - - if (topo->indegree) - { - recv_buffer = (char*)recvbuf; - } - if (topo->outdegree) - { - send_buffer = (char*)sendbuf; - } - - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - int count = 0; - for (int i = 0; i < topo->indegree; i++) - { - if (recvcounts[i]) - { - MPI_Irecv(&(recv_buffer[rdispls[i] * recv_size]), - recvcounts[i], - recvtype, - topo->sources[i], - tag, - comm->global_comm, - &(comm->requests[count++])); - } - } - - for (int i = 0; i < topo->outdegree; i++) - { - if (sendcounts[i]) - { - MPI_Isend(&(send_buffer[sdispls[i] * send_size]), - sendcounts[i], - sendtype, - topo->destinations[i], - tag, - comm->global_comm, - &(comm->requests[count++])); - } - } - - MPI_Waitall(count, comm->requests, comm->statuses); - - return MPI_SUCCESS; -} - -// Non-persistent, locality-aware == call dynamic version -int neighbor_alltoallv_locality(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm) -{ - int rank, num_procs; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - int send_nnz = topo->outdegree; - int send_size = 0; - for (int i = 0; i < send_nnz; i++) - { - send_size += sendcounts[i]; - } - - int recv_nnz, recv_size; - int *src_tmp, *recvcounts_tmp, *rdispls_tmp; - char* recvvals_tmp; - - int recv_bytes; - MPI_Type_size(recvtype, &recv_bytes); - - MPIL_Info* xinfo; - MPIL_Info_init(&xinfo); - - alltoallv_crs_personalized(send_nnz, - send_size, - topo->destinations, - sendcounts, - sdispls, - sendtype, - sendbuf, - &recv_nnz, - &recv_size, - &src_tmp, - &recvcounts_tmp, - &rdispls_tmp, - recvtype, - (void**)&recvvals_tmp, - xinfo, - comm); - - char* recvvals = (char*)recvbuf; - - int idx, proc; - int* new_proc_idx = (int*)malloc(num_procs * sizeof(int)); - for (int i = 0; i < recv_nnz; i++) - { - new_proc_idx[src_tmp[i]] = i; - } - for (int i = 0; i < recv_nnz; i++) - { - proc = topo->sources[i]; - idx = new_proc_idx[proc]; - memcpy(&(recvvals[rdispls[i] * recv_bytes]), - &(recvvals_tmp[rdispls_tmp[idx] * recv_bytes]), - recvcounts[i] * recv_bytes); - } - free(new_proc_idx); - - MPIL_Info_free(&xinfo); - - MPIL_Free(src_tmp); - MPIL_Free(recvcounts_tmp); - MPIL_Free(rdispls_tmp); - MPIL_Free(recvvals_tmp); - - return MPI_SUCCESS; -} diff --git a/src/neighborhood/neighbor_init.c b/src/neighborhood/neighbor_init.c deleted file mode 100644 index 4da7c4c9e..000000000 --- a/src/neighborhood/neighbor_init.c +++ /dev/null @@ -1,560 +0,0 @@ -#include "neighbor_init.h" - -#include "neighbor.h" -#include "persistent/neighbor_persistent.h" - -NeighborAlltoallvInitMethod mpix_neighbor_alltoallv_init_implementation = - NEIGHBOR_ALLTOALLV_INIT_STANDARD; - -int init_communication(const void* sendbuffer, - int n_sends, - const int* send_procs, - const int* send_ptr, - MPI_Datatype sendtype, - void* recvbuffer, - int n_recvs, - const int* recv_procs, - const int* recv_ptr, - MPI_Datatype recvtype, - int tag, - MPI_Comm comm, - int* n_request_ptr, - MPI_Request** request_ptr); - -int MPIL_Neighbor_alltoallv_init_topo(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr) -{ - neighbor_alltoallv_init_ftn method; - - switch (mpix_neighbor_alltoallv_init_implementation) - { - case NEIGHBOR_ALLTOALLV_INIT_STANDARD: - method = neighbor_alltoallv_init_standard; - break; - case NEIGHBOR_ALLTOALLV_INIT_LOCALITY: - method = neighbor_alltoallv_init_locality; - break; - default: - method = neighbor_alltoallv_init_standard; - break; - } - - return method(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - topo, - comm, - info, - request_ptr); -} - -// Standard Persistent Neighbor Alltoallv -// Extension takes array of requests instead of single request -// 'requests' must be of size indegree+outdegree! -int MPIL_Neighbor_alltoallv_init(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr) -{ - MPIL_Topo* topo; - MPIL_Topo_from_neighbor_comm(comm, &topo); - - MPIL_Neighbor_alltoallv_init_topo(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - topo, - comm, - info, - request_ptr); - - MPIL_Topo_free(&topo); - - return MPI_SUCCESS; -} - -int MPIL_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr) -{ - switch (mpix_neighbor_alltoallv_init_implementation) - { - case NEIGHBOR_ALLTOALLV_INIT_STANDARD: - return neighbor_alltoallv_init_standard(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - topo, - comm, - info, - request_ptr); - case NEIGHBOR_ALLTOALLV_INIT_LOCALITY: - return neighbor_alltoallv_init_locality_ext(sendbuf, - sendcounts, - sdispls, - global_sindices, - sendtype, - recvbuf, - recvcounts, - rdispls, - global_rindices, - recvtype, - topo, - comm, - info, - request_ptr); - default: - return neighbor_alltoallv_init_standard(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - topo, - comm, - info, - request_ptr); - } -} - -int MPIL_Neighbor_alltoallv_init_ext(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr) -{ - MPIL_Topo* topo; - MPIL_Topo_from_neighbor_comm(comm, &topo); - - MPIL_Neighbor_alltoallv_init_ext_topo(sendbuf, - sendcounts, - sdispls, - global_sindices, - sendtype, - recvbuf, - recvcounts, - rdispls, - global_rindices, - recvtype, - topo, - comm, - info, - request_ptr); - - MPIL_Topo_free(&topo); - - return MPI_SUCCESS; -} - -int neighbor_alltoallv_init_standard(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr) -{ - MPIL_Request* request; - init_neighbor_request(&request); - - int tag; - MPIL_Comm_tag(comm, &tag); - - request->global_n_msgs = topo->indegree + topo->outdegree; - allocate_requests(request->global_n_msgs, &(request->global_requests)); - - const char* send_buffer = (const char*)(sendbuf); - char* recv_buffer = (char*)(recvbuf); - int send_size, recv_size; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - int ierr = 0; - - for (int i = 0; i < topo->indegree; i++) - { - ierr += MPI_Recv_init(&(recv_buffer[rdispls[i] * recv_size]), - recvcounts[i], - recvtype, - topo->sources[i], - tag, - comm->global_comm, - &(request->global_requests[i])); - } - - for (int i = 0; i < topo->outdegree; i++) - { - ierr += MPI_Send_init(&(send_buffer[sdispls[i] * send_size]), - sendcounts[i], - sendtype, - topo->destinations[i], - tag, - comm->global_comm, - &(request->global_requests[topo->indegree + i])); - } - - *request_ptr = request; - - return ierr; -} - -int neighbor_alltoallv_init_locality(const void* sendbuffer, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuffer, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr) -{ - int rank; - MPI_Comm_rank(comm->global_comm, &rank); - - if (comm->local_comm == MPI_COMM_NULL) - { - MPIL_Comm_topo_init(comm); - } - - int* global_sdispls = NULL; - int* global_rdispls = NULL; - - int ctr; - - if (topo->indegree) - { - global_rdispls = (int*)malloc(topo->indegree * sizeof(int)); - ctr = 0; - for (int i = 0; i < topo->indegree; i++) - { - global_rdispls[i] = ctr; - ctr += recvcounts[i]; - } - } - - if (topo->outdegree) - { - global_sdispls = (int*)malloc(topo->outdegree * sizeof(int)); - ctr = 0; - for (int i = 0; i < topo->outdegree; i++) - { - global_sdispls[i] = ctr; - ctr += sendcounts[i]; - } - } - - long send_size = 0; - long recv_size = 0; - for (int i = 0; i < topo->indegree; i++) - { - recv_size += recvcounts[i]; - } - for (int i = 0; i < topo->outdegree; i++) - { - send_size += sendcounts[i]; - } - - long first_send; - MPI_Exscan(&send_size, &first_send, 1, MPI_LONG, MPI_SUM, comm->global_comm); - if (rank == 0) - { - first_send = 0; - } - - long* global_send_indices = NULL; - long* global_recv_indices = NULL; - - if (recv_size) - { - global_recv_indices = (long*)malloc(recv_size * sizeof(long)); - } - if (send_size) - { - global_send_indices = (long*)malloc(send_size * sizeof(long)); - } - for (int i = 0; i < send_size; i++) - { - global_send_indices[i] = first_send + i; - } - - MPIL_Neighbor_alltoallv_topo(global_send_indices, - sendcounts, - global_sdispls, - MPI_LONG, - global_recv_indices, - recvcounts, - global_rdispls, - MPI_LONG, - topo, - comm); - - int err = neighbor_alltoallv_init_locality_ext(sendbuffer, - sendcounts, - sdispls, - global_send_indices, - sendtype, - recvbuffer, - recvcounts, - rdispls, - global_recv_indices, - recvtype, - topo, - comm, - info, - request_ptr); - - free(global_send_indices); - free(global_recv_indices); - - free(global_sdispls); - free(global_rdispls); - - return err; -} - -// Locality-Aware Extension to Persistent Neighbor Alltoallv -// Needs global indices for each send and receive -int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuffer, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr) -{ - if (comm->local_comm == MPI_COMM_NULL) - { - MPIL_Comm_topo_init(comm); - } - - MPIL_Request* request; - init_neighbor_request(&request); - - // Initialize Locality-Aware Communication Strategy (3-Step) - // E.G. Determine which processes talk to eachother at every step - // TODO : instead of mpi_comm, use comm - // - will need to create local_comm in dist_graph_create_adjacent... - init_locality(topo->outdegree, - topo->destinations, - sdispls, - sendcounts, - topo->indegree, - topo->sources, - rdispls, - recvcounts, - global_sindices, - global_rindices, - sendtype, - recvtype, - comm, // communicator used in dist_graph_create_adjacent - request); - - request->sendbuf = sendbuffer; - request->recvbuf = recvbuffer; - MPI_Type_size(recvtype, &(request->recv_size)); - - // Local L Communication - // init_communication(sendbuffer, - init_communication(request->locality->local_L_comm->send_data->buffer, - request->locality->local_L_comm->send_data->num_msgs, - request->locality->local_L_comm->send_data->procs, - request->locality->local_L_comm->send_data->indptr, - sendtype, - request->locality->local_L_comm->recv_data->buffer, - request->locality->local_L_comm->recv_data->num_msgs, - request->locality->local_L_comm->recv_data->procs, - request->locality->local_L_comm->recv_data->indptr, - recvtype, - request->locality->local_L_comm->tag, - comm->local_comm, - &(request->local_L_n_msgs), - &(request->local_L_requests)); - - // Local S Communication - init_communication(request->locality->local_S_comm->send_data->buffer, - request->locality->local_S_comm->send_data->num_msgs, - request->locality->local_S_comm->send_data->procs, - request->locality->local_S_comm->send_data->indptr, - sendtype, - request->locality->local_S_comm->recv_data->buffer, - request->locality->local_S_comm->recv_data->num_msgs, - request->locality->local_S_comm->recv_data->procs, - request->locality->local_S_comm->recv_data->indptr, - recvtype, - request->locality->local_S_comm->tag, - comm->local_comm, - &(request->local_S_n_msgs), - &(request->local_S_requests)); - - // Global Communication - init_communication(request->locality->global_comm->send_data->buffer, - request->locality->global_comm->send_data->num_msgs, - request->locality->global_comm->send_data->procs, - request->locality->global_comm->send_data->indptr, - sendtype, - request->locality->global_comm->recv_data->buffer, - request->locality->global_comm->recv_data->num_msgs, - request->locality->global_comm->recv_data->procs, - request->locality->global_comm->recv_data->indptr, - recvtype, - request->locality->global_comm->tag, - comm->global_comm, - &(request->global_n_msgs), - &(request->global_requests)); - - // Local R Communication - init_communication(request->locality->local_R_comm->send_data->buffer, - request->locality->local_R_comm->send_data->num_msgs, - request->locality->local_R_comm->send_data->procs, - request->locality->local_R_comm->send_data->indptr, - sendtype, - request->locality->local_R_comm->recv_data->buffer, - request->locality->local_R_comm->recv_data->num_msgs, - request->locality->local_R_comm->recv_data->procs, - request->locality->local_R_comm->recv_data->indptr, - recvtype, - request->locality->local_R_comm->tag, - comm->local_comm, - &(request->local_R_n_msgs), - &(request->local_R_requests)); - - *request_ptr = request; - - return MPI_SUCCESS; -} - -void init_neighbor_request(MPIL_Request** request_ptr) -{ - init_request(request_ptr); - MPIL_Request* request = *request_ptr; - - request->start_function = neighbor_start; - request->wait_function = neighbor_wait; -} - -int init_communication(const void* sendbuffer, - int n_sends, - const int* send_procs, - const int* send_ptr, - MPI_Datatype sendtype, - void* recvbuffer, - int n_recvs, - const int* recv_procs, - const int* recv_ptr, - MPI_Datatype recvtype, - int tag, - MPI_Comm comm, - int* n_request_ptr, - MPI_Request** request_ptr) -{ - int ierr = 0; - int start, size; - int send_size, recv_size; - - char* send_buffer = (char*)sendbuffer; - char* recv_buffer = (char*)recvbuffer; - MPI_Type_size(sendtype, &send_size); - MPI_Type_size(recvtype, &recv_size); - - MPI_Request* requests; - *n_request_ptr = n_recvs + n_sends; - allocate_requests(*n_request_ptr, &requests); - - for (int i = 0; i < n_recvs; i++) - { - start = recv_ptr[i]; - size = recv_ptr[i + 1] - start; - - ierr += MPI_Recv_init(&(recv_buffer[start * recv_size]), - size, - recvtype, - recv_procs[i], - tag, - comm, - &(requests[i])); - } - - for (int i = 0; i < n_sends; i++) - { - start = send_ptr[i]; - size = send_ptr[i + 1] - start; - - ierr += MPI_Send_init(&(send_buffer[start * send_size]), - size, - sendtype, - send_procs[i], - tag, - comm, - &(requests[n_recvs + i])); - } - - *request_ptr = requests; - - return ierr; -} diff --git a/src/neighborhood/neighbor_init.h b/src/neighborhood/neighbor_init.h deleted file mode 100644 index 0956796eb..000000000 --- a/src/neighborhood/neighbor_init.h +++ /dev/null @@ -1,161 +0,0 @@ -#ifndef MPI_ADVANCE_NEIGHBOR_PERSISTENT_H -#define MPI_ADVANCE_NEIGHBOR_PERSISTENT_H - -#include "communicator/locality_comm.h" -#include "neighbor.h" -#include "persistent/persistent.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum NeighborAlltoallvInitMethod -{ - NEIGHBOR_ALLTOALLV_INIT_STANDARD, - NEIGHBOR_ALLTOALLV_INIT_LOCALITY -}; -extern enum NeighborAlltoallvInitMethod mpix_neighbor_alltoallv_init_implementation; - -typedef int (*neighbor_alltoallv_init_ftn)(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr); - -// Starting locality-aware requests -// 1. Start Local_L -// 2. Start and wait for local_S -// 3. Start global -int neighbor_start(MPIL_Request* request); - -// Wait for locality-aware requests -// 1. Wait for global -// 2. Start and wait for local_R -// 3. Wait for local_L -int neighbor_wait(MPIL_Request* request, MPI_Status* status); - -void init_neighbor_request(MPIL_Request** request_ptr); - -// Standard Persistent Neighbor Alltoallv -// Extension takes array of requests instead of single request -// 'requests' must be of size indegree+outdegree! -int MPIL_Neighbor_alltoallv_init_topo(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr); -int MPIL_Neighbor_alltoallv_init(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr); - -// Locality-Aware Extension to Persistent Neighbor Alltoallv -// Needs global indices for each send and receive -int MPIL_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr); -int MPIL_Neighbor_alltoallv_init_ext(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr); - -int neighbor_alltoallv_init_standard(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr); -int neighbor_alltoallv_init_locality(const void* sendbuf, - const int sendcounts[], - const int sdispls[], - MPI_Datatype sendtype, - void* recvbuf, - const int recvcounts[], - const int rdispls[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr); -int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, - const int sendcounts[], - const int sdispls[], - const long global_sindices[], - MPI_Datatype sendtype, - void* recvbuffer, - const int recvcounts[], - const int rdispls[], - const long global_rindices[], - MPI_Datatype recvtype, - MPIL_Topo* topo, - MPIL_Comm* comm, - MPIL_Info* info, - MPIL_Request** request_ptr); - -void init_locality(const int n_sends, - const int* send_procs, - const int* send_indptr, - const int* sendcounts, - const int n_recvs, - const int* recv_procs, - const int* recv_indptr, - const int* recvcounts, - const long* global_send_indices, - const long* global_recv_indices, - const MPI_Datatype sendtype, - const MPI_Datatype recvtype, - MPIL_Comm* mpix_comm, - MPIL_Request* request); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/neighborhood/sparse_coll.c b/src/neighborhood/sparse_coll.c deleted file mode 100644 index 123a0b18e..000000000 --- a/src/neighborhood/sparse_coll.c +++ /dev/null @@ -1,66 +0,0 @@ -#include "sparse_coll.h" - -#include -#include - -int MPIL_Alltoall_crs(const int send_nnz, - const int* dest, - const int sendcount, - MPI_Datatype sendtype, - const void* sendvals, - int* recv_nnz, - int** src_ptr, - int recvcount, - MPI_Datatype recvtype, - void** recvvals_ptr, - MPIL_Info* xinfo, - MPIL_Comm* xcomm) -{ - return alltoall_crs_personalized(send_nnz, - dest, - sendcount, - sendtype, - sendvals, - recv_nnz, - src_ptr, - recvcount, - recvtype, - recvvals_ptr, - xinfo, - xcomm); -} - -int MPIL_Alltoallv_crs(const int send_nnz, - const int send_size, - const int* dest, - const int* sendcounts, - const int* sdispls, - MPI_Datatype sendtype, - const void* sendvals, - int* recv_nnz, - int* recv_size, - int** src_ptr, - int** recvcounts_ptr, - int** rdispls_ptr, - MPI_Datatype recvtype, - void** recvvals_ptr, - MPIL_Info* xinfo, - MPIL_Comm* xcomm) -{ - return alltoallv_crs_personalized(send_nnz, - send_size, - dest, - sendcounts, - sdispls, - sendtype, - sendvals, - recv_nnz, - recv_size, - src_ptr, - recvcounts_ptr, - rdispls_ptr, - recvtype, - recvvals_ptr, - xinfo, - xcomm); -} diff --git a/src/neighborhood/tests/CMakeLists.txt b/src/neighborhood/tests/CMakeLists.txt deleted file mode 100644 index 308faa42d..000000000 --- a/src/neighborhood/tests/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -## Grab all .cpp files in test directory -file(GLOB CPP_SOURCES CONFIGURE_DEPENDS "*.cpp") - -## All should be compiled appropriately (cxx vs nvcc vs hipcc) -set_source_files_properties(${CPP_SOURCES} PROPERTIES LANGUAGE ${locality_aware_LANG}) - -## Go through each CPP file -foreach(src ${CPP_SOURCES}) - - ## Grab the name without full path or extension - get_filename_component(exec_name ${src} NAME_WE) - - ## Create executable - add_executable(${exec_name} ${src}) - - # Link with MPI - target_link_libraries(${exec_name} locality_aware ${MPI_LIBRARIES}) - - # Add to CTEST - add_test(NAME ${exec_name}_Test COMMAND ${MPIRUN} -n 16 ./${exec_name}) -endforeach() - - diff --git a/src/neighborhood/tests/test_neighbor_alltoallv_init.cpp b/src/neighborhood/tests/test_neighbor_alltoallv_init.cpp deleted file mode 100644 index 7a271bdf4..000000000 --- a/src/neighborhood/tests/test_neighbor_alltoallv_init.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include - -#include "neighbor_data.hpp" - -void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::vector& mpix_recv_vals, int s) -{ - for (int i = 0; i < s; i++) - { - if (pmpi_recv_vals[i] != mpix_recv_vals[i]) - { - fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, - pmpi_recv_vals[i], mpix_recv_vals[i]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - -// Get MPI Information - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Initial communication info (standard) - int local_size = 10000; // Number of variables each rank stores - MPIL_Data send_data; - MPIL_Data recv_data; - form_initial_communicator(local_size, &send_data, &recv_data); - std::vector global_send_idx(send_data.size_msgs); - std::vector global_recv_idx(recv_data.size_msgs); - form_global_indices(local_size, send_data, recv_data, global_send_idx, global_recv_idx); - - std::vector pmpi_recv_vals(recv_data.size_msgs); - std::vector mpix_recv_vals(recv_data.size_msgs); - - std::vector send_vals(local_size); - int val = local_size*rank; - for (int i = 0; i < local_size; i++) - { - send_vals[i] = val++; - } - std::vector alltoallv_send_vals(send_data.size_msgs); - for (int i = 0; i < send_data.size_msgs; i++) - alltoallv_send_vals[i] = send_vals[send_data.indices[i]]; - - // Some MPI versions require sendcounts and recvcounts - // to be non-NULL - int* send_counts = send_data.counts.data(); - if (send_data.counts.data() == NULL) - send_counts = new int[1]; - int* recv_counts = recv_data.counts.data(); - if (recv_data.counts.data() == NULL) - recv_counts = new int[1]; - - - MPI_Comm std_comm; - MPI_Status status; - MPIL_Comm* xcomm; - MPIL_Request* xrequest; - - MPIL_Info* xinfo; - MPIL_Info_init(&xinfo); - - - - // Standard MPI Dist Graph Create - MPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, - recv_data.num_msgs, - recv_data.procs.data(), - recv_data.counts.data(), - send_data.num_msgs, - send_data.procs.data(), - send_data.counts.data(), - MPI_INFO_NULL, - 0, - &std_comm); - - // MPI Advance Dist Graph Create - MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, - recv_data.num_msgs, - recv_data.procs.data(), - recv_data.counts.data(), - send_data.num_msgs, - send_data.procs.data(), - send_data.counts.data(), - xinfo, - 0, - &xcomm); - // Update Locality : 4 PPN (for single-node tests) - update_locality(xcomm, 4); - - - - // Standard MPI Implementation of Alltoallv - MPI_Neighbor_alltoallv(alltoallv_send_vals.data(), - send_counts, - send_data.indptr.data(), - MPI_INT, - pmpi_recv_vals.data(), - recv_counts, - recv_data.indptr.data(), - MPI_INT, - std_comm); - - - // Simple Persistent MPI Advance Implementation - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), - send_data.counts.data(), - send_data.indptr.data(), - MPI_INT, - mpix_recv_vals.data(), - recv_data.counts.data(), - recv_data.indptr.data(), - MPI_INT, - xcomm, - xinfo, - &xrequest); - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - - - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), - send_data.counts.data(), - send_data.indptr.data(), - MPI_INT, - mpix_recv_vals.data(), - recv_data.counts.data(), - recv_data.indptr.data(), - MPI_INT, - xcomm, - xinfo, - &xrequest); - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - - - - - - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), - send_data.counts.data(), - send_data.indptr.data(), - global_send_idx.data(), - MPI_INT, - mpix_recv_vals.data(), - recv_data.counts.data(), - recv_data.indptr.data(), - global_recv_idx.data(), - MPI_INT, - xcomm, - xinfo, - &xrequest); - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - - - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), - send_data.counts.data(), - send_data.indptr.data(), - global_send_idx.data(), - MPI_INT, - mpix_recv_vals.data(), - recv_data.counts.data(), - recv_data.indptr.data(), - global_recv_idx.data(), - MPI_INT, - xcomm, - xinfo, - &xrequest); - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - - - MPIL_Info_free(&xinfo); - MPIL_Comm_free(&xcomm); - MPI_Comm_free(&std_comm); - - if (send_data.counts.data() == NULL) - delete[] send_counts; - if (recv_data.counts.data() == NULL) - delete[] recv_counts; - - - MPI_Finalize(); - return 0; -} // end of main() // - - diff --git a/src/neighborhood/tests/test_neighbor_reorder.cpp b/src/neighborhood/tests/test_neighbor_reorder.cpp deleted file mode 100644 index 2ea945e6b..000000000 --- a/src/neighborhood/tests/test_neighbor_reorder.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include - -#include "neighbor_data.hpp" - -void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::vector& mpix_recv_vals, int s) -{ - for (int i = 0; i < s; i++) - { - if (pmpi_recv_vals[i] != mpix_recv_vals[i]) - { - fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, - pmpi_recv_vals[i], mpix_recv_vals[i]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - // Get MPI Information - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Initial communication info (standard) - int local_size = 10000; // Number of variables each rank stores - MPIL_Data send_data; - MPIL_Data recv_data; - form_initial_communicator(local_size, &send_data, &recv_data); - std::vector global_send_idx(send_data.size_msgs); - std::vector global_recv_idx(recv_data.size_msgs); - form_global_indices(local_size, send_data, recv_data, global_send_idx, global_recv_idx); - - // Test correctness of communication - std::vector mpix_recv_vals(recv_data.size_msgs); - std::vector pmpi_recv_vals(recv_data.size_msgs); - - std::vector send_vals(local_size); - int val = local_size*rank; - for (int i = 0; i < local_size; i++) - { - send_vals[i] = val++; - } - - std::vector alltoallv_send_vals(send_data.size_msgs); - for (int i = 0; i < send_data.size_msgs; i++) - alltoallv_send_vals[i] = send_vals[send_data.indices[i]]; - - MPI_Comm std_comm; - MPI_Status status; - MPIL_Comm* xcomm; - MPIL_Request* xrequest; - - MPIL_Info* xinfo; - MPIL_Info_init(&xinfo); - - // Standard MPI Dist Graph Create - MPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, - recv_data.num_msgs, - recv_data.procs.data(), - recv_data.counts.data(), - send_data.num_msgs, - send_data.procs.data(), - send_data.counts.data(), - MPI_INFO_NULL, - 0, - &std_comm); - - // MPI Advance Dist Graph Create - MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, - recv_data.num_msgs, - recv_data.procs.data(), - recv_data.counts.data(), - send_data.num_msgs, - send_data.procs.data(), - send_data.counts.data(), - xinfo, - 0, - &xcomm); - - // Update Locality : 4 PPN (for single-node tests) - update_locality(xcomm, 4); - - - // Standard MPI Implementation of Alltoallv - int* send_counts = send_data.counts.data(); - if (send_data.counts.data() == NULL) - send_counts = new int[1]; - int* recv_counts = recv_data.counts.data(); - if (recv_data.counts.data() == NULL) - recv_counts = new int[1]; - MPI_Neighbor_alltoallv(alltoallv_send_vals.data(), - send_counts, - send_data.indptr.data(), - MPI_INT, - pmpi_recv_vals.data(), - recv_counts, - recv_data.indptr.data(), - MPI_INT, - std_comm); - if (send_data.counts.data() == NULL) - delete[] send_counts; - if (recv_data.counts.data() == NULL) - delete[] recv_counts; - - - // Simple Persistent MPI Advance Implementation - MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), - send_data.counts.data(), - send_data.indptr.data(), - MPI_INT, - mpix_recv_vals.data(), - recv_data.counts.data(), - recv_data.indptr.data(), - MPI_INT, - xcomm, - xinfo, - &xrequest); - - // Reorder during first send/recv - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - xrequest->reorder = 1; - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - - // Standard send/recv with reordered recvs - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - - MPIL_Request_free(&xrequest); - MPIL_Info_free(&xinfo); - MPIL_Comm_free(&xcomm); - MPI_Comm_free(&std_comm); - - MPI_Finalize(); - return 0; -} // end of main() // - diff --git a/src/neighborhood/tests/test_neighbor_topo_alltoallv_init.cpp b/src/neighborhood/tests/test_neighbor_topo_alltoallv_init.cpp deleted file mode 100644 index 7b1dec1a0..000000000 --- a/src/neighborhood/tests/test_neighbor_topo_alltoallv_init.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include - -#include "neighbor_data.hpp" - -void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::vector& mpix_recv_vals, int s) -{ - for (int i = 0; i < s; i++) - { - if (pmpi_recv_vals[i] != mpix_recv_vals[i]) - { - fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, - pmpi_recv_vals[i], mpix_recv_vals[i]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - -// Get MPI Information - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Initial communication info (standard) - int local_size = 10000; // Number of variables each rank stores - MPIL_Data send_data; - MPIL_Data recv_data; - form_initial_communicator(local_size, &send_data, &recv_data); - std::vector global_send_idx(send_data.size_msgs); - std::vector global_recv_idx(recv_data.size_msgs); - form_global_indices(local_size, send_data, recv_data, global_send_idx, global_recv_idx); - - std::vector pmpi_recv_vals(recv_data.size_msgs); - std::vector mpix_recv_vals(recv_data.size_msgs); - - std::vector send_vals(local_size); - int val = local_size*rank; - for (int i = 0; i < local_size; i++) - { - send_vals[i] = val++; - } - std::vector alltoallv_send_vals(send_data.size_msgs); - for (int i = 0; i < send_data.size_msgs; i++) - alltoallv_send_vals[i] = send_vals[send_data.indices[i]]; - - // Some MPI versions require sendcounts and recvcounts - // to be non-NULL - int* send_counts = send_data.counts.data(); - if (send_data.counts.data() == NULL) - send_counts = new int[1]; - int* recv_counts = recv_data.counts.data(); - if (recv_data.counts.data() == NULL) - recv_counts = new int[1]; - - - MPI_Comm std_comm; - MPI_Status status; - MPIL_Comm* xcomm; - MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - update_locality(xcomm, 4); - MPIL_Request* xrequest; - - MPIL_Info* xinfo; - MPIL_Info_init(&xinfo); - - MPIL_Topo* topo; - MPIL_Topo_init(recv_data.num_msgs, - recv_data.procs.data(), - recv_data.counts.data(), - send_data.num_msgs, - send_data.procs.data(), - send_data.counts.data(), - xinfo, - &topo); - - // Standard MPI Dist Graph Create - MPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, - recv_data.num_msgs, - recv_data.procs.data(), - recv_data.counts.data(), - send_data.num_msgs, - send_data.procs.data(), - send_data.counts.data(), - MPI_INFO_NULL, - 0, - &std_comm); - - // Standard MPI Implementation of Alltoallv - MPI_Neighbor_alltoallv(alltoallv_send_vals.data(), - send_counts, - send_data.indptr.data(), - MPI_INT, - pmpi_recv_vals.data(), - recv_counts, - recv_data.indptr.data(), - MPI_INT, - std_comm); - - - // Simple Persistent MPI Advance Implementation - neighbor_alltoallv_init_standard(alltoallv_send_vals.data(), - send_data.counts.data(), - send_data.indptr.data(), - MPI_INT, - mpix_recv_vals.data(), - recv_data.counts.data(), - recv_data.indptr.data(), - MPI_INT, - topo, - xcomm, - xinfo, - &xrequest); - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - - - // Locality-Aware MPI Advance Implementation - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - neighbor_alltoallv_init_locality_ext(alltoallv_send_vals.data(), - send_data.counts.data(), - send_data.indptr.data(), - global_send_idx.data(), - MPI_INT, - mpix_recv_vals.data(), - recv_data.counts.data(), - recv_data.indptr.data(), - global_recv_idx.data(), - MPI_INT, - topo, - xcomm, - xinfo, - &xrequest); - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - - - // Partial Locality-Aware MPI Advance Implementation - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - neighbor_alltoallv_init_locality(alltoallv_send_vals.data(), - send_data.counts.data(), - send_data.indptr.data(), - MPI_INT, - mpix_recv_vals.data(), - recv_data.counts.data(), - recv_data.indptr.data(), - MPI_INT, - topo, - xcomm, - xinfo, - &xrequest); - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, recv_data.size_msgs); - - MPIL_Topo_free(&topo); - MPIL_Info_free(&xinfo); - MPIL_Comm_free(&xcomm); - MPI_Comm_free(&std_comm); - - if (send_data.counts.data() == NULL) - delete[] send_counts; - if (recv_data.counts.data() == NULL) - delete[] recv_counts; - - MPI_Finalize(); - return 0; -} // end of main() // - - diff --git a/src/neighborhood/tests/test_suitesparse_alltoall_crs.cpp b/src/neighborhood/tests/test_suitesparse_alltoall_crs.cpp deleted file mode 100644 index 76e4b0393..000000000 --- a/src/neighborhood/tests/test_suitesparse_alltoall_crs.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - -void compare_alltoall_crs_results(int n_recvs, int send_msgs, int* recvvals, int* src, - std::vector& proc_counts) -{ - if (n_recvs != send_msgs) - { - fprintf(stderr, "NRecvs incorrect (%d), should be %d\n", n_recvs, send_msgs); - MPI_Abort(MPI_COMM_WORLD, -1); - } - for (int i = 0; i < n_recvs; i++) - { - if (recvvals[i] != proc_counts[src[i]]) - { - fprintf(stderr, "RecvVals incorrect: position %d from %d, recvvals %d, should be %d\n", - i, src[i], recvvals[i], proc_counts[src[i]]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -void test_matrix(const char* filename) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - MPIL_Comm* xcomm; - MPIL_Info* xinfo; - - MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIL_Comm_topo_init(xcomm); - - MPIL_Info_init(&xinfo); - - // Update so there are 4 PPN rather than what MPI_Comm_split returns - update_locality(xcomm, 4); - - // Read suitesparse matrix - ParMat A; - readParMatrix(filename, A); - form_comm(A); - std::vector proc_counts(num_procs, 0); - for (int i = 0; i < A.send_comm.n_msgs; i++) - proc_counts[A.send_comm.procs[i]] = A.send_comm.counts[i]; - - int n_recvs; - int* src; - int* recvvals; - - /* TEST RMA VERSION */ - n_recvs = -1; - alltoall_crs_rma(A.recv_comm.n_msgs, A.recv_comm.procs.data(), 1, MPI_INT, - A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, - (void**)&recvvals, xinfo, xcomm); - compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); - MPIL_Free(src); - MPIL_Free(recvvals); - - - /* TEST PERSONALIZED VERSION */ - n_recvs = -1; - alltoall_crs_personalized(A.recv_comm.n_msgs, A.recv_comm.procs.data(), 1, MPI_INT, - A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, - (void**)&recvvals, xinfo, xcomm); - compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); - MPIL_Free(src); - MPIL_Free(recvvals); - - - /* TEST PERSONALIZED LOCALITY VERSION */ - n_recvs = -1; - alltoall_crs_personalized_loc(A.recv_comm.n_msgs, A.recv_comm.procs.data(), 1, MPI_INT, - A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, - (void**)&recvvals, xinfo, xcomm); - compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); - MPIL_Free(src); - MPIL_Free(recvvals); - - /* TEST NONBLOCKING VERSION */ - n_recvs = -1; - alltoall_crs_nonblocking(A.recv_comm.n_msgs, A.recv_comm.procs.data(), 1, MPI_INT, - A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, - (void**)&recvvals, xinfo, xcomm); - compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); - MPIL_Free(src); - MPIL_Free(recvvals); - - /* TEST NONBLOCKING LOCALITY VERSION */ - n_recvs = -1; - alltoall_crs_nonblocking_loc(A.recv_comm.n_msgs, A.recv_comm.procs.data(), 1, MPI_INT, - A.recv_comm.counts.data(), &n_recvs, &src, 1, MPI_INT, - (void**)&recvvals, xinfo, xcomm); - compare_alltoall_crs_results(n_recvs, A.send_comm.n_msgs, recvvals, src, proc_counts); - MPIL_Free(src); - MPIL_Free(recvvals); - - MPIL_Info_free(&xinfo); - MPIL_Comm_free(&xcomm); -} - - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - test_matrix("../../../../test_data/dwt_162.pm"); - test_matrix("../../../../test_data/odepa400.pm"); - test_matrix("../../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../../test_data/bcsstk01.pm"); - test_matrix("../../../../test_data/west0132.pm"); - test_matrix("../../../../test_data/gams10a.pm"); - test_matrix("../../../../test_data/gams10am.pm"); - test_matrix("../../../../test_data/D_10.pm"); - test_matrix("../../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../../test_data/msc01050.pm"); - test_matrix("../../../../test_data/SmaGri.pm"); - test_matrix("../../../../test_data/radfr1.pm"); - test_matrix("../../../../test_data/bibd_49_3.pm"); - test_matrix("../../../../test_data/can_1054.pm"); - test_matrix("../../../../test_data/can_1072.pm"); - test_matrix("../../../../test_data/lp_woodw.pm"); - test_matrix("../../../../test_data/lp_sctap2.pm"); - MPI_Finalize(); - return 0; -} // end of main() // - - - diff --git a/src/neighborhood/tests/test_suitesparse_alltoallv_crs.cpp b/src/neighborhood/tests/test_suitesparse_alltoallv_crs.cpp deleted file mode 100644 index d3ec8e8f8..000000000 --- a/src/neighborhood/tests/test_suitesparse_alltoallv_crs.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - -void compare_alltoallv_crs_results(int n_recvs, int send_msgs, int s_recvs, int send_size, - int* src, std::vector& proc_counts, int* recvcounts, std::vector& proc_displs, - std::vector& send_idx, int* rdispls, long* recvvals, int first_col) -{ - if (n_recvs != send_msgs) - { - fprintf(stderr, "NRecvs incorrect (%d), should be %d\n", n_recvs, send_msgs); - MPI_Abort(MPI_COMM_WORLD, -1); - } - if (s_recvs != send_size) - { - fprintf(stderr, "SRecvs incorrect (%d), should be %d\n", s_recvs, send_size); - MPI_Abort(MPI_COMM_WORLD, -1); - } - for (int i = 0; i < n_recvs; i++) - { - int proc = src[i]; - if (recvcounts[i] != proc_counts[proc]) - { - fprintf(stderr, "Incorrect count at position %d, process %d, recvcounts %d, should be %d\n", - i, proc, recvcounts[i], proc_counts[proc]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - for (int j = 0; j < recvcounts[i]; j++) - { - if (recvvals[rdispls[i] + j] - first_col != send_idx[proc_displs[proc] + j]) - { - fprintf(stderr, "Incorrect recvval from proc %d, position %d, getting %ld, should be %d\n", - proc, j, recvvals[rdispls[i] + j] - first_col, send_idx[proc_displs[proc] + j]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } - } -} - -void test_matrix(const char* filename) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - MPIL_Comm* xcomm; - MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - MPIL_Comm_topo_init(xcomm); - - MPIL_Info* xinfo; - MPIL_Info_init(&xinfo); - - // Update so there are 4 PPN rather than what MPI_Comm_split returns - update_locality(xcomm, 4); - - // Read suitesparse matrix - ParMat A; - readParMatrix(filename, A); - form_comm(A); - std::vector proc_counts(num_procs, 0); - std::vector proc_displs(num_procs, 0); - for (int i = 0; i < A.send_comm.n_msgs; i++) - { - int proc = A.send_comm.procs[i]; - proc_counts[proc] = A.send_comm.counts[i]; - proc_displs[proc] = A.send_comm.ptr[i]; - } - - int n_recvs, s_recvs; - int *src, *recvcounts, *rdispls; - long *recvvals; - - /* TEST PERSONALIZED VERSION */ - s_recvs = -1; - alltoallv_crs_personalized(A.recv_comm.n_msgs, A.recv_comm.size_msgs, A.recv_comm.procs.data(), - A.recv_comm.counts.data(), A.recv_comm.ptr.data(), MPI_LONG, - A.off_proc_columns.data(), - &n_recvs, &s_recvs, &src, &recvcounts, &rdispls, MPI_LONG, (void**)&recvvals, xinfo, xcomm); - compare_alltoallv_crs_results(n_recvs, A.send_comm.n_msgs, s_recvs, A.send_comm.size_msgs, src, - proc_counts, recvcounts, proc_displs, A.send_comm.idx, rdispls, recvvals, A.first_col); - MPIL_Free(src); - MPIL_Free(recvcounts); - MPIL_Free(rdispls); - MPIL_Free(recvvals); - - /* TEST NONBLOCKING VERSION */ - s_recvs = -1; - alltoallv_crs_nonblocking(A.recv_comm.n_msgs, A.recv_comm.size_msgs, A.recv_comm.procs.data(), - A.recv_comm.counts.data(), A.recv_comm.ptr.data(), MPI_LONG, - A.off_proc_columns.data(), - &n_recvs, &s_recvs, &src, &recvcounts, &rdispls, MPI_LONG, (void**)&recvvals, xinfo, xcomm); - compare_alltoallv_crs_results(n_recvs, A.send_comm.n_msgs, s_recvs, A.send_comm.size_msgs, src, - proc_counts, recvcounts, proc_displs, A.send_comm.idx, rdispls, recvvals, A.first_col); - MPIL_Free(src); - MPIL_Free(recvcounts); - MPIL_Free(rdispls); - MPIL_Free(recvvals); - - /* TEST PERSONALIZED LOCALITY VERSION */ - s_recvs = -1; - alltoallv_crs_personalized_loc(A.recv_comm.n_msgs, A.recv_comm.size_msgs, A.recv_comm.procs.data(), - A.recv_comm.counts.data(), A.recv_comm.ptr.data(), MPI_LONG, - A.off_proc_columns.data(), - &n_recvs, &s_recvs, &src, &recvcounts, &rdispls, MPI_LONG, (void**)&recvvals, xinfo, xcomm); - compare_alltoallv_crs_results(n_recvs, A.send_comm.n_msgs, s_recvs, A.send_comm.size_msgs, src, - proc_counts, recvcounts, proc_displs, A.send_comm.idx, rdispls, recvvals, A.first_col); - MPIL_Free(src); - MPIL_Free(recvcounts); - MPIL_Free(rdispls); - MPIL_Free(recvvals); - - /* TEST PERSONALIZED LOCALITY VERSION */ - s_recvs = -1; - alltoallv_crs_nonblocking_loc(A.recv_comm.n_msgs, A.recv_comm.size_msgs, A.recv_comm.procs.data(), - A.recv_comm.counts.data(), A.recv_comm.ptr.data(), MPI_LONG, - A.off_proc_columns.data(), - &n_recvs, &s_recvs, &src, &recvcounts, &rdispls, MPI_LONG, (void**)&recvvals, xinfo, xcomm); - compare_alltoallv_crs_results(n_recvs, A.send_comm.n_msgs, s_recvs, A.send_comm.size_msgs, src, - proc_counts, recvcounts, proc_displs, A.send_comm.idx, rdispls, recvvals, A.first_col); - MPIL_Free(src); - MPIL_Free(recvcounts); - MPIL_Free(rdispls); - MPIL_Free(recvvals); - - MPIL_Info_free(&xinfo); - MPIL_Comm_free(&xcomm); -} - - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - test_matrix("../../../../test_data/dwt_162.pm"); - test_matrix("../../../../test_data/odepa400.pm"); - test_matrix("../../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../../test_data/bcsstk01.pm"); - test_matrix("../../../../test_data/west0132.pm"); - test_matrix("../../../../test_data/gams10a.pm"); - test_matrix("../../../../test_data/gams10am.pm"); - test_matrix("../../../../test_data/D_10.pm"); - test_matrix("../../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../../test_data/msc01050.pm"); - test_matrix("../../../../test_data/SmaGri.pm"); - test_matrix("../../../../test_data/radfr1.pm"); - test_matrix("../../../../test_data/bibd_49_3.pm"); - test_matrix("../../../../test_data/can_1054.pm"); - test_matrix("../../../../test_data/can_1072.pm"); - test_matrix("../../../../test_data/lp_woodw.pm"); - test_matrix("../../../../test_data/lp_sctap2.pm"); - MPI_Finalize(); - return 0; -} // end of main() // - - diff --git a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv.cpp b/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv.cpp deleted file mode 100644 index 000404218..000000000 --- a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - -void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::vector& mpix_recv_vals, int s) -{ - for (int i = 0; i < s; i++) - { - if (pmpi_recv_vals[i] != mpix_recv_vals[i]) - { - fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, - pmpi_recv_vals[i], mpix_recv_vals[i]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -void test_matrix(const char* filename) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Read suitesparse matrix - ParMat A; - int idx; - readParMatrix(filename, A); - form_comm(A); - - std::vector pmpi_recv_vals, mpix_recv_vals; - std::vector send_vals, alltoallv_send_vals; - std::vector send_indices; - - if (A.on_proc.n_cols) - { - send_vals.resize(A.on_proc.n_cols); - std::iota(send_vals.begin(), send_vals.end(), 0); - for (int i = 0; i < A.on_proc.n_cols; i++) - send_vals[i] += (rank*1000); - } - - if (A.recv_comm.size_msgs) - { - pmpi_recv_vals.resize(A.recv_comm.size_msgs); - mpix_recv_vals.resize(A.recv_comm.size_msgs); - } - - if (A.send_comm.size_msgs) - { - alltoallv_send_vals.resize(A.send_comm.size_msgs); - send_indices.resize(A.send_comm.size_msgs); - for (int i = 0; i < A.send_comm.size_msgs; i++) - { - idx = A.send_comm.idx[i]; - alltoallv_send_vals[i] = send_vals[idx]; - send_indices[i] = A.send_comm.idx[i] + A.first_col; - } - } - - communicate(A, send_vals, mpix_recv_vals, MPI_INT); - - MPI_Comm std_comm; - MPI_Status status; - MPIL_Comm* xcomm; - MPIL_Request* xrequest; - MPIL_Info* xinfo; - - MPIL_Info_init(&xinfo); - - int* s = A.recv_comm.procs.data(); - if (A.recv_comm.n_msgs == 0) - s = MPI_WEIGHTS_EMPTY; - int* d = A.send_comm.procs.data(); - if (A.send_comm.n_msgs == 0) - d = MPI_WEIGHTS_EMPTY; - - PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, - A.recv_comm.n_msgs, - s, - MPI_UNWEIGHTED, - A.send_comm.n_msgs, - d, - MPI_UNWEIGHTED, - MPI_INFO_NULL, - 0, - &std_comm); - - int* send_counts = A.send_comm.counts.data(); - if (A.send_comm.counts.data() == NULL) - send_counts = new int[1]; - int* recv_counts = A.recv_comm.counts.data(); - if (A.recv_comm.counts.data() == NULL) - recv_counts = new int[1]; - PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), - send_counts, - A.send_comm.ptr.data(), - MPI_INT, - pmpi_recv_vals.data(), - recv_counts, - A.recv_comm.ptr.data(), - MPI_INT, - std_comm); - if (A.send_comm.counts.data() == NULL) - delete[] send_counts; - if (A.recv_comm.counts.data() == NULL) - delete[] recv_counts; - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, - A.recv_comm.n_msgs, - A.recv_comm.procs.data(), - MPI_UNWEIGHTED, - A.send_comm.n_msgs, - A.send_comm.procs.data(), - MPI_UNWEIGHTED, - xinfo, - 0, - &xcomm); - - update_locality(xcomm, 4); - - - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - xcomm); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - MPIL_Topo *topo; - MPIL_Topo_from_neighbor_comm(xcomm, &topo); - - // 2. Node-Aware Communication - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - neighbor_alltoallv_standard(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - topo, - xcomm); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - // 3. MPI Advance - Optimized Communication - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - neighbor_alltoallv_locality(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - topo, - xcomm); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - MPIL_Topo_free(&topo); - MPIL_Info_free(&xinfo); - MPIL_Comm_free(&xcomm); - PMPI_Comm_free(&std_comm); -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - test_matrix("../../../../test_data/dwt_162.pm"); - test_matrix("../../../../test_data/odepa400.pm"); - test_matrix("../../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../../test_data/bcsstk01.pm"); - test_matrix("../../../../test_data/west0132.pm"); - test_matrix("../../../../test_data/gams10a.pm"); - test_matrix("../../../../test_data/gams10am.pm"); - test_matrix("../../../../test_data/D_10.pm"); - test_matrix("../../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../../test_data/msc01050.pm"); - test_matrix("../../../../test_data/SmaGri.pm"); - test_matrix("../../../../test_data/radfr1.pm"); - test_matrix("../../../../test_data/bibd_49_3.pm"); - test_matrix("../../../../test_data/can_1054.pm"); - test_matrix("../../../../test_data/can_1072.pm"); - test_matrix("../../../../test_data/lp_sctap2.pm"); - test_matrix("../../../../test_data/lp_woodw.pm"); - MPI_Finalize(); - return 0; -} // end of main() // - - - diff --git a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_enum.cpp b/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_enum.cpp deleted file mode 100644 index b5353403c..000000000 --- a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_enum.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - -void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::vector& mpix_recv_vals, int s) -{ - for (int i = 0; i < s; i++) - { - if (pmpi_recv_vals[i] != mpix_recv_vals[i]) - { - fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, - pmpi_recv_vals[i], mpix_recv_vals[i]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -void test_matrix(const char* filename) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Read suitesparse matrix - ParMat A; - int idx; - readParMatrix(filename, A); - form_comm(A); - - std::vector pmpi_recv_vals, mpix_recv_vals; - std::vector send_vals, alltoallv_send_vals; - std::vector send_indices; - - if (A.on_proc.n_cols) - { - send_vals.resize(A.on_proc.n_cols); - std::iota(send_vals.begin(), send_vals.end(), 0); - for (int i = 0; i < A.on_proc.n_cols; i++) - send_vals[i] += (rank*1000); - } - - if (A.recv_comm.size_msgs) - { - pmpi_recv_vals.resize(A.recv_comm.size_msgs); - mpix_recv_vals.resize(A.recv_comm.size_msgs); - } - - if (A.send_comm.size_msgs) - { - alltoallv_send_vals.resize(A.send_comm.size_msgs); - send_indices.resize(A.send_comm.size_msgs); - for (int i = 0; i < A.send_comm.size_msgs; i++) - { - idx = A.send_comm.idx[i]; - alltoallv_send_vals[i] = send_vals[idx]; - send_indices[i] = A.send_comm.idx[i] + A.first_col; - } - } - - communicate(A, send_vals, mpix_recv_vals, MPI_INT); - - MPI_Comm std_comm; - MPI_Status status; - MPIL_Comm* xcomm; - MPIL_Request* xrequest; - MPIL_Info* xinfo; - - MPIL_Info_init(&xinfo); - - int* s = A.recv_comm.procs.data(); - if (A.recv_comm.n_msgs == 0) - s = MPI_WEIGHTS_EMPTY; - int* d = A.send_comm.procs.data(); - if (A.send_comm.n_msgs == 0) - d = MPI_WEIGHTS_EMPTY; - - PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, - A.recv_comm.n_msgs, - s, - MPI_UNWEIGHTED, - A.send_comm.n_msgs, - d, - MPI_UNWEIGHTED, - MPI_INFO_NULL, - 0, - &std_comm); - - int* send_counts = A.send_comm.counts.data(); - if (A.send_comm.counts.data() == NULL) - send_counts = new int[1]; - int* recv_counts = A.recv_comm.counts.data(); - if (A.recv_comm.counts.data() == NULL) - recv_counts = new int[1]; - PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), - send_counts, - A.send_comm.ptr.data(), - MPI_INT, - pmpi_recv_vals.data(), - recv_counts, - A.recv_comm.ptr.data(), - MPI_INT, - std_comm); - if (A.send_comm.counts.data() == NULL) - delete[] send_counts; - if (A.recv_comm.counts.data() == NULL) - delete[] recv_counts; - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, - A.recv_comm.n_msgs, - A.recv_comm.procs.data(), - MPI_UNWEIGHTED, - A.send_comm.n_msgs, - A.send_comm.procs.data(), - MPI_UNWEIGHTED, - xinfo, - 0, - &xcomm); - - update_locality(xcomm, 4); - - mpix_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_STANDARD; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - xcomm); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - mpix_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_LOCALITY; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - xcomm); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - MPIL_Info_free(&xinfo); - MPIL_Comm_free(&xcomm); - PMPI_Comm_free(&std_comm); -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - test_matrix("../../../../test_data/dwt_162.pm"); - test_matrix("../../../../test_data/odepa400.pm"); - test_matrix("../../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../../test_data/bcsstk01.pm"); - test_matrix("../../../../test_data/west0132.pm"); - test_matrix("../../../../test_data/gams10a.pm"); - test_matrix("../../../../test_data/gams10am.pm"); - test_matrix("../../../../test_data/D_10.pm"); - test_matrix("../../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../../test_data/msc01050.pm"); - test_matrix("../../../../test_data/SmaGri.pm"); - test_matrix("../../../../test_data/radfr1.pm"); - test_matrix("../../../../test_data/bibd_49_3.pm"); - test_matrix("../../../../test_data/can_1054.pm"); - test_matrix("../../../../test_data/can_1072.pm"); - test_matrix("../../../../test_data/lp_sctap2.pm"); - test_matrix("../../../../test_data/lp_woodw.pm"); - MPI_Finalize(); - return 0; -} // end of main() // - - - diff --git a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_init.cpp b/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_init.cpp deleted file mode 100644 index 800ce14ad..000000000 --- a/src/neighborhood/tests/test_suitesparse_neighbor_alltoallv_init.cpp +++ /dev/null @@ -1,260 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - -void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::vector& mpix_recv_vals, int s) -{ - for (int i = 0; i < s; i++) - { - if (pmpi_recv_vals[i] != mpix_recv_vals[i]) - { - fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, - pmpi_recv_vals[i], mpix_recv_vals[i]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -void test_matrix(const char* filename) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Read suitesparse matrix - ParMat A; - int idx; - readParMatrix(filename, A); - form_comm(A); - - std::vector pmpi_recv_vals, mpix_recv_vals; - std::vector send_vals, alltoallv_send_vals; - std::vector send_indices; - - if (A.on_proc.n_cols) - { - send_vals.resize(A.on_proc.n_cols); - std::iota(send_vals.begin(), send_vals.end(), 0); - for (int i = 0; i < A.on_proc.n_cols; i++) - send_vals[i] += (rank*1000); - } - - if (A.recv_comm.size_msgs) - { - pmpi_recv_vals.resize(A.recv_comm.size_msgs); - mpix_recv_vals.resize(A.recv_comm.size_msgs); - } - - if (A.send_comm.size_msgs) - { - alltoallv_send_vals.resize(A.send_comm.size_msgs); - send_indices.resize(A.send_comm.size_msgs); - for (int i = 0; i < A.send_comm.size_msgs; i++) - { - idx = A.send_comm.idx[i]; - alltoallv_send_vals[i] = send_vals[idx]; - send_indices[i] = A.send_comm.idx[i] + A.first_col; - } - } - - communicate(A, send_vals, mpix_recv_vals, MPI_INT); - - MPI_Comm std_comm; - MPI_Status status; - MPIL_Comm* xcomm; - MPIL_Request* xrequest; - MPIL_Info* xinfo; - - MPIL_Info_init(&xinfo); - - int* s = A.recv_comm.procs.data(); - if (A.recv_comm.n_msgs == 0) - s = MPI_WEIGHTS_EMPTY; - int* d = A.send_comm.procs.data(); - if (A.send_comm.n_msgs == 0) - d = MPI_WEIGHTS_EMPTY; - - PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, - A.recv_comm.n_msgs, - s, - MPI_UNWEIGHTED, - A.send_comm.n_msgs, - d, - MPI_UNWEIGHTED, - MPI_INFO_NULL, - 0, - &std_comm); - - int* send_counts = A.send_comm.counts.data(); - if (A.send_comm.counts.data() == NULL) - send_counts = new int[1]; - int* recv_counts = A.recv_comm.counts.data(); - if (A.recv_comm.counts.data() == NULL) - recv_counts = new int[1]; - PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), - send_counts, - A.send_comm.ptr.data(), - MPI_INT, - pmpi_recv_vals.data(), - recv_counts, - A.recv_comm.ptr.data(), - MPI_INT, - std_comm); - if (A.send_comm.counts.data() == NULL) - delete[] send_counts; - if (A.recv_comm.counts.data() == NULL) - delete[] recv_counts; - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, - A.recv_comm.n_msgs, - A.recv_comm.procs.data(), - MPI_UNWEIGHTED, - A.send_comm.n_msgs, - A.send_comm.procs.data(), - MPI_UNWEIGHTED, - xinfo, - 0, - &xcomm); - - update_locality(xcomm, 4); - - - mpix_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_STANDARD; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - xcomm); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - // 2. Node-Aware Communication - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - xcomm, - xinfo, - &xrequest); - - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - // 3. MPI Advance - Optimized Communication - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - xcomm, - xinfo, - &xrequest); - - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - // Standard from Extended Interface - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - send_indices.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - A.off_proc_columns.data(), - MPI_INT, - xcomm, - xinfo, - &xrequest); - - - // Full Locality - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - send_indices.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - A.off_proc_columns.data(), - MPI_INT, - xcomm, - xinfo, - &xrequest); - - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - MPIL_Info_free(&xinfo); - MPIL_Comm_free(&xcomm); - PMPI_Comm_free(&std_comm); -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - test_matrix("../../../../test_data/dwt_162.pm"); - test_matrix("../../../../test_data/odepa400.pm"); - test_matrix("../../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../../test_data/bcsstk01.pm"); - test_matrix("../../../../test_data/west0132.pm"); - test_matrix("../../../../test_data/gams10a.pm"); - test_matrix("../../../../test_data/gams10am.pm"); - test_matrix("../../../../test_data/D_10.pm"); - test_matrix("../../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../../test_data/msc01050.pm"); - test_matrix("../../../../test_data/SmaGri.pm"); - test_matrix("../../../../test_data/radfr1.pm"); - test_matrix("../../../../test_data/bibd_49_3.pm"); - test_matrix("../../../../test_data/can_1054.pm"); - test_matrix("../../../../test_data/can_1072.pm"); - test_matrix("../../../../test_data/lp_sctap2.pm"); - test_matrix("../../../../test_data/lp_woodw.pm"); - MPI_Finalize(); - return 0; -} // end of main() // - - - diff --git a/src/neighborhood/tests/test_suitesparse_neighbor_reorder.cpp b/src/neighborhood/tests/test_suitesparse_neighbor_reorder.cpp deleted file mode 100644 index 1c51e1950..000000000 --- a/src/neighborhood/tests/test_suitesparse_neighbor_reorder.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - -void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::vector& mpix_recv_vals, int s) -{ - for (int i = 0; i < s; i++) - { - if (pmpi_recv_vals[i] != mpix_recv_vals[i]) - { - fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, - pmpi_recv_vals[i], mpix_recv_vals[i]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -void test_matrix(const char* filename) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Read suitesparse matrix - ParMat A; - int idx; - readParMatrix(filename, A); - form_comm(A); - - std::vector pmpi_recv_vals, mpix_recv_vals; - std::vector send_vals, alltoallv_send_vals; - std::vector send_indices; - - if (A.on_proc.n_cols) - { - send_vals.resize(A.on_proc.n_cols); - std::iota(send_vals.begin(), send_vals.end(), 0); - for (int i = 0; i < A.on_proc.n_cols; i++) - send_vals[i] += (rank*1000); - } - - if (A.recv_comm.size_msgs) - { - pmpi_recv_vals.resize(A.recv_comm.size_msgs); - mpix_recv_vals.resize(A.recv_comm.size_msgs); - } - - if (A.send_comm.size_msgs) - { - alltoallv_send_vals.resize(A.send_comm.size_msgs); - send_indices.resize(A.send_comm.size_msgs); - for (int i = 0; i < A.send_comm.size_msgs; i++) - { - idx = A.send_comm.idx[i]; - alltoallv_send_vals[i] = send_vals[idx]; - send_indices[i] = A.send_comm.idx[i] + A.first_col; - } - } - - communicate(A, send_vals, mpix_recv_vals, MPI_INT); - - MPI_Comm std_comm; - MPI_Status status; - MPIL_Comm* neighbor_comm; - MPIL_Request* neighbor_request; - MPIL_Info* xinfo; - - MPIL_Info_init(&xinfo); - - int* s = A.recv_comm.procs.data(); - if (A.recv_comm.n_msgs == 0) - s = MPI_WEIGHTS_EMPTY; - int* d = A.send_comm.procs.data(); - if (A.send_comm.n_msgs == 0) - d = MPI_WEIGHTS_EMPTY; - - PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, - A.recv_comm.n_msgs, - s, - MPI_UNWEIGHTED, - A.send_comm.n_msgs, - d, - MPI_UNWEIGHTED, - MPI_INFO_NULL, - 0, - &std_comm); - - - int* send_counts = A.send_comm.counts.data(); - if (A.send_comm.counts.data() == NULL) - send_counts = new int[1]; - int* recv_counts = A.recv_comm.counts.data(); - if (A.recv_comm.counts.data() == NULL) - recv_counts = new int[1]; - PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), - send_counts, - A.send_comm.ptr.data(), - MPI_INT, - pmpi_recv_vals.data(), - recv_counts, - A.recv_comm.ptr.data(), - MPI_INT, - std_comm); - if (A.send_comm.counts.data() == NULL) - delete[] send_counts; - if (A.recv_comm.counts.data() == NULL) - delete[] recv_counts; - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, - A.recv_comm.n_msgs, - A.recv_comm.procs.data(), - MPI_UNWEIGHTED, - A.send_comm.n_msgs, - A.send_comm.procs.data(), - MPI_UNWEIGHTED, - xinfo, - 0, - &neighbor_comm); - - update_locality(neighbor_comm, 4); - - - // 2. Node-Aware Communication - reorder during first send/recv - MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - neighbor_comm, - xinfo, - &neighbor_request); - - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - neighbor_request->reorder = 1; - MPIL_Start(neighbor_request); - MPIL_Wait(neighbor_request, &status); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - // Standard send/recv with reordered recvs - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Start(neighbor_request); - MPIL_Wait(neighbor_request, &status); - MPIL_Request_free(&neighbor_request); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - MPIL_Info_free(&xinfo); - MPIL_Comm_free(&neighbor_comm); - PMPI_Comm_free(&std_comm); -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - test_matrix("../../../../test_data/dwt_162.pm"); - test_matrix("../../../../test_data/odepa400.pm"); - test_matrix("../../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../../test_data/bcsstk01.pm"); - test_matrix("../../../../test_data/west0132.pm"); - test_matrix("../../../../test_data/gams10a.pm"); - test_matrix("../../../../test_data/gams10am.pm"); - test_matrix("../../../../test_data/D_10.pm"); - test_matrix("../../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../../test_data/msc01050.pm"); - test_matrix("../../../../test_data/SmaGri.pm"); - test_matrix("../../../../test_data/radfr1.pm"); - test_matrix("../../../../test_data/bibd_49_3.pm"); - test_matrix("../../../../test_data/can_1054.pm"); - test_matrix("../../../../test_data/can_1072.pm"); - test_matrix("../../../../test_data/lp_sctap2.pm"); - test_matrix("../../../../test_data/lp_woodw.pm"); - MPI_Finalize(); - return 0; -} // end of main() // - - diff --git a/src/neighborhood/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp b/src/neighborhood/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp deleted file mode 100644 index 62754dd3a..000000000 --- a/src/neighborhood/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp +++ /dev/null @@ -1,267 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tests/sparse_mat.hpp" -#include "tests/par_binary_IO.hpp" - -void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, std::vector& mpix_recv_vals, int s) -{ - for (int i = 0; i < s; i++) - { - if (pmpi_recv_vals[i] != mpix_recv_vals[i]) - { - fprintf(stderr, "PMPI recv != MPIL: position %d, pmpi %d, mpix %d\n", i, - pmpi_recv_vals[i], mpix_recv_vals[i]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -void test_matrix(const char* filename) -{ - int rank, num_procs; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - - // Read suitesparse matrix - ParMat A; - int idx; - readParMatrix(filename, A); - form_comm(A); - - std::vector pmpi_recv_vals, mpix_recv_vals; - std::vector send_vals, alltoallv_send_vals; - std::vector send_indices; - - if (A.on_proc.n_cols) - { - send_vals.resize(A.on_proc.n_cols); - std::iota(send_vals.begin(), send_vals.end(), 0); - for (int i = 0; i < A.on_proc.n_cols; i++) - send_vals[i] += (rank*1000); - } - - if (A.recv_comm.size_msgs) - { - pmpi_recv_vals.resize(A.recv_comm.size_msgs); - mpix_recv_vals.resize(A.recv_comm.size_msgs); - } - - if (A.send_comm.size_msgs) - { - alltoallv_send_vals.resize(A.send_comm.size_msgs); - send_indices.resize(A.send_comm.size_msgs); - for (int i = 0; i < A.send_comm.size_msgs; i++) - { - idx = A.send_comm.idx[i]; - alltoallv_send_vals[i] = send_vals[idx]; - send_indices[i] = A.send_comm.idx[i] + A.first_col; - } - } - - communicate(A, send_vals, mpix_recv_vals, MPI_INT); - - MPI_Comm std_comm; - MPI_Status status; - - MPIL_Request* xrequest; - MPIL_Comm* xcomm; - MPIL_Comm_init(&xcomm, MPI_COMM_WORLD); - update_locality(xcomm, 4); - MPIL_Info* xinfo; - MPIL_Info_init(&xinfo); - - MPIL_Topo* topo; - MPIL_Topo_init(A.recv_comm.n_msgs, - A.recv_comm.procs.data(), - A.recv_comm.counts.data(), - A.send_comm.n_msgs, - A.send_comm.procs.data(), - A.send_comm.counts.data(), - xinfo, - &topo); - - int* s = A.recv_comm.procs.data(); - if (A.recv_comm.n_msgs == 0) - s = MPI_WEIGHTS_EMPTY; - int* d = A.send_comm.procs.data(); - if (A.send_comm.n_msgs == 0) - d = MPI_WEIGHTS_EMPTY; - - PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, - A.recv_comm.n_msgs, - s, - MPI_UNWEIGHTED, - A.send_comm.n_msgs, - d, - MPI_UNWEIGHTED, - MPI_INFO_NULL, - 0, - &std_comm); - - int* send_counts = A.send_comm.counts.data(); - if (A.send_comm.counts.data() == NULL) - send_counts = new int[1]; - int* recv_counts = A.recv_comm.counts.data(); - if (A.recv_comm.counts.data() == NULL) - recv_counts = new int[1]; - PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), - send_counts, - A.send_comm.ptr.data(), - MPI_INT, - pmpi_recv_vals.data(), - recv_counts, - A.recv_comm.ptr.data(), - MPI_INT, - std_comm); - if (A.send_comm.counts.data() == NULL) - delete[] send_counts; - if (A.recv_comm.counts.data() == NULL) - delete[] recv_counts; - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - mpix_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_STANDARD; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_topo(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - topo, - xcomm); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init_topo(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - topo, - xcomm, - xinfo, - &xrequest); - - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init_topo(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - MPI_INT, - topo, - xcomm, - xinfo, - &xrequest); - - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - // 3. MPI Advance - Optimized Communication - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init_ext_topo(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - send_indices.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - A.off_proc_columns.data(), - MPI_INT, - topo, - xcomm, - xinfo, - &xrequest); - - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - - mpix_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; - std::fill(mpix_recv_vals.begin(), mpix_recv_vals.end(), 0); - MPIL_Neighbor_alltoallv_init_ext_topo(alltoallv_send_vals.data(), - A.send_comm.counts.data(), - A.send_comm.ptr.data(), - send_indices.data(), - MPI_INT, - mpix_recv_vals.data(), - A.recv_comm.counts.data(), - A.recv_comm.ptr.data(), - A.off_proc_columns.data(), - MPI_INT, - topo, - xcomm, - xinfo, - &xrequest); - - MPIL_Start(xrequest); - MPIL_Wait(xrequest, &status); - MPIL_Request_free(&xrequest); - compare_neighbor_alltoallv_results(pmpi_recv_vals, mpix_recv_vals, A.recv_comm.size_msgs); - - MPIL_Topo_free(&topo); - MPIL_Info_free(&xinfo); - MPIL_Comm_free(&xcomm); - PMPI_Comm_free(&std_comm); -} - -int main(int argc, char** argv) -{ - MPI_Init(&argc, &argv); - test_matrix("../../../../test_data/dwt_162.pm"); - test_matrix("../../../../test_data/odepa400.pm"); - test_matrix("../../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../../test_data/bcsstk01.pm"); - test_matrix("../../../../test_data/west0132.pm"); - test_matrix("../../../../test_data/gams10a.pm"); - test_matrix("../../../../test_data/gams10am.pm"); - test_matrix("../../../../test_data/D_10.pm"); - test_matrix("../../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../../test_data/msc01050.pm"); - test_matrix("../../../../test_data/SmaGri.pm"); - test_matrix("../../../../test_data/radfr1.pm"); - test_matrix("../../../../test_data/bibd_49_3.pm"); - test_matrix("../../../../test_data/can_1054.pm"); - test_matrix("../../../../test_data/can_1072.pm"); - test_matrix("../../../../test_data/lp_sctap2.pm"); - test_matrix("../../../../test_data/lp_woodw.pm"); - MPI_Finalize(); - return 0; -} // end of main() // - - - diff --git a/src/persistent/CMakeLists.txt b/src/persistent/CMakeLists.txt deleted file mode 100644 index 6e3fa98f5..000000000 --- a/src/persistent/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -file(GLOB persistent_HEADERS CONFIGURE_DEPENDS "*.h") -file(GLOB persistent_SOURCES CONFIGURE_DEPENDS "*.c") - -set(persistent_HEADERS ${persistent_HEADERS} CACHE INTERNAL "All headers for persistent files.") -set(persistent_SOURCES ${persistent_SOURCES} CACHE INTERNAL "All source files for persistent directory.") - diff --git a/src/persistent/neighbor_persistent.h b/src/persistent/neighbor_persistent.h deleted file mode 100644 index 69cd50f15..000000000 --- a/src/persistent/neighbor_persistent.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MPI_ADVANCE_NEIGHBOR_INIT_H -#define MPI_ADVANCE_NEIGHBOR_INIT_H - -#include "communicator/locality_comm.h" -#include "neighborhood/neighbor.h" -#include "persistent.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Starting locality-aware requests -// 1. Start Local_L -// 2. Start and wait for local_S -// 3. Start global -int neighbor_start(MPIL_Request* request); - -// Wait for locality-aware requests -// 1. Wait for global -// 2. Start and wait for local_R -// 3. Wait for local_L -int neighbor_wait(MPIL_Request* request, MPI_Status* status); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/persistent/persistent.c b/src/persistent/persistent.c deleted file mode 100644 index e75076166..000000000 --- a/src/persistent/persistent.c +++ /dev/null @@ -1,135 +0,0 @@ -#include "persistent.h" - -void init_request(MPIL_Request** request_ptr) -{ - MPIL_Request* request = (MPIL_Request*)malloc(sizeof(MPIL_Request)); - - request->locality = NULL; - - request->local_L_n_msgs = 0; - request->local_S_n_msgs = 0; - request->local_R_n_msgs = 0; - request->global_n_msgs = 0; - - request->local_L_requests = NULL; - request->local_S_requests = NULL; - request->local_R_requests = NULL; - request->global_requests = NULL; - - request->recv_size = 0; - request->block_size = 1; - -#ifdef GPU - request->cpu_sendbuf = NULL; - request->cpu_recvbuf = NULL; -#endif - - *request_ptr = request; -} - -void allocate_requests(int n_requests, MPI_Request** request_ptr) -{ - if (n_requests) - { - MPI_Request* request = (MPI_Request*)malloc(sizeof(MPI_Request) * n_requests); - *request_ptr = request; - } - else - { - *request_ptr = NULL; - } -} - -// Starting locality-aware requests -// 1. Start Local_L -// 2. Start and wait for local_S -// 3. Start global -int MPIL_Start(MPIL_Request* request) -{ - if (request == NULL) - { - return 0; - } - - mpix_start_ftn start_function = (mpix_start_ftn)(request->start_function); - return start_function(request); -} - -// Wait for locality-aware requests -// 1. Wait for global -// 2. Start and wait for local_R -// 3. Wait for local_L -// TODO : Currently ignores the status! -int MPIL_Wait(MPIL_Request* request, MPI_Status* status) -{ - if (request == NULL) - { - return 0; - } - - mpix_wait_ftn wait_function = (mpix_wait_ftn)(request->wait_function); - return wait_function(request, status); -} - -int MPIL_Request_free(MPIL_Request** request_ptr) -{ - MPIL_Request* request = *request_ptr; - - if (request->local_L_n_msgs) - { - for (int i = 0; i < request->local_L_n_msgs; i++) - { - MPI_Request_free(&(request->local_L_requests[i])); - } - free(request->local_L_requests); - } - if (request->local_S_n_msgs) - { - for (int i = 0; i < request->local_S_n_msgs; i++) - { - MPI_Request_free(&(request->local_S_requests[i])); - } - free(request->local_S_requests); - } - if (request->local_R_n_msgs) - { - for (int i = 0; i < request->local_R_n_msgs; i++) - { - MPI_Request_free(&(request->local_R_requests[i])); - } - free(request->local_R_requests); - } - if (request->global_n_msgs) - { - for (int i = 0; i < request->global_n_msgs; i++) - { - MPI_Request_free(&(request->global_requests[i])); - } - free(request->global_requests); - } - - // If Locality-Aware - if (request->locality != NULL) - { - destroy_locality_comm(request->locality); - } - -// TODO : for safety, may want to check if allocated with malloc? -#ifdef GPU // Assuming cpu buffers allocated in pinned memory - int ierr; - if (request->cpu_sendbuf) - { - ierr = gpuFreeHost(request->cpu_sendbuf); - gpu_check(ierr); - } - if (request->cpu_recvbuf) - { - ierr = gpuFreeHost(request->cpu_recvbuf); - gpu_check(ierr); - } -#endif - - free(request); - - return 0; -} diff --git a/src/tests/compare.hpp b/src/tests/compare.hpp deleted file mode 100644 index f15969c80..000000000 --- a/src/tests/compare.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "locality_aware.h" -#include -#include -#include -#include -#include -#include -#include -#include - -void compare(int n_recvs, int std_n_recvs, std::vector& src, - std::vector& recvvals, std::vector& std_recvvals) -{ - if (n_recvs != std_n_recvs) - { - fprintf(stderr, "Incorrect NRecvs! New %d, Std %d\n", n_recvs, std_n_recvs); - MPI_Abort(MPI_COMM_WORLD, -1); - } - for (int i = 0; i < n_recvs; i++) - { - if (recvvals[i] != std_recvvals[src[i]]) - { - fprintf(stderr, "Recv %d, RecvVals New %d, Std %d\n", i, - recvvals[i], std_recvvals[src[i]]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } -} - -void compare(int n_recvs, int std_n_recvs, int s_recvs, int std_s_recvs, - std::vector& src, std::vector& recvcounts, - std::vector& rdispls, std::vector& recvvals, - int first_col, std::vector& proc_counts, - std::vector& proc_displs, std::vector& indices) -{ - int proc; - if (n_recvs != std_n_recvs) - { - fprintf(stderr, "Incorrect NRecvs! New %d, Std %d\n", n_recvs, std_n_recvs); - MPI_Abort(MPI_COMM_WORLD, -1); - } - if (s_recvs != std_s_recvs) - { - fprintf(stderr, "Incorrect SRecvs! New %d, Std %d\n", s_recvs, std_s_recvs); - MPI_Abort(MPI_COMM_WORLD, -1); - } - for (int i = 0; i < n_recvs; i++) - { - proc = src[i]; - if (recvcounts[i] != proc_counts[proc]) - { - fprintf(stderr, "Recv %d, Recvcounts %d, ProcCounts[%d] %d\n", - i, recvcounts[i], proc, proc_counts[proc]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - for (int j = 0; j < recvcounts[i]; j++) - { - if (recvvals[rdispls[i] + j] - first_col != indices[proc_displs[proc] + j]) - { - fprintf(stderr, "Recv %d, Position %d, Recvvals %d, Indices %d\n", - i, j, recvvals[rdispls[i] + j] - first_col, indices[proc_displs[proc] + j]); - MPI_Abort(MPI_COMM_WORLD, -1); - } - } - } -} diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt deleted file mode 100644 index 215ddaaf7..000000000 --- a/src/utils/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -if (USE_CUDA) - set(gpu_util_HEADERS - src/utils/utils_cuda.h - ) -elseif (USE_HIP) - set(gpu_util_HEADERS - src/utils/utils_hip.h - ) -endif() - -file(GLOB utils_SOURCES CONFIGURE_DEPENDS "*.cpp") -set(utils_SOURCES ${utils_SOURCES} CACHE INTERNAL "All source files for utils directory.") - - -set(utils_HEADERS - src/utils/utils.h - ${gpu_util_HEADERS} - CACHE INTERNAL "All headers for utils files." - ) - diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp deleted file mode 100644 index e7c1ea29b..000000000 --- a/src/utils/utils.cpp +++ /dev/null @@ -1,241 +0,0 @@ -#include "utils.h" - -#include -#include - -#include "mpi.h" -#include "stdio.h" - -#ifdef HIP -#include "hip/hip_runtime.h" -#endif - -// MPIL Info Object Routines -int MPIL_Info_init(MPIL_Info** info_ptr) -{ - MPIL_Info* xinfo = (MPIL_Info*)malloc(sizeof(MPIL_Info)); - xinfo->crs_num_initialized = 0; - xinfo->crs_size_initialized = 0; - - *info_ptr = xinfo; - - return MPI_SUCCESS; -} - -int MPIL_Info_free(MPIL_Info** info_ptr) -{ - MPIL_Info* xinfo = *info_ptr; - free(xinfo); - - return MPI_SUCCESS; -} - -void sort(int n_objects, int* object_indices, int* object_values) -{ - std::sort(object_indices, object_indices + n_objects, [&](const int i, const int j) { - return object_values[i] > object_values[j]; - }); -} - -void rotate(void* recvbuf, int new_first_byte, int last_byte) -{ - char* recv_buffer = (char*)(recvbuf); - std::rotate(recv_buffer, &(recv_buffer[new_first_byte]), &(recv_buffer[last_byte])); -} - -void reverse(void* recvbuf, int n_bytes, int var_bytes) -{ - char* recv_buffer = (char*)(recvbuf); - int n_vars = n_bytes / var_bytes; - for (int i = 0; i < n_vars / 2; i++) - { - for (int j = 0; j < var_bytes; j++) - { - std::swap(recv_buffer[i * var_bytes + j], - recv_buffer[(n_vars - i - 1) * var_bytes + j]); - } - } -} - -// Repack Data on Device -#ifdef GPU -__global__ void device_repack(char* __restrict__ sendbuf, - char* __restrict__ recvbuf, - int size_x, - int size_y, - int size_z) -{ - const int tid_x = threadIdx.x + blockIdx.x * blockDim.x; - const int tid_y = threadIdx.y + blockIdx.y * blockDim.y; - const int tid_z = threadIdx.z + blockIdx.z * blockDim.z; - - if (tid_x >= size_x || tid_y >= size_y || tid_z >= size_z) - { - return; - } - - recvbuf[(tid_y * size_x + tid_x) * size_z + tid_z] = - sendbuf[(tid_x * size_y + tid_y) * size_z + tid_z]; -} - -void gpu_repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf) -{ - dim3 dimBlock(8, 8, 8); - int grid_x = ((size_i - 1) / 8) + 1; - int grid_y = ((size_j - 1) / 8) + 1; - int grid_z = ((size_k - 1) / 8) + 1; - dim3 dimGrid(grid_x, grid_y, grid_z); - device_repack<<>>(sendbuf, recvbuf, size_i, size_j, size_k); -} - -void gpu_check(int ierr) -{ - if (ierr != gpuSuccess) - { - printf("Error in Device Function!\n"); - } -} -#endif - -// Repack Method (calls device if on GPU) -void repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf) -{ -#ifdef GPU - gpuMemoryType send_type, recv_type; - get_mem_types(sendbuf, recvbuf, &send_type, &recv_type); - - if (send_type == gpuMemoryTypeDevice && recv_type == gpuMemoryTypeDevice) - { - // gpu_repack(size_i, size_j, size_k, sendbuf, recvbuf); - for (int i = 0; i < size_i; i++) - { - for (int j = 0; j < size_j; j++) - { - gpuMemcpy(recvbuf + (j * size_i + i) * size_k, - sendbuf + (i * size_j + j) * size_k, - size_k, - gpuMemcpyDeviceToDevice); - } - } - } - else if (send_type == gpuMemoryTypeDevice) - { - for (int i = 0; i < size_i; i++) - { - for (int j = 0; j < size_j; j++) - { - gpuMemcpy(recvbuf + (j * size_i + i) * size_k, - sendbuf + (i * size_j + j) * size_k, - size_k, - gpuMemcpyHostToDevice); - } - } - } - else if (recv_type == gpuMemoryTypeDevice) - { - for (int i = 0; i < size_i; i++) - { - for (int j = 0; j < size_j; j++) - { - gpuMemcpy(recvbuf + (j * size_i + i) * size_k, - sendbuf + (i * size_j + j) * size_k, - size_k, - gpuMemcpyDeviceToHost); - } - } - } - else -#endif - for (int i = 0; i < size_i; i++) - { - for (int j = 0; j < size_j; j++) - { - memcpy(recvbuf + (j * size_i + i) * size_k, - sendbuf + (i * size_j + j) * size_k, - size_k); - } - } -} - -// GPU Method to find where memory was allocated -#ifdef GPU -void get_mem_types(const void* sendbuf, - const void* recvbuf, - gpuMemoryType* send_ptr, - gpuMemoryType* recv_ptr) -{ - gpuMemoryType send_type, recv_type; - - gpuPointerAttributes mem; - gpuPointerGetAttributes(&mem, sendbuf); - int ierr = gpuGetLastError(); - if (ierr == gpuErrorInvalidValue) - { - send_type = gpuMemoryTypeHost; - } - else - { - send_type = mem.type; - } - - gpuPointerGetAttributes(&mem, recvbuf); - ierr = gpuGetLastError(); - if (ierr == gpuErrorInvalidValue) - { - recv_type = gpuMemoryTypeHost; - } - else - { - recv_type = mem.type; - } - - *send_ptr = send_type; - *recv_ptr = recv_type; -} - -void get_memcpy_kind(gpuMemoryType send_type, - gpuMemoryType recv_type, - gpuMemcpyKind* memcpy_kind) -{ - if (send_type == gpuMemoryTypeDevice && recv_type == gpuMemoryTypeDevice) - { - *memcpy_kind = gpuMemcpyDeviceToDevice; - } - else if (send_type == gpuMemoryTypeDevice) - { - *memcpy_kind = gpuMemcpyDeviceToHost; - } - else if (recv_type == gpuMemoryTypeDevice) - { - *memcpy_kind = gpuMemcpyHostToDevice; - } - else - { - *memcpy_kind = gpuMemcpyHostToHost; - } -} -#endif - -int MPIL_Alloc(void** pointer, const int bytes) -{ - if (bytes == 0) - { - *pointer = NULL; - } - else - { - *pointer = new char[bytes]; - } - - return MPI_SUCCESS; -} -int MPIL_Free(void* pointer) -{ - if (pointer != NULL) - { - char* char_ptr = (char*)pointer; - delete[] char_ptr; - } - - return MPI_SUCCESS; -} From 30532715efc6150ba570567449ce33d131cd3ab3 Mon Sep 17 00:00:00 2001 From: Derek Schafer Date: Fri, 24 Oct 2025 09:39:28 -0600 Subject: [PATCH 07/15] Added bug fix for zero-sends in neighbor collectives; updated cmake to make tests and benchmarks share common function; added function to test all matrices --- CMakeLists.txt | 31 +- benchmarks/CMakeLists.txt | 9 +- .../neighbor_alltoallv_init_locality_ext.c | 76 ++++- .../locality/neighbor_locality.cpp | 19 +- library/tests/CMakeLists.txt | 2 +- .../test_suitesparse_neighbor_alltoallv.cpp | 21 +- ...st_suitesparse_neighbor_alltoallv_enum.cpp | 21 +- ...st_suitesparse_neighbor_alltoallv_init.cpp | 21 +- ..._neighbor_alltoallv_init_bidirectional.cpp | 284 ++++++++++++++++++ .../test_suitesparse_neighbor_reorder.cpp | 21 +- ...itesparse_neighbor_topo_alltoallv_init.cpp | 21 +- library/tests/tests/common.hpp | 29 ++ library/tests/tests/compare.hpp | 7 +- 13 files changed, 433 insertions(+), 129 deletions(-) create mode 100644 library/tests/test_suitesparse_neighbor_alltoallv_init_bidirectional.cpp create mode 100644 library/tests/tests/common.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 41f92f7a8..508c71649 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,22 +77,33 @@ endif() if (ENABLE_UNIT_TESTS) enable_testing() - set(TEST_PROCS "16" CACHE STRING "Number of processes to use when making ctests") - function(make_test file) - #get file name for unique handle - get_filename_component(exec_name ${file} NAME_WE) - add_executable(${exec_name} ${file}) - target_link_libraries(${exec_name} locality_aware ${EXTERNAL_LIBS}) - add_test(NAME ${exec_name}_Test COMMAND ${MPIRUN} -n ${TEST_PROCS} ./${exec_name}) - endfunction() + set(TEST_PROCS "16" CACHE STRING "Number of processes to use when running ctests") endif() +# Flag == TRUE means TEST +# Flag == FALSE means BENCHMARK +function(make_executable filename flag) + get_filename_component(exec_name ${filename} NAME_WE) + add_executable(${exec_name} ${filename}) + target_link_libraries(${exec_name} locality_aware ${EXTERNAL_LIBS}) + + if(${flag}) + set(locality_test_name ${exec_name}_Test) + else() + set(locality_test_name ${exec_name}_Benchmark) + install(TARGETS ${exec_name} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + endif() + + if(ENABLE_UNIT_TESTS) + add_test(NAME ${locality_test_name} COMMAND ${MPIRUN} -n ${TEST_PROCS} --oversubscribe ./${exec_name} ${TEST_MATRIX_FILE}) + endif() + +endfunction() + if (BENCHMARKS) add_subdirectory(benchmarks) endif() - - set_target_properties(locality_aware PROPERTIES PUBLIC_HEADER include/locality_aware.h) add_subdirectory(include) add_subdirectory(library) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index c441181ec..e131759b1 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -21,12 +21,5 @@ set(TEST_MATRIX_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../test_data/bcsstk01.pm") ## Go through each CPP file foreach(src ${CPP_SOURCES}) - get_filename_component(exec_name ${src} NAME_WE) - add_executable(${exec_name} ${src}) - target_link_libraries(${exec_name} locality_aware ${MPI_LIBRARIES}) - install(TARGETS ${exec_name} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) - - if(ENABLE_UNIT_TESTS) - add_test(NAME ${exec_name}_Benchmark COMMAND ${MPIRUN} -n ${TEST_PROCS} ./${exec_name} ${TEST_MATRIX_FILE}) - endif() + make_executable(${src} FALSE) endforeach() diff --git a/library/source/neighborhood/locality/neighbor_alltoallv_init_locality_ext.c b/library/source/neighborhood/locality/neighbor_alltoallv_init_locality_ext.c index 923960453..5a251fe18 100644 --- a/library/source/neighborhood/locality/neighbor_alltoallv_init_locality_ext.c +++ b/library/source/neighborhood/locality/neighbor_alltoallv_init_locality_ext.c @@ -1,3 +1,5 @@ +#include + #include "communicator/MPIL_Comm.h" #include "locality_aware.h" #include "neighborhood/MPIL_Topo.h" @@ -29,18 +31,63 @@ int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, MPIL_Request* request; init_neighbor_request(&request); + int indegree = 0; + int outdegree = 0; + + int* sources = NULL; + int* source_counts = NULL; + int* source_displs = NULL; + int* destinations = NULL; + int* dest_counts = NULL; + int* dest_displs = NULL; + + if (topo->indegree) + { + sources = (int*)malloc(topo->indegree * sizeof(int)); + source_counts = (int*)malloc(topo->indegree * sizeof(int)); + source_displs = (int*)malloc(topo->indegree * sizeof(int)); + } + if (topo->outdegree) + { + destinations = (int*)malloc(topo->outdegree * sizeof(int)); + dest_counts = (int*)malloc(topo->outdegree * sizeof(int)); + dest_displs = (int*)malloc(topo->outdegree * sizeof(int)); + } + + for (int i = 0; i < topo->indegree; i++) + { + if (recvcounts[i]) + { + sources[indegree] = topo->sources[i]; + source_counts[indegree] = recvcounts[i]; + source_displs[indegree] = rdispls[i]; + indegree++; + } + } + + for (int i = 0; i < topo->outdegree; i++) + { + if (sendcounts[i]) + { + destinations[outdegree] = topo->destinations[i]; + dest_counts[outdegree] = sendcounts[i]; + dest_displs[outdegree] = sdispls[i]; + outdegree++; + } + } + // Initialize Locality-Aware Communication Strategy (3-Step) // E.G. Determine which processes talk to each other at every step // TODO : instead of mpi_comm, use comm // - will need to create local_comm in dist_graph_create_adjacent... - init_locality(topo->outdegree, - topo->destinations, - sdispls, - sendcounts, - topo->indegree, - topo->sources, - rdispls, - recvcounts, + init_locality(outdegree, + destinations, + dest_displs, + dest_counts, + indegree, + sources, + source_displs, + source_counts, global_sindices, global_rindices, sendtype, @@ -119,5 +166,18 @@ int neighbor_alltoallv_init_locality_ext(const void* sendbuffer, *request_ptr = request; + if (topo->indegree) + { + free(sources); + free(source_counts); + free(source_displs); + } + if (topo->outdegree) + { + free(destinations); + free(dest_counts); + free(dest_displs); + } + return MPI_SUCCESS; } diff --git a/library/source/neighborhood/locality/neighbor_locality.cpp b/library/source/neighborhood/locality/neighbor_locality.cpp index 994db8e4f..54fcafa95 100644 --- a/library/source/neighborhood/locality/neighbor_locality.cpp +++ b/library/source/neighborhood/locality/neighbor_locality.cpp @@ -718,25 +718,32 @@ int cmpfunc(const void* a, const void* b) void remove_duplicates(CommData* comm_pkg) { int start, end; + int has_data = comm_pkg->size_msgs; for (int i = 0; i < comm_pkg->num_msgs; i++) { start = comm_pkg->indptr[i]; end = comm_pkg->indptr[i + 1]; - std::sort(comm_pkg->indices + start, comm_pkg->indices + end); + if (has_data) + { + std::sort(comm_pkg->indices + start, comm_pkg->indices + end); + } } comm_pkg->size_msgs = 0; start = comm_pkg->indptr[0]; for (int i = 0; i < comm_pkg->num_msgs; i++) { - end = comm_pkg->indptr[i + 1]; - comm_pkg->indices[comm_pkg->size_msgs++] = comm_pkg->indices[start]; - for (int j = start; j < end - 1; j++) + end = comm_pkg->indptr[i + 1]; + if (has_data) { - if (comm_pkg->indices[j + 1] != comm_pkg->indices[j]) + comm_pkg->indices[comm_pkg->size_msgs++] = comm_pkg->indices[start]; + for (int j = start; j < end - 1; j++) { - comm_pkg->indices[comm_pkg->size_msgs++] = comm_pkg->indices[j + 1]; + if (comm_pkg->indices[j + 1] != comm_pkg->indices[j]) + { + comm_pkg->indices[comm_pkg->size_msgs++] = comm_pkg->indices[j + 1]; + } } } start = end; diff --git a/library/tests/CMakeLists.txt b/library/tests/CMakeLists.txt index 139aa723e..92065c48a 100644 --- a/library/tests/CMakeLists.txt +++ b/library/tests/CMakeLists.txt @@ -2,7 +2,7 @@ file(GLOB CPP_SOURCES CONFIGURE_DEPENDS "*.cpp" ".c") foreach(src ${CPP_SOURCES}) - make_test(${src}) + make_executable(${src} TRUE) endforeach() target_include_directories(locality_aware PUBLIC diff --git a/library/tests/test_suitesparse_neighbor_alltoallv.cpp b/library/tests/test_suitesparse_neighbor_alltoallv.cpp index fe1c4cb54..b014a7abb 100644 --- a/library/tests/test_suitesparse_neighbor_alltoallv.cpp +++ b/library/tests/test_suitesparse_neighbor_alltoallv.cpp @@ -9,6 +9,7 @@ #include #include "locality_aware.h" +#include "tests/common.hpp" #include "tests/par_binary_IO.hpp" #include "tests/sparse_mat.hpp" @@ -203,25 +204,7 @@ void test_matrix(const char* filename) int main(int argc, char** argv) { MPI_Init(&argc, &argv); - test_matrix("../../../test_data/dwt_162.pm"); - test_matrix("../../../test_data/odepa400.pm"); - test_matrix("../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../test_data/bcsstk01.pm"); - test_matrix("../../../test_data/west0132.pm"); - test_matrix("../../../test_data/gams10a.pm"); - test_matrix("../../../test_data/gams10am.pm"); - test_matrix("../../../test_data/D_10.pm"); - test_matrix("../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../test_data/msc01050.pm"); - test_matrix("../../../test_data/SmaGri.pm"); - test_matrix("../../../test_data/radfr1.pm"); - test_matrix("../../../test_data/bibd_49_3.pm"); - test_matrix("../../../test_data/can_1054.pm"); - test_matrix("../../../test_data/can_1072.pm"); - test_matrix("../../../test_data/lp_sctap2.pm"); - test_matrix("../../../test_data/lp_woodw.pm"); + test_all_matrices(); MPI_Finalize(); return 0; } // end of main() // diff --git a/library/tests/test_suitesparse_neighbor_alltoallv_enum.cpp b/library/tests/test_suitesparse_neighbor_alltoallv_enum.cpp index 6b26b1290..3b5d36d39 100644 --- a/library/tests/test_suitesparse_neighbor_alltoallv_enum.cpp +++ b/library/tests/test_suitesparse_neighbor_alltoallv_enum.cpp @@ -9,6 +9,7 @@ #include #include "locality_aware.h" +#include "tests/common.hpp" #include "tests/par_binary_IO.hpp" void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, @@ -184,25 +185,7 @@ void test_matrix(const char* filename) int main(int argc, char** argv) { MPI_Init(&argc, &argv); - test_matrix("../../../test_data/dwt_162.pm"); - test_matrix("../../../test_data/odepa400.pm"); - test_matrix("../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../test_data/bcsstk01.pm"); - test_matrix("../../../test_data/west0132.pm"); - test_matrix("../../../test_data/gams10a.pm"); - test_matrix("../../../test_data/gams10am.pm"); - test_matrix("../../../test_data/D_10.pm"); - test_matrix("../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../test_data/msc01050.pm"); - test_matrix("../../../test_data/SmaGri.pm"); - test_matrix("../../../test_data/radfr1.pm"); - test_matrix("../../../test_data/bibd_49_3.pm"); - test_matrix("../../../test_data/can_1054.pm"); - test_matrix("../../../test_data/can_1072.pm"); - test_matrix("../../../test_data/lp_sctap2.pm"); - test_matrix("../../../test_data/lp_woodw.pm"); + test_all_matrices(); MPI_Finalize(); return 0; } // end of main() // diff --git a/library/tests/test_suitesparse_neighbor_alltoallv_init.cpp b/library/tests/test_suitesparse_neighbor_alltoallv_init.cpp index 83c2faf4f..245508842 100644 --- a/library/tests/test_suitesparse_neighbor_alltoallv_init.cpp +++ b/library/tests/test_suitesparse_neighbor_alltoallv_init.cpp @@ -9,6 +9,7 @@ #include #include "locality_aware.h" +#include "tests/common.hpp" #include "tests/par_binary_IO.hpp" #include "tests/sparse_mat.hpp" @@ -253,25 +254,7 @@ void test_matrix(const char* filename) int main(int argc, char** argv) { MPI_Init(&argc, &argv); - test_matrix("../../../test_data/dwt_162.pm"); - test_matrix("../../../test_data/odepa400.pm"); - test_matrix("../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../test_data/bcsstk01.pm"); - test_matrix("../../../test_data/west0132.pm"); - test_matrix("../../../test_data/gams10a.pm"); - test_matrix("../../../test_data/gams10am.pm"); - test_matrix("../../../test_data/D_10.pm"); - test_matrix("../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../test_data/msc01050.pm"); - test_matrix("../../../test_data/SmaGri.pm"); - test_matrix("../../../test_data/radfr1.pm"); - test_matrix("../../../test_data/bibd_49_3.pm"); - test_matrix("../../../test_data/can_1054.pm"); - test_matrix("../../../test_data/can_1072.pm"); - test_matrix("../../../test_data/lp_sctap2.pm"); - test_matrix("../../../test_data/lp_woodw.pm"); + test_all_matrices(); MPI_Finalize(); return 0; } // end of main() // diff --git a/library/tests/test_suitesparse_neighbor_alltoallv_init_bidirectional.cpp b/library/tests/test_suitesparse_neighbor_alltoallv_init_bidirectional.cpp new file mode 100644 index 000000000..dc2dc077f --- /dev/null +++ b/library/tests/test_suitesparse_neighbor_alltoallv_init_bidirectional.cpp @@ -0,0 +1,284 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "locality_aware.h" +#include "tests/common.hpp" +#include "tests/par_binary_IO.hpp" +#include "tests/sparse_mat.hpp" + +void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, + std::vector& mpil_recv_vals, + int s) +{ + for (int i = 0; i < s; i++) + { + if (pmpi_recv_vals[i] != mpil_recv_vals[i]) + { + fprintf(stderr, + "PMPI recv != MPIL: position %d, pmpi %d, mpil %d\n", + i, + pmpi_recv_vals[i], + mpil_recv_vals[i]); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } +} + +void test_matrix(const char* filename) +{ + int rank, num_procs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + + // Read suitesparse matrix + ParMat A; + int idx, ctr, proc, size; + readParMatrix(filename, A); + form_comm(A); + + // Create list of all processes + std::vector procs(num_procs); + std::iota(procs.begin(), procs.end(), 0); + std::vector proc_send_pos(num_procs); + std::vector proc_recv_pos(num_procs); + + std::vector proc_send_sizes(num_procs, 0); + std::vector proc_recv_sizes(num_procs, 0); + std::vector proc_send_displs(num_procs + 1); + std::vector proc_recv_displs(num_procs + 1); + std::vector proc_send_indices(A.send_comm.size_msgs); + std::vector proc_recv_indices(A.recv_comm.size_msgs); + + std::vector pmpi_recv_vals, mpil_recv_vals; + std::vector send_vals, alltoallv_send_vals; + + if (A.on_proc.n_cols) + { + send_vals.resize(A.on_proc.n_cols); + std::iota(send_vals.begin(), send_vals.end(), 0); + for (int i = 0; i < A.on_proc.n_cols; i++) + { + send_vals[i] += (rank * 1000); + } + } + + if (A.recv_comm.size_msgs) + { + pmpi_recv_vals.resize(A.recv_comm.size_msgs); + mpil_recv_vals.resize(A.recv_comm.size_msgs); + } + + if (A.send_comm.size_msgs) + { + alltoallv_send_vals.resize(A.send_comm.size_msgs); + } + + // Create dense communication graph + for (int i = 0; i < A.send_comm.n_msgs; i++) + { + proc = A.send_comm.procs[i]; + size = A.send_comm.counts[i]; + proc_send_sizes[proc] = size; + proc_send_pos[proc] = i; + } + for (int i = 0; i < A.recv_comm.n_msgs; i++) + { + proc = A.recv_comm.procs[i]; + size = A.recv_comm.counts[i]; + proc_recv_sizes[proc] = size; + proc_recv_pos[proc] = i; + } + proc_send_displs[0] = 0; + proc_recv_displs[0] = 0; + for (int i = 0; i < num_procs; i++) + { + ctr = proc_send_displs[i]; + if (proc_send_sizes[i]) + { + idx = proc_send_pos[i]; + for (int j = A.send_comm.ptr[idx]; j < A.send_comm.ptr[idx + 1]; j++) + { + alltoallv_send_vals[ctr] = send_vals[A.send_comm.idx[j]]; + proc_send_indices[ctr++] = A.send_comm.idx[j] + A.first_col; + } + } + proc_send_displs[i + 1] = ctr; + + ctr = proc_recv_displs[i]; + if (proc_recv_sizes[i]) + { + idx = proc_recv_pos[i]; + for (int j = A.recv_comm.ptr[idx]; j < A.recv_comm.ptr[idx + 1]; j++) + { + proc_recv_indices[ctr++] = A.off_proc_columns[j]; + } + } + proc_recv_displs[i + 1] = ctr; + } + + // MPI and MPIL Variables + MPI_Status status; + MPIL_Comm* xcomm; + MPIL_Request* xrequest; + MPIL_Info* xinfo; + MPIL_Info_init(&xinfo); + + // Create standard PMPI neighbor communicator + MPI_Comm std_comm; + PMPI_Dist_graph_create_adjacent(MPI_COMM_WORLD, + num_procs, + procs.data(), + MPI_UNWEIGHTED, + num_procs, + procs.data(), + MPI_UNWEIGHTED, + MPI_INFO_NULL, + 0, + &std_comm); + + // Standard PMPI neighbor exchange + PMPI_Neighbor_alltoallv(alltoallv_send_vals.data(), + proc_send_sizes.data(), + proc_send_displs.data(), + MPI_INT, + pmpi_recv_vals.data(), + proc_recv_sizes.data(), + proc_recv_displs.data(), + MPI_INT, + std_comm); + + PMPI_Comm_free(&std_comm); + + // MPI Advance neighbor communicator + MPIL_Dist_graph_create_adjacent(MPI_COMM_WORLD, + num_procs, + procs.data(), + MPI_UNWEIGHTED, + num_procs, + procs.data(), + MPI_UNWEIGHTED, + xinfo, + 0, + &xcomm); + MPIL_Comm_update_locality(xcomm, 4); + + // Standard exchange + mpil_neighbor_alltoallv_implementation = NEIGHBOR_ALLTOALLV_STANDARD; + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv(alltoallv_send_vals.data(), + proc_send_sizes.data(), + proc_send_displs.data(), + MPI_INT, + mpil_recv_vals.data(), + proc_recv_sizes.data(), + proc_recv_displs.data(), + MPI_INT, + xcomm); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + // 2. Node-Aware Communication + mpil_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + proc_send_sizes.data(), + proc_send_displs.data(), + MPI_INT, + mpil_recv_vals.data(), + proc_recv_sizes.data(), + proc_recv_displs.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + // 3. MPI Advance - Optimized Communication + mpil_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init(alltoallv_send_vals.data(), + proc_send_sizes.data(), + proc_send_displs.data(), + MPI_INT, + mpil_recv_vals.data(), + proc_recv_sizes.data(), + proc_recv_displs.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + // Standard from Extended Interface + mpil_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_STANDARD; + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), + proc_send_sizes.data(), + proc_send_displs.data(), + proc_send_indices.data(), + MPI_INT, + mpil_recv_vals.data(), + proc_recv_sizes.data(), + proc_recv_displs.data(), + proc_recv_indices.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + // Full Locality + mpil_neighbor_alltoallv_init_implementation = NEIGHBOR_ALLTOALLV_INIT_LOCALITY; + std::fill(mpil_recv_vals.begin(), mpil_recv_vals.end(), 0); + MPIL_Neighbor_alltoallv_init_ext(alltoallv_send_vals.data(), + proc_send_sizes.data(), + proc_send_displs.data(), + proc_send_indices.data(), + MPI_INT, + mpil_recv_vals.data(), + proc_recv_sizes.data(), + proc_recv_displs.data(), + proc_recv_indices.data(), + MPI_INT, + xcomm, + xinfo, + &xrequest); + MPIL_Start(xrequest); + MPIL_Wait(xrequest, &status); + MPIL_Request_free(&xrequest); + compare_neighbor_alltoallv_results( + pmpi_recv_vals, mpil_recv_vals, A.recv_comm.size_msgs); + + MPIL_Info_free(&xinfo); + MPIL_Comm_free(&xcomm); +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + test_all_matrices(); + + MPI_Finalize(); + return 0; +} // end of main() \ No newline at end of file diff --git a/library/tests/test_suitesparse_neighbor_reorder.cpp b/library/tests/test_suitesparse_neighbor_reorder.cpp index 949a7b3f4..40687799b 100644 --- a/library/tests/test_suitesparse_neighbor_reorder.cpp +++ b/library/tests/test_suitesparse_neighbor_reorder.cpp @@ -9,6 +9,7 @@ #include #include "locality_aware.h" +#include "tests/common.hpp" #include "tests/par_binary_IO.hpp" void compare_neighbor_alltoallv_results(std::vector& pmpi_recv_vals, @@ -184,25 +185,7 @@ void test_matrix(const char* filename) int main(int argc, char** argv) { MPI_Init(&argc, &argv); - test_matrix("../../../test_data/dwt_162.pm"); - test_matrix("../../../test_data/odepa400.pm"); - test_matrix("../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../test_data/bcsstk01.pm"); - test_matrix("../../../test_data/west0132.pm"); - test_matrix("../../../test_data/gams10a.pm"); - test_matrix("../../../test_data/gams10am.pm"); - test_matrix("../../../test_data/D_10.pm"); - test_matrix("../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../test_data/msc01050.pm"); - test_matrix("../../../test_data/SmaGri.pm"); - test_matrix("../../../test_data/radfr1.pm"); - test_matrix("../../../test_data/bibd_49_3.pm"); - test_matrix("../../../test_data/can_1054.pm"); - test_matrix("../../../test_data/can_1072.pm"); - test_matrix("../../../test_data/lp_sctap2.pm"); - test_matrix("../../../test_data/lp_woodw.pm"); + test_all_matrices(); MPI_Finalize(); return 0; } // end of main() // diff --git a/library/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp b/library/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp index 217a46658..a4f514d7a 100644 --- a/library/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp +++ b/library/tests/test_suitesparse_neighbor_topo_alltoallv_init.cpp @@ -9,6 +9,7 @@ #include #include "locality_aware.h" +#include "tests/common.hpp" #include "tests/par_binary_IO.hpp" #include "tests/sparse_mat.hpp" @@ -261,25 +262,7 @@ void test_matrix(const char* filename) int main(int argc, char** argv) { MPI_Init(&argc, &argv); - test_matrix("../../../test_data/dwt_162.pm"); - test_matrix("../../../test_data/odepa400.pm"); - test_matrix("../../../test_data/ww_36_pmec_36.pm"); - test_matrix("../../../test_data/bcsstk01.pm"); - test_matrix("../../../test_data/west0132.pm"); - test_matrix("../../../test_data/gams10a.pm"); - test_matrix("../../../test_data/gams10am.pm"); - test_matrix("../../../test_data/D_10.pm"); - test_matrix("../../../test_data/oscil_dcop_11.pm"); - test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); - test_matrix("../../../test_data/ch5-5-b1.pm"); - test_matrix("../../../test_data/msc01050.pm"); - test_matrix("../../../test_data/SmaGri.pm"); - test_matrix("../../../test_data/radfr1.pm"); - test_matrix("../../../test_data/bibd_49_3.pm"); - test_matrix("../../../test_data/can_1054.pm"); - test_matrix("../../../test_data/can_1072.pm"); - test_matrix("../../../test_data/lp_sctap2.pm"); - test_matrix("../../../test_data/lp_woodw.pm"); + test_all_matrices(); MPI_Finalize(); return 0; } // end of main() // diff --git a/library/tests/tests/common.hpp b/library/tests/tests/common.hpp new file mode 100644 index 000000000..b0edab076 --- /dev/null +++ b/library/tests/tests/common.hpp @@ -0,0 +1,29 @@ +#ifndef MPI_ADVANCE_TEST_ALL_MATRIX_HPP +#define MPI_ADVANCE_TEST_ALL_MATRIX_HPP + +void test_matrix(const char*); + +void test_all_matrices() +{ + test_matrix("../../../test_data/dwt_162.pm"); + test_matrix("../../../test_data/odepa400.pm"); + test_matrix("../../../test_data/ww_36_pmec_36.pm"); + test_matrix("../../../test_data/bcsstk01.pm"); + test_matrix("../../../test_data/west0132.pm"); + test_matrix("../../../test_data/gams10a.pm"); + test_matrix("../../../test_data/gams10am.pm"); + test_matrix("../../../test_data/D_10.pm"); + test_matrix("../../../test_data/oscil_dcop_11.pm"); + test_matrix("../../../test_data/tumorAntiAngiogenesis_4.pm"); + test_matrix("../../../test_data/ch5-5-b1.pm"); + test_matrix("../../../test_data/msc01050.pm"); + test_matrix("../../../test_data/SmaGri.pm"); + test_matrix("../../../test_data/radfr1.pm"); + test_matrix("../../../test_data/bibd_49_3.pm"); + test_matrix("../../../test_data/can_1054.pm"); + test_matrix("../../../test_data/can_1072.pm"); + test_matrix("../../../test_data/lp_sctap2.pm"); + test_matrix("../../../test_data/lp_woodw.pm"); +} + +#endif \ No newline at end of file diff --git a/library/tests/tests/compare.hpp b/library/tests/tests/compare.hpp index eaf881c39..0a7ab91c2 100644 --- a/library/tests/tests/compare.hpp +++ b/library/tests/tests/compare.hpp @@ -1,3 +1,6 @@ +#ifndef MPI_ADVANCE_TEST_COMPARE_HPP +#define MPI_ADVANCE_TEST_COMPARE_HPP + #include #include #include @@ -86,4 +89,6 @@ void compare(int n_recvs, } } } -} \ No newline at end of file +} + +#endif \ No newline at end of file From 9254e04a4314ee71cba2b95373434f67e158b0cf Mon Sep 17 00:00:00 2001 From: Derek Schafer Date: Fri, 24 Oct 2025 09:42:10 -0600 Subject: [PATCH 08/15] Remove oversubscribe flag --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 508c71649..60ea3700f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ function(make_executable filename flag) endif() if(ENABLE_UNIT_TESTS) - add_test(NAME ${locality_test_name} COMMAND ${MPIRUN} -n ${TEST_PROCS} --oversubscribe ./${exec_name} ${TEST_MATRIX_FILE}) + add_test(NAME ${locality_test_name} COMMAND ${MPIRUN} -n ${TEST_PROCS} ./${exec_name} ${TEST_MATRIX_FILE}) endif() endfunction() From 21c482145aa786a126cd94f03706072ea119b6fe Mon Sep 17 00:00:00 2001 From: Derek Schafer Date: Fri, 24 Oct 2025 10:02:38 -0600 Subject: [PATCH 09/15] Added GPU test to new CMake code --- library/tests/gpu_tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/tests/gpu_tests/CMakeLists.txt b/library/tests/gpu_tests/CMakeLists.txt index da6eb8786..26a4034fb 100644 --- a/library/tests/gpu_tests/CMakeLists.txt +++ b/library/tests/gpu_tests/CMakeLists.txt @@ -6,7 +6,7 @@ set_source_files_properties(${CPP_SOURCES} PROPERTIES LANGUAGE ${locality_aware_ ## Go through each CPP file foreach(src ${CPP_SOURCES}) - make_test(${src}) + make_executable(${src} TRUE) endforeach() From 132cb15a3c707243a791be86b92acb204879c473 Mon Sep 17 00:00:00 2001 From: Derek Schafer Date: Wed, 12 Nov 2025 09:39:50 -0700 Subject: [PATCH 10/15] Removed Test Data Folder --- benchmark_tests/dane/10_09_24/microbench.err | 2 - benchmark_tests/dane/10_09_24/microbench.out | 0 benchmark_tests/dane/microbench.err | 2 - benchmark_tests/dane/microbench.out | 1521 ----------- benchmark_tests/dane/test_microbench | 15 - benchmark_tests/googletest.err | 3 - benchmark_tests/googletest.out | 960 ------- .../08_09_22/bruck_n16_ppn16.3848155.out | 359 --- .../08_09_22/bruck_n16_ppn4.3848153.out | 167 -- .../08_09_22/bruck_n32_ppn32.3848855.out | 1143 --------- .../lassen/08_09_22/bruck_n4_ppn4.3848151.out | 107 - .../08_09_22/bruck_n64_ppn4.3848169.out | 407 --- .../bruck_n16_ppn16.3850164.out | 359 --- .../bruck_n16_ppn4.3850166.out | 167 -- .../bruck_n32_ppn32.3850163.out | 1143 --------- .../bruck_allgather/bruck_n4_ppn4.3850165.out | 107 - .../bruck_n64_ppn4.3850167.out | 407 --- .../lassen/loc_alltoall_n4_ppn4.3930387.out | 123 - .../loc_alltoallv_n16_ppn16.3930839.out | 375 --- .../loc_alltoallv_n32_ppn16.3936300.out | 602 ----- .../lassen/loc_alltoallv_n4_ppn16.3936301.out | 171 -- .../lassen/loc_alltoallv_n4_ppn2.3936356.out | 70 - .../lassen/loc_alltoallv_n4_ppn4.3930386.out | 123 - benchmark_tests/lassen/test_bruck_allgather | 13 - benchmark_tests/lassen/test_loc_alltoall | 14 - benchmark_tests/lassen/test_loc_alltoallv | 14 - benchmark_tests/lassen/test_mpi_advance | 15 - benchmark_tests/plots/dane/matching.pdf | Bin 95239 -> 0 bytes benchmark_tests/plots/dane/multimsg.pdf | Bin 96536 -> 0 bytes .../plots/dane/multiproc-off-node-even.pdf | Bin 101791 -> 0 bytes .../plots/dane/multiproc-off-node.pdf | Bin 101556 -> 0 bytes .../plots/dane/multiproc-off-socket.pdf | Bin 100709 -> 0 bytes .../plots/dane/multiproc-on-all-sockets.pdf | Bin 99652 -> 0 bytes .../plots/dane/multiproc-on-socket.pdf | Bin 99760 -> 0 bytes benchmark_tests/plots/dane/standard.pdf | Bin 97093 -> 0 bytes benchmark_tests/plots/lassen_bruck.pdf | Bin 130306 -> 0 bytes benchmark_tests/plots/plot.py | 220 -- benchmark_tests/plots/plot_allgathers.py | 97 - benchmark_tests/plots/pyfancyplot.py | 564 ---- .../plots/quartz_bruck_allgather.pdf | Bin 99908 -> 0 bytes .../02_17_24/alltoall_crs_n16.2244087.err | 0 .../02_17_24/alltoall_crs_n16.2244087.out | 45 - .../02_17_24/alltoall_crs_n2.2244090.err | 0 .../02_17_24/alltoall_crs_n2.2244090.out | 45 - .../02_17_24/alltoall_crs_n32.2244086.err | 1 - .../02_17_24/alltoall_crs_n32.2244086.out | 37 - .../02_17_24/alltoall_crs_n4.2244089.err | 2 - .../02_17_24/alltoall_crs_n4.2244089.out | 45 - .../02_17_24/alltoall_crs_n8.2244088.err | 0 .../02_17_24/alltoall_crs_n8.2244088.out | 45 - .../02_19_24/alltoall_crs_n16.2248984.err | 0 .../02_19_24/alltoall_crs_n16.2248984.out | 45 - .../02_19_24/alltoall_crs_n16.2249350.err | 0 .../02_19_24/alltoall_crs_n16.2249350.out | 45 - .../02_19_24/alltoall_crs_n2.2248978.err | 0 .../02_19_24/alltoall_crs_n2.2248978.out | 45 - .../02_19_24/alltoall_crs_n2.2249353.err | 0 .../02_19_24/alltoall_crs_n2.2249353.out | 45 - .../02_19_24/alltoall_crs_n32.2249381.err | 0 .../02_19_24/alltoall_crs_n32.2249381.out | 45 - .../02_19_24/alltoall_crs_n4.2248982.err | 0 .../02_19_24/alltoall_crs_n4.2248982.out | 45 - .../02_19_24/alltoall_crs_n4.2249352.err | 2 - .../02_19_24/alltoall_crs_n4.2249352.out | 45 - .../02_19_24/alltoall_crs_n8.2248983.err | 0 .../02_19_24/alltoall_crs_n8.2248983.out | 45 - .../02_19_24/alltoall_crs_n8.2249351.err | 0 .../02_19_24/alltoall_crs_n8.2249351.out | 45 - .../alltoallv_crs_n16.2249442.err | 121 - .../alltoallv_crs_n16.2249442.out | 34 - .../alltoallv_crs_n2.2249439.err | 0 .../alltoallv_crs_n2.2249439.out | 40 - .../alltoallv_crs_n32.2249443.err | 2267 ----------------- .../alltoallv_crs_n32.2249443.out | 36 - .../alltoallv_crs_n8.2249441.err | 59 - .../alltoallv_crs_n8.2249441.out | 34 - .../quartz/alltoall_crs_n16.2254736.err | 0 .../quartz/alltoall_crs_n16.2254736.out | 45 - .../quartz/alltoall_crs_n2.2254739.err | 0 .../quartz/alltoall_crs_n2.2254739.out | 45 - .../quartz/alltoall_crs_n32.2254734.err | 0 .../quartz/alltoall_crs_n32.2254734.out | 45 - .../quartz/alltoall_crs_n4.2254738.err | 0 .../quartz/alltoall_crs_n4.2254738.out | 45 - .../quartz/alltoall_crs_n64.2254733.err | 0 .../quartz/alltoall_crs_n64.2254733.out | 45 - .../quartz/alltoall_crs_n8.2254737.err | 0 .../quartz/alltoall_crs_n8.2254737.out | 45 - benchmark_tests/quartz/test_alltoall_crs | 20 - benchmark_tests/quartz/test_alltoallv_crs | 25 - benchmark_tests/quartz/test_bruck_allgather | 14 - benchmark_tests/quartz/test_googletest | 14 - benchmark_tests/quartz/test_loc_alltoall | 15 - benchmark_tests/quartz/test_loc_alltoallv | 15 - .../test_mat/alltoall_crs_n2.2242909.err | 0 .../test_mat/alltoall_crs_n2.2242909.out | 8 - .../test_mat/alltoall_crs_n4.2242913.err | 0 .../test_mat/alltoall_crs_n4.2242913.out | 8 - .../test_mat/alltoall_crs_n8.2242916.err | 0 .../test_mat/alltoall_crs_n8.2242916.out | 8 - benchmark_tests/quartz/tmp_crs_n4.2261030.err | 3 - benchmark_tests/quartz/tmp_crs_n4.2261030.out | 9 - benchmark_tests/quartz/tmp_crs_n4.2261039.err | 0 benchmark_tests/quartz/tmp_crs_n4.2261039.out | 20 - benchmark_tests/quartz/tmp_crs_n4.2261044.err | 0 benchmark_tests/quartz/tmp_crs_n4.2261044.out | 4 - benchmark_tests/test_bruck_allgather | 14 - benchmark_tests/test_googletest | 14 - 108 files changed, 12949 deletions(-) delete mode 100644 benchmark_tests/dane/10_09_24/microbench.err delete mode 100644 benchmark_tests/dane/10_09_24/microbench.out delete mode 100644 benchmark_tests/dane/microbench.err delete mode 100644 benchmark_tests/dane/microbench.out delete mode 100644 benchmark_tests/dane/test_microbench delete mode 100644 benchmark_tests/googletest.err delete mode 100644 benchmark_tests/googletest.out delete mode 100644 benchmark_tests/lassen/08_09_22/bruck_n16_ppn16.3848155.out delete mode 100644 benchmark_tests/lassen/08_09_22/bruck_n16_ppn4.3848153.out delete mode 100644 benchmark_tests/lassen/08_09_22/bruck_n32_ppn32.3848855.out delete mode 100644 benchmark_tests/lassen/08_09_22/bruck_n4_ppn4.3848151.out delete mode 100644 benchmark_tests/lassen/08_09_22/bruck_n64_ppn4.3848169.out delete mode 100644 benchmark_tests/lassen/bruck_allgather/bruck_n16_ppn16.3850164.out delete mode 100644 benchmark_tests/lassen/bruck_allgather/bruck_n16_ppn4.3850166.out delete mode 100644 benchmark_tests/lassen/bruck_allgather/bruck_n32_ppn32.3850163.out delete mode 100644 benchmark_tests/lassen/bruck_allgather/bruck_n4_ppn4.3850165.out delete mode 100644 benchmark_tests/lassen/bruck_allgather/bruck_n64_ppn4.3850167.out delete mode 100644 benchmark_tests/lassen/loc_alltoall_n4_ppn4.3930387.out delete mode 100644 benchmark_tests/lassen/loc_alltoallv_n16_ppn16.3930839.out delete mode 100644 benchmark_tests/lassen/loc_alltoallv_n32_ppn16.3936300.out delete mode 100644 benchmark_tests/lassen/loc_alltoallv_n4_ppn16.3936301.out delete mode 100644 benchmark_tests/lassen/loc_alltoallv_n4_ppn2.3936356.out delete mode 100644 benchmark_tests/lassen/loc_alltoallv_n4_ppn4.3930386.out delete mode 100644 benchmark_tests/lassen/test_bruck_allgather delete mode 100644 benchmark_tests/lassen/test_loc_alltoall delete mode 100644 benchmark_tests/lassen/test_loc_alltoallv delete mode 100644 benchmark_tests/lassen/test_mpi_advance delete mode 100644 benchmark_tests/plots/dane/matching.pdf delete mode 100644 benchmark_tests/plots/dane/multimsg.pdf delete mode 100644 benchmark_tests/plots/dane/multiproc-off-node-even.pdf delete mode 100644 benchmark_tests/plots/dane/multiproc-off-node.pdf delete mode 100644 benchmark_tests/plots/dane/multiproc-off-socket.pdf delete mode 100644 benchmark_tests/plots/dane/multiproc-on-all-sockets.pdf delete mode 100644 benchmark_tests/plots/dane/multiproc-on-socket.pdf delete mode 100644 benchmark_tests/plots/dane/standard.pdf delete mode 100644 benchmark_tests/plots/lassen_bruck.pdf delete mode 100644 benchmark_tests/plots/plot.py delete mode 100644 benchmark_tests/plots/plot_allgathers.py delete mode 100644 benchmark_tests/plots/pyfancyplot.py delete mode 100644 benchmark_tests/plots/quartz_bruck_allgather.pdf delete mode 100644 benchmark_tests/quartz/02_17_24/alltoall_crs_n16.2244087.err delete mode 100644 benchmark_tests/quartz/02_17_24/alltoall_crs_n16.2244087.out delete mode 100644 benchmark_tests/quartz/02_17_24/alltoall_crs_n2.2244090.err delete mode 100644 benchmark_tests/quartz/02_17_24/alltoall_crs_n2.2244090.out delete mode 100644 benchmark_tests/quartz/02_17_24/alltoall_crs_n32.2244086.err delete mode 100644 benchmark_tests/quartz/02_17_24/alltoall_crs_n32.2244086.out delete mode 100644 benchmark_tests/quartz/02_17_24/alltoall_crs_n4.2244089.err delete mode 100644 benchmark_tests/quartz/02_17_24/alltoall_crs_n4.2244089.out delete mode 100644 benchmark_tests/quartz/02_17_24/alltoall_crs_n8.2244088.err delete mode 100644 benchmark_tests/quartz/02_17_24/alltoall_crs_n8.2244088.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n16.2248984.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n16.2248984.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n16.2249350.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n16.2249350.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n2.2248978.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n2.2248978.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n2.2249353.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n2.2249353.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n32.2249381.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n32.2249381.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n4.2248982.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n4.2248982.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n4.2249352.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n4.2249352.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n8.2248983.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n8.2248983.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n8.2249351.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoall_crs_n8.2249351.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoallv_broken/alltoallv_crs_n16.2249442.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoallv_broken/alltoallv_crs_n16.2249442.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoallv_broken/alltoallv_crs_n2.2249439.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoallv_broken/alltoallv_crs_n2.2249439.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoallv_broken/alltoallv_crs_n32.2249443.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoallv_broken/alltoallv_crs_n32.2249443.out delete mode 100644 benchmark_tests/quartz/02_19_24/alltoallv_broken/alltoallv_crs_n8.2249441.err delete mode 100644 benchmark_tests/quartz/02_19_24/alltoallv_broken/alltoallv_crs_n8.2249441.out delete mode 100644 benchmark_tests/quartz/alltoall_crs_n16.2254736.err delete mode 100644 benchmark_tests/quartz/alltoall_crs_n16.2254736.out delete mode 100644 benchmark_tests/quartz/alltoall_crs_n2.2254739.err delete mode 100644 benchmark_tests/quartz/alltoall_crs_n2.2254739.out delete mode 100644 benchmark_tests/quartz/alltoall_crs_n32.2254734.err delete mode 100644 benchmark_tests/quartz/alltoall_crs_n32.2254734.out delete mode 100644 benchmark_tests/quartz/alltoall_crs_n4.2254738.err delete mode 100644 benchmark_tests/quartz/alltoall_crs_n4.2254738.out delete mode 100644 benchmark_tests/quartz/alltoall_crs_n64.2254733.err delete mode 100644 benchmark_tests/quartz/alltoall_crs_n64.2254733.out delete mode 100644 benchmark_tests/quartz/alltoall_crs_n8.2254737.err delete mode 100644 benchmark_tests/quartz/alltoall_crs_n8.2254737.out delete mode 100644 benchmark_tests/quartz/test_alltoall_crs delete mode 100644 benchmark_tests/quartz/test_alltoallv_crs delete mode 100644 benchmark_tests/quartz/test_bruck_allgather delete mode 100644 benchmark_tests/quartz/test_googletest delete mode 100644 benchmark_tests/quartz/test_loc_alltoall delete mode 100644 benchmark_tests/quartz/test_loc_alltoallv delete mode 100644 benchmark_tests/quartz/test_mat/alltoall_crs_n2.2242909.err delete mode 100644 benchmark_tests/quartz/test_mat/alltoall_crs_n2.2242909.out delete mode 100644 benchmark_tests/quartz/test_mat/alltoall_crs_n4.2242913.err delete mode 100644 benchmark_tests/quartz/test_mat/alltoall_crs_n4.2242913.out delete mode 100644 benchmark_tests/quartz/test_mat/alltoall_crs_n8.2242916.err delete mode 100644 benchmark_tests/quartz/test_mat/alltoall_crs_n8.2242916.out delete mode 100644 benchmark_tests/quartz/tmp_crs_n4.2261030.err delete mode 100644 benchmark_tests/quartz/tmp_crs_n4.2261030.out delete mode 100644 benchmark_tests/quartz/tmp_crs_n4.2261039.err delete mode 100644 benchmark_tests/quartz/tmp_crs_n4.2261039.out delete mode 100644 benchmark_tests/quartz/tmp_crs_n4.2261044.err delete mode 100644 benchmark_tests/quartz/tmp_crs_n4.2261044.out delete mode 100644 benchmark_tests/test_bruck_allgather delete mode 100644 benchmark_tests/test_googletest diff --git a/benchmark_tests/dane/10_09_24/microbench.err b/benchmark_tests/dane/10_09_24/microbench.err deleted file mode 100644 index 8ba2a12e6..000000000 --- a/benchmark_tests/dane/10_09_24/microbench.err +++ /dev/null @@ -1,2 +0,0 @@ -srun: unrecognized option '--auto-affinity=verbose' -Try "srun --help" for more information diff --git a/benchmark_tests/dane/10_09_24/microbench.out b/benchmark_tests/dane/10_09_24/microbench.out deleted file mode 100644 index e69de29bb..000000000 diff --git a/benchmark_tests/dane/microbench.err b/benchmark_tests/dane/microbench.err deleted file mode 100644 index 8ba2a12e6..000000000 --- a/benchmark_tests/dane/microbench.err +++ /dev/null @@ -1,2 +0,0 @@ -srun: unrecognized option '--auto-affinity=verbose' -Try "srun --help" for more information diff --git a/benchmark_tests/dane/microbench.out b/benchmark_tests/dane/microbench.out deleted file mode 100644 index efb202955..000000000 --- a/benchmark_tests/dane/microbench.out +++ /dev/null @@ -1,1521 +0,0 @@ -On-NUMA Ping-Pong: -Size 1: 5.928218e-07 -Size 2: 6.048648e-07 -Size 4: 6.031912e-07 -Size 8: 6.049279e-07 -Size 16: 6.072289e-07 -Size 32: 8.216235e-07 -Size 64: 8.237920e-07 -Size 128: 9.345476e-07 -Size 256: 9.526012e-07 -Size 512: 9.970086e-07 -Size 1024: 1.247769e-06 -Size 2048: 1.520272e-06 -Size 4096: 2.071614e-06 -Size 8192: 3.513990e-06 -Size 16384: 5.664203e-06 -Size 32768: 9.902182e-06 -Size 65536: 8.630842e-06 -Size 131072: 1.463380e-05 -Size 262144: 2.649738e-05 -Size 524288: 5.032401e-05 -Size 1048576: 9.718358e-05 -Size 2097152: 2.092258e-04 -Size 4194304: 5.192516e-04 -Size 8388608: 1.048407e-03 -Size 16777216: 3.075299e-03 - -On-Socket Ping-Pong: -Size 1: 5.781312e-07 -Size 2: 5.762798e-07 -Size 4: 5.748167e-07 -Size 8: 5.772478e-07 -Size 16: 5.826815e-07 -Size 32: 8.865769e-07 -Size 64: 8.893104e-07 -Size 128: 1.035063e-06 -Size 256: 1.048478e-06 -Size 512: 1.080814e-06 -Size 1024: 1.304908e-06 -Size 2048: 1.647343e-06 -Size 4096: 2.267667e-06 -Size 8192: 3.802372e-06 -Size 16384: 6.006954e-06 -Size 32768: 1.039320e-05 -Size 65536: 8.678815e-06 -Size 131072: 1.462916e-05 -Size 262144: 2.631591e-05 -Size 524288: 4.969404e-05 -Size 1048576: 9.601696e-05 -Size 2097152: 2.055006e-04 -Size 4194304: 5.088166e-04 -Size 8388608: 1.028344e-03 -Size 16777216: 2.090661e-03 - -On-Node, Off-Socket Ping-Pong: -Size 1: 1.258649e-06 -Size 2: 1.260046e-06 -Size 4: 1.257336e-06 -Size 8: 1.259727e-06 -Size 16: 1.268373e-06 -Size 32: 1.872967e-06 -Size 64: 1.871606e-06 -Size 128: 2.168231e-06 -Size 256: 2.213574e-06 -Size 512: 2.344189e-06 -Size 1024: 2.910000e-06 -Size 2048: 3.439216e-06 -Size 4096: 4.452006e-06 -Size 8192: 6.156395e-06 -Size 16384: 8.894495e-06 -Size 32768: 1.475359e-05 -Size 65536: 1.042745e-05 -Size 131072: 1.642632e-05 -Size 262144: 2.817052e-05 -Size 524288: 5.163906e-05 -Size 1048576: 9.866514e-05 -Size 2097152: 2.095631e-04 -Size 4194304: 5.162798e-04 -Size 8388608: 1.042234e-03 -Size 16777216: 2.112735e-03 - -Off-Node Ping-Pong: -Size 1: 2.105323e-06 -Size 2: 2.135620e-06 -Size 4: 2.131319e-06 -Size 8: 2.157693e-06 -Size 16: 2.294766e-06 -Size 32: 2.338719e-06 -Size 64: 2.364407e-06 -Size 128: 2.363347e-06 -Size 256: 2.484590e-06 -Size 512: 2.619104e-06 -Size 1024: 2.851420e-06 -Size 2048: 3.290228e-06 -Size 4096: 4.165272e-06 -Size 8192: 4.889685e-06 -Size 16384: 1.427946e-05 -Size 32768: 1.483562e-05 -Size 65536: 3.003679e-05 -Size 131072: 3.741824e-05 -Size 262144: 5.419831e-05 -Size 524288: 8.516897e-05 -Size 1048576: 1.425872e-04 -Size 2097152: 2.667920e-04 -Size 4194304: 4.805063e-04 -Size 8388608: 9.210144e-04 -Size 16777216: 2.688863e-03 - -On-NUMA MultiProc Ping-Pong: -Active Procs: 1 -Size 1: 5.096926e-07 -Size 2: 5.083870e-07 -Size 4: 5.080022e-07 -Size 8: 5.077501e-07 -Size 16: 5.121551e-07 -Size 32: 7.657663e-07 -Size 64: 7.708276e-07 -Size 128: 8.811747e-07 -Size 256: 8.949694e-07 -Size 512: 9.404469e-07 -Size 1024: 1.168674e-06 -Size 2048: 1.492244e-06 -Size 4096: 2.073328e-06 -Size 8192: 3.532789e-06 -Size 16384: 5.659292e-06 -Size 32768: 9.905089e-06 -Size 65536: 8.618918e-06 -Size 131072: 1.463845e-05 -Size 262144: 2.661082e-05 -Size 524288: 5.028041e-05 -Size 1048576: 9.747428e-05 -Size 2097152: 2.099616e-04 -Size 4194304: 5.202753e-04 -Size 8388608: 1.050050e-03 -Size 16777216: 3.087265e-03 - -Active Procs: 2 -Size 1: 5.531857e-07 -Size 2: 5.475818e-07 -Size 4: 5.511304e-07 -Size 8: 5.500076e-07 -Size 16: 5.571045e-07 -Size 32: 7.660012e-07 -Size 64: 7.718043e-07 -Size 128: 8.824003e-07 -Size 256: 8.952582e-07 -Size 512: 9.439010e-07 -Size 1024: 1.173174e-06 -Size 2048: 1.506477e-06 -Size 4096: 2.083827e-06 -Size 8192: 3.553681e-06 -Size 16384: 5.684019e-06 -Size 32768: 9.937957e-06 -Size 65536: 1.049256e-05 -Size 131072: 1.867109e-05 -Size 262144: 3.481530e-05 -Size 524288: 6.774304e-05 -Size 1048576: 1.309977e-04 -Size 2097152: 2.732483e-04 -Size 4194304: 6.222202e-04 -Size 8388608: 1.548971e-03 -Size 16777216: 4.434847e-03 - -Active Procs: 4 -Size 1: 5.776644e-07 -Size 2: 5.896112e-07 -Size 4: 5.892939e-07 -Size 8: 5.909803e-07 -Size 16: 5.945784e-07 -Size 32: 8.282795e-07 -Size 64: 8.317276e-07 -Size 128: 8.930056e-07 -Size 256: 9.012033e-07 -Size 512: 9.457748e-07 -Size 1024: 1.180593e-06 -Size 2048: 1.533514e-06 -Size 4096: 2.116163e-06 -Size 8192: 3.565062e-06 -Size 16384: 5.771066e-06 -Size 32768: 1.009587e-05 -Size 65536: 1.870963e-05 -Size 131072: 3.662542e-05 -Size 262144: 7.218965e-05 -Size 524288: 1.428253e-04 -Size 1048576: 2.842256e-04 -Size 2097152: 5.585611e-04 -Size 4194304: 1.087178e-03 -Size 8388608: 3.011064e-03 -Size 16777216: 7.259015e-03 - -Active Procs: 7 -Size 1: 5.765341e-07 -Size 2: 5.660217e-07 -Size 4: 5.649736e-07 -Size 8: 5.643438e-07 -Size 16: 5.699035e-07 -Size 32: 7.746443e-07 -Size 64: 7.797426e-07 -Size 128: 8.922009e-07 -Size 256: 9.074318e-07 -Size 512: 9.536218e-07 -Size 1024: 1.192481e-06 -Size 2048: 1.559800e-06 -Size 4096: 2.231012e-06 -Size 8192: 3.600358e-06 -Size 16384: 6.371736e-06 -Size 32768: 1.148668e-05 -Size 65536: 3.510278e-05 -Size 131072: 6.806184e-05 -Size 262144: 1.337797e-04 -Size 524288: 2.637445e-04 -Size 1048576: 5.265626e-04 -Size 2097152: 1.045190e-03 -Size 4194304: 2.034906e-03 -Size 8388608: 5.031315e-03 -Size 16777216: 1.072894e-02 - -On-Socket MultiProc Ping-Pong: -Active Procs: 1 -Size 1: 5.775231e-07 -Size 2: 5.784044e-07 -Size 4: 5.767025e-07 -Size 8: 5.784107e-07 -Size 16: 5.812402e-07 -Size 32: 8.810444e-07 -Size 64: 8.871752e-07 -Size 128: 1.034575e-06 -Size 256: 1.050363e-06 -Size 512: 1.079286e-06 -Size 1024: 1.306308e-06 -Size 2048: 1.659952e-06 -Size 4096: 2.269528e-06 -Size 8192: 3.791663e-06 -Size 16384: 6.002352e-06 -Size 32768: 1.040308e-05 -Size 65536: 8.655232e-06 -Size 131072: 1.455371e-05 -Size 262144: 2.632272e-05 -Size 524288: 4.964829e-05 -Size 1048576: 9.609326e-05 -Size 2097152: 2.058183e-04 -Size 4194304: 5.089989e-04 -Size 8388608: 1.028411e-03 -Size 16777216: 2.092441e-03 - -Active Procs: 2 -Size 1: 6.230810e-07 -Size 2: 6.176357e-07 -Size 4: 6.340365e-07 -Size 8: 6.285073e-07 -Size 16: 6.309140e-07 -Size 32: 8.840623e-07 -Size 64: 8.908256e-07 -Size 128: 1.036453e-06 -Size 256: 1.052097e-06 -Size 512: 1.083324e-06 -Size 1024: 1.315018e-06 -Size 2048: 1.677784e-06 -Size 4096: 2.287048e-06 -Size 8192: 3.834181e-06 -Size 16384: 6.046280e-06 -Size 32768: 1.048799e-05 -Size 65536: 1.053489e-05 -Size 131072: 1.859991e-05 -Size 262144: 3.470745e-05 -Size 524288: 6.691332e-05 -Size 1048576: 1.309045e-04 -Size 2097152: 2.717980e-04 -Size 4194304: 6.119192e-04 -Size 8388608: 1.249210e-03 -Size 16777216: 3.259451e-03 - -Active Procs: 4 -Size 1: 6.334804e-07 -Size 2: 6.387362e-07 -Size 4: 6.385237e-07 -Size 8: 6.390470e-07 -Size 16: 6.500720e-07 -Size 32: 9.537994e-07 -Size 64: 9.569506e-07 -Size 128: 1.101148e-06 -Size 256: 1.123747e-06 -Size 512: 1.110202e-06 -Size 1024: 1.350658e-06 -Size 2048: 1.729275e-06 -Size 4096: 2.344433e-06 -Size 8192: 3.991856e-06 -Size 16384: 6.157605e-06 -Size 32768: 1.066536e-05 -Size 65536: 1.992496e-05 -Size 131072: 3.913705e-05 -Size 262144: 7.737665e-05 -Size 524288: 1.533631e-04 -Size 1048576: 3.056395e-04 -Size 2097152: 5.952367e-04 -Size 4194304: 1.161662e-03 -Size 8388608: 2.367914e-03 -Size 16777216: 5.524937e-03 - -Active Procs: 8 -Size 1: 6.404558e-07 -Size 2: 6.341212e-07 -Size 4: 6.361585e-07 -Size 8: 6.349935e-07 -Size 16: 6.421402e-07 -Size 32: 9.341449e-07 -Size 64: 9.374713e-07 -Size 128: 1.058397e-06 -Size 256: 1.058074e-06 -Size 512: 1.096147e-06 -Size 1024: 1.349284e-06 -Size 2048: 1.729102e-06 -Size 4096: 2.359253e-06 -Size 8192: 4.024460e-06 -Size 16384: 6.217284e-06 -Size 32768: 1.079290e-05 -Size 65536: 4.521179e-05 -Size 131072: 8.795544e-05 -Size 262144: 1.729384e-04 -Size 524288: 3.440603e-04 -Size 1048576: 6.829645e-04 -Size 2097152: 1.354790e-03 -Size 4194304: 2.670275e-03 -Size 8388608: 5.163410e-03 -Size 16777216: 1.047329e-02 - -Active Procs: 16 -Size 1: 6.384629e-07 -Size 2: 6.503736e-07 -Size 4: 6.529499e-07 -Size 8: 6.342819e-07 -Size 16: 6.324005e-07 -Size 32: 9.643636e-07 -Size 64: 9.684000e-07 -Size 128: 1.117644e-06 -Size 256: 1.133696e-06 -Size 512: 1.180072e-06 -Size 1024: 1.468087e-06 -Size 2048: 1.816037e-06 -Size 4096: 2.469456e-06 -Size 8192: 4.066208e-06 -Size 16384: 6.821460e-06 -Size 32768: 1.245112e-05 -Size 65536: 9.695043e-05 -Size 131072: 1.879063e-04 -Size 262144: 3.697149e-04 -Size 524288: 7.337968e-04 -Size 1048576: 1.456730e-03 -Size 2097152: 2.884675e-03 -Size 4194304: 5.676784e-03 -Size 8388608: 1.126725e-02 -Size 16777216: 2.242033e-02 - -Active Procs: 28 -Size 1: 6.452901e-07 -Size 2: 6.433990e-07 -Size 4: 6.410101e-07 -Size 8: 6.419863e-07 -Size 16: 6.557453e-07 -Size 32: 9.434225e-07 -Size 64: 9.059180e-07 -Size 128: 1.054171e-06 -Size 256: 1.070401e-06 -Size 512: 1.110995e-06 -Size 1024: 1.380636e-06 -Size 2048: 1.774563e-06 -Size 4096: 2.474242e-06 -Size 8192: 4.064918e-06 -Size 16384: 6.913248e-06 -Size 32768: 1.293598e-05 -Size 65536: 1.836561e-04 -Size 131072: 3.557272e-04 -Size 262144: 6.994418e-04 -Size 524288: 1.385332e-03 -Size 1048576: 2.750511e-03 -Size 2097152: 5.460683e-03 -Size 4194304: 1.086513e-02 -Size 8388608: 2.164009e-02 -Size 16777216: 4.313061e-02 - -On-Node, Off-Socket MultiProc Ping-Pong: -Active Procs: 1 -Size 1: 1.263603e-06 -Size 2: 1.264107e-06 -Size 4: 1.282126e-06 -Size 8: 1.252491e-06 -Size 16: 1.258992e-06 -Size 32: 1.868633e-06 -Size 64: 1.869681e-06 -Size 128: 2.160357e-06 -Size 256: 2.211784e-06 -Size 512: 2.319410e-06 -Size 1024: 2.889865e-06 -Size 2048: 3.472639e-06 -Size 4096: 4.454625e-06 -Size 8192: 6.175913e-06 -Size 16384: 8.891719e-06 -Size 32768: 1.473441e-05 -Size 65536: 1.041244e-05 -Size 131072: 1.640660e-05 -Size 262144: 2.827528e-05 -Size 524288: 5.173235e-05 -Size 1048576: 9.856215e-05 -Size 2097152: 2.093392e-04 -Size 4194304: 5.180932e-04 -Size 8388608: 1.045303e-03 -Size 16777216: 2.113444e-03 - -Active Procs: 2 -Size 1: 1.276647e-06 -Size 2: 1.260912e-06 -Size 4: 1.273199e-06 -Size 8: 1.268484e-06 -Size 16: 1.275863e-06 -Size 32: 1.859555e-06 -Size 64: 1.866210e-06 -Size 128: 2.154828e-06 -Size 256: 2.209624e-06 -Size 512: 2.342316e-06 -Size 1024: 2.964288e-06 -Size 2048: 3.532800e-06 -Size 4096: 4.485732e-06 -Size 8192: 6.248414e-06 -Size 16384: 9.091733e-06 -Size 32768: 1.525835e-05 -Size 65536: 1.215118e-05 -Size 131072: 2.063034e-05 -Size 262144: 3.802475e-05 -Size 524288: 7.210061e-05 -Size 1048576: 1.399991e-04 -Size 2097152: 2.861287e-04 -Size 4194304: 6.396743e-04 -Size 8388608: 1.312275e-03 -Size 16777216: 3.401749e-03 - -Active Procs: 4 -Size 1: 1.264942e-06 -Size 2: 1.259622e-06 -Size 4: 1.271976e-06 -Size 8: 1.266901e-06 -Size 16: 1.275078e-06 -Size 32: 2.029913e-06 -Size 64: 1.879908e-06 -Size 128: 2.167068e-06 -Size 256: 2.266384e-06 -Size 512: 2.430761e-06 -Size 1024: 3.034716e-06 -Size 2048: 3.653478e-06 -Size 4096: 4.632043e-06 -Size 8192: 6.561845e-06 -Size 16384: 9.976857e-06 -Size 32768: 1.819871e-05 -Size 65536: 2.644359e-05 -Size 131072: 5.263513e-05 -Size 262144: 1.052622e-04 -Size 524288: 2.090547e-04 -Size 1048576: 4.164276e-04 -Size 2097152: 8.238083e-04 -Size 4194304: 1.565680e-03 -Size 8388608: 2.991902e-03 -Size 16777216: 6.496038e-03 - -Active Procs: 8 -Size 1: 1.276048e-06 -Size 2: 1.257755e-06 -Size 4: 1.273688e-06 -Size 8: 1.275141e-06 -Size 16: 1.277047e-06 -Size 32: 1.893761e-06 -Size 64: 1.898872e-06 -Size 128: 2.184434e-06 -Size 256: 2.268293e-06 -Size 512: 2.453860e-06 -Size 1024: 3.284059e-06 -Size 2048: 4.112975e-06 -Size 4096: 5.162996e-06 -Size 8192: 8.138571e-06 -Size 16384: 1.458958e-05 -Size 32768: 2.912691e-05 -Size 65536: 6.775990e-05 -Size 131072: 1.305183e-04 -Size 262144: 2.569211e-04 -Size 524288: 4.988221e-04 -Size 1048576: 9.684481e-04 -Size 2097152: 1.883472e-03 -Size 4194304: 3.575439e-03 -Size 8388608: 7.802381e-03 -Size 16777216: 1.519871e-02 - -Active Procs: 16 -Size 1: 1.274549e-06 -Size 2: 1.263714e-06 -Size 4: 1.282678e-06 -Size 8: 1.280100e-06 -Size 16: 1.291901e-06 -Size 32: 1.922873e-06 -Size 64: 1.921230e-06 -Size 128: 2.207043e-06 -Size 256: 2.275111e-06 -Size 512: 2.563894e-06 -Size 1024: 3.910507e-06 -Size 2048: 5.139326e-06 -Size 4096: 6.824716e-06 -Size 8192: 1.423591e-05 -Size 16384: 2.404102e-05 -Size 32768: 4.673194e-05 -Size 65536: 1.357566e-04 -Size 131072: 2.618285e-04 -Size 262144: 5.132693e-04 -Size 524288: 1.017824e-03 -Size 1048576: 2.023713e-03 -Size 2097152: 4.010040e-03 -Size 4194304: 7.628589e-03 -Size 8388608: 1.583020e-02 -Size 16777216: 3.169360e-02 - -Active Procs: 32 -Size 1: 1.302649e-06 -Size 2: 1.285689e-06 -Size 4: 1.311194e-06 -Size 8: 1.304735e-06 -Size 16: 1.315729e-06 -Size 32: 2.124905e-06 -Size 64: 1.933009e-06 -Size 128: 2.239990e-06 -Size 256: 2.286187e-06 -Size 512: 2.590600e-06 -Size 1024: 4.042399e-06 -Size 2048: 5.370586e-06 -Size 4096: 7.277723e-06 -Size 8192: 1.593087e-05 -Size 16384: 2.630440e-05 -Size 32768: 5.049342e-05 -Size 65536: 2.917995e-04 -Size 131072: 5.644599e-04 -Size 262144: 1.107737e-03 -Size 524288: 2.187663e-03 -Size 1048576: 4.347002e-03 -Size 2097152: 8.540726e-03 -Size 4194304: 1.678191e-02 -Size 8388608: 3.323504e-02 -Size 16777216: 6.630808e-02 - -Active Procs: 56 -Size 1: 1.330944e-06 -Size 2: 1.308756e-06 -Size 4: 1.335241e-06 -Size 8: 1.333154e-06 -Size 16: 1.340519e-06 -Size 32: 2.263314e-06 -Size 64: 2.269762e-06 -Size 128: 2.599445e-06 -Size 256: 2.469454e-06 -Size 512: 2.836283e-06 -Size 1024: 4.960374e-06 -Size 2048: 7.999753e-06 -Size 4096: 1.226458e-05 -Size 8192: 2.383670e-05 -Size 16384: 4.118560e-05 -Size 32768: 8.542929e-05 -Size 65536: 5.417076e-04 -Size 131072: 1.048259e-03 -Size 262144: 2.048384e-03 -Size 524288: 4.045273e-03 -Size 1048576: 8.027367e-03 -Size 2097152: 1.556720e-02 -Size 4194304: 3.044487e-02 -Size 8388608: 6.014674e-02 -Size 16777216: 1.175507e-01 - -Off-Node MultiProc Ping-Pong: -Active Procs: 1 -Size 1: 2.108231e-06 -Size 2: 2.108313e-06 -Size 4: 2.146561e-06 -Size 8: 2.106925e-06 -Size 16: 2.291676e-06 -Size 32: 2.343238e-06 -Size 64: 2.361775e-06 -Size 128: 2.360955e-06 -Size 256: 2.442882e-06 -Size 512: 2.627059e-06 -Size 1024: 2.881243e-06 -Size 2048: 3.266843e-06 -Size 4096: 4.145652e-06 -Size 8192: 4.898436e-06 -Size 16384: 1.433567e-05 -Size 32768: 1.439647e-05 -Size 65536: 2.790562e-05 -Size 131072: 3.685398e-05 -Size 262144: 4.839755e-05 -Size 524288: 7.576545e-05 -Size 1048576: 1.285706e-04 -Size 2097152: 2.405184e-04 -Size 4194304: 4.372615e-04 -Size 8388608: 7.842712e-04 -Size 16777216: 2.444482e-03 - -Active Procs: 2 -Size 1: 2.097113e-06 -Size 2: 2.151595e-06 -Size 4: 2.088394e-06 -Size 8: 2.091351e-06 -Size 16: 2.329463e-06 -Size 32: 2.302218e-06 -Size 64: 2.349569e-06 -Size 128: 2.407545e-06 -Size 256: 2.435752e-06 -Size 512: 2.632445e-06 -Size 1024: 2.841922e-06 -Size 2048: 3.318047e-06 -Size 4096: 4.177199e-06 -Size 8192: 4.822469e-06 -Size 16384: 1.429981e-05 -Size 32768: 1.450984e-05 -Size 65536: 2.844018e-05 -Size 131072: 4.226152e-05 -Size 262144: 5.595447e-05 -Size 524288: 9.952743e-05 -Size 1048576: 1.950042e-04 -Size 2097152: 3.618530e-04 -Size 4194304: 7.027159e-04 -Size 8388608: 1.393419e-03 -Size 16777216: 3.085632e-03 - -Active Procs: 4 -Size 1: 2.083903e-06 -Size 2: 2.105323e-06 -Size 4: 2.100792e-06 -Size 8: 2.075554e-06 -Size 16: 2.316649e-06 -Size 32: 2.320363e-06 -Size 64: 2.337375e-06 -Size 128: 2.343157e-06 -Size 256: 2.421384e-06 -Size 512: 2.611218e-06 -Size 1024: 2.889660e-06 -Size 2048: 3.340797e-06 -Size 4096: 4.419919e-06 -Size 8192: 5.163384e-06 -Size 16384: 1.469302e-05 -Size 32768: 1.731048e-05 -Size 65536: 3.289732e-05 -Size 131072: 4.629743e-05 -Size 262144: 8.317680e-05 -Size 524288: 1.759893e-04 -Size 1048576: 3.501579e-04 -Size 2097152: 7.269576e-04 -Size 4194304: 1.351247e-03 -Size 8388608: 2.279163e-03 -Size 16777216: 6.737205e-03 - -Active Procs: 8 -Size 1: 2.082167e-06 -Size 2: 2.118648e-06 -Size 4: 2.090554e-06 -Size 8: 2.062834e-06 -Size 16: 2.311807e-06 -Size 32: 2.282905e-06 -Size 64: 2.332434e-06 -Size 128: 2.363696e-06 -Size 256: 2.409792e-06 -Size 512: 2.566505e-06 -Size 1024: 2.865559e-06 -Size 2048: 3.389607e-06 -Size 4096: 4.484526e-06 -Size 8192: 5.606887e-06 -Size 16384: 1.604418e-05 -Size 32768: 2.388754e-05 -Size 65536: 5.000242e-05 -Size 131072: 7.208371e-05 -Size 262144: 1.363167e-04 -Size 524288: 2.281894e-04 -Size 1048576: 4.318892e-04 -Size 2097152: 9.348775e-04 -Size 4194304: 1.886525e-03 -Size 8388608: 4.181941e-03 -Size 16777216: 9.656159e-03 - -Active Procs: 16 -Size 1: 2.113916e-06 -Size 2: 2.098587e-06 -Size 4: 2.095162e-06 -Size 8: 2.139798e-06 -Size 16: 2.396577e-06 -Size 32: 2.368136e-06 -Size 64: 2.410771e-06 -Size 128: 2.450202e-06 -Size 256: 2.533669e-06 -Size 512: 2.671088e-06 -Size 1024: 3.024888e-06 -Size 2048: 3.585752e-06 -Size 4096: 4.727239e-06 -Size 8192: 7.014362e-06 -Size 16384: 1.744489e-05 -Size 32768: 3.840733e-05 -Size 65536: 8.399911e-05 -Size 131072: 1.137849e-04 -Size 262144: 2.342453e-04 -Size 524288: 4.232558e-04 -Size 1048576: 8.575990e-04 -Size 2097152: 1.768186e-03 -Size 4194304: 3.583393e-03 -Size 8388608: 7.912603e-03 -Size 16777216: 1.808626e-02 - -Active Procs: 32 -Size 1: 2.122123e-06 -Size 2: 2.097579e-06 -Size 4: 2.130481e-06 -Size 8: 2.096710e-06 -Size 16: 2.423277e-06 -Size 32: 2.389234e-06 -Size 64: 2.431278e-06 -Size 128: 2.481696e-06 -Size 256: 2.580772e-06 -Size 512: 2.718328e-06 -Size 1024: 3.158946e-06 -Size 2048: 3.809979e-06 -Size 4096: 6.375523e-06 -Size 8192: 1.365428e-05 -Size 16384: 2.550540e-05 -Size 32768: 7.485437e-05 -Size 65536: 1.227972e-04 -Size 131072: 2.357012e-04 -Size 262144: 4.675804e-04 -Size 524288: 8.649964e-04 -Size 1048576: 1.734018e-03 -Size 2097152: 3.548097e-03 -Size 4194304: 7.494072e-03 -Size 8388608: 1.484099e-02 -Size 16777216: 2.952326e-02 - -Active Procs: 64 -Size 1: 2.535025e-06 -Size 2: 2.478474e-06 -Size 4: 2.487024e-06 -Size 8: 2.475304e-06 -Size 16: 3.164619e-06 -Size 32: 3.187694e-06 -Size 64: 3.175042e-06 -Size 128: 3.237247e-06 -Size 256: 3.346972e-06 -Size 512: 3.574450e-06 -Size 1024: 4.170001e-06 -Size 2048: 6.526926e-06 -Size 4096: 1.417185e-05 -Size 8192: 2.788587e-05 -Size 16384: 5.818470e-05 -Size 32768: 1.416422e-04 -Size 65536: 2.332240e-04 -Size 131072: 4.665326e-04 -Size 262144: 9.528352e-04 -Size 524288: 1.811812e-03 -Size 1048576: 3.610781e-03 -Size 2097152: 7.269422e-03 -Size 4194304: 1.521420e-02 -Size 8388608: 3.054117e-02 -Size 16777216: 6.041151e-02 - -Active Procs: 112 -Size 1: 2.702258e-06 -Size 2: 2.499021e-06 -Size 4: 2.489373e-06 -Size 8: 2.500150e-06 -Size 16: 3.186698e-06 -Size 32: 3.206561e-06 -Size 64: 3.928669e-06 -Size 128: 3.276366e-06 -Size 256: 3.435464e-06 -Size 512: 3.823452e-06 -Size 1024: 5.858971e-06 -Size 2048: 1.210162e-05 -Size 4096: 2.521051e-05 -Size 8192: 5.271255e-05 -Size 16384: 1.050238e-04 -Size 32768: 2.491330e-04 -Size 65536: 4.039533e-04 -Size 131072: 8.129392e-04 -Size 262144: 1.651416e-03 -Size 524288: 3.311032e-03 -Size 1048576: 7.420421e-03 -Size 2097152: 1.498433e-02 -Size 4194304: 3.235437e-02 -Size 8388608: 6.654892e-02 -Size 16777216: 1.358247e-01 - -On-NUMA MultiProc Ping-Pong, All NUMAs Active: -Active Procs: 1 -Size 1: 6.041639e-07 -Size 2: 5.953200e-07 -Size 4: 5.802838e-07 -Size 8: 5.832337e-07 -Size 16: 5.926691e-07 -Size 32: 8.788878e-07 -Size 64: 8.792624e-07 -Size 128: 9.857705e-07 -Size 256: 9.653232e-07 -Size 512: 9.967857e-07 -Size 1024: 1.243571e-06 -Size 2048: 1.506005e-06 -Size 4096: 2.081786e-06 -Size 8192: 3.541042e-06 -Size 16384: 5.682371e-06 -Size 32768: 9.982519e-06 -Size 65536: 7.173813e-05 -Size 131072: 1.373923e-04 -Size 262144: 2.684441e-04 -Size 524288: 5.299958e-04 -Size 1048576: 1.053128e-03 -Size 2097152: 2.104441e-03 -Size 4194304: 4.216045e-03 -Size 8388608: 8.433741e-03 -Size 16777216: 1.704197e-02 - -Active Procs: 2 -Size 1: 5.860758e-07 -Size 2: 5.821658e-07 -Size 4: 5.815993e-07 -Size 8: 5.830915e-07 -Size 16: 5.880215e-07 -Size 32: 8.662821e-07 -Size 64: 8.679141e-07 -Size 128: 9.698601e-07 -Size 256: 9.886242e-07 -Size 512: 9.849936e-07 -Size 1024: 1.179437e-06 -Size 2048: 1.527582e-06 -Size 4096: 2.100241e-06 -Size 8192: 3.574289e-06 -Size 16384: 5.701675e-06 -Size 32768: 1.000875e-05 -Size 65536: 1.283116e-04 -Size 131072: 2.475487e-04 -Size 262144: 4.848606e-04 -Size 524288: 9.597640e-04 -Size 1048576: 1.910878e-03 -Size 2097152: 3.812315e-03 -Size 4194304: 7.606637e-03 -Size 8388608: 1.528426e-02 -Size 16777216: 3.075913e-02 - -Active Procs: 4 -Size 1: 5.939128e-07 -Size 2: 5.853625e-07 -Size 4: 5.848951e-07 -Size 8: 5.838960e-07 -Size 16: 5.880466e-07 -Size 32: 8.712982e-07 -Size 64: 8.727526e-07 -Size 128: 9.772946e-07 -Size 256: 1.001234e-06 -Size 512: 1.003399e-06 -Size 1024: 1.273879e-06 -Size 2048: 1.618704e-06 -Size 4096: 2.157700e-06 -Size 8192: 3.632090e-06 -Size 16384: 5.788352e-06 -Size 32768: 1.011909e-05 -Size 65536: 2.662267e-04 -Size 131072: 5.151359e-04 -Size 262144: 1.010574e-03 -Size 524288: 2.002222e-03 -Size 1048576: 3.988419e-03 -Size 2097152: 7.959740e-03 -Size 4194304: 1.591670e-02 -Size 8388608: 3.180711e-02 -Size 16777216: 6.368957e-02 - -Active Procs: 7 -Size 1: 5.962351e-07 -Size 2: 5.846728e-07 -Size 4: 5.840366e-07 -Size 8: 5.849728e-07 -Size 16: 5.896735e-07 -Size 32: 8.848348e-07 -Size 64: 8.859529e-07 -Size 128: 9.670673e-07 -Size 256: 9.479964e-07 -Size 512: 9.531348e-07 -Size 1024: 1.205970e-06 -Size 2048: 1.586144e-06 -Size 4096: 2.246988e-06 -Size 8192: 3.619129e-06 -Size 16384: 6.464768e-06 -Size 32768: 1.186234e-05 -Size 65536: 4.804190e-04 -Size 131072: 9.321699e-04 -Size 262144: 1.828418e-03 -Size 524288: 3.616671e-03 -Size 1048576: 7.197259e-03 -Size 2097152: 1.439700e-02 -Size 4194304: 2.883791e-02 -Size 8388608: 5.773773e-02 -Size 16777216: 1.155032e-01 - -On-Socket MultiProc Ping-Pong, All Sockets Active: -Active Procs: 1 -Size 1: 6.464152e-07 -Size 2: 6.057569e-07 -Size 4: 5.974861e-07 -Size 8: 5.979726e-07 -Size 16: 6.027098e-07 -Size 32: 8.813333e-07 -Size 64: 8.887960e-07 -Size 128: 1.034163e-06 -Size 256: 1.043009e-06 -Size 512: 1.081521e-06 -Size 1024: 1.302155e-06 -Size 2048: 1.659269e-06 -Size 4096: 2.267892e-06 -Size 8192: 3.818393e-06 -Size 16384: 5.999694e-06 -Size 32768: 1.042243e-05 -Size 65536: 1.184351e-05 -Size 131072: 2.084386e-05 -Size 262144: 3.911783e-05 -Size 524288: 7.469315e-05 -Size 1048576: 1.449242e-04 -Size 2097152: 2.946670e-04 -Size 4194304: 6.541199e-04 -Size 8388608: 1.305206e-03 -Size 16777216: 2.594081e-03 - -Active Procs: 2 -Size 1: 6.352530e-07 -Size 2: 6.206459e-07 -Size 4: 6.301197e-07 -Size 8: 6.283849e-07 -Size 16: 6.322890e-07 -Size 32: 8.831707e-07 -Size 64: 8.895096e-07 -Size 128: 1.036700e-06 -Size 256: 1.047851e-06 -Size 512: 1.086930e-06 -Size 1024: 1.313081e-06 -Size 2048: 1.676247e-06 -Size 4096: 2.287624e-06 -Size 8192: 3.845606e-06 -Size 16384: 6.045628e-06 -Size 32768: 1.051090e-05 -Size 65536: 3.197392e-05 -Size 131072: 6.244098e-05 -Size 262144: 1.244003e-04 -Size 524288: 2.462950e-04 -Size 1048576: 4.905736e-04 -Size 2097152: 9.358480e-04 -Size 4194304: 1.785464e-03 -Size 8388608: 3.570440e-03 -Size 16777216: 6.218943e-03 - -Active Procs: 4 -Size 1: 6.417984e-07 -Size 2: 6.459539e-07 -Size 4: 6.435763e-07 -Size 8: 6.457297e-07 -Size 16: 6.541092e-07 -Size 32: 9.561354e-07 -Size 64: 9.594437e-07 -Size 128: 1.104527e-06 -Size 256: 1.136517e-06 -Size 512: 1.182355e-06 -Size 1024: 1.452982e-06 -Size 2048: 1.792222e-06 -Size 4096: 2.386247e-06 -Size 8192: 3.990885e-06 -Size 16384: 6.160495e-06 -Size 32768: 1.067667e-05 -Size 65536: 7.245182e-05 -Size 131072: 1.395072e-04 -Size 262144: 2.727023e-04 -Size 524288: 5.392528e-04 -Size 1048576: 1.073098e-03 -Size 2097152: 2.124753e-03 -Size 4194304: 4.217973e-03 -Size 8388608: 8.468219e-03 -Size 16777216: 1.659975e-02 - -Active Procs: 8 -Size 1: 6.410783e-07 -Size 2: 6.371723e-07 -Size 4: 6.361648e-07 -Size 8: 6.360716e-07 -Size 16: 6.320110e-07 -Size 32: 8.883983e-07 -Size 64: 8.937605e-07 -Size 128: 1.039892e-06 -Size 256: 1.060492e-06 -Size 512: 1.103784e-06 -Size 1024: 1.350786e-06 -Size 2048: 1.730946e-06 -Size 4096: 2.356332e-06 -Size 8192: 4.020351e-06 -Size 16384: 6.216470e-06 -Size 32768: 1.077728e-05 -Size 65536: 1.256125e-04 -Size 131072: 2.427444e-04 -Size 262144: 4.761659e-04 -Size 524288: 9.440689e-04 -Size 1048576: 1.878012e-03 -Size 2097152: 3.731032e-03 -Size 4194304: 7.422106e-03 -Size 8388608: 1.479251e-02 -Size 16777216: 2.956108e-02 - -Active Procs: 16 -Size 1: 6.366077e-07 -Size 2: 6.400479e-07 -Size 4: 6.403834e-07 -Size 8: 6.384983e-07 -Size 16: 6.481999e-07 -Size 32: 9.228101e-07 -Size 64: 9.041380e-07 -Size 128: 1.051683e-06 -Size 256: 1.071665e-06 -Size 512: 1.114451e-06 -Size 1024: 1.369187e-06 -Size 2048: 1.762307e-06 -Size 4096: 2.468792e-06 -Size 8192: 4.051775e-06 -Size 16384: 6.814523e-06 -Size 32768: 1.243138e-05 -Size 65536: 2.646331e-04 -Size 131072: 5.121913e-04 -Size 262144: 1.004917e-03 -Size 524288: 1.991037e-03 -Size 1048576: 3.965146e-03 -Size 2097152: 7.897938e-03 -Size 4194304: 1.567990e-02 -Size 8388608: 3.124180e-02 -Size 16777216: 6.230760e-02 - -Active Procs: 28 -Size 1: 6.389933e-07 -Size 2: 6.502447e-07 -Size 4: 6.449614e-07 -Size 8: 6.479511e-07 -Size 16: 6.544937e-07 -Size 32: 9.631342e-07 -Size 64: 9.218812e-07 -Size 128: 1.070737e-06 -Size 256: 1.068956e-06 -Size 512: 1.113928e-06 -Size 1024: 1.377228e-06 -Size 2048: 1.769044e-06 -Size 4096: 2.471377e-06 -Size 8192: 4.070582e-06 -Size 16384: 6.908001e-06 -Size 32768: 1.299705e-05 -Size 65536: 4.810549e-04 -Size 131072: 9.321364e-04 -Size 262144: 1.829521e-03 -Size 524288: 3.621197e-03 -Size 1048576: 7.208908e-03 -Size 2097152: 1.440538e-02 -Size 4194304: 2.880816e-02 -Size 8388608: 5.746716e-02 -Size 16777216: 1.149771e-01 - -Off-Node MultiProc Ping-Pong, Even NUMA Regions: -Active Procs: 1 -Size 1: 2.504863e-06 -Size 2: 2.515204e-06 -Size 4: 2.509565e-06 -Size 8: 2.513068e-06 -Size 16: 3.169965e-06 -Size 32: 3.189510e-06 -Size 64: 3.179251e-06 -Size 128: 3.214401e-06 -Size 256: 3.313886e-06 -Size 512: 3.457925e-06 -Size 1024: 3.753470e-06 -Size 2048: 4.436800e-06 -Size 4096: 5.245494e-06 -Size 8192: 6.669482e-06 -Size 16384: 1.730402e-05 -Size 32768: 2.324954e-05 -Size 65536: 5.283712e-05 -Size 131072: 7.978874e-05 -Size 262144: 1.431226e-04 -Size 524288: 2.203610e-04 -Size 1048576: 4.360962e-04 -Size 2097152: 1.092840e-03 -Size 4194304: 2.323840e-03 -Size 8388608: 5.078337e-03 -Size 16777216: 1.173736e-02 - -Active Procs: 2 -Size 1: 2.492624e-06 -Size 2: 2.505504e-06 -Size 4: 2.498076e-06 -Size 8: 2.498988e-06 -Size 16: 3.160796e-06 -Size 32: 3.182079e-06 -Size 64: 3.170396e-06 -Size 128: 3.212974e-06 -Size 256: 3.300907e-06 -Size 512: 3.470482e-06 -Size 1024: 3.790164e-06 -Size 2048: 4.506454e-06 -Size 4096: 5.676511e-06 -Size 8192: 7.818954e-06 -Size 16384: 1.829206e-05 -Size 32768: 3.551241e-05 -Size 65536: 8.016577e-05 -Size 131072: 1.320778e-04 -Size 262144: 2.367842e-04 -Size 524288: 4.713200e-04 -Size 1048576: 9.235514e-04 -Size 2097152: 1.954512e-03 -Size 4194304: 4.005362e-03 -Size 8388608: 8.529465e-03 -Size 16777216: 1.922695e-02 - -Active Procs: 4 -Size 1: 2.505893e-06 -Size 2: 2.486889e-06 -Size 4: 2.495773e-06 -Size 8: 2.497437e-06 -Size 16: 3.155807e-06 -Size 32: 3.180404e-06 -Size 64: 3.168283e-06 -Size 128: 3.209707e-06 -Size 256: 3.326480e-06 -Size 512: 3.495571e-06 -Size 1024: 3.893660e-06 -Size 2048: 4.811444e-06 -Size 4096: 6.764394e-06 -Size 8192: 1.331107e-05 -Size 16384: 2.584710e-05 -Size 32768: 7.133626e-05 -Size 65536: 1.420084e-04 -Size 131072: 2.710441e-04 -Size 262144: 5.370914e-04 -Size 524288: 1.076491e-03 -Size 1048576: 2.192638e-03 -Size 2097152: 4.433780e-03 -Size 4194304: 8.816610e-03 -Size 8388608: 1.787236e-02 -Size 16777216: 3.574629e-02 - -Active Procs: 8 -Size 1: 2.481292e-06 -Size 2: 2.489880e-06 -Size 4: 2.476580e-06 -Size 8: 2.478910e-06 -Size 16: 3.168203e-06 -Size 32: 3.196714e-06 -Size 64: 3.184785e-06 -Size 128: 3.226843e-06 -Size 256: 3.354269e-06 -Size 512: 3.570913e-06 -Size 1024: 4.190256e-06 -Size 2048: 6.417765e-06 -Size 4096: 1.324340e-05 -Size 8192: 2.662274e-05 -Size 16384: 5.278159e-05 -Size 32768: 1.395572e-04 -Size 65536: 2.403926e-04 -Size 131072: 4.695074e-04 -Size 262144: 9.443069e-04 -Size 524288: 1.895410e-03 -Size 1048576: 4.031968e-03 -Size 2097152: 8.375545e-03 -Size 4194304: 1.699381e-02 -Size 8388608: 3.406613e-02 -Size 16777216: 6.985882e-02 - -Active Procs: 14 -Size 1: 2.492163e-06 -Size 2: 2.490514e-06 -Size 4: 2.501525e-06 -Size 8: 2.500175e-06 -Size 16: 3.207960e-06 -Size 32: 3.202744e-06 -Size 64: 3.205976e-06 -Size 128: 3.276859e-06 -Size 256: 3.438113e-06 -Size 512: 3.814264e-06 -Size 1024: 5.881796e-06 -Size 2048: 1.181639e-05 -Size 4096: 2.416079e-05 -Size 8192: 4.835068e-05 -Size 16384: 9.738309e-05 -Size 32768: 2.408598e-04 -Size 65536: 4.034952e-04 -Size 131072: 8.115032e-04 -Size 262144: 1.631322e-03 -Size 524288: 3.336361e-03 -Size 1048576: 6.699834e-03 -Size 2097152: 1.423453e-02 -Size 4194304: 3.053663e-02 -Size 8388608: 6.489953e-02 -Size 16777216: 1.447791e-01 - -Off-Node MultiProc Ping-Pong, Even Sockets: -Active Procs: 1 -Size 1: 2.531999e-06 -Size 2: 2.535129e-06 -Size 4: 2.543478e-06 -Size 8: 2.535797e-06 -Size 16: 3.180810e-06 -Size 32: 3.187577e-06 -Size 64: 3.179150e-06 -Size 128: 3.236657e-06 -Size 256: 3.314906e-06 -Size 512: 3.465005e-06 -Size 1024: 3.749704e-06 -Size 2048: 4.341740e-06 -Size 4096: 5.097762e-06 -Size 8192: 6.318707e-06 -Size 16384: 1.665112e-05 -Size 32768: 1.952696e-05 -Size 65536: 4.209062e-05 -Size 131072: 5.940331e-05 -Size 262144: 8.519221e-05 -Size 524288: 1.418260e-04 -Size 1048576: 2.368233e-04 -Size 2097152: 4.151311e-04 -Size 4194304: 7.666012e-04 -Size 8388608: 1.667801e-03 -Size 16777216: 4.413718e-03 - -Active Procs: 2 -Size 1: 2.523179e-06 -Size 2: 2.529464e-06 -Size 4: 2.526754e-06 -Size 8: 2.521092e-06 -Size 16: 3.178942e-06 -Size 32: 3.187874e-06 -Size 64: 3.178203e-06 -Size 128: 3.227691e-06 -Size 256: 3.317715e-06 -Size 512: 3.455455e-06 -Size 1024: 3.755270e-06 -Size 2048: 4.379559e-06 -Size 4096: 5.176571e-06 -Size 8192: 6.461022e-06 -Size 16384: 1.665571e-05 -Size 32768: 2.006795e-05 -Size 65536: 4.385242e-05 -Size 131072: 6.405301e-05 -Size 262144: 1.006032e-04 -Size 524288: 1.596828e-04 -Size 1048576: 2.984360e-04 -Size 2097152: 5.668667e-04 -Size 4194304: 1.116916e-03 -Size 8388608: 2.212959e-03 -Size 16777216: 5.116341e-03 - -Active Procs: 4 -Size 1: 2.503732e-06 -Size 2: 2.514423e-06 -Size 4: 2.509847e-06 -Size 8: 2.502632e-06 -Size 16: 3.188449e-06 -Size 32: 3.194858e-06 -Size 64: 3.183500e-06 -Size 128: 3.219016e-06 -Size 256: 3.300736e-06 -Size 512: 3.454057e-06 -Size 1024: 3.753230e-06 -Size 2048: 4.442049e-06 -Size 4096: 5.315491e-06 -Size 8192: 6.834918e-06 -Size 16384: 1.721015e-05 -Size 32768: 2.392767e-05 -Size 65536: 6.374409e-05 -Size 131072: 8.463458e-05 -Size 262144: 1.524155e-04 -Size 524288: 2.533181e-04 -Size 1048576: 4.488241e-04 -Size 2097152: 9.049978e-04 -Size 4194304: 2.120111e-03 -Size 8388608: 4.442170e-03 -Size 16777216: 9.978845e-03 - -Active Procs: 8 -Size 1: 2.500388e-06 -Size 2: 2.492795e-06 -Size 4: 2.482344e-06 -Size 8: 2.490757e-06 -Size 16: 3.141618e-06 -Size 32: 3.162669e-06 -Size 64: 3.149779e-06 -Size 128: 3.204057e-06 -Size 256: 3.290338e-06 -Size 512: 3.455362e-06 -Size 1024: 3.768771e-06 -Size 2048: 4.506113e-06 -Size 4096: 5.648368e-06 -Size 8192: 7.815635e-06 -Size 16384: 1.827679e-05 -Size 32768: 4.001967e-05 -Size 65536: 8.622841e-05 -Size 131072: 1.339182e-04 -Size 262144: 2.403051e-04 -Size 524288: 4.625093e-04 -Size 1048576: 9.410009e-04 -Size 2097152: 1.981743e-03 -Size 4194304: 3.939920e-03 -Size 8388608: 8.245121e-03 -Size 16777216: 1.789781e-02 - -Active Procs: 16 -Size 1: 2.491915e-06 -Size 2: 2.474187e-06 -Size 4: 2.471575e-06 -Size 8: 2.473394e-06 -Size 16: 3.164400e-06 -Size 32: 3.169542e-06 -Size 64: 3.157415e-06 -Size 128: 3.200172e-06 -Size 256: 3.330642e-06 -Size 512: 3.500355e-06 -Size 1024: 3.893872e-06 -Size 2048: 4.785879e-06 -Size 4096: 6.786725e-06 -Size 8192: 1.316671e-05 -Size 16384: 2.577700e-05 -Size 32768: 7.380402e-05 -Size 65536: 1.417729e-04 -Size 131072: 2.315270e-04 -Size 262144: 4.671396e-04 -Size 524288: 9.298039e-04 -Size 1048576: 1.898287e-03 -Size 2097152: 3.812348e-03 -Size 4194304: 7.654172e-03 -Size 8388608: 1.518586e-02 -Size 16777216: 3.171748e-02 - -Active Procs: 32 -Size 1: 2.481711e-06 -Size 2: 2.493898e-06 -Size 4: 2.488179e-06 -Size 8: 2.480801e-06 -Size 16: 3.165753e-06 -Size 32: 3.192529e-06 -Size 64: 3.163209e-06 -Size 128: 3.225086e-06 -Size 256: 3.355567e-06 -Size 512: 3.593741e-06 -Size 1024: 4.173495e-06 -Size 2048: 6.449779e-06 -Size 4096: 1.328713e-05 -Size 8192: 2.658675e-05 -Size 16384: 5.225507e-05 -Size 32768: 1.472033e-04 -Size 65536: 2.459094e-04 -Size 131072: 4.626712e-04 -Size 262144: 9.253988e-04 -Size 524288: 1.863777e-03 -Size 1048576: 3.754717e-03 -Size 2097152: 7.509400e-03 -Size 4194304: 1.724787e-02 -Size 8388608: 3.757310e-02 -Size 16777216: 7.707181e-02 - -Active Procs: 56 -Size 1: 2.492236e-06 -Size 2: 2.490714e-06 -Size 4: 2.500865e-06 -Size 8: 2.501885e-06 -Size 16: 3.210145e-06 -Size 32: 3.205448e-06 -Size 64: 3.208234e-06 -Size 128: 3.275098e-06 -Size 256: 3.431062e-06 -Size 512: 3.815479e-06 -Size 1024: 5.853841e-06 -Size 2048: 1.185625e-05 -Size 4096: 2.475935e-05 -Size 8192: 4.857359e-05 -Size 16384: 1.005986e-04 -Size 32768: 2.451982e-04 -Size 65536: 4.383186e-04 -Size 131072: 8.651026e-04 -Size 262144: 1.766079e-03 -Size 524288: 3.442330e-03 -Size 1048576: 6.724601e-03 -Size 2097152: 1.460980e-02 -Size 4194304: 2.968546e-02 -Size 8388608: 6.026085e-02 -Size 16777216: 1.305463e-01 - -On-NUMA Multi Ping-Pong: -Size 1: 6.285618e-07 -Size 2: 9.421386e-07 -Size 4: 1.529340e-06 -Size 8: 2.827040e-06 -Size 16: 5.496157e-06 -Size 32: 1.071863e-05 -Size 64: 2.263110e-05 -Size 128: 4.544169e-05 -Size 256: 9.072570e-05 -Size 512: 1.790889e-04 -Size 1024: 3.494989e-04 -Size 2048: 7.969182e-04 -Size 4096: 1.787925e-03 -Size 8192: 4.611576e-03 -Size 16384: 1.393425e-02 - -On-Socket Multi Ping-Pong: -Size 1: 7.181307e-07 -Size 2: 1.016086e-06 -Size 4: 1.616201e-06 -Size 8: 3.052137e-06 -Size 16: 5.813802e-06 -Size 32: 1.184344e-05 -Size 64: 2.342011e-05 -Size 128: 4.612478e-05 -Size 256: 9.223547e-05 -Size 512: 1.831611e-04 -Size 1024: 3.582188e-04 -Size 2048: 8.091265e-04 -Size 4096: 1.790021e-03 -Size 8192: 4.141448e-03 -Size 16384: 1.001289e-02 - -On-Node, Off-Socket Multi Ping-Pong: -Size 1: 1.401870e-06 -Size 2: 1.778477e-06 -Size 4: 2.601231e-06 -Size 8: 4.217457e-06 -Size 16: 7.650302e-06 -Size 32: 1.449433e-05 -Size 64: 2.804184e-05 -Size 128: 5.583078e-05 -Size 256: 1.100022e-04 -Size 512: 2.179864e-04 -Size 1024: 4.192582e-04 -Size 2048: 9.862011e-04 -Size 4096: 1.931521e-03 -Size 8192: 4.713905e-03 -Size 16384: 1.264059e-02 - -Off-Node Multi Ping-Pong: -Size 1: 2.262183e-06 -Size 2: 2.594672e-06 -Size 4: 3.349479e-06 -Size 8: 4.782221e-06 -Size 16: 7.589888e-06 -Size 32: 1.428213e-05 -Size 64: 2.761828e-05 -Size 128: 5.419990e-05 -Size 256: 1.294447e-04 -Size 512: 2.965938e-04 -Size 1024: 8.253986e-04 -Size 2048: 2.115349e-03 -Size 4096: 4.115638e-03 -Size 8192: 6.725250e-03 -Size 16384: 1.410693e-02 - -On-NUMA Matching Ping-Pong: -Size 1: 6.483013e-07 -Size 2: 9.908190e-07 -Size 4: 1.550344e-06 -Size 8: 2.846799e-06 -Size 16: 5.374932e-06 -Size 32: 1.133738e-05 -Size 64: 2.280330e-05 -Size 128: 4.541794e-05 -Size 256: 9.077020e-05 -Size 512: 1.837268e-04 -Size 1024: 3.791165e-04 -Size 2048: 9.365066e-04 -Size 4096: 3.126262e-03 -Size 8192: 1.243027e-02 -Size 16384: 7.663190e-02 - -On-Socket Matching Ping-Pong: -Size 1: 7.378578e-07 -Size 2: 1.036201e-06 -Size 4: 1.625419e-06 -Size 8: 3.005870e-06 -Size 16: 5.659212e-06 -Size 32: 1.178810e-05 -Size 64: 2.322224e-05 -Size 128: 4.646952e-05 -Size 256: 9.400024e-05 -Size 512: 1.876463e-04 -Size 1024: 3.841061e-04 -Size 2048: 9.439501e-04 -Size 4096: 3.179914e-03 -Size 8192: 1.238894e-02 -Size 16384: 7.208928e-02 - -On-Node, Off-Socket Matching Ping-Pong: -Size 1: 1.422663e-06 -Size 2: 1.789858e-06 -Size 4: 2.591623e-06 -Size 8: 4.317029e-06 -Size 16: 7.746572e-06 -Size 32: 1.462310e-05 -Size 64: 2.877783e-05 -Size 128: 5.658337e-05 -Size 256: 1.129922e-04 -Size 512: 2.278499e-04 -Size 1024: 4.659868e-04 -Size 2048: 1.021030e-03 -Size 4096: 3.833994e-03 -Size 8192: 1.379553e-02 -Size 16384: 7.495897e-02 - -Off-Node Matching Ping-Pong: -Size 1: 2.290894e-06 -Size 2: 2.605072e-06 -Size 4: 3.385513e-06 -Size 8: 5.008831e-06 -Size 16: 8.110811e-06 -Size 32: 1.522389e-05 -Size 64: 3.253006e-05 -Size 128: 5.530714e-05 -Size 256: 1.319247e-04 -Size 512: 3.177800e-04 -Size 1024: 8.308201e-04 -Size 2048: 3.206212e-03 -Size 4096: 8.969305e-03 -Size 8192: 1.911074e-02 -Size 16384: 8.202945e-02 - diff --git a/benchmark_tests/dane/test_microbench b/benchmark_tests/dane/test_microbench deleted file mode 100644 index d83a369cd..000000000 --- a/benchmark_tests/dane/test_microbench +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -#SBATCH --output=microbench.out -#SBATCH --error=microbench.err -#SBATCH --nodes=2 -#SBATCH --ntasks=224 -#SBATCH --cpus-per-task=1 -#SBATCH --time=01:00:00 -#SBATCH --partition=pdebug - -cd /g/g14/bienz1/locality_aware/build_dane/benchmarks - -#srun -n 224 --auto-affinity=verbose ./microbenchmarks -srun -n 224 ./microbenchmarks - diff --git a/benchmark_tests/googletest.err b/benchmark_tests/googletest.err deleted file mode 100644 index 40d6ab88f..000000000 --- a/benchmark_tests/googletest.err +++ /dev/null @@ -1,3 +0,0 @@ -/var/spool/slurmd/job10908115/slurm_script: line 12: mpirun: command not found -/var/spool/slurmd/job10908118/slurm_script: line 13: mpirun: command not found -srun: error: quartz46: tasks 0-15: Exited with exit code 1 diff --git a/benchmark_tests/googletest.out b/benchmark_tests/googletest.out deleted file mode 100644 index 91d1b83de..000000000 --- a/benchmark_tests/googletest.out +++ /dev/null @@ -1,960 +0,0 @@ -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test case. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (7 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (7 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (7 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (7 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (7 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (7 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (7 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (7 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (7 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test case ran. (8 ms total) -[ PASSED ] 1 test. -[==========] 1 test from 1 test case ran. (7 ms total) -[ PASSED ] 1 test. -[==========] 1 test from 1 test case ran. (8 ms total) -[ PASSED ] 1 test. -[==========] 1 test from 1 test case ran. (8 ms total) -[ PASSED ] 1 test. -[==========] 1 test from 1 test case ran. (8 ms total) -[ PASSED ] 1 test. -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[ OK ] RandomCommTest.TestsInTests (9 ms) -[----------] 1 test from RandomCommTest (9 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (9 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (9 ms) -[----------] 1 test from RandomCommTest (9 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (9 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (9 ms) -[----------] 1 test from RandomCommTest (9 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (9 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (9 ms) -[----------] 1 test from RandomCommTest (9 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (9 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (9 ms) -[----------] 1 test from RandomCommTest (9 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (9 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (9 ms) -[----------] 1 test from RandomCommTest (9 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (9 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (9 ms) -[----------] 1 test from RandomCommTest (9 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (9 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (9 ms) -[----------] 1 test from RandomCommTest (9 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (9 ms total) -[ PASSED ] 1 test. -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 400 - loc_bruck_allgather[j] - Which is: 0 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 400 - loc_bruck_allgather[j] - Which is: 0 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 400 - loc_bruck_allgather[j] - Which is: 0 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 400 - loc_bruck_allgather[j] - Which is: 0 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 400 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 400 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 400 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 400 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 800 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 800 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 800 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 800 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 1200 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 1200 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 1200 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -/g/g14/bienz1/locality_aware/src/collective/tests/test_allgather.cpp:129: Failure -Expected equality of these values: - std_allgather[j] - Which is: 0 - loc_bruck_allgather[j] - Which is: 1200 -[ FAILED ] RandomCommTest.TestsInTests (1 ms) -[----------] 1 test from RandomCommTest (1 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (2 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] RandomCommTest.TestsInTests - - 1 FAILED TEST -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[----------] 1 test from RandomCommTest -[ RUN ] RandomCommTest.TestsInTests -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (8 ms) -[----------] 1 test from RandomCommTest (8 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. -[ OK ] RandomCommTest.TestsInTests (7 ms) -[----------] 1 test from RandomCommTest (7 ms total) - -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (8 ms total) -[ PASSED ] 1 test. diff --git a/benchmark_tests/lassen/08_09_22/bruck_n16_ppn16.3848155.out b/benchmark_tests/lassen/08_09_22/bruck_n16_ppn16.3848155.out deleted file mode 100644 index 242b9ccd6..000000000 --- a/benchmark_tests/lassen/08_09_22/bruck_n16_ppn16.3848155.out +++ /dev/null @@ -1,359 +0,0 @@ -Testing Size 1 -PMPI_Allgather Time 1.348484e-05 -allgather_bruck Time 3.086655e-05 -allgather_loc_bruck Time 1.438450e-05 -allgather_hier_bruck Time 1.748394e-05 -allgather_mult_hier_bruck Time 1.507101e-05 -Testing Size 2 -PMPI_Allgather Time 1.472128e-05 -allgather_bruck Time 2.455486e-05 -allgather_loc_bruck Time 1.208269e-05 -allgather_hier_bruck Time 1.802344e-05 -allgather_mult_hier_bruck Time 1.846121e-05 -Testing Size 4 -PMPI_Allgather Time 1.546898e-05 -allgather_bruck Time 3.218525e-05 -allgather_loc_bruck Time 1.604811e-05 -allgather_hier_bruck Time 1.979492e-05 -allgather_mult_hier_bruck Time 2.480596e-05 -Testing Size 8 -PMPI_Allgather Time 1.702425e-05 -allgather_bruck Time 4.931005e-05 -allgather_loc_bruck Time 2.012537e-05 -allgather_hier_bruck Time 2.436395e-05 -allgather_mult_hier_bruck Time 3.852927e-05 -Testing Size 16 -PMPI_Allgather Time 2.160193e-05 -allgather_bruck Time 8.626850e-05 -allgather_loc_bruck Time 3.193717e-05 -allgather_hier_bruck Time 3.263221e-05 -allgather_mult_hier_bruck Time 6.306730e-05 -app 0: ./bruck_allgather -rank: 0: { host: 1; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 4: { host: 1; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 5: { host: 1; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 6: { host: 1; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 7: { host: 1; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 8: { host: 1; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 9: { host: 1; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 10: { host: 1; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 11: { host: 1; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 12: { host: 1; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 13: { host: 1; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 14: { host: 1; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 15: { host: 1; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 16: { host: 2; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 17: { host: 2; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 18: { host: 2; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 19: { host: 2; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 20: { host: 2; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 21: { host: 2; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 22: { host: 2; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 23: { host: 2; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 24: { host: 2; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 25: { host: 2; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 26: { host: 2; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 27: { host: 2; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 28: { host: 2; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 29: { host: 2; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 30: { host: 2; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 31: { host: 2; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 32: { host: 3; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 33: { host: 3; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 34: { host: 3; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 35: { host: 3; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 36: { host: 3; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 37: { host: 3; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 38: { host: 3; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 39: { host: 3; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 40: { host: 3; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 41: { host: 3; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 42: { host: 3; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 43: { host: 3; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 44: { host: 3; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 45: { host: 3; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 46: { host: 3; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 47: { host: 3; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 48: { host: 4; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 49: { host: 4; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 50: { host: 4; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 51: { host: 4; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 52: { host: 4; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 53: { host: 4; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 54: { host: 4; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 55: { host: 4; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 56: { host: 4; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 57: { host: 4; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 58: { host: 4; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 59: { host: 4; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 60: { host: 4; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 61: { host: 4; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 62: { host: 4; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 63: { host: 4; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 64: { host: 5; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 65: { host: 5; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 66: { host: 5; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 67: { host: 5; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 68: { host: 5; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 69: { host: 5; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 70: { host: 5; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 71: { host: 5; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 72: { host: 5; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 73: { host: 5; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 74: { host: 5; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 75: { host: 5; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 76: { host: 5; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 77: { host: 5; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 78: { host: 5; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 79: { host: 5; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 80: { host: 6; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 81: { host: 6; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 82: { host: 6; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 83: { host: 6; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 84: { host: 6; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 85: { host: 6; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 86: { host: 6; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 87: { host: 6; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 88: { host: 6; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 89: { host: 6; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 90: { host: 6; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 91: { host: 6; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 92: { host: 6; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 93: { host: 6; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 94: { host: 6; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 95: { host: 6; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 96: { host: 7; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 97: { host: 7; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 98: { host: 7; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 99: { host: 7; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 100: { host: 7; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 101: { host: 7; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 102: { host: 7; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 103: { host: 7; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 104: { host: 7; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 105: { host: 7; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 106: { host: 7; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 107: { host: 7; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 108: { host: 7; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 109: { host: 7; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 110: { host: 7; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 111: { host: 7; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 112: { host: 8; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 113: { host: 8; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 114: { host: 8; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 115: { host: 8; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 116: { host: 8; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 117: { host: 8; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 118: { host: 8; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 119: { host: 8; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 120: { host: 8; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 121: { host: 8; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 122: { host: 8; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 123: { host: 8; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 124: { host: 8; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 125: { host: 8; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 126: { host: 8; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 127: { host: 8; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 128: { host: 9; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 129: { host: 9; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 130: { host: 9; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 131: { host: 9; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 132: { host: 9; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 133: { host: 9; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 134: { host: 9; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 135: { host: 9; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 136: { host: 9; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 137: { host: 9; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 138: { host: 9; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 139: { host: 9; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 140: { host: 9; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 141: { host: 9; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 142: { host: 9; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 143: { host: 9; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 144: { host: 10; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 145: { host: 10; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 146: { host: 10; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 147: { host: 10; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 148: { host: 10; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 149: { host: 10; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 150: { host: 10; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 151: { host: 10; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 152: { host: 10; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 153: { host: 10; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 154: { host: 10; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 155: { host: 10; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 156: { host: 10; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 157: { host: 10; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 158: { host: 10; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 159: { host: 10; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 160: { host: 11; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 161: { host: 11; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 162: { host: 11; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 163: { host: 11; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 164: { host: 11; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 165: { host: 11; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 166: { host: 11; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 167: { host: 11; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 168: { host: 11; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 169: { host: 11; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 170: { host: 11; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 171: { host: 11; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 172: { host: 11; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 173: { host: 11; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 174: { host: 11; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 175: { host: 11; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 176: { host: 12; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 177: { host: 12; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 178: { host: 12; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 179: { host: 12; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 180: { host: 12; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 181: { host: 12; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 182: { host: 12; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 183: { host: 12; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 184: { host: 12; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 185: { host: 12; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 186: { host: 12; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 187: { host: 12; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 188: { host: 12; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 189: { host: 12; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 190: { host: 12; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 191: { host: 12; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 192: { host: 13; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 193: { host: 13; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 194: { host: 13; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 195: { host: 13; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 196: { host: 13; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 197: { host: 13; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 198: { host: 13; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 199: { host: 13; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 200: { host: 13; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 201: { host: 13; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 202: { host: 13; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 203: { host: 13; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 204: { host: 13; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 205: { host: 13; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 206: { host: 13; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 207: { host: 13; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 208: { host: 14; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 209: { host: 14; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 210: { host: 14; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 211: { host: 14; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 212: { host: 14; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 213: { host: 14; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 214: { host: 14; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 215: { host: 14; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 216: { host: 14; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 217: { host: 14; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 218: { host: 14; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 219: { host: 14; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 220: { host: 14; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 221: { host: 14; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 222: { host: 14; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 223: { host: 14; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 224: { host: 15; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 225: { host: 15; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 226: { host: 15; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 227: { host: 15; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 228: { host: 15; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 229: { host: 15; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 230: { host: 15; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 231: { host: 15; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 232: { host: 15; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 233: { host: 15; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 234: { host: 15; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 235: { host: 15; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 236: { host: 15; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 237: { host: 15; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 238: { host: 15; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 239: { host: 15; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 240: { host: 16; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 241: { host: 16; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 242: { host: 16; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 243: { host: 16; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 244: { host: 16; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 245: { host: 16; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 246: { host: 16; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 247: { host: 16; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 248: { host: 16; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 249: { host: 16; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 250: { host: 16; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 251: { host: 16; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 252: { host: 16; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 253: { host: 16; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 254: { host: 16; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 255: { host: 16; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3848155: in cluster Done - -Job was submitted from host by user in cluster at Tue Aug 9 15:09:47 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Tue Aug 9 15:10:01 2022 - <40*lassen34> - <40*lassen32> - <40*lassen31> - <40*lassen30> - <40*lassen29> - <40*lassen28> - <40*lassen27> - <40*lassen26> - <40*lassen23> - <40*lassen22> - <40*lassen21> - <40*lassen20> - <40*lassen19> - <40*lassen18> - <40*lassen17> - <40*lassen16> - was used as the home directory. - was used as the working directory. -Started at Tue Aug 9 15:10:01 2022 -Terminated at Tue Aug 9 15:10:14 2022 -Results reported at Tue Aug 9 15:10:14 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J bruck_n16_ppn16 -#BSUB -e bruck_n16_ppn16.%J.err -#BSUB -o bruck_n16_ppn16.%J.out -#BSUB -nnodes 16 -#BSUB -q pdebug -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a16 -c16 -r1 -n16 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.21 sec. - Max Memory : - - Average Memory : - - Total Requested Memory : - - Delta Memory : - - Max Swap : - - Max Processes : - - Max Threads : - - Run time : 12 sec. - Turnaround time : 27 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/08_09_22/bruck_n16_ppn4.3848153.out b/benchmark_tests/lassen/08_09_22/bruck_n16_ppn4.3848153.out deleted file mode 100644 index 9a0cbc532..000000000 --- a/benchmark_tests/lassen/08_09_22/bruck_n16_ppn4.3848153.out +++ /dev/null @@ -1,167 +0,0 @@ -Testing Size 1 -PMPI_Allgather Time 1.252153e-05 -allgather_bruck Time 1.457844e-05 -allgather_loc_bruck Time 1.086560e-05 -allgather_hier_bruck Time 1.215331e-05 -allgather_mult_hier_bruck Time 9.415990e-06 -Testing Size 2 -PMPI_Allgather Time 1.265730e-05 -allgather_bruck Time 1.209526e-05 -allgather_loc_bruck Time 9.525420e-06 -allgather_hier_bruck Time 1.244865e-05 -allgather_mult_hier_bruck Time 1.026908e-05 -Testing Size 4 -PMPI_Allgather Time 1.183020e-05 -allgather_bruck Time 1.346258e-05 -allgather_loc_bruck Time 1.029108e-05 -allgather_hier_bruck Time 1.392185e-05 -allgather_mult_hier_bruck Time 1.247071e-05 -Testing Size 8 -PMPI_Allgather Time 1.121744e-05 -allgather_bruck Time 1.585459e-05 -allgather_loc_bruck Time 1.193025e-05 -allgather_hier_bruck Time 1.720260e-05 -allgather_mult_hier_bruck Time 1.506074e-05 -Testing Size 16 -PMPI_Allgather Time 1.236024e-05 -allgather_bruck Time 1.949280e-05 -allgather_loc_bruck Time 1.426348e-05 -allgather_hier_bruck Time 1.794513e-05 -allgather_mult_hier_bruck Time 2.091520e-05 -app 0: ./bruck_allgather -rank: 0: { host: 1; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 4: { host: 2; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 5: { host: 2; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 6: { host: 2; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 7: { host: 2; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 8: { host: 3; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 9: { host: 3; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 10: { host: 3; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 11: { host: 3; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 12: { host: 4; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 13: { host: 4; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 14: { host: 4; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 15: { host: 4; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 16: { host: 5; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 17: { host: 5; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 18: { host: 5; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 19: { host: 5; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 20: { host: 6; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 21: { host: 6; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 22: { host: 6; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 23: { host: 6; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 24: { host: 7; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 25: { host: 7; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 26: { host: 7; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 27: { host: 7; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 28: { host: 8; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 29: { host: 8; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 30: { host: 8; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 31: { host: 8; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 32: { host: 9; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 33: { host: 9; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 34: { host: 9; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 35: { host: 9; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 36: { host: 10; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 37: { host: 10; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 38: { host: 10; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 39: { host: 10; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 40: { host: 11; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 41: { host: 11; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 42: { host: 11; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 43: { host: 11; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 44: { host: 12; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 45: { host: 12; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 46: { host: 12; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 47: { host: 12; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 48: { host: 13; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 49: { host: 13; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 50: { host: 13; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 51: { host: 13; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 52: { host: 14; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 53: { host: 14; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 54: { host: 14; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 55: { host: 14; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 56: { host: 15; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 57: { host: 15; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 58: { host: 15; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 59: { host: 15; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 60: { host: 16; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 61: { host: 16; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 62: { host: 16; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 63: { host: 16; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3848153: in cluster Done - -Job was submitted from host by user in cluster at Tue Aug 9 15:09:25 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Tue Aug 9 15:09:27 2022 - <40*lassen34> - <40*lassen32> - <40*lassen31> - <40*lassen30> - <40*lassen29> - <40*lassen28> - <40*lassen27> - <40*lassen26> - <40*lassen23> - <40*lassen22> - <40*lassen21> - <40*lassen20> - <40*lassen19> - <40*lassen18> - <40*lassen17> - <40*lassen16> - was used as the home directory. - was used as the working directory. -Started at Tue Aug 9 15:09:27 2022 -Terminated at Tue Aug 9 15:09:37 2022 -Results reported at Tue Aug 9 15:09:37 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J bruck_n16_ppn4 -#BSUB -e bruck_n16_ppn4.%J.err -#BSUB -o bruck_n16_ppn4.%J.out -#BSUB -nnodes 16 -#BSUB -q pdebug -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n16 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.14 sec. - Max Memory : - - Average Memory : - - Total Requested Memory : - - Delta Memory : - - Max Swap : - - Max Processes : - - Max Threads : - - Run time : 10 sec. - Turnaround time : 12 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/08_09_22/bruck_n32_ppn32.3848855.out b/benchmark_tests/lassen/08_09_22/bruck_n32_ppn32.3848855.out deleted file mode 100644 index c7a73ffbc..000000000 --- a/benchmark_tests/lassen/08_09_22/bruck_n32_ppn32.3848855.out +++ /dev/null @@ -1,1143 +0,0 @@ -Testing Size 1 -PMPI_Allgather Time 4.501481e-05 -allgather_bruck Time 7.111676e-05 -allgather_loc_bruck Time 2.653348e-05 -allgather_hier_bruck Time 4.514395e-05 -allgather_mult_hier_bruck Time 3.386168e-05 -Testing Size 2 -PMPI_Allgather Time 6.036889e-05 -allgather_bruck Time 1.124158e-04 -allgather_loc_bruck Time 3.201105e-05 -allgather_hier_bruck Time 5.389701e-05 -allgather_mult_hier_bruck Time 5.137772e-05 -Testing Size 4 -PMPI_Allgather Time 7.006821e-05 -allgather_bruck Time 1.958974e-04 -allgather_loc_bruck Time 4.856987e-05 -allgather_hier_bruck Time 5.473255e-05 -allgather_mult_hier_bruck Time 7.916991e-05 -Testing Size 8 -PMPI_Allgather Time 6.327743e-05 -allgather_bruck Time 3.638118e-04 -allgather_loc_bruck Time 7.870705e-05 -allgather_hier_bruck Time 6.860131e-05 -allgather_mult_hier_bruck Time 1.531245e-04 -Testing Size 16 -PMPI_Allgather Time 8.154951e-05 -allgather_bruck Time 7.102930e-04 -allgather_loc_bruck Time 1.165182e-04 -allgather_hier_bruck Time 1.047385e-04 -allgather_mult_hier_bruck Time 2.440670e-04 -app 0: ./bruck_allgather -rank: 0: { host: 1; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 4: { host: 1; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 5: { host: 1; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 6: { host: 1; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 7: { host: 1; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 8: { host: 1; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 9: { host: 1; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 10: { host: 1; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 11: { host: 1; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 12: { host: 1; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 13: { host: 1; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 14: { host: 1; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 15: { host: 1; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 16: { host: 1; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 17: { host: 1; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 18: { host: 1; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 19: { host: 1; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 20: { host: 1; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 21: { host: 1; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 22: { host: 1; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 23: { host: 1; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 24: { host: 1; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 25: { host: 1; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 26: { host: 1; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 27: { host: 1; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 28: { host: 1; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 29: { host: 1; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 30: { host: 1; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 31: { host: 1; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 32: { host: 2; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 33: { host: 2; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 34: { host: 2; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 35: { host: 2; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 36: { host: 2; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 37: { host: 2; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 38: { host: 2; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 39: { host: 2; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 40: { host: 2; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 41: { host: 2; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 42: { host: 2; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 43: { host: 2; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 44: { host: 2; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 45: { host: 2; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 46: { host: 2; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 47: { host: 2; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 48: { host: 2; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 49: { host: 2; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 50: { host: 2; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 51: { host: 2; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 52: { host: 2; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 53: { host: 2; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 54: { host: 2; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 55: { host: 2; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 56: { host: 2; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 57: { host: 2; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 58: { host: 2; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 59: { host: 2; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 60: { host: 2; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 61: { host: 2; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 62: { host: 2; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 63: { host: 2; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 64: { host: 3; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 65: { host: 3; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 66: { host: 3; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 67: { host: 3; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 68: { host: 3; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 69: { host: 3; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 70: { host: 3; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 71: { host: 3; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 72: { host: 3; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 73: { host: 3; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 74: { host: 3; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 75: { host: 3; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 76: { host: 3; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 77: { host: 3; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 78: { host: 3; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 79: { host: 3; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 80: { host: 3; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 81: { host: 3; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 82: { host: 3; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 83: { host: 3; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 84: { host: 3; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 85: { host: 3; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 86: { host: 3; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 87: { host: 3; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 88: { host: 3; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 89: { host: 3; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 90: { host: 3; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 91: { host: 3; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 92: { host: 3; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 93: { host: 3; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 94: { host: 3; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 95: { host: 3; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 96: { host: 4; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 97: { host: 4; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 98: { host: 4; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 99: { host: 4; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 100: { host: 4; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 101: { host: 4; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 102: { host: 4; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 103: { host: 4; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 104: { host: 4; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 105: { host: 4; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 106: { host: 4; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 107: { host: 4; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 108: { host: 4; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 109: { host: 4; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 110: { host: 4; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 111: { host: 4; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 112: { host: 4; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 113: { host: 4; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 114: { host: 4; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 115: { host: 4; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 116: { host: 4; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 117: { host: 4; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 118: { host: 4; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 119: { host: 4; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 120: { host: 4; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 121: { host: 4; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 122: { host: 4; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 123: { host: 4; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 124: { host: 4; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 125: { host: 4; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 126: { host: 4; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 127: { host: 4; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 128: { host: 5; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 129: { host: 5; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 130: { host: 5; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 131: { host: 5; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 132: { host: 5; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 133: { host: 5; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 134: { host: 5; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 135: { host: 5; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 136: { host: 5; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 137: { host: 5; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 138: { host: 5; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 139: { host: 5; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 140: { host: 5; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 141: { host: 5; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 142: { host: 5; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 143: { host: 5; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 144: { host: 5; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 145: { host: 5; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 146: { host: 5; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 147: { host: 5; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 148: { host: 5; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 149: { host: 5; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 150: { host: 5; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 151: { host: 5; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 152: { host: 5; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 153: { host: 5; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 154: { host: 5; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 155: { host: 5; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 156: { host: 5; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 157: { host: 5; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 158: { host: 5; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 159: { host: 5; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 160: { host: 6; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 161: { host: 6; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 162: { host: 6; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 163: { host: 6; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 164: { host: 6; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 165: { host: 6; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 166: { host: 6; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 167: { host: 6; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 168: { host: 6; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 169: { host: 6; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 170: { host: 6; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 171: { host: 6; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 172: { host: 6; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 173: { host: 6; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 174: { host: 6; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 175: { host: 6; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 176: { host: 6; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 177: { host: 6; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 178: { host: 6; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 179: { host: 6; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 180: { host: 6; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 181: { host: 6; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 182: { host: 6; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 183: { host: 6; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 184: { host: 6; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 185: { host: 6; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 186: { host: 6; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 187: { host: 6; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 188: { host: 6; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 189: { host: 6; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 190: { host: 6; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 191: { host: 6; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 192: { host: 7; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 193: { host: 7; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 194: { host: 7; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 195: { host: 7; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 196: { host: 7; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 197: { host: 7; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 198: { host: 7; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 199: { host: 7; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 200: { host: 7; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 201: { host: 7; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 202: { host: 7; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 203: { host: 7; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 204: { host: 7; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 205: { host: 7; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 206: { host: 7; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 207: { host: 7; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 208: { host: 7; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 209: { host: 7; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 210: { host: 7; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 211: { host: 7; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 212: { host: 7; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 213: { host: 7; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 214: { host: 7; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 215: { host: 7; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 216: { host: 7; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 217: { host: 7; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 218: { host: 7; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 219: { host: 7; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 220: { host: 7; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 221: { host: 7; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 222: { host: 7; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 223: { host: 7; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 224: { host: 8; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 225: { host: 8; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 226: { host: 8; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 227: { host: 8; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 228: { host: 8; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 229: { host: 8; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 230: { host: 8; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 231: { host: 8; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 232: { host: 8; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 233: { host: 8; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 234: { host: 8; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 235: { host: 8; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 236: { host: 8; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 237: { host: 8; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 238: { host: 8; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 239: { host: 8; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 240: { host: 8; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 241: { host: 8; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 242: { host: 8; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 243: { host: 8; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 244: { host: 8; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 245: { host: 8; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 246: { host: 8; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 247: { host: 8; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 248: { host: 8; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 249: { host: 8; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 250: { host: 8; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 251: { host: 8; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 252: { host: 8; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 253: { host: 8; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 254: { host: 8; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 255: { host: 8; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 256: { host: 9; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 257: { host: 9; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 258: { host: 9; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 259: { host: 9; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 260: { host: 9; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 261: { host: 9; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 262: { host: 9; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 263: { host: 9; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 264: { host: 9; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 265: { host: 9; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 266: { host: 9; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 267: { host: 9; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 268: { host: 9; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 269: { host: 9; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 270: { host: 9; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 271: { host: 9; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 272: { host: 9; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 273: { host: 9; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 274: { host: 9; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 275: { host: 9; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 276: { host: 9; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 277: { host: 9; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 278: { host: 9; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 279: { host: 9; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 280: { host: 9; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 281: { host: 9; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 282: { host: 9; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 283: { host: 9; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 284: { host: 9; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 285: { host: 9; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 286: { host: 9; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 287: { host: 9; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 288: { host: 10; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 289: { host: 10; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 290: { host: 10; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 291: { host: 10; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 292: { host: 10; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 293: { host: 10; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 294: { host: 10; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 295: { host: 10; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 296: { host: 10; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 297: { host: 10; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 298: { host: 10; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 299: { host: 10; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 300: { host: 10; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 301: { host: 10; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 302: { host: 10; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 303: { host: 10; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 304: { host: 10; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 305: { host: 10; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 306: { host: 10; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 307: { host: 10; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 308: { host: 10; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 309: { host: 10; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 310: { host: 10; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 311: { host: 10; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 312: { host: 10; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 313: { host: 10; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 314: { host: 10; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 315: { host: 10; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 316: { host: 10; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 317: { host: 10; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 318: { host: 10; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 319: { host: 10; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 320: { host: 11; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 321: { host: 11; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 322: { host: 11; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 323: { host: 11; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 324: { host: 11; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 325: { host: 11; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 326: { host: 11; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 327: { host: 11; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 328: { host: 11; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 329: { host: 11; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 330: { host: 11; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 331: { host: 11; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 332: { host: 11; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 333: { host: 11; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 334: { host: 11; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 335: { host: 11; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 336: { host: 11; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 337: { host: 11; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 338: { host: 11; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 339: { host: 11; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 340: { host: 11; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 341: { host: 11; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 342: { host: 11; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 343: { host: 11; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 344: { host: 11; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 345: { host: 11; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 346: { host: 11; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 347: { host: 11; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 348: { host: 11; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 349: { host: 11; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 350: { host: 11; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 351: { host: 11; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 352: { host: 12; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 353: { host: 12; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 354: { host: 12; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 355: { host: 12; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 356: { host: 12; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 357: { host: 12; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 358: { host: 12; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 359: { host: 12; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 360: { host: 12; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 361: { host: 12; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 362: { host: 12; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 363: { host: 12; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 364: { host: 12; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 365: { host: 12; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 366: { host: 12; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 367: { host: 12; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 368: { host: 12; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 369: { host: 12; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 370: { host: 12; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 371: { host: 12; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 372: { host: 12; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 373: { host: 12; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 374: { host: 12; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 375: { host: 12; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 376: { host: 12; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 377: { host: 12; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 378: { host: 12; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 379: { host: 12; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 380: { host: 12; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 381: { host: 12; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 382: { host: 12; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 383: { host: 12; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 384: { host: 13; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 385: { host: 13; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 386: { host: 13; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 387: { host: 13; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 388: { host: 13; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 389: { host: 13; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 390: { host: 13; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 391: { host: 13; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 392: { host: 13; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 393: { host: 13; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 394: { host: 13; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 395: { host: 13; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 396: { host: 13; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 397: { host: 13; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 398: { host: 13; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 399: { host: 13; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 400: { host: 13; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 401: { host: 13; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 402: { host: 13; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 403: { host: 13; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 404: { host: 13; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 405: { host: 13; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 406: { host: 13; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 407: { host: 13; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 408: { host: 13; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 409: { host: 13; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 410: { host: 13; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 411: { host: 13; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 412: { host: 13; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 413: { host: 13; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 414: { host: 13; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 415: { host: 13; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 416: { host: 14; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 417: { host: 14; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 418: { host: 14; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 419: { host: 14; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 420: { host: 14; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 421: { host: 14; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 422: { host: 14; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 423: { host: 14; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 424: { host: 14; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 425: { host: 14; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 426: { host: 14; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 427: { host: 14; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 428: { host: 14; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 429: { host: 14; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 430: { host: 14; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 431: { host: 14; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 432: { host: 14; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 433: { host: 14; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 434: { host: 14; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 435: { host: 14; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 436: { host: 14; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 437: { host: 14; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 438: { host: 14; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 439: { host: 14; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 440: { host: 14; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 441: { host: 14; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 442: { host: 14; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 443: { host: 14; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 444: { host: 14; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 445: { host: 14; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 446: { host: 14; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 447: { host: 14; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 448: { host: 15; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 449: { host: 15; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 450: { host: 15; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 451: { host: 15; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 452: { host: 15; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 453: { host: 15; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 454: { host: 15; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 455: { host: 15; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 456: { host: 15; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 457: { host: 15; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 458: { host: 15; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 459: { host: 15; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 460: { host: 15; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 461: { host: 15; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 462: { host: 15; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 463: { host: 15; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 464: { host: 15; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 465: { host: 15; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 466: { host: 15; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 467: { host: 15; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 468: { host: 15; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 469: { host: 15; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 470: { host: 15; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 471: { host: 15; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 472: { host: 15; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 473: { host: 15; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 474: { host: 15; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 475: { host: 15; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 476: { host: 15; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 477: { host: 15; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 478: { host: 15; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 479: { host: 15; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 480: { host: 16; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 481: { host: 16; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 482: { host: 16; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 483: { host: 16; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 484: { host: 16; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 485: { host: 16; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 486: { host: 16; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 487: { host: 16; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 488: { host: 16; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 489: { host: 16; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 490: { host: 16; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 491: { host: 16; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 492: { host: 16; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 493: { host: 16; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 494: { host: 16; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 495: { host: 16; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 496: { host: 16; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 497: { host: 16; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 498: { host: 16; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 499: { host: 16; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 500: { host: 16; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 501: { host: 16; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 502: { host: 16; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 503: { host: 16; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 504: { host: 16; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 505: { host: 16; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 506: { host: 16; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 507: { host: 16; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 508: { host: 16; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 509: { host: 16; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 510: { host: 16; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 511: { host: 16; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 512: { host: 17; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 513: { host: 17; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 514: { host: 17; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 515: { host: 17; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 516: { host: 17; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 517: { host: 17; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 518: { host: 17; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 519: { host: 17; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 520: { host: 17; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 521: { host: 17; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 522: { host: 17; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 523: { host: 17; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 524: { host: 17; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 525: { host: 17; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 526: { host: 17; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 527: { host: 17; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 528: { host: 17; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 529: { host: 17; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 530: { host: 17; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 531: { host: 17; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 532: { host: 17; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 533: { host: 17; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 534: { host: 17; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 535: { host: 17; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 536: { host: 17; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 537: { host: 17; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 538: { host: 17; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 539: { host: 17; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 540: { host: 17; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 541: { host: 17; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 542: { host: 17; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 543: { host: 17; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 544: { host: 18; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 545: { host: 18; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 546: { host: 18; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 547: { host: 18; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 548: { host: 18; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 549: { host: 18; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 550: { host: 18; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 551: { host: 18; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 552: { host: 18; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 553: { host: 18; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 554: { host: 18; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 555: { host: 18; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 556: { host: 18; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 557: { host: 18; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 558: { host: 18; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 559: { host: 18; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 560: { host: 18; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 561: { host: 18; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 562: { host: 18; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 563: { host: 18; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 564: { host: 18; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 565: { host: 18; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 566: { host: 18; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 567: { host: 18; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 568: { host: 18; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 569: { host: 18; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 570: { host: 18; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 571: { host: 18; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 572: { host: 18; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 573: { host: 18; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 574: { host: 18; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 575: { host: 18; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 576: { host: 19; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 577: { host: 19; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 578: { host: 19; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 579: { host: 19; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 580: { host: 19; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 581: { host: 19; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 582: { host: 19; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 583: { host: 19; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 584: { host: 19; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 585: { host: 19; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 586: { host: 19; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 587: { host: 19; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 588: { host: 19; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 589: { host: 19; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 590: { host: 19; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 591: { host: 19; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 592: { host: 19; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 593: { host: 19; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 594: { host: 19; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 595: { host: 19; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 596: { host: 19; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 597: { host: 19; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 598: { host: 19; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 599: { host: 19; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 600: { host: 19; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 601: { host: 19; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 602: { host: 19; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 603: { host: 19; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 604: { host: 19; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 605: { host: 19; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 606: { host: 19; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 607: { host: 19; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 608: { host: 20; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 609: { host: 20; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 610: { host: 20; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 611: { host: 20; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 612: { host: 20; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 613: { host: 20; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 614: { host: 20; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 615: { host: 20; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 616: { host: 20; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 617: { host: 20; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 618: { host: 20; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 619: { host: 20; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 620: { host: 20; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 621: { host: 20; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 622: { host: 20; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 623: { host: 20; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 624: { host: 20; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 625: { host: 20; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 626: { host: 20; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 627: { host: 20; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 628: { host: 20; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 629: { host: 20; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 630: { host: 20; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 631: { host: 20; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 632: { host: 20; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 633: { host: 20; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 634: { host: 20; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 635: { host: 20; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 636: { host: 20; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 637: { host: 20; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 638: { host: 20; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 639: { host: 20; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 640: { host: 21; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 641: { host: 21; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 642: { host: 21; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 643: { host: 21; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 644: { host: 21; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 645: { host: 21; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 646: { host: 21; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 647: { host: 21; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 648: { host: 21; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 649: { host: 21; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 650: { host: 21; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 651: { host: 21; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 652: { host: 21; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 653: { host: 21; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 654: { host: 21; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 655: { host: 21; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 656: { host: 21; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 657: { host: 21; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 658: { host: 21; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 659: { host: 21; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 660: { host: 21; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 661: { host: 21; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 662: { host: 21; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 663: { host: 21; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 664: { host: 21; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 665: { host: 21; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 666: { host: 21; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 667: { host: 21; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 668: { host: 21; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 669: { host: 21; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 670: { host: 21; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 671: { host: 21; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 672: { host: 22; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 673: { host: 22; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 674: { host: 22; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 675: { host: 22; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 676: { host: 22; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 677: { host: 22; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 678: { host: 22; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 679: { host: 22; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 680: { host: 22; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 681: { host: 22; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 682: { host: 22; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 683: { host: 22; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 684: { host: 22; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 685: { host: 22; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 686: { host: 22; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 687: { host: 22; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 688: { host: 22; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 689: { host: 22; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 690: { host: 22; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 691: { host: 22; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 692: { host: 22; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 693: { host: 22; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 694: { host: 22; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 695: { host: 22; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 696: { host: 22; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 697: { host: 22; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 698: { host: 22; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 699: { host: 22; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 700: { host: 22; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 701: { host: 22; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 702: { host: 22; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 703: { host: 22; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 704: { host: 23; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 705: { host: 23; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 706: { host: 23; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 707: { host: 23; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 708: { host: 23; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 709: { host: 23; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 710: { host: 23; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 711: { host: 23; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 712: { host: 23; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 713: { host: 23; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 714: { host: 23; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 715: { host: 23; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 716: { host: 23; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 717: { host: 23; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 718: { host: 23; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 719: { host: 23; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 720: { host: 23; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 721: { host: 23; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 722: { host: 23; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 723: { host: 23; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 724: { host: 23; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 725: { host: 23; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 726: { host: 23; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 727: { host: 23; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 728: { host: 23; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 729: { host: 23; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 730: { host: 23; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 731: { host: 23; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 732: { host: 23; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 733: { host: 23; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 734: { host: 23; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 735: { host: 23; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 736: { host: 24; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 737: { host: 24; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 738: { host: 24; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 739: { host: 24; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 740: { host: 24; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 741: { host: 24; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 742: { host: 24; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 743: { host: 24; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 744: { host: 24; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 745: { host: 24; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 746: { host: 24; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 747: { host: 24; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 748: { host: 24; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 749: { host: 24; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 750: { host: 24; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 751: { host: 24; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 752: { host: 24; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 753: { host: 24; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 754: { host: 24; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 755: { host: 24; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 756: { host: 24; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 757: { host: 24; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 758: { host: 24; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 759: { host: 24; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 760: { host: 24; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 761: { host: 24; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 762: { host: 24; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 763: { host: 24; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 764: { host: 24; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 765: { host: 24; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 766: { host: 24; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 767: { host: 24; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 768: { host: 25; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 769: { host: 25; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 770: { host: 25; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 771: { host: 25; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 772: { host: 25; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 773: { host: 25; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 774: { host: 25; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 775: { host: 25; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 776: { host: 25; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 777: { host: 25; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 778: { host: 25; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 779: { host: 25; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 780: { host: 25; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 781: { host: 25; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 782: { host: 25; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 783: { host: 25; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 784: { host: 25; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 785: { host: 25; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 786: { host: 25; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 787: { host: 25; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 788: { host: 25; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 789: { host: 25; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 790: { host: 25; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 791: { host: 25; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 792: { host: 25; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 793: { host: 25; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 794: { host: 25; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 795: { host: 25; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 796: { host: 25; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 797: { host: 25; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 798: { host: 25; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 799: { host: 25; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 800: { host: 26; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 801: { host: 26; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 802: { host: 26; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 803: { host: 26; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 804: { host: 26; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 805: { host: 26; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 806: { host: 26; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 807: { host: 26; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 808: { host: 26; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 809: { host: 26; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 810: { host: 26; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 811: { host: 26; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 812: { host: 26; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 813: { host: 26; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 814: { host: 26; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 815: { host: 26; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 816: { host: 26; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 817: { host: 26; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 818: { host: 26; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 819: { host: 26; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 820: { host: 26; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 821: { host: 26; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 822: { host: 26; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 823: { host: 26; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 824: { host: 26; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 825: { host: 26; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 826: { host: 26; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 827: { host: 26; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 828: { host: 26; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 829: { host: 26; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 830: { host: 26; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 831: { host: 26; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 832: { host: 27; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 833: { host: 27; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 834: { host: 27; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 835: { host: 27; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 836: { host: 27; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 837: { host: 27; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 838: { host: 27; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 839: { host: 27; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 840: { host: 27; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 841: { host: 27; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 842: { host: 27; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 843: { host: 27; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 844: { host: 27; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 845: { host: 27; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 846: { host: 27; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 847: { host: 27; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 848: { host: 27; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 849: { host: 27; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 850: { host: 27; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 851: { host: 27; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 852: { host: 27; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 853: { host: 27; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 854: { host: 27; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 855: { host: 27; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 856: { host: 27; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 857: { host: 27; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 858: { host: 27; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 859: { host: 27; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 860: { host: 27; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 861: { host: 27; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 862: { host: 27; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 863: { host: 27; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 864: { host: 28; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 865: { host: 28; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 866: { host: 28; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 867: { host: 28; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 868: { host: 28; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 869: { host: 28; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 870: { host: 28; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 871: { host: 28; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 872: { host: 28; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 873: { host: 28; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 874: { host: 28; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 875: { host: 28; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 876: { host: 28; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 877: { host: 28; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 878: { host: 28; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 879: { host: 28; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 880: { host: 28; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 881: { host: 28; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 882: { host: 28; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 883: { host: 28; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 884: { host: 28; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 885: { host: 28; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 886: { host: 28; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 887: { host: 28; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 888: { host: 28; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 889: { host: 28; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 890: { host: 28; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 891: { host: 28; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 892: { host: 28; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 893: { host: 28; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 894: { host: 28; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 895: { host: 28; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 896: { host: 29; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 897: { host: 29; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 898: { host: 29; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 899: { host: 29; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 900: { host: 29; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 901: { host: 29; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 902: { host: 29; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 903: { host: 29; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 904: { host: 29; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 905: { host: 29; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 906: { host: 29; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 907: { host: 29; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 908: { host: 29; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 909: { host: 29; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 910: { host: 29; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 911: { host: 29; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 912: { host: 29; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 913: { host: 29; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 914: { host: 29; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 915: { host: 29; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 916: { host: 29; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 917: { host: 29; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 918: { host: 29; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 919: { host: 29; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 920: { host: 29; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 921: { host: 29; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 922: { host: 29; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 923: { host: 29; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 924: { host: 29; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 925: { host: 29; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 926: { host: 29; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 927: { host: 29; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 928: { host: 30; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 929: { host: 30; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 930: { host: 30; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 931: { host: 30; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 932: { host: 30; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 933: { host: 30; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 934: { host: 30; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 935: { host: 30; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 936: { host: 30; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 937: { host: 30; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 938: { host: 30; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 939: { host: 30; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 940: { host: 30; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 941: { host: 30; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 942: { host: 30; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 943: { host: 30; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 944: { host: 30; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 945: { host: 30; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 946: { host: 30; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 947: { host: 30; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 948: { host: 30; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 949: { host: 30; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 950: { host: 30; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 951: { host: 30; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 952: { host: 30; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 953: { host: 30; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 954: { host: 30; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 955: { host: 30; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 956: { host: 30; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 957: { host: 30; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 958: { host: 30; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 959: { host: 30; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 960: { host: 31; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 961: { host: 31; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 962: { host: 31; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 963: { host: 31; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 964: { host: 31; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 965: { host: 31; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 966: { host: 31; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 967: { host: 31; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 968: { host: 31; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 969: { host: 31; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 970: { host: 31; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 971: { host: 31; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 972: { host: 31; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 973: { host: 31; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 974: { host: 31; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 975: { host: 31; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 976: { host: 31; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 977: { host: 31; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 978: { host: 31; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 979: { host: 31; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 980: { host: 31; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 981: { host: 31; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 982: { host: 31; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 983: { host: 31; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 984: { host: 31; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 985: { host: 31; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 986: { host: 31; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 987: { host: 31; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 988: { host: 31; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 989: { host: 31; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 990: { host: 31; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 991: { host: 31; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 992: { host: 32; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 993: { host: 32; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 994: { host: 32; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 995: { host: 32; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 996: { host: 32; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 997: { host: 32; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 998: { host: 32; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 999: { host: 32; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1000: { host: 32; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1001: { host: 32; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1002: { host: 32; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1003: { host: 32; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1004: { host: 32; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1005: { host: 32; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1006: { host: 32; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1007: { host: 32; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1008: { host: 32; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1009: { host: 32; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1010: { host: 32; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1011: { host: 32; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1012: { host: 32; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1013: { host: 32; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1014: { host: 32; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1015: { host: 32; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1016: { host: 32; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1017: { host: 32; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1018: { host: 32; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1019: { host: 32; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1020: { host: 32; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1021: { host: 32; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1022: { host: 32; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1023: { host: 32; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3848855: in cluster Done - -Job was submitted from host by user in cluster at Tue Aug 9 20:07:22 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Tue Aug 9 20:16:34 2022 - <40*lassen408> - <40*lassen252> - <40*lassen254> - <40*lassen584> - <40*lassen586> - <40*lassen587> - <40*lassen84> - <40*lassen414> - <40*lassen591> - <40*lassen268> - <40*lassen95> - <40*lassen96> - <40*lassen99> - <40*lassen420> - <40*lassen421> - <40*lassen751> - <40*lassen752> - <40*lassen753> - <40*lassen425> - <40*lassen427> - <40*lassen429> - <40*lassen275> - <40*lassen102> - <40*lassen432> - <40*lassen104> - <40*lassen105> - <40*lassen764> - <40*lassen765> - <40*lassen281> - <40*lassen282> - <40*lassen109> - <40*lassen439> - was used as the home directory. - was used as the working directory. -Started at Tue Aug 9 20:16:34 2022 -Terminated at Tue Aug 9 20:16:57 2022 -Results reported at Tue Aug 9 20:16:57 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J bruck_n32_ppn32 -#BSUB -e bruck_n32_ppn32.%J.err -#BSUB -o bruck_n32_ppn32.%J.out -#BSUB -nnodes 32 -#BSUB -q pbatch -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a32 -c32 -r1 -n32 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.40 sec. - Max Memory : - - Average Memory : - - Total Requested Memory : - - Delta Memory : - - Max Swap : - - Max Processes : - - Max Threads : - - Run time : 22 sec. - Turnaround time : 575 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/08_09_22/bruck_n4_ppn4.3848151.out b/benchmark_tests/lassen/08_09_22/bruck_n4_ppn4.3848151.out deleted file mode 100644 index e83fc53b2..000000000 --- a/benchmark_tests/lassen/08_09_22/bruck_n4_ppn4.3848151.out +++ /dev/null @@ -1,107 +0,0 @@ -Testing Size 1 -PMPI_Allgather Time 4.945950e-06 -allgather_bruck Time 8.353370e-06 -allgather_loc_bruck Time 7.430840e-06 -allgather_hier_bruck Time 6.230800e-06 -allgather_mult_hier_bruck Time 4.808920e-06 -Testing Size 2 -PMPI_Allgather Time 4.732960e-06 -allgather_bruck Time 5.648070e-06 -allgather_loc_bruck Time 4.518990e-06 -allgather_hier_bruck Time 6.805820e-06 -allgather_mult_hier_bruck Time 4.906200e-06 -Testing Size 4 -PMPI_Allgather Time 4.728230e-06 -allgather_bruck Time 6.512180e-06 -allgather_loc_bruck Time 4.902590e-06 -allgather_hier_bruck Time 7.886940e-06 -allgather_mult_hier_bruck Time 5.665780e-06 -Testing Size 8 -PMPI_Allgather Time 4.572350e-06 -allgather_bruck Time 7.337740e-06 -allgather_loc_bruck Time 5.855230e-06 -allgather_hier_bruck Time 8.576220e-06 -allgather_mult_hier_bruck Time 6.552050e-06 -Testing Size 16 -PMPI_Allgather Time 4.891890e-06 -allgather_bruck Time 8.615810e-06 -allgather_loc_bruck Time 6.703200e-06 -allgather_hier_bruck Time 8.656690e-06 -allgather_mult_hier_bruck Time 7.922050e-06 -app 0: ./bruck_allgather -rank: 0: { host: 1; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 4: { host: 2; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 5: { host: 2; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 6: { host: 2; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 7: { host: 2; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 8: { host: 3; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 9: { host: 3; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 10: { host: 3; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 11: { host: 3; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 12: { host: 4; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 13: { host: 4; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 14: { host: 4; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 15: { host: 4; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3848151: in cluster Done - -Job was submitted from host by user in cluster at Tue Aug 9 15:07:18 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Tue Aug 9 15:07:20 2022 - <40*lassen29> - <40*lassen28> - <40*lassen27> - <40*lassen26> - was used as the home directory. - was used as the working directory. -Started at Tue Aug 9 15:07:20 2022 -Terminated at Tue Aug 9 15:07:29 2022 -Results reported at Tue Aug 9 15:07:29 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J bruck_n4_ppn4 -#BSUB -e bruck_n4_ppn4.%J.err -#BSUB -o bruck_n4_ppn4.%J.out -#BSUB -nnodes 4 -#BSUB -q pdebug -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n4 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.21 sec. - Max Memory : - - Average Memory : - - Total Requested Memory : - - Delta Memory : - - Max Swap : - - Max Processes : - - Max Threads : - - Run time : 8 sec. - Turnaround time : 11 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/08_09_22/bruck_n64_ppn4.3848169.out b/benchmark_tests/lassen/08_09_22/bruck_n64_ppn4.3848169.out deleted file mode 100644 index b689c0be8..000000000 --- a/benchmark_tests/lassen/08_09_22/bruck_n64_ppn4.3848169.out +++ /dev/null @@ -1,407 +0,0 @@ -Testing Size 1 -PMPI_Allgather Time 2.680752e-05 -allgather_bruck Time 2.491159e-05 -allgather_loc_bruck Time 1.989449e-05 -allgather_hier_bruck Time 2.524424e-05 -allgather_mult_hier_bruck Time 2.285438e-05 -Testing Size 2 -PMPI_Allgather Time 2.475632e-05 -allgather_bruck Time 2.517627e-05 -allgather_loc_bruck Time 2.615120e-05 -allgather_hier_bruck Time 2.805772e-05 -allgather_mult_hier_bruck Time 3.482212e-05 -Testing Size 4 -PMPI_Allgather Time 2.866569e-05 -allgather_bruck Time 5.219216e-05 -allgather_loc_bruck Time 2.139652e-05 -allgather_hier_bruck Time 2.887569e-05 -allgather_mult_hier_bruck Time 2.863647e-05 -Testing Size 8 -PMPI_Allgather Time 3.132032e-05 -allgather_bruck Time 3.691391e-05 -allgather_loc_bruck Time 3.123688e-05 -allgather_hier_bruck Time 4.403855e-05 -allgather_mult_hier_bruck Time 4.297258e-05 -Testing Size 16 -PMPI_Allgather Time 5.485926e-05 -allgather_bruck Time 6.415140e-05 -allgather_loc_bruck Time 5.042445e-05 -allgather_hier_bruck Time 4.797087e-05 -allgather_mult_hier_bruck Time 7.274998e-05 -app 0: ./bruck_allgather -rank: 0: { host: 1; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 4: { host: 2; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 5: { host: 2; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 6: { host: 2; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 7: { host: 2; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 8: { host: 3; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 9: { host: 3; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 10: { host: 3; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 11: { host: 3; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 12: { host: 4; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 13: { host: 4; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 14: { host: 4; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 15: { host: 4; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 16: { host: 5; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 17: { host: 5; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 18: { host: 5; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 19: { host: 5; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 20: { host: 6; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 21: { host: 6; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 22: { host: 6; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 23: { host: 6; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 24: { host: 7; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 25: { host: 7; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 26: { host: 7; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 27: { host: 7; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 28: { host: 8; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 29: { host: 8; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 30: { host: 8; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 31: { host: 8; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 32: { host: 9; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 33: { host: 9; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 34: { host: 9; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 35: { host: 9; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 36: { host: 10; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 37: { host: 10; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 38: { host: 10; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 39: { host: 10; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 40: { host: 11; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 41: { host: 11; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 42: { host: 11; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 43: { host: 11; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 44: { host: 12; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 45: { host: 12; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 46: { host: 12; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 47: { host: 12; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 48: { host: 13; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 49: { host: 13; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 50: { host: 13; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 51: { host: 13; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 52: { host: 14; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 53: { host: 14; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 54: { host: 14; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 55: { host: 14; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 56: { host: 15; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 57: { host: 15; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 58: { host: 15; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 59: { host: 15; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 60: { host: 16; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 61: { host: 16; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 62: { host: 16; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 63: { host: 16; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 64: { host: 17; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 65: { host: 17; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 66: { host: 17; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 67: { host: 17; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 68: { host: 18; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 69: { host: 18; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 70: { host: 18; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 71: { host: 18; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 72: { host: 19; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 73: { host: 19; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 74: { host: 19; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 75: { host: 19; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 76: { host: 20; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 77: { host: 20; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 78: { host: 20; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 79: { host: 20; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 80: { host: 21; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 81: { host: 21; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 82: { host: 21; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 83: { host: 21; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 84: { host: 22; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 85: { host: 22; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 86: { host: 22; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 87: { host: 22; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 88: { host: 23; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 89: { host: 23; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 90: { host: 23; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 91: { host: 23; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 92: { host: 24; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 93: { host: 24; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 94: { host: 24; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 95: { host: 24; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 96: { host: 25; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 97: { host: 25; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 98: { host: 25; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 99: { host: 25; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 100: { host: 26; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 101: { host: 26; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 102: { host: 26; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 103: { host: 26; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 104: { host: 27; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 105: { host: 27; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 106: { host: 27; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 107: { host: 27; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 108: { host: 28; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 109: { host: 28; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 110: { host: 28; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 111: { host: 28; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 112: { host: 29; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 113: { host: 29; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 114: { host: 29; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 115: { host: 29; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 116: { host: 30; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 117: { host: 30; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 118: { host: 30; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 119: { host: 30; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 120: { host: 31; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 121: { host: 31; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 122: { host: 31; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 123: { host: 31; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 124: { host: 32; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 125: { host: 32; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 126: { host: 32; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 127: { host: 32; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 128: { host: 33; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 129: { host: 33; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 130: { host: 33; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 131: { host: 33; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 132: { host: 34; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 133: { host: 34; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 134: { host: 34; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 135: { host: 34; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 136: { host: 35; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 137: { host: 35; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 138: { host: 35; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 139: { host: 35; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 140: { host: 36; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 141: { host: 36; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 142: { host: 36; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 143: { host: 36; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 144: { host: 37; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 145: { host: 37; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 146: { host: 37; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 147: { host: 37; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 148: { host: 38; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 149: { host: 38; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 150: { host: 38; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 151: { host: 38; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 152: { host: 39; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 153: { host: 39; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 154: { host: 39; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 155: { host: 39; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 156: { host: 40; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 157: { host: 40; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 158: { host: 40; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 159: { host: 40; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 160: { host: 41; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 161: { host: 41; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 162: { host: 41; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 163: { host: 41; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 164: { host: 42; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 165: { host: 42; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 166: { host: 42; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 167: { host: 42; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 168: { host: 43; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 169: { host: 43; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 170: { host: 43; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 171: { host: 43; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 172: { host: 44; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 173: { host: 44; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 174: { host: 44; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 175: { host: 44; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 176: { host: 45; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 177: { host: 45; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 178: { host: 45; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 179: { host: 45; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 180: { host: 46; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 181: { host: 46; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 182: { host: 46; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 183: { host: 46; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 184: { host: 47; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 185: { host: 47; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 186: { host: 47; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 187: { host: 47; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 188: { host: 48; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 189: { host: 48; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 190: { host: 48; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 191: { host: 48; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 192: { host: 49; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 193: { host: 49; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 194: { host: 49; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 195: { host: 49; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 196: { host: 50; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 197: { host: 50; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 198: { host: 50; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 199: { host: 50; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 200: { host: 51; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 201: { host: 51; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 202: { host: 51; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 203: { host: 51; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 204: { host: 52; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 205: { host: 52; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 206: { host: 52; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 207: { host: 52; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 208: { host: 53; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 209: { host: 53; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 210: { host: 53; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 211: { host: 53; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 212: { host: 54; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 213: { host: 54; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 214: { host: 54; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 215: { host: 54; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 216: { host: 55; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 217: { host: 55; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 218: { host: 55; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 219: { host: 55; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 220: { host: 56; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 221: { host: 56; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 222: { host: 56; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 223: { host: 56; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 224: { host: 57; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 225: { host: 57; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 226: { host: 57; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 227: { host: 57; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 228: { host: 58; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 229: { host: 58; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 230: { host: 58; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 231: { host: 58; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 232: { host: 59; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 233: { host: 59; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 234: { host: 59; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 235: { host: 59; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 236: { host: 60; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 237: { host: 60; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 238: { host: 60; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 239: { host: 60; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 240: { host: 61; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 241: { host: 61; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 242: { host: 61; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 243: { host: 61; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 244: { host: 62; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 245: { host: 62; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 246: { host: 62; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 247: { host: 62; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 248: { host: 63; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 249: { host: 63; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 250: { host: 63; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 251: { host: 63; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 252: { host: 64; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 253: { host: 64; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 254: { host: 64; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 255: { host: 64; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3848169: in cluster Done - -Job was submitted from host by user in cluster at Tue Aug 9 15:12:34 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Tue Aug 9 16:11:41 2022 - <40*lassen406> - <40*lassen250> - <40*lassen580> - <40*lassen407> - <40*lassen251> - <40*lassen581> - <40*lassen738> - <40*lassen588> - <40*lassen80> - <40*lassen83> - <40*lassen418> - <40*lassen597> - <40*lassen91> - <40*lassen93> - <40*lassen270> - <40*lassen271> - <40*lassen272> - <40*lassen757> - <40*lassen436> - <40*lassen438> - <40*lassen284> - <40*lassen286> - <40*lassen110> - <40*lassen773> - <40*lassen779> - <40*lassen604> - <40*lassen782> - <40*lassen456> - <40*lassen786> - <40*lassen458> - <40*lassen796> - <40*lassen154> - <40*lassen167> - <40*lassen168> - <40*lassen169> - <40*lassen805> - <40*lassen806> - <40*lassen328> - <40*lassen184> - <40*lassen185> - <40*lassen186> - <40*lassen187> - <40*lassen348> - <40*lassen391> - <40*lassen218> - <40*lassen219> - <40*lassen549> - <40*lassen394> - <40*lassen395> - <40*lassen396> - <40*lassen397> - <40*lassen398> - <40*lassen399> - <40*lassen42> - <40*lassen44> - <40*lassen46> - <40*lassen220> - <40*lassen550> - <40*lassen221> - <40*lassen551> - <40*lassen552> - <40*lassen225> - <40*lassen229> - <40*lassen233> - was used as the home directory. - was used as the working directory. -Started at Tue Aug 9 16:11:41 2022 -Terminated at Tue Aug 9 16:12:00 2022 -Results reported at Tue Aug 9 16:12:00 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J bruck_n64_ppn4 -#BSUB -e bruck_n64_ppn4.%J.err -#BSUB -o bruck_n64_ppn4.%J.out -#BSUB -nnodes 64 -#BSUB -q pbatch -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n64 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.14 sec. - Max Memory : 357 MB - Average Memory : 357.00 MB - Total Requested Memory : - - Delta Memory : - - Max Swap : 678 MB - Max Processes : 1 - Max Threads : 2 - Run time : 19 sec. - Turnaround time : 3566 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/bruck_allgather/bruck_n16_ppn16.3850164.out b/benchmark_tests/lassen/bruck_allgather/bruck_n16_ppn16.3850164.out deleted file mode 100644 index c7f77a92a..000000000 --- a/benchmark_tests/lassen/bruck_allgather/bruck_n16_ppn16.3850164.out +++ /dev/null @@ -1,359 +0,0 @@ -Testing Size 1 -PMPI_Allgather Time 2.178991e-05 -allgather_bruck Time 3.303612e-05 -allgather_loc_bruck Time 1.773347e-05 -allgather_hier_bruck Time 2.114916e-05 -allgather_mult_hier_bruck Time 1.675325e-05 -Testing Size 2 -PMPI_Allgather Time 1.881344e-05 -allgather_bruck Time 2.639042e-05 -allgather_loc_bruck Time 1.240927e-05 -allgather_hier_bruck Time 2.074329e-05 -allgather_mult_hier_bruck Time 2.199155e-05 -Testing Size 4 -PMPI_Allgather Time 1.637973e-05 -allgather_bruck Time 3.358147e-05 -allgather_loc_bruck Time 1.958699e-05 -allgather_hier_bruck Time 2.332698e-05 -allgather_mult_hier_bruck Time 2.504114e-05 -Testing Size 8 -PMPI_Allgather Time 1.815925e-05 -allgather_bruck Time 5.363446e-05 -allgather_loc_bruck Time 2.002859e-05 -allgather_hier_bruck Time 2.868329e-05 -allgather_mult_hier_bruck Time 4.069726e-05 -Testing Size 16 -PMPI_Allgather Time 2.114283e-05 -allgather_bruck Time 8.176817e-05 -allgather_loc_bruck Time 2.899488e-05 -allgather_hier_bruck Time 3.866861e-05 -allgather_mult_hier_bruck Time 6.456095e-05 -app 0: ./bruck_allgather -rank: 0: { host: 1; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 4: { host: 1; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 5: { host: 1; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 6: { host: 1; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 7: { host: 1; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 8: { host: 1; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 9: { host: 1; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 10: { host: 1; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 11: { host: 1; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 12: { host: 1; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 13: { host: 1; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 14: { host: 1; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 15: { host: 1; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 16: { host: 2; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 17: { host: 2; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 18: { host: 2; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 19: { host: 2; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 20: { host: 2; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 21: { host: 2; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 22: { host: 2; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 23: { host: 2; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 24: { host: 2; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 25: { host: 2; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 26: { host: 2; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 27: { host: 2; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 28: { host: 2; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 29: { host: 2; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 30: { host: 2; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 31: { host: 2; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 32: { host: 3; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 33: { host: 3; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 34: { host: 3; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 35: { host: 3; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 36: { host: 3; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 37: { host: 3; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 38: { host: 3; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 39: { host: 3; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 40: { host: 3; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 41: { host: 3; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 42: { host: 3; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 43: { host: 3; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 44: { host: 3; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 45: { host: 3; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 46: { host: 3; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 47: { host: 3; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 48: { host: 4; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 49: { host: 4; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 50: { host: 4; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 51: { host: 4; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 52: { host: 4; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 53: { host: 4; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 54: { host: 4; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 55: { host: 4; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 56: { host: 4; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 57: { host: 4; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 58: { host: 4; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 59: { host: 4; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 60: { host: 4; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 61: { host: 4; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 62: { host: 4; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 63: { host: 4; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 64: { host: 5; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 65: { host: 5; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 66: { host: 5; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 67: { host: 5; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 68: { host: 5; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 69: { host: 5; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 70: { host: 5; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 71: { host: 5; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 72: { host: 5; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 73: { host: 5; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 74: { host: 5; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 75: { host: 5; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 76: { host: 5; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 77: { host: 5; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 78: { host: 5; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 79: { host: 5; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 80: { host: 6; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 81: { host: 6; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 82: { host: 6; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 83: { host: 6; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 84: { host: 6; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 85: { host: 6; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 86: { host: 6; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 87: { host: 6; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 88: { host: 6; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 89: { host: 6; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 90: { host: 6; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 91: { host: 6; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 92: { host: 6; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 93: { host: 6; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 94: { host: 6; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 95: { host: 6; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 96: { host: 7; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 97: { host: 7; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 98: { host: 7; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 99: { host: 7; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 100: { host: 7; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 101: { host: 7; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 102: { host: 7; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 103: { host: 7; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 104: { host: 7; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 105: { host: 7; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 106: { host: 7; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 107: { host: 7; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 108: { host: 7; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 109: { host: 7; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 110: { host: 7; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 111: { host: 7; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 112: { host: 8; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 113: { host: 8; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 114: { host: 8; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 115: { host: 8; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 116: { host: 8; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 117: { host: 8; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 118: { host: 8; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 119: { host: 8; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 120: { host: 8; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 121: { host: 8; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 122: { host: 8; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 123: { host: 8; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 124: { host: 8; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 125: { host: 8; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 126: { host: 8; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 127: { host: 8; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 128: { host: 9; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 129: { host: 9; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 130: { host: 9; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 131: { host: 9; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 132: { host: 9; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 133: { host: 9; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 134: { host: 9; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 135: { host: 9; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 136: { host: 9; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 137: { host: 9; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 138: { host: 9; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 139: { host: 9; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 140: { host: 9; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 141: { host: 9; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 142: { host: 9; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 143: { host: 9; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 144: { host: 10; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 145: { host: 10; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 146: { host: 10; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 147: { host: 10; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 148: { host: 10; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 149: { host: 10; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 150: { host: 10; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 151: { host: 10; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 152: { host: 10; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 153: { host: 10; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 154: { host: 10; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 155: { host: 10; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 156: { host: 10; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 157: { host: 10; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 158: { host: 10; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 159: { host: 10; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 160: { host: 11; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 161: { host: 11; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 162: { host: 11; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 163: { host: 11; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 164: { host: 11; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 165: { host: 11; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 166: { host: 11; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 167: { host: 11; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 168: { host: 11; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 169: { host: 11; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 170: { host: 11; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 171: { host: 11; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 172: { host: 11; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 173: { host: 11; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 174: { host: 11; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 175: { host: 11; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 176: { host: 12; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 177: { host: 12; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 178: { host: 12; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 179: { host: 12; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 180: { host: 12; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 181: { host: 12; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 182: { host: 12; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 183: { host: 12; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 184: { host: 12; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 185: { host: 12; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 186: { host: 12; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 187: { host: 12; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 188: { host: 12; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 189: { host: 12; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 190: { host: 12; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 191: { host: 12; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 192: { host: 13; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 193: { host: 13; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 194: { host: 13; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 195: { host: 13; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 196: { host: 13; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 197: { host: 13; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 198: { host: 13; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 199: { host: 13; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 200: { host: 13; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 201: { host: 13; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 202: { host: 13; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 203: { host: 13; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 204: { host: 13; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 205: { host: 13; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 206: { host: 13; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 207: { host: 13; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 208: { host: 14; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 209: { host: 14; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 210: { host: 14; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 211: { host: 14; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 212: { host: 14; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 213: { host: 14; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 214: { host: 14; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 215: { host: 14; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 216: { host: 14; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 217: { host: 14; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 218: { host: 14; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 219: { host: 14; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 220: { host: 14; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 221: { host: 14; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 222: { host: 14; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 223: { host: 14; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 224: { host: 15; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 225: { host: 15; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 226: { host: 15; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 227: { host: 15; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 228: { host: 15; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 229: { host: 15; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 230: { host: 15; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 231: { host: 15; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 232: { host: 15; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 233: { host: 15; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 234: { host: 15; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 235: { host: 15; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 236: { host: 15; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 237: { host: 15; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 238: { host: 15; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 239: { host: 15; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 240: { host: 16; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 241: { host: 16; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 242: { host: 16; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 243: { host: 16; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 244: { host: 16; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 245: { host: 16; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 246: { host: 16; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 247: { host: 16; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 248: { host: 16; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 249: { host: 16; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 250: { host: 16; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 251: { host: 16; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 252: { host: 16; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 253: { host: 16; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 254: { host: 16; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 255: { host: 16; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3850164: in cluster Done - -Job was submitted from host by user in cluster at Wed Aug 10 15:19:04 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Wed Aug 10 15:42:47 2022 - <40*lassen107> - <40*lassen768> - <40*lassen140> - <40*lassen667> - <40*lassen338> - <40*lassen182> - <40*lassen668> - <40*lassen669> - <40*lassen821> - <40*lassen825> - <40*lassen524> - <40*lassen529> - <40*lassen576> - <40*lassen383> - <40*lassen573> - <40*lassen544> - was used as the home directory. - was used as the working directory. -Started at Wed Aug 10 15:42:47 2022 -Terminated at Wed Aug 10 15:43:00 2022 -Results reported at Wed Aug 10 15:43:00 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J bruck_n16_ppn16 -#BSUB -e bruck_n16_ppn16.%J.err -#BSUB -o bruck_n16_ppn16.%J.out -#BSUB -nnodes 16 -#BSUB -q pbatch -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a16 -c16 -r1 -n16 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.18 sec. - Max Memory : - - Average Memory : - - Total Requested Memory : - - Delta Memory : - - Max Swap : - - Max Processes : - - Max Threads : - - Run time : 12 sec. - Turnaround time : 1436 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/bruck_allgather/bruck_n16_ppn4.3850166.out b/benchmark_tests/lassen/bruck_allgather/bruck_n16_ppn4.3850166.out deleted file mode 100644 index cd82f1d65..000000000 --- a/benchmark_tests/lassen/bruck_allgather/bruck_n16_ppn4.3850166.out +++ /dev/null @@ -1,167 +0,0 @@ -Testing Size 1 -PMPI_Allgather Time 1.419631e-05 -allgather_bruck Time 1.614772e-05 -allgather_loc_bruck Time 1.142898e-05 -allgather_hier_bruck Time 1.388181e-05 -allgather_mult_hier_bruck Time 1.091554e-05 -Testing Size 2 -PMPI_Allgather Time 1.351331e-05 -allgather_bruck Time 1.443491e-05 -allgather_loc_bruck Time 1.048200e-05 -allgather_hier_bruck Time 1.503446e-05 -allgather_mult_hier_bruck Time 1.224388e-05 -Testing Size 4 -PMPI_Allgather Time 1.185340e-05 -allgather_bruck Time 1.601713e-05 -allgather_loc_bruck Time 1.103031e-05 -allgather_hier_bruck Time 1.607396e-05 -allgather_mult_hier_bruck Time 1.639517e-05 -Testing Size 8 -PMPI_Allgather Time 1.149245e-05 -allgather_bruck Time 1.840335e-05 -allgather_loc_bruck Time 1.652291e-05 -allgather_hier_bruck Time 2.274527e-05 -allgather_mult_hier_bruck Time 1.703384e-05 -Testing Size 16 -PMPI_Allgather Time 1.222389e-05 -allgather_bruck Time 2.388525e-05 -allgather_loc_bruck Time 1.593518e-05 -allgather_hier_bruck Time 2.175137e-05 -allgather_mult_hier_bruck Time 2.351718e-05 -app 0: ./bruck_allgather -rank: 0: { host: 1; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 4: { host: 2; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 5: { host: 2; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 6: { host: 2; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 7: { host: 2; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 8: { host: 3; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 9: { host: 3; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 10: { host: 3; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 11: { host: 3; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 12: { host: 4; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 13: { host: 4; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 14: { host: 4; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 15: { host: 4; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 16: { host: 5; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 17: { host: 5; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 18: { host: 5; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 19: { host: 5; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 20: { host: 6; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 21: { host: 6; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 22: { host: 6; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 23: { host: 6; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 24: { host: 7; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 25: { host: 7; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 26: { host: 7; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 27: { host: 7; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 28: { host: 8; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 29: { host: 8; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 30: { host: 8; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 31: { host: 8; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 32: { host: 9; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 33: { host: 9; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 34: { host: 9; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 35: { host: 9; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 36: { host: 10; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 37: { host: 10; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 38: { host: 10; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 39: { host: 10; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 40: { host: 11; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 41: { host: 11; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 42: { host: 11; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 43: { host: 11; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 44: { host: 12; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 45: { host: 12; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 46: { host: 12; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 47: { host: 12; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 48: { host: 13; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 49: { host: 13; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 50: { host: 13; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 51: { host: 13; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 52: { host: 14; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 53: { host: 14; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 54: { host: 14; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 55: { host: 14; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 56: { host: 15; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 57: { host: 15; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 58: { host: 15; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 59: { host: 15; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 60: { host: 16; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 61: { host: 16; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 62: { host: 16; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 63: { host: 16; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3850166: in cluster Done - -Job was submitted from host by user in cluster at Wed Aug 10 15:19:33 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Wed Aug 10 15:43:23 2022 - <40*lassen107> - <40*lassen768> - <40*lassen140> - <40*lassen667> - <40*lassen338> - <40*lassen182> - <40*lassen668> - <40*lassen669> - <40*lassen821> - <40*lassen825> - <40*lassen524> - <40*lassen529> - <40*lassen576> - <40*lassen383> - <40*lassen573> - <40*lassen544> - was used as the home directory. - was used as the working directory. -Started at Wed Aug 10 15:43:23 2022 -Terminated at Wed Aug 10 15:43:33 2022 -Results reported at Wed Aug 10 15:43:33 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J bruck_n16_ppn4 -#BSUB -e bruck_n16_ppn4.%J.err -#BSUB -o bruck_n16_ppn4.%J.out -#BSUB -nnodes 16 -#BSUB -q pbatch -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n16 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.19 sec. - Max Memory : - - Average Memory : - - Total Requested Memory : - - Delta Memory : - - Max Swap : - - Max Processes : - - Max Threads : - - Run time : 10 sec. - Turnaround time : 1440 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/bruck_allgather/bruck_n32_ppn32.3850163.out b/benchmark_tests/lassen/bruck_allgather/bruck_n32_ppn32.3850163.out deleted file mode 100644 index f1cf95d38..000000000 --- a/benchmark_tests/lassen/bruck_allgather/bruck_n32_ppn32.3850163.out +++ /dev/null @@ -1,1143 +0,0 @@ -Testing Size 1 -PMPI_Allgather Time 3.265007e-05 -allgather_bruck Time 6.669274e-05 -allgather_loc_bruck Time 2.671478e-05 -allgather_hier_bruck Time 4.799123e-05 -allgather_mult_hier_bruck Time 3.494048e-05 -Testing Size 2 -PMPI_Allgather Time 4.424768e-05 -allgather_bruck Time 9.014198e-05 -allgather_loc_bruck Time 3.466918e-05 -allgather_hier_bruck Time 5.442615e-05 -allgather_mult_hier_bruck Time 4.777386e-05 -Testing Size 4 -PMPI_Allgather Time 5.641474e-05 -allgather_bruck Time 1.575861e-04 -allgather_loc_bruck Time 4.939389e-05 -allgather_hier_bruck Time 5.302419e-05 -allgather_mult_hier_bruck Time 8.365143e-05 -Testing Size 8 -PMPI_Allgather Time 5.874884e-05 -allgather_bruck Time 2.942817e-04 -allgather_loc_bruck Time 7.128660e-05 -allgather_hier_bruck Time 7.203634e-05 -allgather_mult_hier_bruck Time 1.360758e-04 -Testing Size 16 -PMPI_Allgather Time 8.384884e-05 -allgather_bruck Time 5.704894e-04 -allgather_loc_bruck Time 1.133530e-04 -allgather_hier_bruck Time 1.034919e-04 -allgather_mult_hier_bruck Time 2.268318e-04 -app 0: ./bruck_allgather -rank: 0: { host: 1; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 4: { host: 1; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 5: { host: 1; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 6: { host: 1; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 7: { host: 1; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 8: { host: 1; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 9: { host: 1; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 10: { host: 1; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 11: { host: 1; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 12: { host: 1; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 13: { host: 1; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 14: { host: 1; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 15: { host: 1; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 16: { host: 1; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 17: { host: 1; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 18: { host: 1; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 19: { host: 1; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 20: { host: 1; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 21: { host: 1; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 22: { host: 1; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 23: { host: 1; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 24: { host: 1; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 25: { host: 1; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 26: { host: 1; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 27: { host: 1; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 28: { host: 1; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 29: { host: 1; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 30: { host: 1; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 31: { host: 1; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 32: { host: 2; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 33: { host: 2; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 34: { host: 2; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 35: { host: 2; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 36: { host: 2; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 37: { host: 2; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 38: { host: 2; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 39: { host: 2; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 40: { host: 2; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 41: { host: 2; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 42: { host: 2; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 43: { host: 2; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 44: { host: 2; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 45: { host: 2; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 46: { host: 2; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 47: { host: 2; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 48: { host: 2; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 49: { host: 2; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 50: { host: 2; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 51: { host: 2; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 52: { host: 2; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 53: { host: 2; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 54: { host: 2; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 55: { host: 2; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 56: { host: 2; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 57: { host: 2; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 58: { host: 2; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 59: { host: 2; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 60: { host: 2; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 61: { host: 2; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 62: { host: 2; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 63: { host: 2; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 64: { host: 3; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 65: { host: 3; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 66: { host: 3; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 67: { host: 3; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 68: { host: 3; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 69: { host: 3; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 70: { host: 3; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 71: { host: 3; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 72: { host: 3; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 73: { host: 3; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 74: { host: 3; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 75: { host: 3; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 76: { host: 3; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 77: { host: 3; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 78: { host: 3; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 79: { host: 3; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 80: { host: 3; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 81: { host: 3; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 82: { host: 3; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 83: { host: 3; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 84: { host: 3; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 85: { host: 3; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 86: { host: 3; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 87: { host: 3; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 88: { host: 3; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 89: { host: 3; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 90: { host: 3; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 91: { host: 3; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 92: { host: 3; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 93: { host: 3; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 94: { host: 3; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 95: { host: 3; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 96: { host: 4; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 97: { host: 4; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 98: { host: 4; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 99: { host: 4; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 100: { host: 4; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 101: { host: 4; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 102: { host: 4; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 103: { host: 4; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 104: { host: 4; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 105: { host: 4; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 106: { host: 4; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 107: { host: 4; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 108: { host: 4; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 109: { host: 4; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 110: { host: 4; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 111: { host: 4; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 112: { host: 4; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 113: { host: 4; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 114: { host: 4; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 115: { host: 4; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 116: { host: 4; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 117: { host: 4; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 118: { host: 4; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 119: { host: 4; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 120: { host: 4; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 121: { host: 4; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 122: { host: 4; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 123: { host: 4; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 124: { host: 4; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 125: { host: 4; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 126: { host: 4; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 127: { host: 4; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 128: { host: 5; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 129: { host: 5; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 130: { host: 5; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 131: { host: 5; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 132: { host: 5; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 133: { host: 5; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 134: { host: 5; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 135: { host: 5; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 136: { host: 5; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 137: { host: 5; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 138: { host: 5; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 139: { host: 5; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 140: { host: 5; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 141: { host: 5; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 142: { host: 5; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 143: { host: 5; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 144: { host: 5; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 145: { host: 5; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 146: { host: 5; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 147: { host: 5; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 148: { host: 5; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 149: { host: 5; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 150: { host: 5; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 151: { host: 5; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 152: { host: 5; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 153: { host: 5; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 154: { host: 5; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 155: { host: 5; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 156: { host: 5; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 157: { host: 5; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 158: { host: 5; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 159: { host: 5; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 160: { host: 6; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 161: { host: 6; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 162: { host: 6; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 163: { host: 6; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 164: { host: 6; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 165: { host: 6; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 166: { host: 6; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 167: { host: 6; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 168: { host: 6; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 169: { host: 6; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 170: { host: 6; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 171: { host: 6; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 172: { host: 6; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 173: { host: 6; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 174: { host: 6; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 175: { host: 6; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 176: { host: 6; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 177: { host: 6; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 178: { host: 6; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 179: { host: 6; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 180: { host: 6; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 181: { host: 6; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 182: { host: 6; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 183: { host: 6; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 184: { host: 6; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 185: { host: 6; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 186: { host: 6; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 187: { host: 6; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 188: { host: 6; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 189: { host: 6; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 190: { host: 6; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 191: { host: 6; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 192: { host: 7; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 193: { host: 7; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 194: { host: 7; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 195: { host: 7; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 196: { host: 7; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 197: { host: 7; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 198: { host: 7; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 199: { host: 7; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 200: { host: 7; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 201: { host: 7; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 202: { host: 7; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 203: { host: 7; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 204: { host: 7; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 205: { host: 7; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 206: { host: 7; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 207: { host: 7; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 208: { host: 7; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 209: { host: 7; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 210: { host: 7; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 211: { host: 7; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 212: { host: 7; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 213: { host: 7; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 214: { host: 7; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 215: { host: 7; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 216: { host: 7; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 217: { host: 7; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 218: { host: 7; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 219: { host: 7; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 220: { host: 7; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 221: { host: 7; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 222: { host: 7; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 223: { host: 7; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 224: { host: 8; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 225: { host: 8; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 226: { host: 8; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 227: { host: 8; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 228: { host: 8; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 229: { host: 8; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 230: { host: 8; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 231: { host: 8; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 232: { host: 8; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 233: { host: 8; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 234: { host: 8; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 235: { host: 8; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 236: { host: 8; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 237: { host: 8; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 238: { host: 8; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 239: { host: 8; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 240: { host: 8; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 241: { host: 8; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 242: { host: 8; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 243: { host: 8; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 244: { host: 8; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 245: { host: 8; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 246: { host: 8; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 247: { host: 8; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 248: { host: 8; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 249: { host: 8; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 250: { host: 8; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 251: { host: 8; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 252: { host: 8; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 253: { host: 8; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 254: { host: 8; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 255: { host: 8; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 256: { host: 9; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 257: { host: 9; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 258: { host: 9; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 259: { host: 9; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 260: { host: 9; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 261: { host: 9; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 262: { host: 9; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 263: { host: 9; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 264: { host: 9; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 265: { host: 9; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 266: { host: 9; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 267: { host: 9; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 268: { host: 9; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 269: { host: 9; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 270: { host: 9; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 271: { host: 9; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 272: { host: 9; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 273: { host: 9; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 274: { host: 9; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 275: { host: 9; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 276: { host: 9; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 277: { host: 9; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 278: { host: 9; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 279: { host: 9; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 280: { host: 9; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 281: { host: 9; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 282: { host: 9; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 283: { host: 9; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 284: { host: 9; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 285: { host: 9; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 286: { host: 9; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 287: { host: 9; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 288: { host: 10; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 289: { host: 10; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 290: { host: 10; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 291: { host: 10; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 292: { host: 10; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 293: { host: 10; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 294: { host: 10; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 295: { host: 10; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 296: { host: 10; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 297: { host: 10; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 298: { host: 10; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 299: { host: 10; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 300: { host: 10; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 301: { host: 10; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 302: { host: 10; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 303: { host: 10; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 304: { host: 10; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 305: { host: 10; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 306: { host: 10; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 307: { host: 10; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 308: { host: 10; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 309: { host: 10; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 310: { host: 10; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 311: { host: 10; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 312: { host: 10; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 313: { host: 10; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 314: { host: 10; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 315: { host: 10; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 316: { host: 10; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 317: { host: 10; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 318: { host: 10; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 319: { host: 10; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 320: { host: 11; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 321: { host: 11; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 322: { host: 11; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 323: { host: 11; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 324: { host: 11; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 325: { host: 11; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 326: { host: 11; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 327: { host: 11; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 328: { host: 11; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 329: { host: 11; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 330: { host: 11; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 331: { host: 11; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 332: { host: 11; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 333: { host: 11; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 334: { host: 11; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 335: { host: 11; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 336: { host: 11; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 337: { host: 11; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 338: { host: 11; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 339: { host: 11; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 340: { host: 11; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 341: { host: 11; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 342: { host: 11; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 343: { host: 11; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 344: { host: 11; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 345: { host: 11; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 346: { host: 11; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 347: { host: 11; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 348: { host: 11; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 349: { host: 11; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 350: { host: 11; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 351: { host: 11; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 352: { host: 12; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 353: { host: 12; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 354: { host: 12; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 355: { host: 12; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 356: { host: 12; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 357: { host: 12; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 358: { host: 12; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 359: { host: 12; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 360: { host: 12; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 361: { host: 12; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 362: { host: 12; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 363: { host: 12; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 364: { host: 12; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 365: { host: 12; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 366: { host: 12; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 367: { host: 12; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 368: { host: 12; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 369: { host: 12; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 370: { host: 12; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 371: { host: 12; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 372: { host: 12; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 373: { host: 12; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 374: { host: 12; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 375: { host: 12; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 376: { host: 12; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 377: { host: 12; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 378: { host: 12; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 379: { host: 12; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 380: { host: 12; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 381: { host: 12; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 382: { host: 12; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 383: { host: 12; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 384: { host: 13; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 385: { host: 13; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 386: { host: 13; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 387: { host: 13; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 388: { host: 13; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 389: { host: 13; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 390: { host: 13; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 391: { host: 13; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 392: { host: 13; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 393: { host: 13; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 394: { host: 13; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 395: { host: 13; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 396: { host: 13; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 397: { host: 13; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 398: { host: 13; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 399: { host: 13; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 400: { host: 13; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 401: { host: 13; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 402: { host: 13; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 403: { host: 13; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 404: { host: 13; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 405: { host: 13; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 406: { host: 13; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 407: { host: 13; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 408: { host: 13; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 409: { host: 13; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 410: { host: 13; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 411: { host: 13; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 412: { host: 13; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 413: { host: 13; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 414: { host: 13; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 415: { host: 13; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 416: { host: 14; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 417: { host: 14; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 418: { host: 14; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 419: { host: 14; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 420: { host: 14; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 421: { host: 14; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 422: { host: 14; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 423: { host: 14; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 424: { host: 14; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 425: { host: 14; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 426: { host: 14; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 427: { host: 14; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 428: { host: 14; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 429: { host: 14; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 430: { host: 14; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 431: { host: 14; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 432: { host: 14; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 433: { host: 14; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 434: { host: 14; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 435: { host: 14; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 436: { host: 14; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 437: { host: 14; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 438: { host: 14; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 439: { host: 14; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 440: { host: 14; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 441: { host: 14; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 442: { host: 14; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 443: { host: 14; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 444: { host: 14; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 445: { host: 14; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 446: { host: 14; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 447: { host: 14; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 448: { host: 15; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 449: { host: 15; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 450: { host: 15; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 451: { host: 15; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 452: { host: 15; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 453: { host: 15; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 454: { host: 15; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 455: { host: 15; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 456: { host: 15; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 457: { host: 15; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 458: { host: 15; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 459: { host: 15; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 460: { host: 15; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 461: { host: 15; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 462: { host: 15; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 463: { host: 15; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 464: { host: 15; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 465: { host: 15; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 466: { host: 15; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 467: { host: 15; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 468: { host: 15; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 469: { host: 15; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 470: { host: 15; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 471: { host: 15; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 472: { host: 15; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 473: { host: 15; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 474: { host: 15; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 475: { host: 15; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 476: { host: 15; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 477: { host: 15; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 478: { host: 15; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 479: { host: 15; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 480: { host: 16; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 481: { host: 16; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 482: { host: 16; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 483: { host: 16; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 484: { host: 16; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 485: { host: 16; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 486: { host: 16; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 487: { host: 16; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 488: { host: 16; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 489: { host: 16; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 490: { host: 16; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 491: { host: 16; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 492: { host: 16; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 493: { host: 16; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 494: { host: 16; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 495: { host: 16; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 496: { host: 16; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 497: { host: 16; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 498: { host: 16; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 499: { host: 16; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 500: { host: 16; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 501: { host: 16; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 502: { host: 16; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 503: { host: 16; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 504: { host: 16; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 505: { host: 16; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 506: { host: 16; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 507: { host: 16; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 508: { host: 16; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 509: { host: 16; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 510: { host: 16; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 511: { host: 16; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 512: { host: 17; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 513: { host: 17; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 514: { host: 17; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 515: { host: 17; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 516: { host: 17; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 517: { host: 17; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 518: { host: 17; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 519: { host: 17; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 520: { host: 17; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 521: { host: 17; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 522: { host: 17; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 523: { host: 17; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 524: { host: 17; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 525: { host: 17; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 526: { host: 17; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 527: { host: 17; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 528: { host: 17; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 529: { host: 17; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 530: { host: 17; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 531: { host: 17; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 532: { host: 17; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 533: { host: 17; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 534: { host: 17; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 535: { host: 17; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 536: { host: 17; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 537: { host: 17; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 538: { host: 17; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 539: { host: 17; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 540: { host: 17; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 541: { host: 17; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 542: { host: 17; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 543: { host: 17; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 544: { host: 18; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 545: { host: 18; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 546: { host: 18; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 547: { host: 18; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 548: { host: 18; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 549: { host: 18; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 550: { host: 18; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 551: { host: 18; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 552: { host: 18; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 553: { host: 18; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 554: { host: 18; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 555: { host: 18; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 556: { host: 18; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 557: { host: 18; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 558: { host: 18; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 559: { host: 18; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 560: { host: 18; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 561: { host: 18; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 562: { host: 18; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 563: { host: 18; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 564: { host: 18; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 565: { host: 18; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 566: { host: 18; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 567: { host: 18; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 568: { host: 18; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 569: { host: 18; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 570: { host: 18; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 571: { host: 18; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 572: { host: 18; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 573: { host: 18; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 574: { host: 18; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 575: { host: 18; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 576: { host: 19; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 577: { host: 19; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 578: { host: 19; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 579: { host: 19; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 580: { host: 19; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 581: { host: 19; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 582: { host: 19; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 583: { host: 19; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 584: { host: 19; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 585: { host: 19; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 586: { host: 19; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 587: { host: 19; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 588: { host: 19; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 589: { host: 19; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 590: { host: 19; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 591: { host: 19; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 592: { host: 19; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 593: { host: 19; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 594: { host: 19; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 595: { host: 19; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 596: { host: 19; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 597: { host: 19; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 598: { host: 19; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 599: { host: 19; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 600: { host: 19; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 601: { host: 19; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 602: { host: 19; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 603: { host: 19; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 604: { host: 19; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 605: { host: 19; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 606: { host: 19; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 607: { host: 19; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 608: { host: 20; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 609: { host: 20; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 610: { host: 20; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 611: { host: 20; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 612: { host: 20; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 613: { host: 20; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 614: { host: 20; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 615: { host: 20; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 616: { host: 20; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 617: { host: 20; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 618: { host: 20; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 619: { host: 20; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 620: { host: 20; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 621: { host: 20; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 622: { host: 20; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 623: { host: 20; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 624: { host: 20; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 625: { host: 20; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 626: { host: 20; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 627: { host: 20; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 628: { host: 20; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 629: { host: 20; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 630: { host: 20; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 631: { host: 20; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 632: { host: 20; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 633: { host: 20; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 634: { host: 20; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 635: { host: 20; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 636: { host: 20; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 637: { host: 20; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 638: { host: 20; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 639: { host: 20; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 640: { host: 21; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 641: { host: 21; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 642: { host: 21; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 643: { host: 21; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 644: { host: 21; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 645: { host: 21; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 646: { host: 21; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 647: { host: 21; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 648: { host: 21; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 649: { host: 21; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 650: { host: 21; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 651: { host: 21; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 652: { host: 21; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 653: { host: 21; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 654: { host: 21; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 655: { host: 21; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 656: { host: 21; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 657: { host: 21; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 658: { host: 21; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 659: { host: 21; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 660: { host: 21; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 661: { host: 21; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 662: { host: 21; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 663: { host: 21; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 664: { host: 21; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 665: { host: 21; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 666: { host: 21; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 667: { host: 21; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 668: { host: 21; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 669: { host: 21; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 670: { host: 21; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 671: { host: 21; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 672: { host: 22; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 673: { host: 22; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 674: { host: 22; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 675: { host: 22; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 676: { host: 22; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 677: { host: 22; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 678: { host: 22; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 679: { host: 22; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 680: { host: 22; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 681: { host: 22; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 682: { host: 22; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 683: { host: 22; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 684: { host: 22; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 685: { host: 22; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 686: { host: 22; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 687: { host: 22; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 688: { host: 22; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 689: { host: 22; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 690: { host: 22; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 691: { host: 22; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 692: { host: 22; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 693: { host: 22; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 694: { host: 22; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 695: { host: 22; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 696: { host: 22; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 697: { host: 22; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 698: { host: 22; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 699: { host: 22; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 700: { host: 22; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 701: { host: 22; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 702: { host: 22; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 703: { host: 22; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 704: { host: 23; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 705: { host: 23; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 706: { host: 23; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 707: { host: 23; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 708: { host: 23; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 709: { host: 23; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 710: { host: 23; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 711: { host: 23; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 712: { host: 23; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 713: { host: 23; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 714: { host: 23; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 715: { host: 23; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 716: { host: 23; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 717: { host: 23; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 718: { host: 23; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 719: { host: 23; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 720: { host: 23; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 721: { host: 23; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 722: { host: 23; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 723: { host: 23; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 724: { host: 23; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 725: { host: 23; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 726: { host: 23; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 727: { host: 23; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 728: { host: 23; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 729: { host: 23; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 730: { host: 23; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 731: { host: 23; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 732: { host: 23; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 733: { host: 23; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 734: { host: 23; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 735: { host: 23; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 736: { host: 24; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 737: { host: 24; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 738: { host: 24; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 739: { host: 24; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 740: { host: 24; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 741: { host: 24; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 742: { host: 24; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 743: { host: 24; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 744: { host: 24; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 745: { host: 24; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 746: { host: 24; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 747: { host: 24; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 748: { host: 24; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 749: { host: 24; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 750: { host: 24; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 751: { host: 24; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 752: { host: 24; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 753: { host: 24; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 754: { host: 24; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 755: { host: 24; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 756: { host: 24; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 757: { host: 24; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 758: { host: 24; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 759: { host: 24; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 760: { host: 24; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 761: { host: 24; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 762: { host: 24; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 763: { host: 24; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 764: { host: 24; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 765: { host: 24; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 766: { host: 24; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 767: { host: 24; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 768: { host: 25; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 769: { host: 25; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 770: { host: 25; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 771: { host: 25; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 772: { host: 25; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 773: { host: 25; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 774: { host: 25; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 775: { host: 25; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 776: { host: 25; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 777: { host: 25; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 778: { host: 25; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 779: { host: 25; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 780: { host: 25; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 781: { host: 25; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 782: { host: 25; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 783: { host: 25; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 784: { host: 25; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 785: { host: 25; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 786: { host: 25; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 787: { host: 25; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 788: { host: 25; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 789: { host: 25; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 790: { host: 25; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 791: { host: 25; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 792: { host: 25; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 793: { host: 25; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 794: { host: 25; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 795: { host: 25; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 796: { host: 25; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 797: { host: 25; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 798: { host: 25; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 799: { host: 25; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 800: { host: 26; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 801: { host: 26; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 802: { host: 26; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 803: { host: 26; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 804: { host: 26; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 805: { host: 26; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 806: { host: 26; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 807: { host: 26; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 808: { host: 26; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 809: { host: 26; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 810: { host: 26; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 811: { host: 26; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 812: { host: 26; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 813: { host: 26; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 814: { host: 26; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 815: { host: 26; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 816: { host: 26; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 817: { host: 26; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 818: { host: 26; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 819: { host: 26; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 820: { host: 26; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 821: { host: 26; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 822: { host: 26; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 823: { host: 26; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 824: { host: 26; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 825: { host: 26; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 826: { host: 26; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 827: { host: 26; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 828: { host: 26; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 829: { host: 26; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 830: { host: 26; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 831: { host: 26; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 832: { host: 27; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 833: { host: 27; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 834: { host: 27; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 835: { host: 27; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 836: { host: 27; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 837: { host: 27; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 838: { host: 27; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 839: { host: 27; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 840: { host: 27; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 841: { host: 27; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 842: { host: 27; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 843: { host: 27; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 844: { host: 27; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 845: { host: 27; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 846: { host: 27; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 847: { host: 27; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 848: { host: 27; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 849: { host: 27; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 850: { host: 27; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 851: { host: 27; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 852: { host: 27; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 853: { host: 27; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 854: { host: 27; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 855: { host: 27; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 856: { host: 27; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 857: { host: 27; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 858: { host: 27; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 859: { host: 27; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 860: { host: 27; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 861: { host: 27; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 862: { host: 27; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 863: { host: 27; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 864: { host: 28; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 865: { host: 28; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 866: { host: 28; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 867: { host: 28; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 868: { host: 28; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 869: { host: 28; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 870: { host: 28; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 871: { host: 28; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 872: { host: 28; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 873: { host: 28; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 874: { host: 28; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 875: { host: 28; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 876: { host: 28; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 877: { host: 28; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 878: { host: 28; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 879: { host: 28; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 880: { host: 28; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 881: { host: 28; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 882: { host: 28; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 883: { host: 28; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 884: { host: 28; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 885: { host: 28; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 886: { host: 28; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 887: { host: 28; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 888: { host: 28; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 889: { host: 28; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 890: { host: 28; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 891: { host: 28; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 892: { host: 28; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 893: { host: 28; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 894: { host: 28; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 895: { host: 28; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 896: { host: 29; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 897: { host: 29; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 898: { host: 29; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 899: { host: 29; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 900: { host: 29; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 901: { host: 29; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 902: { host: 29; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 903: { host: 29; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 904: { host: 29; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 905: { host: 29; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 906: { host: 29; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 907: { host: 29; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 908: { host: 29; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 909: { host: 29; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 910: { host: 29; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 911: { host: 29; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 912: { host: 29; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 913: { host: 29; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 914: { host: 29; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 915: { host: 29; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 916: { host: 29; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 917: { host: 29; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 918: { host: 29; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 919: { host: 29; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 920: { host: 29; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 921: { host: 29; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 922: { host: 29; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 923: { host: 29; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 924: { host: 29; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 925: { host: 29; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 926: { host: 29; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 927: { host: 29; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 928: { host: 30; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 929: { host: 30; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 930: { host: 30; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 931: { host: 30; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 932: { host: 30; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 933: { host: 30; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 934: { host: 30; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 935: { host: 30; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 936: { host: 30; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 937: { host: 30; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 938: { host: 30; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 939: { host: 30; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 940: { host: 30; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 941: { host: 30; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 942: { host: 30; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 943: { host: 30; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 944: { host: 30; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 945: { host: 30; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 946: { host: 30; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 947: { host: 30; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 948: { host: 30; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 949: { host: 30; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 950: { host: 30; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 951: { host: 30; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 952: { host: 30; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 953: { host: 30; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 954: { host: 30; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 955: { host: 30; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 956: { host: 30; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 957: { host: 30; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 958: { host: 30; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 959: { host: 30; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 960: { host: 31; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 961: { host: 31; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 962: { host: 31; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 963: { host: 31; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 964: { host: 31; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 965: { host: 31; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 966: { host: 31; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 967: { host: 31; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 968: { host: 31; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 969: { host: 31; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 970: { host: 31; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 971: { host: 31; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 972: { host: 31; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 973: { host: 31; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 974: { host: 31; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 975: { host: 31; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 976: { host: 31; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 977: { host: 31; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 978: { host: 31; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 979: { host: 31; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 980: { host: 31; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 981: { host: 31; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 982: { host: 31; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 983: { host: 31; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 984: { host: 31; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 985: { host: 31; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 986: { host: 31; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 987: { host: 31; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 988: { host: 31; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 989: { host: 31; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 990: { host: 31; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 991: { host: 31; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 -rank: 992: { host: 32; cpu: {0-3}, {4-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 993: { host: 32; cpu: {4-7}, {0-3,8-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 994: { host: 32; cpu: {8-11}, {0-7,12-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 995: { host: 32; cpu: {12-15}, {0-11,16-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 996: { host: 32; cpu: {16-19}, {0-15,20-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 997: { host: 32; cpu: {20-23}, {0-19,24-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 998: { host: 32; cpu: {24-27}, {0-23,28-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 999: { host: 32; cpu: {28-31}, {0-27,32-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1000: { host: 32; cpu: {32-35}, {0-31,36-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1001: { host: 32; cpu: {36-39}, {0-35,40-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1002: { host: 32; cpu: {40-43}, {0-39,44-47,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1003: { host: 32; cpu: {44-47}, {0-43,80-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1004: { host: 32; cpu: {80-83}, {0-47,84-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1005: { host: 32; cpu: {84-87}, {0-47,80-83,88-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1006: { host: 32; cpu: {88-91}, {0-47,80-87,92-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1007: { host: 32; cpu: {92-95}, {0-47,80-91,96-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1008: { host: 32; cpu: {96-99}, {0-47,80-95,100-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1009: { host: 32; cpu: {100-103}, {0-47,80-99,104-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1010: { host: 32; cpu: {104-107}, {0-47,80-103,108-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1011: { host: 32; cpu: {108-111}, {0-47,80-107,112-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1012: { host: 32; cpu: {112-115}, {0-47,80-111,116-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1013: { host: 32; cpu: {116-119}, {0-47,80-115,120-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1014: { host: 32; cpu: {120-123}, {0-47,80-119,124-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1015: { host: 32; cpu: {124-127}, {0-47,80-123,128-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1016: { host: 32; cpu: {128-131}, {0-47,80-127,132-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1017: { host: 32; cpu: {132-135}, {0-47,80-131,136-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1018: { host: 32; cpu: {136-139}, {0-47,80-135,140-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1019: { host: 32; cpu: {140-143}, {0-47,80-139,144-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1020: { host: 32; cpu: {144-147}, {0-47,80-143,148-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1021: { host: 32; cpu: {148-151}, {0-47,80-147,152-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1022: { host: 32; cpu: {152-155}, {0-47,80-151,156-159} ; mem: {0-97811,163036-326055} } : app 0 -rank: 1023: { host: 32; cpu: {156-159}, {0-47,80-155} ; mem: {0-97811,163036-326055} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3850163: in cluster Done - -Job was submitted from host by user in cluster at Wed Aug 10 15:18:45 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Wed Aug 10 16:00:48 2022 - <40*lassen100> - <40*lassen762> - <40*lassen107> - <40*lassen768> - <40*lassen769> - <40*lassen113> - <40*lassen442> - <40*lassen772> - <40*lassen293> - <40*lassen140> - <40*lassen667> - <40*lassen338> - <40*lassen182> - <40*lassen668> - <40*lassen669> - <40*lassen821> - <40*lassen825> - <40*lassen512> - <40*lassen524> - <40*lassen529> - <40*lassen576> - <40*lassen383> - <40*lassen573> - <40*lassen39> - <40*lassen212> - <40*lassen544> - <40*lassen545> - <40*lassen46> - <40*lassen47> - <40*lassen49> - <40*lassen550> - <40*lassen69> - was used as the home directory. - was used as the working directory. -Started at Wed Aug 10 16:00:48 2022 -Terminated at Wed Aug 10 16:01:30 2022 -Results reported at Wed Aug 10 16:01:30 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J bruck_n32_ppn32 -#BSUB -e bruck_n32_ppn32.%J.err -#BSUB -o bruck_n32_ppn32.%J.out -#BSUB -nnodes 32 -#BSUB -q pbatch -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a32 -c32 -r1 -n32 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 2.00 sec. - Max Memory : 46 MB - Average Memory : 46.00 MB - Total Requested Memory : - - Delta Memory : - - Max Swap : 1382 MB - Max Processes : 2 - Max Threads : 23 - Run time : 42 sec. - Turnaround time : 2565 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/bruck_allgather/bruck_n4_ppn4.3850165.out b/benchmark_tests/lassen/bruck_allgather/bruck_n4_ppn4.3850165.out deleted file mode 100644 index 75e028d05..000000000 --- a/benchmark_tests/lassen/bruck_allgather/bruck_n4_ppn4.3850165.out +++ /dev/null @@ -1,107 +0,0 @@ -Testing Size 1 -PMPI_Allgather Time 5.062580e-06 -allgather_bruck Time 9.841030e-06 -allgather_loc_bruck Time 7.819450e-06 -allgather_hier_bruck Time 7.605290e-06 -allgather_mult_hier_bruck Time 6.406370e-06 -Testing Size 2 -PMPI_Allgather Time 7.078480e-06 -allgather_bruck Time 6.945730e-06 -allgather_loc_bruck Time 4.899170e-06 -allgather_hier_bruck Time 8.009980e-06 -allgather_mult_hier_bruck Time 5.991410e-06 -Testing Size 4 -PMPI_Allgather Time 5.385020e-06 -allgather_bruck Time 8.669770e-06 -allgather_loc_bruck Time 5.784600e-06 -allgather_hier_bruck Time 9.532710e-06 -allgather_mult_hier_bruck Time 6.745500e-06 -Testing Size 8 -PMPI_Allgather Time 4.902230e-06 -allgather_bruck Time 9.957980e-06 -allgather_loc_bruck Time 7.437570e-06 -allgather_hier_bruck Time 1.192791e-05 -allgather_mult_hier_bruck Time 7.610870e-06 -Testing Size 16 -PMPI_Allgather Time 5.240090e-06 -allgather_bruck Time 1.189100e-05 -allgather_loc_bruck Time 8.103620e-06 -allgather_hier_bruck Time 1.265706e-05 -allgather_mult_hier_bruck Time 9.882510e-06 -app 0: ./bruck_allgather -rank: 0: { host: 1; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 4: { host: 2; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 5: { host: 2; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 6: { host: 2; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 7: { host: 2; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 8: { host: 3; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 9: { host: 3; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 10: { host: 3; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 11: { host: 3; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 12: { host: 4; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 13: { host: 4; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 14: { host: 4; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 15: { host: 4; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3850165: in cluster Done - -Job was submitted from host by user in cluster at Wed Aug 10 15:19:17 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Wed Aug 10 15:19:20 2022 - <40*lassen782> - <40*lassen576> - <40*lassen383> - <40*lassen573> - was used as the home directory. - was used as the working directory. -Started at Wed Aug 10 15:19:20 2022 -Terminated at Wed Aug 10 15:19:28 2022 -Results reported at Wed Aug 10 15:19:28 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J bruck_n4_ppn4 -#BSUB -e bruck_n4_ppn4.%J.err -#BSUB -o bruck_n4_ppn4.%J.out -#BSUB -nnodes 4 -#BSUB -q pbatch -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n4 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.17 sec. - Max Memory : 213 MB - Average Memory : 213.00 MB - Total Requested Memory : - - Delta Memory : - - Max Swap : 650 MB - Max Processes : 1 - Max Threads : 1 - Run time : 8 sec. - Turnaround time : 11 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/bruck_allgather/bruck_n64_ppn4.3850167.out b/benchmark_tests/lassen/bruck_allgather/bruck_n64_ppn4.3850167.out deleted file mode 100644 index 12c21b743..000000000 --- a/benchmark_tests/lassen/bruck_allgather/bruck_n64_ppn4.3850167.out +++ /dev/null @@ -1,407 +0,0 @@ -Testing Size 1 -PMPI_Allgather Time 2.634701e-05 -allgather_bruck Time 2.284433e-05 -allgather_loc_bruck Time 1.999439e-05 -allgather_hier_bruck Time 2.358274e-05 -allgather_mult_hier_bruck Time 1.942514e-05 -Testing Size 2 -PMPI_Allgather Time 2.271362e-05 -allgather_bruck Time 2.691103e-05 -allgather_loc_bruck Time 1.897984e-05 -allgather_hier_bruck Time 2.652152e-05 -allgather_mult_hier_bruck Time 2.361070e-05 -Testing Size 4 -PMPI_Allgather Time 2.883978e-05 -allgather_bruck Time 3.363753e-05 -allgather_loc_bruck Time 2.113655e-05 -allgather_hier_bruck Time 2.990115e-05 -allgather_mult_hier_bruck Time 2.950169e-05 -Testing Size 8 -PMPI_Allgather Time 3.556168e-05 -allgather_bruck Time 4.002429e-05 -allgather_loc_bruck Time 2.702085e-05 -allgather_hier_bruck Time 3.680562e-05 -allgather_mult_hier_bruck Time 3.926074e-05 -Testing Size 16 -PMPI_Allgather Time 5.117181e-05 -allgather_bruck Time 5.467228e-05 -allgather_loc_bruck Time 3.953899e-05 -allgather_hier_bruck Time 4.522020e-05 -allgather_mult_hier_bruck Time 6.745060e-05 -app 0: ./bruck_allgather -rank: 0: { host: 1; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 4: { host: 2; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 5: { host: 2; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 6: { host: 2; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 7: { host: 2; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 8: { host: 3; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 9: { host: 3; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 10: { host: 3; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 11: { host: 3; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 12: { host: 4; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 13: { host: 4; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 14: { host: 4; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 15: { host: 4; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 16: { host: 5; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 17: { host: 5; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 18: { host: 5; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 19: { host: 5; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 20: { host: 6; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 21: { host: 6; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 22: { host: 6; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 23: { host: 6; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 24: { host: 7; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 25: { host: 7; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 26: { host: 7; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 27: { host: 7; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 28: { host: 8; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 29: { host: 8; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 30: { host: 8; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 31: { host: 8; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 32: { host: 9; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 33: { host: 9; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 34: { host: 9; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 35: { host: 9; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 36: { host: 10; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 37: { host: 10; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 38: { host: 10; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 39: { host: 10; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 40: { host: 11; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 41: { host: 11; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 42: { host: 11; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 43: { host: 11; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 44: { host: 12; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 45: { host: 12; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 46: { host: 12; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 47: { host: 12; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 48: { host: 13; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 49: { host: 13; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 50: { host: 13; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 51: { host: 13; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 52: { host: 14; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 53: { host: 14; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 54: { host: 14; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 55: { host: 14; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 56: { host: 15; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 57: { host: 15; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 58: { host: 15; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 59: { host: 15; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 60: { host: 16; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 61: { host: 16; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 62: { host: 16; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 63: { host: 16; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 64: { host: 17; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 65: { host: 17; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 66: { host: 17; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 67: { host: 17; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 68: { host: 18; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 69: { host: 18; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 70: { host: 18; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 71: { host: 18; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 72: { host: 19; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 73: { host: 19; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 74: { host: 19; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 75: { host: 19; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 76: { host: 20; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 77: { host: 20; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 78: { host: 20; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 79: { host: 20; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 80: { host: 21; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 81: { host: 21; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 82: { host: 21; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 83: { host: 21; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 84: { host: 22; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 85: { host: 22; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 86: { host: 22; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 87: { host: 22; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 88: { host: 23; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 89: { host: 23; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 90: { host: 23; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 91: { host: 23; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 92: { host: 24; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 93: { host: 24; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 94: { host: 24; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 95: { host: 24; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 96: { host: 25; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 97: { host: 25; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 98: { host: 25; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 99: { host: 25; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 100: { host: 26; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 101: { host: 26; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 102: { host: 26; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 103: { host: 26; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 104: { host: 27; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 105: { host: 27; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 106: { host: 27; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 107: { host: 27; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 108: { host: 28; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 109: { host: 28; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 110: { host: 28; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 111: { host: 28; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 112: { host: 29; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 113: { host: 29; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 114: { host: 29; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 115: { host: 29; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 116: { host: 30; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 117: { host: 30; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 118: { host: 30; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 119: { host: 30; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 120: { host: 31; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 121: { host: 31; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 122: { host: 31; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 123: { host: 31; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 124: { host: 32; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 125: { host: 32; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 126: { host: 32; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 127: { host: 32; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 128: { host: 33; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 129: { host: 33; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 130: { host: 33; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 131: { host: 33; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 132: { host: 34; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 133: { host: 34; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 134: { host: 34; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 135: { host: 34; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 136: { host: 35; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 137: { host: 35; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 138: { host: 35; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 139: { host: 35; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 140: { host: 36; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 141: { host: 36; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 142: { host: 36; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 143: { host: 36; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 144: { host: 37; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 145: { host: 37; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 146: { host: 37; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 147: { host: 37; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 148: { host: 38; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 149: { host: 38; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 150: { host: 38; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 151: { host: 38; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 152: { host: 39; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 153: { host: 39; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 154: { host: 39; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 155: { host: 39; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 156: { host: 40; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 157: { host: 40; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 158: { host: 40; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 159: { host: 40; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 160: { host: 41; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 161: { host: 41; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 162: { host: 41; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 163: { host: 41; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 164: { host: 42; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 165: { host: 42; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 166: { host: 42; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 167: { host: 42; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 168: { host: 43; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 169: { host: 43; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 170: { host: 43; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 171: { host: 43; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 172: { host: 44; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 173: { host: 44; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 174: { host: 44; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 175: { host: 44; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 176: { host: 45; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 177: { host: 45; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 178: { host: 45; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 179: { host: 45; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 180: { host: 46; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 181: { host: 46; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 182: { host: 46; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 183: { host: 46; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 184: { host: 47; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 185: { host: 47; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 186: { host: 47; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 187: { host: 47; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 188: { host: 48; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 189: { host: 48; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 190: { host: 48; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 191: { host: 48; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 192: { host: 49; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 193: { host: 49; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 194: { host: 49; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 195: { host: 49; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 196: { host: 50; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 197: { host: 50; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 198: { host: 50; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 199: { host: 50; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 200: { host: 51; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 201: { host: 51; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 202: { host: 51; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 203: { host: 51; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 204: { host: 52; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 205: { host: 52; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 206: { host: 52; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 207: { host: 52; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 208: { host: 53; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 209: { host: 53; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 210: { host: 53; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 211: { host: 53; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 212: { host: 54; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 213: { host: 54; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 214: { host: 54; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 215: { host: 54; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 216: { host: 55; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 217: { host: 55; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 218: { host: 55; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 219: { host: 55; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 220: { host: 56; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 221: { host: 56; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 222: { host: 56; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 223: { host: 56; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 224: { host: 57; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 225: { host: 57; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 226: { host: 57; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 227: { host: 57; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 228: { host: 58; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 229: { host: 58; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 230: { host: 58; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 231: { host: 58; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 232: { host: 59; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 233: { host: 59; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 234: { host: 59; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 235: { host: 59; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 236: { host: 60; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 237: { host: 60; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 238: { host: 60; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 239: { host: 60; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 240: { host: 61; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 241: { host: 61; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 242: { host: 61; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 243: { host: 61; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 244: { host: 62; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 245: { host: 62; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 246: { host: 62; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 247: { host: 62; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 248: { host: 63; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 249: { host: 63; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 250: { host: 63; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 251: { host: 63; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 252: { host: 64; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 253: { host: 64; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 254: { host: 64; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 255: { host: 64; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3850167: in cluster Done - -Job was submitted from host by user in cluster at Wed Aug 10 15:19:43 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Wed Aug 10 16:31:57 2022 - <40*lassen412> - <40*lassen418> - <40*lassen263> - <40*lassen93> - <40*lassen428> - <40*lassen429> - <40*lassen759> - <40*lassen100> - <40*lassen104> - <40*lassen762> - <40*lassen763> - <40*lassen107> - <40*lassen768> - <40*lassen769> - <40*lassen441> - <40*lassen770> - <40*lassen113> - <40*lassen442> - <40*lassen771> - <40*lassen443> - <40*lassen772> - <40*lassen446> - <40*lassen775> - <40*lassen777> - <40*lassen293> - <40*lassen790> - <40*lassen134> - <40*lassen135> - <40*lassen140> - <40*lassen470> - <40*lassen142> - <40*lassen471> - <40*lassen633> - <40*lassen667> - <40*lassen338> - <40*lassen182> - <40*lassen668> - <40*lassen669> - <40*lassen821> - <40*lassen825> - <40*lassen199> - <40*lassen512> - <40*lassen514> - <40*lassen363> - <40*lassen524> - <40*lassen529> - <40*lassen374> - <40*lassen576> - <40*lassen200> - <40*lassen201> - <40*lassen202> - <40*lassen203> - <40*lassen204> - <40*lassen205> - <40*lassen206> - <40*lassen207> - <40*lassen208> - <40*lassen209> - <40*lassen383> - <40*lassen388> - <40*lassen389> - <40*lassen210> - <40*lassen393> - <40*lassen395> - was used as the home directory. - was used as the working directory. -Started at Wed Aug 10 16:31:57 2022 -Terminated at Wed Aug 10 16:32:12 2022 -Results reported at Wed Aug 10 16:32:12 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J bruck_n64_ppn4 -#BSUB -e bruck_n64_ppn4.%J.err -#BSUB -o bruck_n64_ppn4.%J.out -#BSUB -nnodes 64 -#BSUB -q pbatch -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n64 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.21 sec. - Max Memory : 253 MB - Average Memory : 253.00 MB - Total Requested Memory : - - Delta Memory : - - Max Swap : 682 MB - Max Processes : 1 - Max Threads : 2 - Run time : 14 sec. - Turnaround time : 4349 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/loc_alltoall_n4_ppn4.3930387.out b/benchmark_tests/lassen/loc_alltoall_n4_ppn4.3930387.out deleted file mode 100644 index 83e5bc70d..000000000 --- a/benchmark_tests/lassen/loc_alltoall_n4_ppn4.3930387.out +++ /dev/null @@ -1,123 +0,0 @@ -Testing Size 1 -PMPI_Alltoall Time 1.172267e-05 -MPIX_Alltoall Time 1.145548e-05 -Testing Size 2 -PMPI_Alltoall Time 6.773480e-06 -MPIX_Alltoall Time 7.280030e-06 -Testing Size 4 -PMPI_Alltoall Time 7.656770e-06 -MPIX_Alltoall Time 8.213830e-06 -Testing Size 8 -PMPI_Alltoall Time 8.406900e-06 -MPIX_Alltoall Time 9.724360e-06 -Testing Size 16 -PMPI_Alltoall Time 9.035360e-06 -MPIX_Alltoall Time 1.206371e-05 -Testing Size 32 -PMPI_Alltoall Time 1.094501e-05 -MPIX_Alltoall Time 1.617601e-05 -Testing Size 64 -PMPI_Alltoall Time 1.006640e-05 -MPIX_Alltoall Time 2.432211e-05 -Testing Size 128 -PMPI_Alltoall Time 1.156332e-05 -MPIX_Alltoall Time 4.248499e-05 -Testing Size 256 -PMPI_Alltoall Time 1.432788e-05 -MPIX_Alltoall Time 7.292451e-05 -Testing Size 512 -PMPI_Alltoall Time 2.370915e-05 -MPIX_Alltoall Time 1.432300e-04 -Testing Size 1024 -PMPI_Alltoall Time 4.532413e-05 -MPIX_Alltoall Time 2.849408e-04 -Testing Size 2048 -PMPI_Alltoall Time 8.191000e-05 -MPIX_Alltoall Time 7.582655e-04 -Testing Size 4096 -PMPI_Alltoall Time 1.604125e-04 -MPIX_Alltoall Time 1.202044e-03 -Testing Size 8192 -PMPI_Alltoall Time 3.162174e-04 -MPIX_Alltoall Time 2.285120e-03 -Testing Size 16384 -PMPI_Alltoall Time 8.373790e-04 -MPIX_Alltoall Time 4.459829e-03 -app 0: ./p2p_alltoall -rank: 0: { host: 1; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 4: { host: 2; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 5: { host: 2; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 6: { host: 2; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 7: { host: 2; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 8: { host: 3; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 9: { host: 3; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 10: { host: 3; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 11: { host: 3; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 12: { host: 4; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 13: { host: 4; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 14: { host: 4; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 15: { host: 4; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3930387: in cluster Done - -Job was submitted from host by user in cluster at Tue Sep 20 13:41:06 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Tue Sep 20 13:41:09 2022 - <40*lassen27> - <40*lassen26> - <40*lassen25> - <40*lassen8> - was used as the home directory. - was used as the working directory. -Started at Tue Sep 20 13:41:09 2022 -Terminated at Tue Sep 20 13:41:18 2022 -Results reported at Tue Sep 20 13:41:18 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J loc_alltoall_n4_ppn4 -#BSUB -e loc_alltoall_n4_ppn4.%J.err -#BSUB -o loc_alltoall_n4_ppn4.%J.out -#BSUB -nnodes 4 -#BSUB -q pdebug -##BSUB -q pbatch -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n4 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./p2p_alltoall - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.19 sec. - Max Memory : - - Average Memory : - - Total Requested Memory : - - Delta Memory : - - Max Swap : - - Max Processes : - - Max Threads : - - Run time : 9 sec. - Turnaround time : 12 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/loc_alltoallv_n16_ppn16.3930839.out b/benchmark_tests/lassen/loc_alltoallv_n16_ppn16.3930839.out deleted file mode 100644 index 6f805a1f0..000000000 --- a/benchmark_tests/lassen/loc_alltoallv_n16_ppn16.3930839.out +++ /dev/null @@ -1,375 +0,0 @@ -Testing Size 1 -PMPI_Alltoallv Time 3.624133e-04 -MPIX_Alltoallv Time 4.396937e-05 -Testing Size 2 -PMPI_Alltoallv Time 3.281724e-04 -MPIX_Alltoallv Time 6.171120e-05 -Testing Size 4 -PMPI_Alltoallv Time 3.529237e-04 -MPIX_Alltoallv Time 6.878883e-05 -Testing Size 8 -PMPI_Alltoallv Time 6.619081e-04 -MPIX_Alltoallv Time 9.185969e-05 -Testing Size 16 -PMPI_Alltoallv Time 6.401256e-04 -MPIX_Alltoallv Time 1.439687e-04 -Testing Size 32 -PMPI_Alltoallv Time 7.631137e-04 -MPIX_Alltoallv Time 2.496543e-04 -Testing Size 64 -PMPI_Alltoallv Time 8.168502e-04 -MPIX_Alltoallv Time 4.799150e-04 -Testing Size 128 -PMPI_Alltoallv Time 9.539845e-04 -MPIX_Alltoallv Time 1.382853e-03 -Testing Size 256 -PMPI_Alltoallv Time 1.241662e-03 -MPIX_Alltoallv Time 2.522828e-03 -Testing Size 512 -PMPI_Alltoallv Time 2.183684e-03 -MPIX_Alltoallv Time 4.624466e-03 -Testing Size 1024 -PMPI_Alltoallv Time 4.416573e-03 -MPIX_Alltoallv Time 8.334900e-03 -Testing Size 2048 -PMPI_Alltoallv Time 9.540338e-03 -MPIX_Alltoallv Time 1.591855e-02 -Testing Size 4096 -PMPI_Alltoallv Time 2.042508e-02 -MPIX_Alltoallv Time 3.021802e-02 -Testing Size 8192 -PMPI_Alltoallv Time 4.283172e-02 -MPIX_Alltoallv Time 5.979274e-02 -Testing Size 16384 -PMPI_Alltoallv Time 8.365350e-02 -MPIX_Alltoallv Time 1.235682e-01 -app 0: ./p2p_alltoallv -rank: 0: { host: 1; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 4: { host: 1; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 5: { host: 1; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 6: { host: 1; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 7: { host: 1; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 8: { host: 1; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 9: { host: 1; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 10: { host: 1; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 11: { host: 1; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 12: { host: 1; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 13: { host: 1; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 14: { host: 1; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 15: { host: 1; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 16: { host: 2; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 17: { host: 2; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 18: { host: 2; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 19: { host: 2; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 20: { host: 2; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 21: { host: 2; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 22: { host: 2; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 23: { host: 2; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 24: { host: 2; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 25: { host: 2; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 26: { host: 2; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 27: { host: 2; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 28: { host: 2; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 29: { host: 2; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 30: { host: 2; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 31: { host: 2; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 32: { host: 3; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 33: { host: 3; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 34: { host: 3; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 35: { host: 3; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 36: { host: 3; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 37: { host: 3; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 38: { host: 3; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 39: { host: 3; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 40: { host: 3; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 41: { host: 3; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 42: { host: 3; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 43: { host: 3; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 44: { host: 3; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 45: { host: 3; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 46: { host: 3; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 47: { host: 3; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 48: { host: 4; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 49: { host: 4; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 50: { host: 4; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 51: { host: 4; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 52: { host: 4; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 53: { host: 4; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 54: { host: 4; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 55: { host: 4; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 56: { host: 4; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 57: { host: 4; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 58: { host: 4; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 59: { host: 4; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 60: { host: 4; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 61: { host: 4; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 62: { host: 4; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 63: { host: 4; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 64: { host: 5; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 65: { host: 5; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 66: { host: 5; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 67: { host: 5; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 68: { host: 5; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 69: { host: 5; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 70: { host: 5; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 71: { host: 5; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 72: { host: 5; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 73: { host: 5; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 74: { host: 5; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 75: { host: 5; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 76: { host: 5; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 77: { host: 5; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 78: { host: 5; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 79: { host: 5; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 80: { host: 6; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 81: { host: 6; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 82: { host: 6; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 83: { host: 6; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 84: { host: 6; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 85: { host: 6; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 86: { host: 6; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 87: { host: 6; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 88: { host: 6; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 89: { host: 6; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 90: { host: 6; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 91: { host: 6; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 92: { host: 6; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 93: { host: 6; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 94: { host: 6; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 95: { host: 6; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 96: { host: 7; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 97: { host: 7; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 98: { host: 7; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 99: { host: 7; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 100: { host: 7; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 101: { host: 7; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 102: { host: 7; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 103: { host: 7; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 104: { host: 7; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 105: { host: 7; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 106: { host: 7; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 107: { host: 7; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 108: { host: 7; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 109: { host: 7; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 110: { host: 7; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 111: { host: 7; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 112: { host: 8; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 113: { host: 8; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 114: { host: 8; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 115: { host: 8; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 116: { host: 8; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 117: { host: 8; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 118: { host: 8; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 119: { host: 8; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 120: { host: 8; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 121: { host: 8; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 122: { host: 8; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 123: { host: 8; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 124: { host: 8; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 125: { host: 8; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 126: { host: 8; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 127: { host: 8; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 128: { host: 9; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 129: { host: 9; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 130: { host: 9; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 131: { host: 9; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 132: { host: 9; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 133: { host: 9; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 134: { host: 9; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 135: { host: 9; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 136: { host: 9; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 137: { host: 9; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 138: { host: 9; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 139: { host: 9; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 140: { host: 9; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 141: { host: 9; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 142: { host: 9; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 143: { host: 9; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 144: { host: 10; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 145: { host: 10; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 146: { host: 10; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 147: { host: 10; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 148: { host: 10; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 149: { host: 10; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 150: { host: 10; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 151: { host: 10; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 152: { host: 10; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 153: { host: 10; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 154: { host: 10; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 155: { host: 10; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 156: { host: 10; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 157: { host: 10; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 158: { host: 10; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 159: { host: 10; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 160: { host: 11; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 161: { host: 11; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 162: { host: 11; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 163: { host: 11; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 164: { host: 11; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 165: { host: 11; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 166: { host: 11; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 167: { host: 11; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 168: { host: 11; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 169: { host: 11; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 170: { host: 11; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 171: { host: 11; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 172: { host: 11; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 173: { host: 11; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 174: { host: 11; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 175: { host: 11; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 176: { host: 12; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 177: { host: 12; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 178: { host: 12; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 179: { host: 12; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 180: { host: 12; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 181: { host: 12; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 182: { host: 12; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 183: { host: 12; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 184: { host: 12; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 185: { host: 12; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 186: { host: 12; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 187: { host: 12; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 188: { host: 12; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 189: { host: 12; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 190: { host: 12; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 191: { host: 12; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 192: { host: 13; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 193: { host: 13; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 194: { host: 13; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 195: { host: 13; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 196: { host: 13; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 197: { host: 13; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 198: { host: 13; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 199: { host: 13; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 200: { host: 13; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 201: { host: 13; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 202: { host: 13; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 203: { host: 13; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 204: { host: 13; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 205: { host: 13; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 206: { host: 13; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 207: { host: 13; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 208: { host: 14; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 209: { host: 14; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 210: { host: 14; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 211: { host: 14; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 212: { host: 14; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 213: { host: 14; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 214: { host: 14; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 215: { host: 14; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 216: { host: 14; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 217: { host: 14; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 218: { host: 14; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 219: { host: 14; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 220: { host: 14; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 221: { host: 14; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 222: { host: 14; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 223: { host: 14; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 224: { host: 15; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 225: { host: 15; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 226: { host: 15; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 227: { host: 15; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 228: { host: 15; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 229: { host: 15; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 230: { host: 15; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 231: { host: 15; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 232: { host: 15; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 233: { host: 15; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 234: { host: 15; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 235: { host: 15; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 236: { host: 15; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 237: { host: 15; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 238: { host: 15; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 239: { host: 15; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 240: { host: 16; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 241: { host: 16; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 242: { host: 16; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 243: { host: 16; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 244: { host: 16; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 245: { host: 16; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 246: { host: 16; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 247: { host: 16; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 248: { host: 16; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 249: { host: 16; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 250: { host: 16; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 251: { host: 16; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 252: { host: 16; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 253: { host: 16; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 254: { host: 16; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 255: { host: 16; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3930839: in cluster Done - -Job was submitted from host by user in cluster at Tue Sep 20 15:07:29 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Tue Sep 20 16:08:02 2022 - <40*lassen808> - <40*lassen665> - <40*lassen336> - <40*lassen343> - <40*lassen195> - <40*lassen680> - <40*lassen507> - <40*lassen353> - <40*lassen357> - <40*lassen510> - <40*lassen516> - <40*lassen367> - <40*lassen249> - <40*lassen578> - <40*lassen540> - <40*lassen546> - was used as the home directory. - was used as the working directory. -Started at Tue Sep 20 16:08:02 2022 -Terminated at Tue Sep 20 16:09:39 2022 -Results reported at Tue Sep 20 16:09:39 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J loc_alltoall_n16_ppn16 -#BSUB -e loc_alltoall_n16_ppn16.%J.err -#BSUB -o loc_alltoall_n16_ppn16.%J.out -#BSUB -nnodes 16 -#BSUB -q pbatch -##BSUB -q pdebug -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a16 -c16 -r1 -n16 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./p2p_alltoallv - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 11.00 sec. - Max Memory : 62 MB - Average Memory : 57.50 MB - Total Requested Memory : - - Delta Memory : - - Max Swap : 1426 MB - Max Processes : 4 - Max Threads : 27 - Run time : 97 sec. - Turnaround time : 3730 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/loc_alltoallv_n32_ppn16.3936300.out b/benchmark_tests/lassen/loc_alltoallv_n32_ppn16.3936300.out deleted file mode 100644 index e3e2de6a2..000000000 --- a/benchmark_tests/lassen/loc_alltoallv_n32_ppn16.3936300.out +++ /dev/null @@ -1,602 +0,0 @@ -app 0: ./p2p_alltoallv -rank: 0: { host: 1; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 4: { host: 1; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 5: { host: 1; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 6: { host: 1; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 7: { host: 1; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 8: { host: 1; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 9: { host: 1; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 10: { host: 1; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 11: { host: 1; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 12: { host: 1; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 13: { host: 1; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 14: { host: 1; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 15: { host: 1; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 16: { host: 2; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 17: { host: 2; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 18: { host: 2; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 19: { host: 2; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 20: { host: 2; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 21: { host: 2; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 22: { host: 2; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 23: { host: 2; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 24: { host: 2; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 25: { host: 2; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 26: { host: 2; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 27: { host: 2; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 28: { host: 2; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 29: { host: 2; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 30: { host: 2; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 31: { host: 2; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 32: { host: 3; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 33: { host: 3; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 34: { host: 3; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 35: { host: 3; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 36: { host: 3; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 37: { host: 3; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 38: { host: 3; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 39: { host: 3; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 40: { host: 3; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 41: { host: 3; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 42: { host: 3; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 43: { host: 3; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 44: { host: 3; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 45: { host: 3; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 46: { host: 3; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 47: { host: 3; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 48: { host: 4; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 49: { host: 4; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 50: { host: 4; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 51: { host: 4; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 52: { host: 4; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 53: { host: 4; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 54: { host: 4; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 55: { host: 4; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 56: { host: 4; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 57: { host: 4; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 58: { host: 4; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 59: { host: 4; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 60: { host: 4; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 61: { host: 4; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 62: { host: 4; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 63: { host: 4; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 64: { host: 5; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 65: { host: 5; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 66: { host: 5; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 67: { host: 5; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 68: { host: 5; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 69: { host: 5; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 70: { host: 5; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 71: { host: 5; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 72: { host: 5; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 73: { host: 5; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 74: { host: 5; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 75: { host: 5; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 76: { host: 5; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 77: { host: 5; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 78: { host: 5; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 79: { host: 5; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 80: { host: 6; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 81: { host: 6; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 82: { host: 6; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 83: { host: 6; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 84: { host: 6; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 85: { host: 6; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 86: { host: 6; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 87: { host: 6; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 88: { host: 6; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 89: { host: 6; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 90: { host: 6; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 91: { host: 6; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 92: { host: 6; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 93: { host: 6; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 94: { host: 6; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 95: { host: 6; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 96: { host: 7; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 97: { host: 7; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 98: { host: 7; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 99: { host: 7; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 100: { host: 7; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 101: { host: 7; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 102: { host: 7; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 103: { host: 7; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 104: { host: 7; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 105: { host: 7; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 106: { host: 7; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 107: { host: 7; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 108: { host: 7; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 109: { host: 7; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 110: { host: 7; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 111: { host: 7; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 112: { host: 8; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 113: { host: 8; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 114: { host: 8; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 115: { host: 8; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 116: { host: 8; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 117: { host: 8; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 118: { host: 8; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 119: { host: 8; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 120: { host: 8; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 121: { host: 8; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 122: { host: 8; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 123: { host: 8; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 124: { host: 8; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 125: { host: 8; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 126: { host: 8; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 127: { host: 8; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 128: { host: 9; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 129: { host: 9; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 130: { host: 9; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 131: { host: 9; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 132: { host: 9; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 133: { host: 9; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 134: { host: 9; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 135: { host: 9; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 136: { host: 9; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 137: { host: 9; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 138: { host: 9; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 139: { host: 9; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 140: { host: 9; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 141: { host: 9; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 142: { host: 9; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 143: { host: 9; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 144: { host: 10; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 145: { host: 10; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 146: { host: 10; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 147: { host: 10; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 148: { host: 10; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 149: { host: 10; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 150: { host: 10; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 151: { host: 10; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 152: { host: 10; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 153: { host: 10; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 154: { host: 10; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 155: { host: 10; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 156: { host: 10; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 157: { host: 10; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 158: { host: 10; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 159: { host: 10; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 160: { host: 11; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 161: { host: 11; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 162: { host: 11; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 163: { host: 11; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 164: { host: 11; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 165: { host: 11; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 166: { host: 11; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 167: { host: 11; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 168: { host: 11; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 169: { host: 11; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 170: { host: 11; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 171: { host: 11; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 172: { host: 11; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 173: { host: 11; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 174: { host: 11; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 175: { host: 11; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 176: { host: 12; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 177: { host: 12; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 178: { host: 12; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 179: { host: 12; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 180: { host: 12; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 181: { host: 12; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 182: { host: 12; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 183: { host: 12; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 184: { host: 12; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 185: { host: 12; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 186: { host: 12; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 187: { host: 12; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 188: { host: 12; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 189: { host: 12; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 190: { host: 12; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 191: { host: 12; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 192: { host: 13; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 193: { host: 13; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 194: { host: 13; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 195: { host: 13; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 196: { host: 13; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 197: { host: 13; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 198: { host: 13; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 199: { host: 13; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 200: { host: 13; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 201: { host: 13; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 202: { host: 13; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 203: { host: 13; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 204: { host: 13; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 205: { host: 13; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 206: { host: 13; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 207: { host: 13; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 208: { host: 14; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 209: { host: 14; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 210: { host: 14; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 211: { host: 14; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 212: { host: 14; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 213: { host: 14; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 214: { host: 14; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 215: { host: 14; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 216: { host: 14; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 217: { host: 14; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 218: { host: 14; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 219: { host: 14; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 220: { host: 14; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 221: { host: 14; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 222: { host: 14; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 223: { host: 14; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 224: { host: 15; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 225: { host: 15; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 226: { host: 15; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 227: { host: 15; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 228: { host: 15; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 229: { host: 15; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 230: { host: 15; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 231: { host: 15; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 232: { host: 15; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 233: { host: 15; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 234: { host: 15; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 235: { host: 15; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 236: { host: 15; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 237: { host: 15; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 238: { host: 15; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 239: { host: 15; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 240: { host: 16; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 241: { host: 16; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 242: { host: 16; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 243: { host: 16; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 244: { host: 16; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 245: { host: 16; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 246: { host: 16; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 247: { host: 16; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 248: { host: 16; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 249: { host: 16; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 250: { host: 16; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 251: { host: 16; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 252: { host: 16; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 253: { host: 16; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 254: { host: 16; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 255: { host: 16; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 256: { host: 17; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 257: { host: 17; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 258: { host: 17; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 259: { host: 17; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 260: { host: 17; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 261: { host: 17; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 262: { host: 17; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 263: { host: 17; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 264: { host: 17; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 265: { host: 17; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 266: { host: 17; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 267: { host: 17; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 268: { host: 17; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 269: { host: 17; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 270: { host: 17; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 271: { host: 17; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 272: { host: 18; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 273: { host: 18; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 274: { host: 18; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 275: { host: 18; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 276: { host: 18; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 277: { host: 18; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 278: { host: 18; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 279: { host: 18; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 280: { host: 18; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 281: { host: 18; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 282: { host: 18; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 283: { host: 18; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 284: { host: 18; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 285: { host: 18; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 286: { host: 18; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 287: { host: 18; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 288: { host: 19; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 289: { host: 19; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 290: { host: 19; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 291: { host: 19; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 292: { host: 19; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 293: { host: 19; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 294: { host: 19; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 295: { host: 19; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 296: { host: 19; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 297: { host: 19; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 298: { host: 19; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 299: { host: 19; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 300: { host: 19; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 301: { host: 19; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 302: { host: 19; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 303: { host: 19; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 304: { host: 20; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 305: { host: 20; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 306: { host: 20; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 307: { host: 20; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 308: { host: 20; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 309: { host: 20; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 310: { host: 20; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 311: { host: 20; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 312: { host: 20; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 313: { host: 20; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 314: { host: 20; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 315: { host: 20; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 316: { host: 20; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 317: { host: 20; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 318: { host: 20; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 319: { host: 20; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 320: { host: 21; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 321: { host: 21; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 322: { host: 21; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 323: { host: 21; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 324: { host: 21; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 325: { host: 21; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 326: { host: 21; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 327: { host: 21; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 328: { host: 21; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 329: { host: 21; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 330: { host: 21; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 331: { host: 21; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 332: { host: 21; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 333: { host: 21; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 334: { host: 21; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 335: { host: 21; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 336: { host: 22; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 337: { host: 22; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 338: { host: 22; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 339: { host: 22; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 340: { host: 22; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 341: { host: 22; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 342: { host: 22; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 343: { host: 22; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 344: { host: 22; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 345: { host: 22; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 346: { host: 22; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 347: { host: 22; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 348: { host: 22; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 349: { host: 22; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 350: { host: 22; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 351: { host: 22; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 352: { host: 23; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 353: { host: 23; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 354: { host: 23; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 355: { host: 23; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 356: { host: 23; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 357: { host: 23; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 358: { host: 23; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 359: { host: 23; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 360: { host: 23; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 361: { host: 23; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 362: { host: 23; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 363: { host: 23; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 364: { host: 23; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 365: { host: 23; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 366: { host: 23; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 367: { host: 23; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 368: { host: 24; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 369: { host: 24; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 370: { host: 24; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 371: { host: 24; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 372: { host: 24; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 373: { host: 24; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 374: { host: 24; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 375: { host: 24; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 376: { host: 24; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 377: { host: 24; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 378: { host: 24; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 379: { host: 24; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 380: { host: 24; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 381: { host: 24; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 382: { host: 24; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 383: { host: 24; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 384: { host: 25; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 385: { host: 25; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 386: { host: 25; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 387: { host: 25; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 388: { host: 25; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 389: { host: 25; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 390: { host: 25; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 391: { host: 25; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 392: { host: 25; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 393: { host: 25; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 394: { host: 25; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 395: { host: 25; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 396: { host: 25; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 397: { host: 25; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 398: { host: 25; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 399: { host: 25; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 400: { host: 26; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 401: { host: 26; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 402: { host: 26; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 403: { host: 26; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 404: { host: 26; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 405: { host: 26; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 406: { host: 26; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 407: { host: 26; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 408: { host: 26; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 409: { host: 26; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 410: { host: 26; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 411: { host: 26; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 412: { host: 26; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 413: { host: 26; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 414: { host: 26; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 415: { host: 26; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 416: { host: 27; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 417: { host: 27; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 418: { host: 27; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 419: { host: 27; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 420: { host: 27; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 421: { host: 27; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 422: { host: 27; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 423: { host: 27; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 424: { host: 27; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 425: { host: 27; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 426: { host: 27; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 427: { host: 27; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 428: { host: 27; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 429: { host: 27; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 430: { host: 27; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 431: { host: 27; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 432: { host: 28; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 433: { host: 28; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 434: { host: 28; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 435: { host: 28; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 436: { host: 28; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 437: { host: 28; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 438: { host: 28; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 439: { host: 28; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 440: { host: 28; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 441: { host: 28; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 442: { host: 28; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 443: { host: 28; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 444: { host: 28; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 445: { host: 28; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 446: { host: 28; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 447: { host: 28; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 448: { host: 29; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 449: { host: 29; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 450: { host: 29; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 451: { host: 29; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 452: { host: 29; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 453: { host: 29; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 454: { host: 29; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 455: { host: 29; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 456: { host: 29; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 457: { host: 29; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 458: { host: 29; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 459: { host: 29; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 460: { host: 29; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 461: { host: 29; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 462: { host: 29; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 463: { host: 29; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 464: { host: 30; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 465: { host: 30; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 466: { host: 30; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 467: { host: 30; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 468: { host: 30; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 469: { host: 30; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 470: { host: 30; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 471: { host: 30; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 472: { host: 30; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 473: { host: 30; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 474: { host: 30; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 475: { host: 30; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 476: { host: 30; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 477: { host: 30; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 478: { host: 30; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 479: { host: 30; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 480: { host: 31; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 481: { host: 31; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 482: { host: 31; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 483: { host: 31; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 484: { host: 31; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 485: { host: 31; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 486: { host: 31; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 487: { host: 31; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 488: { host: 31; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 489: { host: 31; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 490: { host: 31; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 491: { host: 31; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 492: { host: 31; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 493: { host: 31; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 494: { host: 31; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 495: { host: 31; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 496: { host: 32; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 497: { host: 32; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 498: { host: 32; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 499: { host: 32; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 500: { host: 32; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 501: { host: 32; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 502: { host: 32; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 503: { host: 32; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 504: { host: 32; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 505: { host: 32; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 506: { host: 32; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 507: { host: 32; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 508: { host: 32; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 509: { host: 32; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 510: { host: 32; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 511: { host: 32; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3936300: in cluster Exited - -Job was submitted from host by user in cluster at Thu Sep 22 12:01:12 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Thu Sep 22 12:12:46 2022 - <40*lassen268> - <40*lassen276> - <40*lassen287> - <40*lassen605> - <40*lassen127> - <40*lassen611> - <40*lassen154> - <40*lassen163> - <40*lassen165> - <40*lassen329> - <40*lassen816> - <40*lassen338> - <40*lassen669> - <40*lassen187> - <40*lassen188> - <40*lassen820> - <40*lassen500> - <40*lassen508> - <40*lassen731> - <40*lassen511> - <40*lassen401> - <40*lassen512> - <40*lassen730> - <40*lassen515> - <40*lassen361> - <40*lassen519> - <40*lassen363> - <40*lassen364> - <40*lassen365> - <40*lassen366> - <40*lassen367> - <40*lassen369> - was used as the home directory. - was used as the working directory. -Started at Thu Sep 22 12:12:46 2022 -Terminated at Thu Sep 22 12:13:29 2022 -Results reported at Thu Sep 22 12:13:29 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J loc_alltoallv_n32_ppn16 -#BSUB -e loc_alltoallv_n32_ppn16.%J.err -#BSUB -o loc_alltoallv_n32_ppn16.%J.out -#BSUB -nnodes 32 -#BSUB -q pbatch -##BSUB -q pdebug -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a16 -c16 -r1 -n32 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./p2p_alltoallv - - - ------------------------------------------------------------- - -Exited with exit code 15. - -Resource usage summary: - - CPU time : 5.00 sec. - Max Memory : 57 MB - Average Memory : 52.43 MB - Total Requested Memory : - - Delta Memory : - - Max Swap : 1382 MB - Max Processes : 5 - Max Threads : 23 - Run time : 43 sec. - Turnaround time : 737 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/loc_alltoallv_n4_ppn16.3936301.out b/benchmark_tests/lassen/loc_alltoallv_n4_ppn16.3936301.out deleted file mode 100644 index e711935e0..000000000 --- a/benchmark_tests/lassen/loc_alltoallv_n4_ppn16.3936301.out +++ /dev/null @@ -1,171 +0,0 @@ -Testing Size 1 -PMPI_Alltoallv Time 6.300334e-05 -MPIX_Alltoallv Time 3.320819e-05 -Testing Size 2 -PMPI_Alltoallv Time 5.054956e-05 -MPIX_Alltoallv Time 3.817481e-05 -Testing Size 4 -PMPI_Alltoallv Time 5.126899e-05 -MPIX_Alltoallv Time 4.533742e-05 -Testing Size 8 -PMPI_Alltoallv Time 1.110714e-04 -MPIX_Alltoallv Time 6.234362e-05 -Testing Size 16 -PMPI_Alltoallv Time 1.060387e-04 -MPIX_Alltoallv Time 9.025399e-05 -Testing Size 32 -PMPI_Alltoallv Time 1.346562e-04 -MPIX_Alltoallv Time 1.538806e-04 -Testing Size 64 -PMPI_Alltoallv Time 1.511502e-04 -MPIX_Alltoallv Time 2.726524e-04 -Testing Size 128 -PMPI_Alltoallv Time 1.723730e-04 -MPIX_Alltoallv Time 5.600992e-04 -Testing Size 256 -PMPI_Alltoallv Time 2.282821e-04 -MPIX_Alltoallv Time 1.279991e-03 -Testing Size 512 -PMPI_Alltoallv Time 3.690835e-04 -MPIX_Alltoallv Time 2.400896e-03 -Testing Size 1024 -PMPI_Alltoallv Time 6.827285e-04 -MPIX_Alltoallv Time 4.263352e-03 -Testing Size 2048 -PMPI_Alltoallv Time 1.461860e-03 -MPIX_Alltoallv Time 8.574374e-03 -Testing Size 4096 -PMPI_Alltoallv Time 3.420348e-03 -MPIX_Alltoallv Time 1.750791e-02 -Testing Size 8192 -PMPI_Alltoallv Time 7.517743e-03 -MPIX_Alltoallv Time 3.551125e-02 -Testing Size 16384 -PMPI_Alltoallv Time 1.483285e-02 -MPIX_Alltoallv Time 6.836832e-02 -app 0: ./p2p_alltoallv -rank: 0: { host: 1; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 4: { host: 1; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 5: { host: 1; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 6: { host: 1; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 7: { host: 1; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 8: { host: 1; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 9: { host: 1; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 10: { host: 1; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 11: { host: 1; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 12: { host: 1; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 13: { host: 1; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 14: { host: 1; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 15: { host: 1; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 16: { host: 2; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 17: { host: 2; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 18: { host: 2; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 19: { host: 2; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 20: { host: 2; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 21: { host: 2; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 22: { host: 2; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 23: { host: 2; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 24: { host: 2; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 25: { host: 2; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 26: { host: 2; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 27: { host: 2; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 28: { host: 2; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 29: { host: 2; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 30: { host: 2; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 31: { host: 2; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 32: { host: 3; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 33: { host: 3; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 34: { host: 3; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 35: { host: 3; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 36: { host: 3; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 37: { host: 3; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 38: { host: 3; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 39: { host: 3; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 40: { host: 3; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 41: { host: 3; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 42: { host: 3; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 43: { host: 3; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 44: { host: 3; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 45: { host: 3; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 46: { host: 3; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 47: { host: 3; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 -rank: 48: { host: 4; cpu: {0-3}, {4-63} ; mem: {0-130415} } : app 0 -rank: 49: { host: 4; cpu: {4-7}, {0-3,8-63} ; mem: {0-130415} } : app 0 -rank: 50: { host: 4; cpu: {8-11}, {0-7,12-63} ; mem: {0-130415} } : app 0 -rank: 51: { host: 4; cpu: {12-15}, {0-11,16-63} ; mem: {0-130415} } : app 0 -rank: 52: { host: 4; cpu: {16-19}, {0-15,20-63} ; mem: {0-130415} } : app 0 -rank: 53: { host: 4; cpu: {20-23}, {0-19,24-63} ; mem: {0-130415} } : app 0 -rank: 54: { host: 4; cpu: {24-27}, {0-23,28-63} ; mem: {0-130415} } : app 0 -rank: 55: { host: 4; cpu: {28-31}, {0-27,32-63} ; mem: {0-130415} } : app 0 -rank: 56: { host: 4; cpu: {32-35}, {0-31,36-63} ; mem: {0-130415} } : app 0 -rank: 57: { host: 4; cpu: {36-39}, {0-35,40-63} ; mem: {0-130415} } : app 0 -rank: 58: { host: 4; cpu: {40-43}, {0-39,44-63} ; mem: {0-130415} } : app 0 -rank: 59: { host: 4; cpu: {44-47}, {0-43,48-63} ; mem: {0-130415} } : app 0 -rank: 60: { host: 4; cpu: {48-51}, {0-47,52-63} ; mem: {0-130415} } : app 0 -rank: 61: { host: 4; cpu: {52-55}, {0-51,56-63} ; mem: {0-130415} } : app 0 -rank: 62: { host: 4; cpu: {56-59}, {0-55,60-63} ; mem: {0-130415} } : app 0 -rank: 63: { host: 4; cpu: {60-63}, {0-59} ; mem: {0-130415} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3936301: in cluster Done - -Job was submitted from host by user in cluster at Thu Sep 22 12:01:35 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Thu Sep 22 12:01:38 2022 - <40*lassen187> - <40*lassen730> - <40*lassen519> - <40*lassen369> - was used as the home directory. - was used as the working directory. -Started at Thu Sep 22 12:01:38 2022 -Terminated at Thu Sep 22 12:02:48 2022 -Results reported at Thu Sep 22 12:02:48 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J loc_alltoallv_n4_ppn16 -#BSUB -e loc_alltoallv_n4_ppn16.%J.err -#BSUB -o loc_alltoallv_n4_ppn16.%J.out -#BSUB -nnodes 4 -#BSUB -q pbatch -##BSUB -q pdebug -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a16 -c16 -r1 -n4 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./p2p_alltoallv - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.25 sec. - Max Memory : 59 MB - Average Memory : 54.00 MB - Total Requested Memory : - - Delta Memory : - - Max Swap : 1425 MB - Max Processes : 4 - Max Threads : 27 - Run time : 68 sec. - Turnaround time : 73 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/loc_alltoallv_n4_ppn2.3936356.out b/benchmark_tests/lassen/loc_alltoallv_n4_ppn2.3936356.out deleted file mode 100644 index 96b7f78e3..000000000 --- a/benchmark_tests/lassen/loc_alltoallv_n4_ppn2.3936356.out +++ /dev/null @@ -1,70 +0,0 @@ -app 0: ./p2p_alltoallv -rank: 0: { host: 1; cpu: {0-3}, {4-7} ; mem: {0-16301} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3} ; mem: {0-16301} } : app 0 -rank: 2: { host: 2; cpu: {0-3}, {4-7} ; mem: {0-16301} } : app 0 -rank: 3: { host: 2; cpu: {4-7}, {0-3} ; mem: {0-16301} } : app 0 -rank: 4: { host: 3; cpu: {0-3}, {4-7} ; mem: {0-16301} } : app 0 -rank: 5: { host: 3; cpu: {4-7}, {0-3} ; mem: {0-16301} } : app 0 -rank: 6: { host: 4; cpu: {0-3}, {4-7} ; mem: {0-16301} } : app 0 -rank: 7: { host: 4; cpu: {4-7}, {0-3} ; mem: {0-16301} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3936356: in cluster Exited - -Job was submitted from host by user in cluster at Thu Sep 22 12:25:00 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Thu Sep 22 12:25:03 2022 - <40*lassen33> - <40*lassen32> - <40*lassen31> - <40*lassen30> - was used as the home directory. - was used as the working directory. -Started at Thu Sep 22 12:25:03 2022 -Terminated at Thu Sep 22 12:26:19 2022 -Results reported at Thu Sep 22 12:26:19 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J loc_alltoallv_n4_ppn2 -#BSUB -e loc_alltoallv_n4_ppn2.%J.err -#BSUB -o loc_alltoallv_n4_ppn2.%J.out -#BSUB -nnodes 4 -##BSUB -q pbatch -#BSUB -q pdebug -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a2 -c2 -r1 -n4 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./p2p_alltoallv - - - ------------------------------------------------------------- - -Exited with exit code 139. - -Resource usage summary: - - CPU time : 0.23 sec. - Max Memory : 59 MB - Average Memory : 55.25 MB - Total Requested Memory : - - Delta Memory : - - Max Swap : 1425 MB - Max Processes : 4 - Max Threads : 27 - Run time : 78 sec. - Turnaround time : 79 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/loc_alltoallv_n4_ppn4.3930386.out b/benchmark_tests/lassen/loc_alltoallv_n4_ppn4.3930386.out deleted file mode 100644 index 73d39ed44..000000000 --- a/benchmark_tests/lassen/loc_alltoallv_n4_ppn4.3930386.out +++ /dev/null @@ -1,123 +0,0 @@ -Testing Size 1 -PMPI_Alltoallv Time 1.708206e-05 -MPIX_Alltoallv Time 1.345477e-05 -Testing Size 2 -PMPI_Alltoallv Time 1.187026e-05 -MPIX_Alltoallv Time 1.506677e-05 -Testing Size 4 -PMPI_Alltoallv Time 1.209269e-05 -MPIX_Alltoallv Time 1.515937e-05 -Testing Size 8 -PMPI_Alltoallv Time 1.195546e-05 -MPIX_Alltoallv Time 1.753534e-05 -Testing Size 16 -PMPI_Alltoallv Time 1.252954e-05 -MPIX_Alltoallv Time 1.952966e-05 -Testing Size 32 -PMPI_Alltoallv Time 1.308248e-05 -MPIX_Alltoallv Time 2.390346e-05 -Testing Size 64 -PMPI_Alltoallv Time 1.431191e-05 -MPIX_Alltoallv Time 3.147769e-05 -Testing Size 128 -PMPI_Alltoallv Time 1.665726e-05 -MPIX_Alltoallv Time 5.037270e-05 -Testing Size 256 -PMPI_Alltoallv Time 1.880835e-05 -MPIX_Alltoallv Time 8.184131e-05 -Testing Size 512 -PMPI_Alltoallv Time 2.806967e-05 -MPIX_Alltoallv Time 1.498649e-04 -Testing Size 1024 -PMPI_Alltoallv Time 4.902119e-05 -MPIX_Alltoallv Time 2.552568e-04 -Testing Size 2048 -PMPI_Alltoallv Time 8.721921e-05 -MPIX_Alltoallv Time 7.180145e-04 -Testing Size 4096 -PMPI_Alltoallv Time 1.602938e-04 -MPIX_Alltoallv Time 1.267297e-03 -Testing Size 8192 -PMPI_Alltoallv Time 2.968970e-04 -MPIX_Alltoallv Time 2.368909e-03 -Testing Size 16384 -PMPI_Alltoallv Time 6.148113e-04 -MPIX_Alltoallv Time 4.497337e-03 -app 0: ./p2p_alltoallv -rank: 0: { host: 1; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 1: { host: 1; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 2: { host: 1; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 3: { host: 1; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 4: { host: 2; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 5: { host: 2; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 6: { host: 2; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 7: { host: 2; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 8: { host: 3; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 9: { host: 3; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 10: { host: 3; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 11: { host: 3; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 -rank: 12: { host: 4; cpu: {0-3}, {4-15} ; mem: {0-32603} } : app 0 -rank: 13: { host: 4; cpu: {4-7}, {0-3,8-15} ; mem: {0-32603} } : app 0 -rank: 14: { host: 4; cpu: {8-11}, {0-7,12-15} ; mem: {0-32603} } : app 0 -rank: 15: { host: 4; cpu: {12-15}, {0-11} ; mem: {0-32603} } : app 0 - ------------------------------------------------------------- -Sender: LSF System -Subject: Job 3930386: in cluster Done - -Job was submitted from host by user in cluster at Tue Sep 20 13:39:07 2022 -Job was executed on host(s) <1*lassen710>, in queue , as user in cluster at Tue Sep 20 13:39:10 2022 - <40*lassen27> - <40*lassen26> - <40*lassen25> - <40*lassen8> - was used as the home directory. - was used as the working directory. -Started at Tue Sep 20 13:39:10 2022 -Terminated at Tue Sep 20 13:39:25 2022 -Results reported at Tue Sep 20 13:39:25 2022 - -Your job looked like: - ------------------------------------------------------------- -# LSBATCH: User input -#!/bin/bash -#BSUB -J loc_alltoall_n4_ppn4 -#BSUB -e loc_alltoall_n4_ppn4.%J.err -#BSUB -o loc_alltoall_n4_ppn4.%J.out -#BSUB -nnodes 4 -##BSUB -q pbatch -#BSUB -q pdebug -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n4 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./p2p_alltoallv - - - ------------------------------------------------------------- - -Successfully completed. - -Resource usage summary: - - CPU time : 0.18 sec. - Max Memory : - - Average Memory : - - Total Requested Memory : - - Delta Memory : - - Max Swap : - - Max Processes : - - Max Threads : - - Run time : 14 sec. - Turnaround time : 18 sec. - -The output (if any) is above this job summary. - - - -PS: - -Read file for stderr output of this job. - diff --git a/benchmark_tests/lassen/test_bruck_allgather b/benchmark_tests/lassen/test_bruck_allgather deleted file mode 100644 index 779d3258a..000000000 --- a/benchmark_tests/lassen/test_bruck_allgather +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -#BSUB -J bruck_n64_ppn4 -#BSUB -e bruck_n64_ppn4.%J.err -#BSUB -o bruck_n64_ppn4.%J.out -#BSUB -nnodes 64 -#BSUB -q pbatch -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n64 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./bruck_allgather - - diff --git a/benchmark_tests/lassen/test_loc_alltoall b/benchmark_tests/lassen/test_loc_alltoall deleted file mode 100644 index 4b371e17f..000000000 --- a/benchmark_tests/lassen/test_loc_alltoall +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -#BSUB -J loc_alltoall_n4_ppn4 -#BSUB -e loc_alltoall_n4_ppn4.%J.err -#BSUB -o loc_alltoall_n4_ppn4.%J.out -#BSUB -nnodes 4 -#BSUB -q pdebug -##BSUB -q pbatch -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n4 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./p2p_alltoall - - diff --git a/benchmark_tests/lassen/test_loc_alltoallv b/benchmark_tests/lassen/test_loc_alltoallv deleted file mode 100644 index d10959b92..000000000 --- a/benchmark_tests/lassen/test_loc_alltoallv +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -#BSUB -J loc_alltoall_n4_ppn4 -#BSUB -e loc_alltoall_n4_ppn4.%J.err -#BSUB -o loc_alltoall_n4_ppn4.%J.out -#BSUB -nnodes 4 -##BSUB -q pbatch -#BSUB -q pdebug -#BSUB -W 00:15 - -cd /g/g14/bienz1/locality_aware/build_lassen/benchmarks - -jsrun -a4 -c4 -r1 -n4 --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 ./p2p_alltoallv - - diff --git a/benchmark_tests/lassen/test_mpi_advance b/benchmark_tests/lassen/test_mpi_advance deleted file mode 100644 index 9197f20ed..000000000 --- a/benchmark_tests/lassen/test_mpi_advance +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -#BSUB -J mpi_advance_n1_ppn16 -#BSUB -e mpi_advance_n1_ppn16.%J.err -#BSUB -o mpi_advance_n1_ppn16.%J.out -#BSUB -nnodes 1 -#BSUB -q pdebug -##BSUB -q pbatch -#BSUB -W 00:15 -#BSUB -G unm - -cd /g/g14/bienz1/locality_aware/build_lassen - -jsrun -a16 -c16 -g4 -r1 -n1 -M"--gpu" --latency_priority=cpu-cpu --launch_distribution=packed --print_placement=1 make test - - diff --git a/benchmark_tests/plots/dane/matching.pdf b/benchmark_tests/plots/dane/matching.pdf deleted file mode 100644 index 8af491b31925e3e6d422afe9eda12be13890f28b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95239 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
  • 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 z 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 RasfFx*bylyMdd(<{{=I233~tl diff --git a/benchmark_tests/plots/dane/multiproc-off-node-even.pdf b/benchmark_tests/plots/dane/multiproc-off-node-even.pdf deleted file mode 100644 index 9761813d518119a7a64fad0f8cf767ff34f3a6d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101791 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 AZ~y=R diff --git a/benchmark_tests/plots/dane/multiproc-off-node.pdf b/benchmark_tests/plots/dane/multiproc-off-node.pdf deleted file mode 100644 index c8e8240517d20521a473de26d9cc86d0a7b03119..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101556 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%0A=qO5mhz2EDjrAWJthPiqM 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 diff --git a/benchmark_tests/plots/dane/multiproc-off-socket.pdf b/benchmark_tests/plots/dane/multiproc-off-socket.pdf deleted file mode 100644 index c0a18faca071c8263c437e7d8809e9ccf4df2354..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100709 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%0A=qO5mhz2EDj zrAWJthPiqMOdwsaOLwkXpulcNz>_vYuGrrZco*C^M;pzZH=POb;P#~;IjTW(lYKaU zI`Icxd|hnRd3TF!>a}LnS__GonlID~8M=Z%b@$;RNQxOXEh_QvNPW!>E{7lD)*`nl zx%h~5C$Dv9u4(X&(o7d0Dl~BFt?`rew?_#LWi4=;iFTCOMKYnRs`cIkk({Zk|Zzrb-Z~4d-D7TnAhUI-lB#C z!1vJhVRCB88s2dZzbx2hWaq?|T#{|4iZug=q0&ICC|81NwckF<%4zqLK}fpxMH{^* zY5%f1|82)RS#y}+;|<`dmT6ezN|DQxawO9LcT|xJeq3Zb%(>LO%5tmgI9&d+jvz8w z`;G?UM!!6)>Ljj98E5%Dp&<%E)s}Coh z=}U``mo&HdXOuuq}jNq9>3>&)~W~O$u|4SftE#Xwo>(XubSgj zgqxCUx9Vli1T{%*y06(NHIA)u3ZeT1m&PJUn{Rkw`7ac(hmk*daCj&j_2rK9JDY`3 z@1=!sz?kt>Z3*V%i;T_(kQkoqz zqXsHctFEYZ;89gID8d|fG8z&TX6UZG5mG@A|IK`#nhD>f4})Px3!5b$`94-Hcn%o| z`ZZg$0^v4}S&xvdW6#AAvOm<;gn1z=RjL!qkSdiw@~d7Def+3ss_iu~x;jAj$NHq^ zUFLn9`B8;hwXeAJc<`_dGMkGS!BN+pj7`hEkTIL`(zR-|;9(Ob!Yi5S+fu zfDJqQIKOo8$*}?=$@eSTT&Yv2c0@PLPLJlCy@b&&{p-^oY$vjo=Z-??K70??XD{{y zgXWg^8kM&#gSWPZ9Ze)vK8&nP)#-Cc_21FnLGU*=GjrGX@Vr{zSjgsV_&L$YW5nb> z?Si&U&dQWhqQqyvmnK&3m>IXB`K{}90em=H2MMvT+pEQQtf^GaSJkwcb9U~7DjmEZ;rTpG2gA| zFQ590tfPI}=P>oqZ`@I?H-&;-Gjl~J8bQZ?6Gd=zZ=AN}T@{?5ywmN_PxuC5Hx~Me zOt^_6so*?ny@@gh*a+u?G2kY+Fli_G`@T)kFW&~fBqn^HI-tYg_pFYHGGrb%SB9JM z2Q7j|>x6+GXkU{(4bWoV5$Z{iNj2?STy7=~QDaGK`&7#NSeBj@59YgDKfTzJpk8Fn zaZCA;%~>0+6q*YW*AVZqzScd?SIao2$iDGP^U_evms;U?zCX$R^()Vjc#qq4kQtAw zkXQxWNmpXNk3eyjNw=fcn50Mn5v&msFy7o21%0s_RZw>if7=gv(3lIKut-J#iOL`k z$*!AB+)-x2SwIKBIIJisL2MpnjN;2^x|XqRh0m(rl7J8Mu|8Dny;aLAcRRfH!j@tp zxI^I-G=09<4^d7XCQVzRCotd3Qb~-AQPQaCrJ_842DITBY7`x_v@*>`s~VhRN*IRfFN&7eiT$V&sn!iHgc5A@<0Ah=8?#bUpj z{(44(h^O)9lZ({%F;VZt%utVGYfoB2>-Lxwt&gm1DfhZLt_5BJexDK)H<-v(jZdsy zT}olUxT9J6?r>o4(+c+L`hV1WnJ@j@vTD+kg*{)g_4=iH{VD(*iMo}Nbu}bu?&|$+ zK`x!VmG~RAthMyJ2mZR(TzptEt#_BFn<{qVO$V-3Z8q|Pb+uxH8Ep&|5U}=Qj`uYi zMm*wuzHdY0>^vCIp__K4*v>WOu$k)ZDsHz+_X`v~{2cdRY{V>gl4%Sb`TQ;yYNN6Q z{WNJ6gFbB5P1zQl)LAG#Q8(lB@*&|jbL4U*z18gN7dTRa zUtVOO-1wX}Wy`$%NlKV9J)4u5=y$P20rBR`JfY(1XlW7JmW^3n5X(qs4vdb+zDCHt zl3LBdrGnkaW(wJCB-T|dVWuw>&9aS%laHsg&)WmDPm1_CBhvU&^6G9Iq_ZhW3P?`p zJKSGd-Wzq?X_8{W72c2wu3*$NF_-Iv^e?Q*=kWA|lbHo!b!PTkDqkirOH?oR4BTF| zs$H({bEt@Zhlzv=frD?zb6~KrFWrB|TSWO<6c?itG5zBkdOO3D89SQk@x061kBs7^ zcr++YHM%qq`X+veX8nW&CywG@EpZ}?Q&c6k%$u9HU^hdvOvsFYdiQC1(Bno7Z~$Jv zvK1_n%CdpCI96>~hbm|l=p3)iT~M;?NMF;GJ2DAw2O!VUe5pVbX+|+nMSkGyxLe4W zq3`6RjN^N!)bE7!bHcMvMO1b;s)sFJAJ9@^K~fJ#fm1O2s*h5v4SSKQed`0J4;_pm z?Ws^Z!`H9j+R=F2aTfq*;^przd}{Ve-FS($Ou1@HDA=yj99mQeI~(dY-^o@Rz0!d! zl`;>yIvOTPbwq+h3aVBqwYpA_SDl^j0rdB^$!F=6BP9$h#CnRY)$X*a2xI zDuMNzZB4?r`Ka&^%4K@%tRQ?H))!~E0M^ujkLZV$jwKNL&8KdAc0~v(psw?u5kKLB#~hg?eUS zH11=|;Ib(mPzj;!;N8p#8#YY|`?hHuD_pR27W({k_n>qNAlo>FeB&U=c%Mz;#)H+r zL!bx*JDD!T7~Wj=cM#q3Jq|(1ht;VgCVxzrEpDUY=T$NP?VL0f3+a8+{&kz$mJVBKw&O8g^xP8c0MTNaso?EF+xg^8F^v@OehdWP3FM`3jQKz)l>o|z}@ za-;~)k5Xv(ou1u-JX~;WxQxa<%{OwCpZ|HmQuEgfK)!tEUdkwA4kH}QK&CL#a@vhx*<)4c_UVe26vOQaixyFZka zfoTzYZgrF*TX~?{1bb0RQ^+DgFOCAn`3P)TQ11l-(V0HjvcP!d;T|wUT#+`Uz%^k*EoE{~K zHK6r8<<~x_3q#eZM5_|Fd$eM!yeS(8eZRhZeDHdyM{OP*QN7L!Ccysjp&EvL$~us~ z;MCKhkDGmDn4s{(RWpDl!R>frd%Hg^c8+?B9BfkB#bb|+6%I9@H^lKCcDdwpWdT7X z&~nS~x$Ojj{?e9;r~D^UR-i$VjO3j_YtEsN61vnmRAjD4gwd~%B0j4~H<8)E z)h516hz89*fV#%wwq0>6%@vk3>`qfAsE@I3;p6-=q=Wk5KP=z3M z6=3(yg%&VqwhAehk^uj}VcZ1(=7T|^)}KU!X_0&`7~sBtiPYUvK<1~YWZCR!*u%bx zqj&3n%*E@Nn4(0WW<}}t9r9t$2E;V*Ot>zr5Z3wkk3{Au_q%xPbf1#wJg|J#g0Pp{ z1115M?n8@P(m4XAQM1hl5IMlc+Of!V*lYgS7jg#53-NYZ)t~^@$9k>SM+~EY|80RV z*$%*f;@XRr^viou|F6Ed(!!JJi>BWUJK-qAzOo4y=Pah_WAv>H{#YCx%H`5+qVwHt zvoRRNAr9D|2oqc6Ijpn({0wFku@cDI$7`Y6`dNW7)!S{1$v}H)Ny9uUiH<5yQNA%9 zwtWFM;FzPqc}xNJDSa{KyW9V=P~{^?H~c|?P{+vOhj-Kr5Mx+f5k8`8jTXD<2b&4k zdwE}~oN<`gsIZE`Nib#u4{l0j`R`3%rqPWXzb|qQAXYt)KAxp&R&ClOMYLG~QgXG)>{b%1tf|7KJ+p4R=#Foi9^Xn!p3i!g5W;w6iR zeFpGMerc4k(MfU=J|Dj!$AP9p>C1!+`hU|YT6*oVqW#z=Rc0Czp zGqijiS_m5|c(1t?R9su-tcShJ@-g6;%c$w@^tS{Naa*gHx^9#h7mb(;2*qZ27^eqE ziaY;I27vlP3DaHQeF;?9q#)(ls?Um^NqHd&o))HzXnt6)=7;UTex~r`G3kws_NU~u__uUMKY<{;0;7*sEN=8}AQiaL~w{l9;ihYvOoha%4Y zgt5bNo&+lb>2XZ0PixCaI$ehJF~ouCK_yR>HE9@5ij+0?Ed2NZRwVFI^9gRyC);Y@ zE~LU76nFN0s_2SFYg62n&5RGRV;3YZhS^9ht6^1;afmxFMZBoUKcSiT{^xu$2)tkv&c*vrYXl5vq=Vb16>0jqID#5-h=f*6v zGC=X=|7I3G2dT?6m_tYh;DkekvW^*NDj~J5kGO=heQdQB+q~exZ9Jz#$S?j7KTkAe zQ;(5ROItT6=?xsZMRGO@jC+?@p(ULYsui+){LgnK7&|>-H$%8BZ0_3~!crklD=e0@ zinEf*UfqtR!`TfYaM)jdb_3w*v-A#^AAT4&=cgp!2M1(bV#*L0@_Wfv zi*lWlJX_7}kmS27{yU9#jG~-;-`kf|W`sc9RG1kGZm`p-C3V_#qeI7AkJoRU9MOHt z-Sesm0^wgFtN-a>)1clfzLstjtZ$O>hkgWRVFQfv$vf{dtDfm%DTqa9I|}Qix>!8N zq0YByksaET&kcP$z6G&9UJ3(iFrEu6HLw4HmMv)tG3$ZF_pSg5(nk+JivwAzj0W7i z9Gx#1xm_{|Dm(6pGd3p04dPXy*1IF_@oFnEN!_6 zx_N1Z^k1TXBD)?CAgVUE7Hn2C|I{cMNxEBPxOBY>{iQH_rG#s}YYcCe=o!#km-EcK zbj9_1ZS9bUCCsP2O8>W~h{zyk)z?;{nX8WA(PcoA`Gjw2_*}G+!bo}dAy1&XzE82S z-I72Ai>NUakVrzXl{!|98i7AHeR<$;dG=6=-izpcD2T$X7kdU6qAu{mkKp(nz?Pk{ zvnZ2ya79QS?RAl1jPu9@{tNDaRhGemE-h1|PAS}9thZ4|gktbxNb83U8ut?F{Xr^8 z%sf$=BOl(f#zID~%3;LBJrUuD9|E>x>vl6_`LrYSW+PBy0PlR()?bdOYmsh3P3BTJ z`y$Bw(cf|d&709GA5DGTE{uO!bXObI+X>sGLEu{;fHbQdf^%^M@hGU4h4`sHua{jm z{p;Lfr+#_8HM#2s6rYXH!s#QjkZpPA$z->0J|sq{sT#kP>2D@q|>t)d$>f8!uh?GtM`U(*|ser_?GcsOWO-v z^8a~<0Kue-O#;L0@qU{N0!c}QlNch7XK80JFjF86kA5#rdNJ4gM^F=X(IZY-w<2Zp z!s28IaH>xHjpL6qM0CvocXm;8YLO6KWgQoI@UOv8r`~=>wG*YJU#FiSXVZfK8WjJ+ zS{KaO1X~ov1f`zIKzl0ODm2=;zOHt{@}~87i5Njj-6u}NatZSw|JpGM!D!KT$x7Mv z?Z9$6Vfr9M5!;iV8~0q-^%OXZR2C>&KYU-}waj!YmMnb@kFE_{xMfTh4!NKcN(6dZ z){Dall_>EV21rImCC$bpK+7#Pe{$bq2f}?-a_uJ!H%{`22j(4*eR2*NjJjt2W$K&B zKFS5p$Abp=heq9sJ{0bbK@*9-?fJBcGEWYC*O>{$4-3Fr)dAw|pNJZho`Onn+oCUh z1)e>!uyxi#LDC*tGnnhN6q zILa|r=Xz_QJ-owb^{3Jy$<5|%!h*!EH&OeQ8$6T}@8I|J!V$seTsr-F@Qirwcz_`G zMbr=&rk_s{M)5KU6f$ptnx+Ju<4i?-3Q$97{ONkWgA8E3SPwUBu}x}()gSubQ@&1% zWjEp^n2S6bVkQBE{2D2gG-L9Os{sCG4g+4ECpQ;wy_*a9j=@u)WAP#>&o}x)JHyDr zY?1ab^2*0Bi)L#q&UW&D(ra!oId})_DSx$`gF@ALT-ujMV&kDkNekXPAwV64KNt|VtUo#(8}OIQIp zPL9hcKDMx>t;NI>XL27Upfh?=@3{r7E-Wm(TrJ0A>>q?pw(O0pqS*y`^!AD?6!ofN zL)D`EHEbI#Q~Ym)e%4DVak1sYw^E0ubkCVZRPZS(203~xH+zSX(9Z5h_?J@qN3W7w zH*X*vwGw!_yE5<#OooDlP+0e?qxz{ne)pdb81Tm6F_9#A@oNq@iC!o$?URP$}B$$odi?qxW&KC9*CsANaiJ{M&0NS2hkqlN^c!} ziUOIuUx$G+Yff-Y2YaeBo7Z#|79jqmkDJ#ocpkp3Ij7;YS_;5g4+9xm24ko~&4iG-$wd*f_Fqm0i@F^Yhan)=cBx|YJuOJKd5oliikGjc9*tJk-U3_(V`jtj zUkMpNmLLk+Cat-5?4z^|PHUY;{?X)e=Xa3sTn=Nf^GMDmoH*NZmG92y`S+uTGP#A z+12vjd#%}!{8TcEdGlDXA?NDijx&4*MXuD4twCaH&4#Xcp@T%3H9h|sOOtN1?rWHE$6I}R&4BWEs{}gX!z&vkC7EuUQv?7E})t;f|*%b%yawCGkxCI6h&X$b!_s7 z@cSXNQ6>o*T`SD4wN}31l5RF@gX*GOOz zw`^|b^2Y!r;aeGsRG%=?xupR~aCF5rrve#)l_Jv50^%UBEz0Rrxsv)(eWP9y3SQ<8 zbjuR-TMA&A1s)m%v*Wz&&d?MpeLgoxm5r-ii<-2OyD)1Pa9z^v1yO(M0P+-UZqx*6i2O$#+a!mO- z1Cm;IkIt%rQQI)K2^yXQSRW$5(`J*lq8ISR6$>dG@_B#Vfh%IaW?I{r6-~}LVPf2X zifyKEp|6jgMl&Zubj}V1RFblL4e@KsKbg=9@_Y%{+&JM*#HT*{L17dqlg(~@%mqzu zQ_yra*O#Ly{Ly^TpPWD;HtI|eW0NLC4O1Cg$!+eJ+Y+ z7{Jid7Qa{bf;D9ryH)d}2=uNk09a9k7#v?;{a#R?{K5zHBIJE=FRJFFR(ga5E%gmnE$>Sk%Gik*)iP?;-P zTUODYCwfYtyLE|>#h?|iE~+LSCXuws4Pw-?QlZM;2zFFUh;8PDr7xIVs69zd%7{5~ zagM{#Zs`iCF+?RkcnVTi$yR32nZP3S&)!7X#VvlBQB$?E!E4Bqlkp^rl&_HVxmh44L!0gnmv04#)^1iXKIe!kHp+MCB*jBUZag*GFw4v&VMPJi=f_O#A1SeuYxjti(qNKR3+YK#0W1yB*m(N_!M zknj;$wvFqXlHO;&Qgi*uwA=|J zq6GlqnBb&$5zAH&++RlhK+s6A!`>Z}>c9em>4D@uw_m9&NYsYAFE=Tv|B`#!;-auLzhnWFpY zW*WuI=Dnma>3Pj+NY>Yy0NCG7*8GNVjIrm}kTx1z!q~`&3S*CU;gz?(22Mv(>>kr~ z5hu2j1bUns^}SVU(0GbLraq=>vN~S}5H;f?{hGjAEVxsbc2JqPjK|gxATXjT&?f~D zI<9N-Z0kl@N{L>k)6H-jC6FWLHl$1Cb5~Vmr3RBg>MTUpXfz9f{E>Yg_mx{LUrE2n zsbK74!7%M@d-%QO*UUeHH>YQr66K>dK|56^#wY@APw&gHTpn$DK(MN#yG(kv&sjwM zN?~Rr2>L!+&Q%&n^Ay<+6K91XPh09lM1s_X4qP+2G)}C`1JUP`OF8*5A9Sy1Tf&p5 zqwh6uljAW~KBb2mz$1vq^QQ2yO!l6eyNFhho}+BhgnDBWKrN?^Aa&8pl^-EWKxlpg zIL!$$n;<@!fGS{}f2dL6ER&~BJ?}EJU7&tK&SisO99dp4UXT7`h5Y>=|NAKqhfbjc za*UG{LGYcuVHaO>MvMrtH2p?Pbn2Su_3=~xi>arP_p-dkx;5JS`Rr(+WIC0O-&+pH zhrEQh?*oUH4t;L6?x4z4)foAEV*^F~gSrDeShrQ{%nN=t6>J7ew1SL?TL;i+w6>mD zC`ERErHsVs82&s!QxX3y?J71q^_52$OKY#OFFULKb)}dh$!rI`v(v0hcUc1Ri3qkP zVw&R2zk>HFOVD)F$BUp);Ia=vwiLuarkdtI&aV~#XfZ`7is}UdDkF(^5OCuo0_oNK zBMZ}`(c(Uj*kHv{(&y4zy&9Md6s~-XQM#E38cvxJjCoC+ZujAie{X-m3^W`-VBA^p zzUCCDYMdv|2VVC!S7S}?Z#pzAX4COvR5c!D)*qg8#8R4{t-8K_rQYxIevW8;2F}?% zt|2Hm9=4OQaD}2-sZ|OFbVj_)FOwz_W8}*v8$fE6>ARA7#Qij#H~s7)DZJ*AiAdsN z0Zz)paBODDimI&G4cv9peHPm^av11n9JuP`NOM(l9qd|B*k)Z*iN~5mas@ z@Ysh7)i;Kct6u0=BFk~^6y<^Eu$xv(QfV9yBQ`V>x3Wn>Nu$`wCXuJHSEc|Gq^?Bh zp%B|_S91Tw4jerfyxtedXR6b=nQb8#edgNkn-rlG;6*Wt zrc+(t1CRQpM6rl%M>f7CiQ9oiFLCRY79q&3#k#Cv=f=68qgln%*2 zRM~S`3U*ks*D`iO4jK}S^!on8JESIXjn?jZ0mY5g$ooukQ{AUbbpCk32^!dDn7+iAXqhTL}p z&maZRiMYx_MGoPvwI)0|%IbZwFIEnvpfvJ^4Q++*>8xCL-1nOXPskmy_2k`>CGur) zlnH{bcwQeaE=P49_`NI|$JdQSaL|X+Sr8rdsX%haem0`yrcuf&7z((ha*W|Vy!Vq* zCLrTNge5}+?1jtZv~G&}T5J=Qo&yH5{%#v1@C!L&hKzu6tGFlvbd3Rc74wTx^8GTt z_-$9$;|=~TWOtY5t>=7Qz+6pI<49R6itHhb(KTBe zO|Qqa25pr3zx!a9Ux$-6sJnb};h?>JIBGu&h!z2kEsoBADl_y01skA;8|N{1Dp{DE zhfeIG4Kcd92=)J|XxF7cehZ>wV*&3OdATw+OinXGFkDz!X=J*Ur1?hAu`_jX)H$6y zC-)gZ@Piw1s^w2lb9|Gv#eOVI5CuorsFj>}klK5&I_s&`8Qv+^s zxhsneJhKKs>bR;wsAbjk%vSoYL8MA^?hkG2zuquC*^iIM*AP+ac+ggKotw_5e&StT z=6L$NeZTarnLp)dI~KGVTk^x2m%HtFyUKCF)Dnig;9KZ=) zRDwr@ydu*G=!h9DQ$4e<=Q+*a?DI(|K}JClMMv?y5ysT!k*bNDK75%PFadCWTpQVj z5pD^z-8b&+!0SLHw^ObnzHJiAkAW``S6&Rf73H1 zytDg3GvQYr>P8PZ@lnPGp|OK)B233K0M(CAvJAfo9tu{sxo+8Gfa_*)X!mdl(WiuDyyQ9C^wJCRpR?-)wplGWMk1j&g&^3!V z3kf-hqqJ~FXc)hgjykammWvevh&@&)+hU?;?Ln^pP2oNCLK2I+9MsIts0AaGUL1(z zaUi1U;&I0TM36yg=bcHRldqE?&^;{Cji^3dws(Z9T- zJDxqmPB?C!*agj^D_C2w!L3n6oYrSFl+0d7@#mv041Vb`u@0UH%wV= z?^EnCun{JWqVK+$+ga?hUC$U+0dt#oolAST)}Zw|wGA3La_j_D*YBt-Lcb6PnUZoG zX;=AH<=I2t@ZuEN ztfUS9UgG-;ZmzyAq+7`OvrL=f_Pe*c@KebcY~2XPgyiueh@7Nai++sUtSW(oLsus5 zw?irytXE_$6xby}a5(6royr`fs(E}-zIBQXI4f!mvy4(@1Fc{68_V&nNVr-GTE7R`l`(L)El1Kw1064S||6v!JckOyI2!$!m)}6 zIw2l-oApC(b|>zNB9|}EAyGUG;Q9)^hVCI5#bdqBM;PiGOfVE&pK#d~t zDHFo9UCo&hdZTbMrqw-TzG7h~uh88Awwsy~&m0=oJTjc*qfvkp&VB`Fodzd+P4RVJ zTqgmJZb-=iB*{j58yn0hutf4lN+~Sd>E{z>ZR)HGn}TQPcTUwR1ouzlL*PZPhX;-) z@@%F-nbNmogIh;^h=Zom?h~3b1+1}BN96htixHJ$h9n={GHPpBhct@(g0koF^ff7l zB`2RjSaWlel)ZbHXKy;F`~WEWp*~*5*MlX2NcP1yd&T34<^0%9J@E|CgnFd1Q+j|i z(rM+PU%X(u$gC9(iUpEt{v z3Yu|fP~0XC9r=fJZ7`&ho#FFw0%A2=45s*^ua?L;l5zQ;f4#4apIls5s$hEIV3_MB zG?cffUh%9{*t^;lOZ<~a1cdRwLwA1@NXFSAM|PFrS-`653e;m>8kF5*bS2-qF#6cG zZQHipu{!S9w(WFm+crA3ZFFqwCjY(nJ?Gx{ycZwlsG6*&=BhPDvZ`wSD#6;xskuW; z8QnjU%Aj(Se&P(rV`(2SlrPsd^3owR%lR;G%WwQzr5RZ}yyPwr;rz?)e{;I5WGnsb zK!N5yWQgALgVmvS#+DS#G4A$LKS=>QdE2SDbTDzOrok0FK=}%CZXO7vAGA;pOmULEneN#)rg^qj#YZ1A7Kx)lNcvsoj+&(L+K|&bM4VD&aR;N zgX`B2o6yHBm7Z9xD0*$SrOehI+| z=1A+)s4uuR#=lCZ>@u~}8HG;rB562G(^dEz$IqV4k4x`BU$eehkIgS1eSpgl+U{jn zLbk+(P-{m~JS&rAqCi0#lQhu%{oRBLs}_4rqQLV0HB15b$mUZftevRlXfSHMcJXbK zMPfOARvjx(ifgEVZxo>IKn;_}(G&%WLt!Ste95@b&3!D+X?8LyvOO4_(68$SSOHzu zNtc~&#~|ylN&6LbCeV$!b?c)zU`LUPPL0BmIJDw|T!Bpgv%$tfxuqywx+swC_I%TB z7*unx9vNqD76y>ja;5-)c#X-;h<}h!n1x>^n}DYEjHfaB6!dCR5Z*Fkr)^_^)Swb1 zQ~dORs-GjFT8;iK(Nfz6&_#GV&8HTXQ`tV*B{u(#K@dIHbop~qI~`2@s8NxEz97Dw z-ifQ8N5GAiVcZfs5FI4O>hx3FtR-CX+Oh-c(rc=MSb$}}886%Q=9H4bznS>@!q)W1 z+j;_;`CuNurDk{)s*P@2BtG2u&*@T7RRruKIUwr?QZ=0_HcX#CbYt8+VF=(1OmPIo zv40ap6#Xpu`K;XHl2GSdo zi9^}ljHGxHsViUUoALGE%2n<)%%wle$eZn|d{c~%XV!lxRDixj{7G`&dXl-iMQAH} zs}K$l43v22#;gzFNjwT1FJQtSLh^Iu!YkquKc`N^B5xsc^oL)3S4kMw!1zV@cU%Ub znS}-NcJO)JzW6PA3sgoaM$IX$=3PB=AGG9&u+KGw9|y!dYrGbFo;D*%bbdZ$Qbvne zcW1AL=HP3ky0CuWkB4Ccw-VqW2YgJy8xG;OV4d!m973^lSjlzQK6lRw3~Y-=+s(A# z*7Nxo0v20cZk-U5d+RIlT@nU=jETmZ&!&KwPF;j|XmAn}KHa?-B?oFBd#va$NT}N= zt;(o+(g={Ye0VAG=F$O@#^9cfY;b*fn@1#KQ%U$wF~_dsG3V)1p?7h19@#6zRBOUM zX$j=6iyv2r4C!s{V<1+esKWXxJ*zUed3C09ba5^?P<_AMmKnBrlDdWK`Hi2ZDH-RE!68#OJ)dx zlT8%TVH`50mN9evG&TXp0tw{Eu~~i)ZIly#Z&sFmOaeQ}BF~?Zy%YRclQ1I~(ZQRi zY6cm?V=$$!`FZQaixs%wSy45xh3Z;$R&c`&T&?GL{PS;g5CMAQ>l0Fa>N_sRbk&U< zmGs_@g>>7|gZ+$n0Ndhd-_8xkT{T>z=m;Gpn`RMAA|fI{3H+k+!Lnk^3}bEhJbz3u za08s0WJhkH^EfQLtF?ux)}p?*Nae;|>Z59(ujz->Nc?ZpMDNKk8p3P~YA;RMnygSR zPr=}ywlCdG7@z|z&I!VX);4?NRCi30=;k^w z&@#FTA_d10$O|rl+ZDl=!SYZ`7h%&L=TDdlV=V43ZcT;tm^ z6O;~i0^s5hG^tOK6l{#F|@{>qLMisi_BsnOODX zb_65flV%#pTZG_%T!va&piBtro@f|7{f;TS5SbJ#3h34&W&Ur_=h4ZOmK0R$C+Da& zzZPD{E;wh%GVYVWb4N+e!6r{?47tRh+q_{4v$tBPF3nKXdeAHsFgPNl9Pc+juQ_)r zh`MKW?jleM`POw9H(yoxVAoDL*N<7K=*_1c_NGCouClxbnI7a4 zaAbN^3=#)ewqq6j-H>|6tR{r1CGG0<>MsQH6;wCOP5HgySz~o#6{${|Craz-Is2oR zq~=O)wr!XXHiMMFn*TClXM(;+_#pmZZm3=}F%8PgJ%Zu>hKRxZq zh&l>uI|lF&B!B}xb#rTwrvN)+Mw!rV4$wc<40m(=A?U|iPRW&?G;r~B7(h+tknUNp z0283;y7P*~u~iezi4a0DHjsNm_wLhv)*|7`d>an_w0%jngd4v(JoQZofk;ug@{AuYjnfy#p5cV2(Is~!RryaPt4}A`1M67;V0%AY+ znGz=MHZwhOJBdhM!18Csc@S)w#%4d_@K^I8z}|TXfuRi`uV}CAH9lzOJ8T9`yRK~u z0v@*XLld@T74zV!sA?`w7@;-@-8#?tD$^e(VZ`Ry1YkVD$!Da^Q@g6V^VEYv(?zgz zZ;}6%F=k@?Qdj?jwEh=FOF*yeWZ>v#`=!r*7k(M7V!!Mi9EHt(+W(`p{`~hoBNICT zz1lA$M>7Y4f1qO}D0-ov4#wYD?0>D#_l`tMLI%fDmLB4B0z(vjJ}v|`3D z9hv1z{QbgrzqMdCwl4+vyX?PhS(&~B<8Qe4E3vbD@wZ=ziRBymWrbp4Hjsxclmc5Ut6gE#PeOp%=#@9e@V;V_5W_q%jPWHovwmsR-(&nUe_#DLzsLL@`zx`seL=_nj`?f* zPh9_r={x@KzTb28pY7M2{&Qa6`G4Ad=Q&vZ+5euOf7<=4-SU`e-9*oU5EcOkj(t89W#7=!e3X`*BSrsf#k2s z{|+QG{C@_L+5fqo|0O2>{{oV~JO1ap_b(Fpe+80Rzs&Ie0Z3-zWMlrOp#R^2WR-=r zMLwELpjip4w)VJ0xa0WEo}v{;_Sp>zLP80N=7I*t0AEKJ;`q;rFCh79-6ir8I}(t` zVS#xm?KbsRT(qcQl*HV?0wlfxl$)`ZvHk{lV6&v=8bDoxGetvVC9bq&o+hJ0&?h~v zltln88m&qHgB*DPM?io)R2JtiNb2Jt8ceKH6IhKMz;es;itCex2H;smh399c*=Y|v z0(hHx3cw}JGy{{47mQ@-rmnQ3r>M~uzmfXFd)F2 z0GZ>0Y_Z5uev1G$X7ohBuv0UTx-}p2YR{>%M2v-3f+M~KuHA?Akg)x*&})o4$WCEdOR9! z5uUD&#}c^J1hOV9r7{W@;%q-O@=ZWty${d`rq$Ku_h&~X>hdD$*)uysBS3lzpX<+E z%b`5DrWzNpk9aT_=@(T zAq^1#oEt8m*4fu$347PRf5Lrq|9;aW{Hz)KRGa^VCzdp|IPD(%^_h0ny(fSTZSmM` zDzLk}0vV5NV@a3__>tY3mh)+JgB=?dR6p?vGsXc(xIzfUR{wEn45UtjLrS|YKqiGI zH+c97ncEFMj~_p^3IwB6=hWaDiw%H`O?}!8PDp8L2|9x!KKuuu~C4nN&{dj?+ygvFY^}&LKNBT@el--M(@B3_=Id2 z2i|S+J%nJgrr5ger#qP}pUB@aVi5AoAtCFZLqNGuEWsiZdsc$OSirbh97Aw~7W)A1 zb>X;uOJHViLnIKUAt5erLjXUmV8U1|PRZ#3e_Dgt*Z7sh$2IQdW9|Z6ja(p{G|3YL zHdN-bPS3o}fc*Sgb>t}v{_zz==!xp1)fZT3d1hsDa)%J@lTnC57-$U3?AFXbw#lW* z2}tniT^!&8(@z1MdkuqEB6KBx4aaALcR)Nu967KHcNkE~G^--`382AG9T(qxlwY;0 zTmJQyLN1isIo2G!2`rVbw(i~PeaR@)RA4BC7N-&~3SsK3A`@Tdz|$3GtylvHQ3K7v zh1+2|pFU&V2eG-v%V-$#`d})3KhSj-(7witMIgiX4mM^4_~+&R3kO`B92&U;_;dm$ z{EYSKo2#KV{I&HpNmmPjBQBxw0X_`jDFkGXcojmeKH$3@qBcUAenhc|E54_9TA{Wl z{Mxem+phzYcN>fpfwHrF%syZ z(!X+fS6?|4d{+2A(PKox7K*Rl=t~%{zs5%n{inG$9?0R;V$ivu z=7**3GG5?;-Z}JIKpW35{bjs6(PPTPXBXuD2Lk3#Yr|!{`NLntUl|Uc1}6BlJ+qU$ z>e=200zUoxFCaPR8%TEL_y&>-{{FJKB@mO>N(cW3NLKg;lEME0k_G<(lFz?@Wbl80 zWRKWyAh`+rA0XKS;TuS{g#QOfejxY;l9%xkzk%dsJQmoe26n^uFCf`t`Wr~Lgim`Y zKsE4)W%^T`a{C1&H-Y~HB**?6NM2q6ob}*%zn2A-Ed1Q<=1UPIV2@?I8BeUdE*BUZ z?I{|wR}@k!x+m^#TdS0go>5;c*u;dSX& zB@SdEp%_J4+2pps59z8sGz zN(^v1UZp%xUHHMmj=+}ViT)?92jTaS*wk zC0#$o2;pKOXWQqRh8A=o=yM7Sr+AL>4Sc<3`W>>>uIAUj2rivS7%8G3uMBx#3wHnd zRR1wd!_P#HDxoKV`z|EV{UMum|1shC0F6k<;APu`L*rtWk}$>WEmK?2nV*I2Q%8^l zOs~~nWT7a4fnK@d&|s_BfeV|=;Z@b51e=7I5&+Hqww{~|I5u4fPWztk-aj~Q7@vJh zM5FV5Xep_zW)ra$W^qj0pT)v}yOKg~9(3SYX2ss7+Q}$OktBmtBqpAPfk~aHQ`O!s`4D;WBcr;GONv&~cgYe7c9m_(bcMy? zRw8^Y=K_pPkey?G|8J3%H6L*{{V^7D{$9MtU7{C-LH&GPVNjIaP(&%Eo;u_Ks&UVI zV{rRe#6|AVxzh>=>zR?@#O1OcVix5d+E6et+Pl$@{ih13HGsW&aWt*0+q`?Pt-CZs z5zWPB1XR||%!_o=>l)u&4#^odyG&xmyK-Z)3FK)(*jQKFq^5EJxC16xjK5>b`|Ukw zw4l!rk1e$IBR!9C?fj-z57C}GsD3#|59ys8z|YOs@a@&2_)MOh9i@maqNirVMX)Hv z&WGKPO^7tW68=EUK+TGak~)#dVwL>wi`O2t$ec8> zal=kH^?q^M5CMDZcvFjsTdQ+Tj|VNA6}~p)#=R(wKF|Jw2WI*pOHb$@%)lbl>ss(I zm&#W01~CHHLga`IuA6W0i{affVU5V`kDy2mZCxo{Xi>#ZEk9mMuAsq!m(KF7H9@5l z%7ZPk(aD)zn-L` zXHmfz@2B*(wN&YtW`Wn&>LI7yXCQ?ZsTvvZ$a=nI za=+D5!1E&_jIkcaYPdo`#uJ(hnIpKR)x}Ec-R#TQ?!6w=Z?(X(aOJ&7NP5Uqe(6|u z!hdEQk7X}K8(>g)0)!*9RHf$<8lOd;^1Ic!scjEgGoe#AY`E%LQzr>=&5v^*ro-gd zV`Ev?4QG_|QBHt1lFEFEO4?fIxj?PZF(72_FZ|7qi4afGNxsD7aVm-mlkL1nE)F`& zIilHQ8$b0tFPw@)T?i`V`#i10pQog{)pXDokB>Wl*j(z zS^}kTbBcw|ab+=nQY3dO6Q(?7>jIZ++K)x4tv`F9Q?>g&J`Y**p?%V38a(eWo7u-X z$sM|?sOeRfX~~`C%YT?*I`rVirJvFwd)Zrc5Pc=mX3{riW^d*&0WT$p`TV)f5c37X zu3Fmfz(*FPD_j(e2%nlBi+?P`a!Y$766D6`s*A5cokAc49$2+T>b=H9{ia>3$21m1+sMx^Vq1;!eYPT{b8JKNj z5bVzJ=7+mTo)012d0F_aEaV)-LJOH5FN*r+iX6P+b$+nD{Go00{A@2;Zv_b2I=;9F zbTr^USn3Lh^sFiJM+AFgR%I*wyV^E7FqA4Y+TS{*! zX4`pC;Dm&X(BIj6G!gzRL&w~geaU_p6b<-iYx|Y3_|~ZpQ2`w_ppHf-%;U0K3<(ry z%?`xQ^NxAWSn_18@!M!X9jGUS%YBK`Ivow@!J5n1;Q*Z;VZ`4cYcl0OFCjh~d>0Ew zc{UaUlL<>liQ(PPLLTV%wMl@I=pk70ca1Yb%Ax_r;Pl|Zfp<1ElQh8(?_(gMdw+qt zmpieFjpF3bG?y3-K}&S^vNv=+R#H2x4-zmcuqlY70Mg97F*t;wGVk!v7rcW#{W#Ow z#pK+Nd}LykGFy0v%0pC_q#zoYw6p&ddZBZ#(5a@;xH`_o`(-miN#M+xg;qY!mi+ zLsM148pLXx@DP{n$J|&s?dJW8*fj??VB?rC`6Q_ahlhcJe-J(qus(3I{Sqd#iSM zYipaLV}b9}@^MGF@5X0?&R%C=dOcozPnaHz0ti43`ulpdM07}b@|dQ!aP3hU-S(bb zqoI+2FK_inQW-6a*tmP2WUDqk7cs&O^j`a2vv??hzHDz4w7{`O3Qa&HrPHF*vNd** z36)kT3%edNzu@$z?Xdu1C6PdwKQnLb$AaVb!^RBFY@7#>mr;Ljj8_CE>S$ppr|=XP z)veTgn%PlDdand!Q6CSDh!i?))nhgr{Tj4U*h zTb!nctLirdE5l7stSaPpvLwOU9TdZ+7}ej}JjdT#Gw>cA=<6$dB|=heWr?woJ-Gk% zkbdOcB~=ih>y?`$MM*Wds&08q54bhTSH87h9dx2A*7XArnv3q2+zd(mq|G1A)^hC3 zYYk$}{T}k09A)z4{OUKhrjmx4l6cWAnjJvI$i<`H&gzV}H<$;BX&=Q?5;xu+BHhQP~?1yhrc4p?Kj-_m;O=GRcF^SQ6p@_|@MxeFa z^QA-@x2CBl_o^G@`3VK0dhW#_cNl57Z0BW92O$yhAetY=3(BBH$BBIE=C^@q?Q#+D z^O+CpugHgNTLhP_33PZB1J|plns==67HSu6^&A1M3X+s49o>S>#>zS`JlgQE32-cv zWH6gmNnC40>kF!bEiOG!LpT&Pz{orr1_x{v16N7dro=W+eyl{K-2x^G%3|t z-T^;ovL~+_Y45ci?--S#e*`H|9NOk!*SBj&2WSng&&J!61@5~@7xjvRFHcie@l%**C7)lpWX%l?#7@*&iIM zo4hx;<@X*U0x>9naNn@7EL1R07(<%UIcEkHJ2xbemS->)M%Ko(V71~3ATeG#j+v-R z+g_6xFR`Hj$sHJKv>?W%dHA)S!&4Sv7s*^Al%Q^3lBkM|U4eTK;E)P`Hs=vKW<9rT=p|Y3#=ymW6L^fXM9#=&l7Jw+N{*N9<+FMcNC9J zz#$MFp??tNRlof{T3L9rpkun;(>zw4a%1uc;bU}VV10cnwZKz*hQC+x5y*u%uV~i* z=eRqqaIZ^s(p=NYlX3WUH_r#A$k+E;SFHyR(dwCwNIC|vXNp}nccHDIaTKZXFts4u zeKeG3YQEDIrs_QU$6gVmlqDGY<+t@qYHIhG*~stF9j>A4n|*)5$5I8$Mh#h?1(lnIHh2vx^Pq-t}HA>(KWrFPX10M#QF~o3M#KX1vwJe z(Qv(3Djkg4Bq2GUgO?~XK-hJMeHT-Q6xz*OEz*?h)IF0-#?PH@TsD*x3^#l1n@%}Q&(L}c{QC_5GWe~K zXvR+~O$;;ay^Cf|-N7WOlcz{g@1nRHROO^JidI7D1-EeS2iwgN-UEaAe=QKM;k5W$ zEfMi2*JMVB2_|Saez?)^I0fN|ah$~8V31jXl5tqQ=t!@w3aY0%Z-#mMqeUx`d+oPCj(|t6k#2ELjvSb0^3tJ0={gD1#LZ+&7$&#cmS)~946(Z#e z((_<5>m-YPctPxy!mNYi#S}m5QsxGzc_m_9!MO~3lY2ZuWH%gWZdF4f6%-}QfM)su z6UCM9t#{=?oR{E=n4nSy6Zb=7uz-e3XXq-D!sZS5V5;sl#@>}{py3OKYI1Y3tE$ zA^^~e|2C~PXLY(mo`bWUf*@*6@n&5zs*LEup-B1NO>|$$QzomsjrAxlEM!V?WPML< z|6cnnw4cgG{lM}FBJLO#-BScT$~++Vc4-@$X^($T++cos7M#v|5fB;nA`yPN>Gvk* z6FAX>sZEqsfbVVj?u1{C!3H{;J|cdTTl`B}G--99r5=5ZlYsH(i*^ys5l9DZ{#;l6cj?*|*E31H~ter^~TuIkR$KUyhdu(z>HLB%m6B1mQEsE+xMw5ZC4%c?^{SI`9)f}jBsN2an!S&XOe=^guwObQ zEv4VJx%R&9>FO1GMqKDi{zkaC$@5+a51N$4oos^E8jx!)>!8d9j(^Yc;FXAIOY6cr_yqz)oJq@vHESQ+OdH@<* z>@kmJm-_EdgQX-@oPh)q=tdWW`L8GI>2NuA6ecaI{i9GL^MVbgkaciRIEQ=|SvKGK zy0Iu0KS9pvGi<_6kV!27Ux6mD)$I6Q_aA>_;$#lA1y#ou*TlL$tgQ_W9EC+1P*Io! z*Bct@J2`wUildNb+q`7yeg(62N+CAaLbkUo%e4$LLEkUA1F8slpLkr+X|~Z;XWK4q zj!RdQ?*LrhDx-~TIAgsG!lySBz4x`67HN3?ngyrYlLu9Iq+0gk!||@?kHO%4l7a2x z3L`v?0rQ~Zey5CcTmdTcF7K7(M;VI^xJr7F=k^w3?X}#+!5Glq=;N{RuOG#L&gm38 zGpD)p$8RLcjE?pvnSR>!0>$aP!k!)4Xe&&i=jrWed;DRZ2TMnMW13FhYZJ!2@b6m# z+9yVlD`{h6Eu#G8EZX_ZLAEh_d7jv%jNxlhj-U+csF+^!Y$eBNns%aJunI`o-fE&%)r6y(?dDcXTbtVwr&KzrBZ#M= z06-p=6Q#a}$m8Xyd@`?dLwr$=6<^k|N!L22aD<%!07gdUc#Bs^%ympolcIFpkBpSm zmUeH4hPIJnJACB?O*vn{rM^Cv7oPvcsHjUn*#>n+O%O-r*+Lk;<0@{fs3cFgEjH{t zg^VM_m^XUzbmod_fvqUj5@fU08e7Thd|M;KXK4`o=C5WPbC1ht>*$AmbZaIvR|0r0 z*!93QDnUA>%AcpBx8#|wudBlR=Fk+F{uDKtJ6ELjNl0z|xL8FnvEN&6dqU~0Ks8o< z_V(lLO|L20%rA>RkLy zhtQy+Xqet8JYw(Nh|3rtStgXwX0Bdcb1x&hkn0w7kuv9iIM8b<9MFLh!CReOtRf=F zBwiU+*e({Dhb?4~Gw%W`O*lMv!p^$Yyo?a;ZoPcXQ)Eq?Z8vG26F0d7^R*_;ZS&ui zHy85U_-q&b48%Rrp^rCEI?tom=s4AH4ZgCrCe&>d~hvylwVJ2D}SJjp`?aT3!^xgtjo6)iE=6POM;&o+{*)N{dSwb7mjyy!%5u z{2zfDjgwZZcLwEY%&*0kuo_9Q+bBwZ7RbZ<_69kY-7g4C5ey}3Z%``*o;Di3?CS2D z{cduVNrTeNnmlN@D}lVDg6Hj2osk1Hws|09O3=)x%BaXfCpuo%G8w%Z;BAAm*C>-} zw4yZD^2@v~#oLtIoqqY*cZSk@=ct>oDpUjBT#0g*h#$b0EH6%)b2WX=f(pQl?3Vu$ z5!iY*3uk`Ko`HU>w1_=QZ_^HBZjsA=U}c%`o5h6r+MP<^%}Y2F5rY#^{MR*G^J)AY zI)}`KKgZ#z5$@#M8!+o29;%M9m)5HUSMnuq6}IVUV_97)j@pW$_x=29u%kw2UgrD zB1bM1epbziEZF1JG|%TOvc1;@s8WB%^q+4h2)q=nTVDO+pOQIErN~zN?Yl4K3Qm=7 z=$28%p2NY2QaMz<^sQD!=WdMD{E%@gmm)YvMs}>1i=4xJ=^*cu0qoirz3e_e*fJFt zinz-sAG9R9u`7)YrIc=6+=nC88JkQpz-83y?!llH2g`7R;A@);Bl&_26H% zMDI=g-;@`0j8L-=8rPCy|9)R#y^gA3^72!A`RmZ&G4b@ zW3b{yFpx3)p%OW|8WkEoxV}lmh?6^ zR3X*@(io-w`S8a|4Ij03Z^e@0QUuL+GB9<4We|M=iLLNPs1&lnb|O*`ql4*`4)d7# z_SkqSrfuFdUS-bMs>$jS#phc zsZ0{@;#VEa6T?Phda&(&Bay`3ac_YmFb)#$i27AMoolIdm$ToJK7)1XZ54OLA`K#< zR{Yp~&fMu5n-K6z-_Nr`DHb%S5g={M+KW%{<3gvbx)eIkMda5JRJ%7l_O6z}oLC<) zFAjdn#25r)rPz;@rf1jk2cqgTs;ZBzfD-N0N}Zetoq7zvNDb?3E9c2^=#E+}K*PZ4 zdH4Oy;A!tlX6lmCQgAZs^fHMCOhEd8xX}MgZ`+cY!A^XOb?*?s6c9ybYk3!flvocG z5qcoAJ@~FkZu6tQMk}Mbs#zmED0 zOg?zxs+w20JEHHoqpH^{OF}C+oe7_Mytxb-WPCr7JvDY1g2nLpu5*Nb$wxK`lMx^x z6lsIoKWRWFca=|Tnp;CYhsI8fQy6(CO^+pHP&CBx$sD9jj}$dG1X^K5AUhBf=yH(O ziH9(0stGOa(Vi|dKH4+oLZ)Qy6LZ!o9>`#AdN?F_bLI?3C>-L<`!%A(J=o~Ts8sUE zdQ^wd1oFIm8;q+TdG3f)$&*zMT#t_{X#jHDlf!3^hE$+HF`Ku%h>Goa(6nN-oW^uj z8Q6;gJMCmvB0j0$At*zPTP5ni76dAVx5eW|CJ~&lp>O}Z6>r}I`m>ocGXGO*CF#t< z9ygiEpRH&_8q>TN2&Aqs3;H~G%jvOn`Hs2KlI8c&=FMQYDeeKh;$I?-q7iYXrQh)8x!C$1j@e!s0#FAH{-~&>uu~5rq@|esVDXb*;lSBMP|o!Ar-R1S`{rM%pa=LRyDAIsHA?G z(hPrXdSXws8GEa77XvdE=<2n?XMtj+eu!7Ay>;P)yC1)kCI?)Qwi4W}>^+YoX%-_w z>>K}?kRWaTFh;1~sG9t{0uk3)g}Q#iTv8mhdiaCO9HP3$Y`o= zDKyOGk5!rVm8DlL$he=!WV?y+G&d&FkfHIq&t)J*r8AS2-+ z#$L3y`XdQF!(Ed`x4JQ;*R8mI-e>Nj$y^@=dlwi~Ihb)!zfpBFdag@Q1(4M7 zKGZRAn*2#6tiZf+MWclO!&3zNJMnR-pU#b=AFe!For%j;fIZ|{JcY+SvFQ7;& zo2(;LX-%nb7p5f7rB+G^6V)b>g)O*fc4y$y4@i_&aLlXN&iCmVS+29NgzTe|ftE`% z%r4PY+N6?1-35k4x`i6I^?iKV31+o6*X5D@uFV9e9~f8Mcm8j5#ha7LI{cWa9O?Qq z5hvy71(0ssUNK>}+%G7RpYff1EA?|DD!E6T!T;XO%esU9w7)^~dnYobxMMjcL%4vfOPnj?CQxh1ja zgC%}|Si?yl>HpeY7*-%BbxiJQHa*CqU626_1?e-FZo9tzgN*JDMGW;e5Fj+wTt&VM zW)drPxyBznXS5i@tnR%(PN5T$ypSiU_(x2>cZm9 z%qkmgzp};0IpsrbpvPTJSa}N5=33ZA?|(Ugo>7-5rUwD*D{hrFCs9Q=vegCLxy{tN z{MDuG;`4U94PYcL>31rsc21z{ZaV#KMdLVj$vJ^FFF$vK{zx%aN>3pbW7fI~xuZ4n z08O$x$ zZ^AvT6sWO5S|Oij&@SZqbLr#(&*Q(Y-dQ4KlBF)SNbv?iY6j*lJ z@k`5_bc}*YVH8b4?&(ynNg|%bpN`BX|Mm%Pe*@&B1BVMgn*fh~Im;KdI9#HmccMr< zRMrFJ2&4^}zqV%&{4$vzIQs6Cji^sMHCme6K78qi`_Nlk`bV-BdW#w#{^jIQg2utM> zin3EDsYKF>eoDus%IoAVUrdR6jp+l}yp%&*I}R|}u)IWDme zu#VKL<;?}(2v{;O<$3V6hgz`O#)zaHutdfgbf#oIQ$r<1xa|9XSf&#z;*lEoZGKVgL;zt_CsdVm&vZE z6Wh#%xitKR)oywBjsj$=HIwVaI@KB#d!;@yZGPvxajF-s^+4VJwQA8A8l-y36SLnD zN5_6CuQw>ISvzX@RL%rA-tA_b$_yYenR`kx8!(2MNu6G{!QY!Z{~&MYsDuYn2d*1 z36Q<}amx-qe$0iO_}eTpk`)p)MXX|ycme!$0_?+I#T;HmBwx>P9lt<&j>-?(*DgJ5 zUg)|p1vDra*1=p=XxuxJc6AW)kK%!ED6sw&p8yJmR6|BRip799v;sLdC>8OH#>5ug zS!5NCq&qC*@pXP4WRb`OYU9=0)}JUWknq6R!~SdLc+(6?u4utvFiK?SVop>O!r=aJ zDE=WQUEqW`2BVW(-U+v6%L5X08Qv4C)hl*u-TwU_~t@YRp`OYFf5&1 zJ)ofwZX`Qg(Rh8%KeKv1^#{$#gN98N1BJU;XA1B{6*otj*>acBw%WR?NVkzSidV9x zZJcpzbJ^jNgUNyvE{yJK+`!R&of8yZ;Y4KLM6{_(U$xd8J}tvY7o=Yj&;c(C zH}@d)5o=%%8rFZ^V*3k?5+*0^JMB__A2dqi$ZfFAoV&N+Sf&Y+G{F;#aWagYfT%ym zAG-xnP6B)|nXjn1bn3CA(O0fHk42JE8ENgl)J>XGO7H6FMk`BuI9^%h>&cti4^29a zf2d!Zs!X;cni`vHvO~o--L->EJ^JYBN(r&$yRNL0$RYT|O>~fnx!olt_mWC<@kSAA zXV+Nyu=BeV?1~G29*nakW|^jMpvGM=i5}i^vqMi)qn|?w(J(naCEAo6EeGW8UKsn+ zR=Jn0-Jwd?iLBq*RzPu{ORLVO5)v%nSkz(On=- zW7KwzRY%f|ghim$uiQs8;YbC|Z8nmNfRe6)*Y@_>TQKVtCHo&T&r%3(digFm_>Fwo zshTsyuN~G8(ohf2db<8sVtP{!n;8GxbPqe`Yrfk=eT z$Ajz94rYz;-#XrLTk5NVxaak>(UwdXo3?rqDQfG3oPa$^h4S50LsC9Z5dA6q)6}Ni zbDT|`=Ur|77AP{CUCsiU@sK^F(-Wz`6kNA#WRw?Bg_x*k!vUD~CVgCmY`ilYg4O;G zaq7M9eI9tK!ej5%-j=B61DNK@Xe&XRy?dCVV?1d~adC&_!n8XyIxX$L4i`pI)I zX)kzo{2~`EZL!Orji07LVDfe+$T66-`uEUT`MJgC$;}BdE!Z+gGf`(}a>)nlM&0ka z7cpQEkf_J!BDx9cD=yWAf`XS9lSw$K3879j|St{xqT$&|bSMOU6lHwH^4yrZ$Z1+@gFn$6jL6`t^~ZVa+!p-rZq#3sL_l~Xkx-d;L1m8oSHv~azgJmJvNPNk`jR%IQ1A)(Kc@dNI% z9kgy2cIwB~kBN>WRv#$jJO6^2_>BSyA!+!Z2_LGHhvV*>isD9H^TnW07K>@cQ(? znMkVWnPql@pZM|s>0(p|NAVwZ+GRgD1T$S%3kZ&A?v++8CPdPJhAk}}u8RF&q`owA z`qRfMPtAPz{Cwa@a@R`$85bqeQ`f@kWnc`OoG#1=lA-@&_0#r|RYY3oG!kd}N!fJv zeE+52x{}@$>!Tg*G5nd2Xez5}OMLpmMF~NzbFwon&D2%G9HeSRZys71k!tgT&`@gu z4vWb&86a~qE4p7c-J_^HZdEy>h*m1v%0tDbi2c)13TuctAjw_i<9*qCCZThLY+MX| zhKLbi+Jv4K#m;v)PRYBI)iov91k>)=xil+Qe{I)JBpZWu5Ly4jpdsIG6bX`*vjJTA zjMNc{K5TL-WzVxpo_L^JOdLpL{%B>}0L$W>F!Gq42L8vEZb03TfoUUZ)q!GOLI?~t zshBg(W0z|fb=!uPf*TET+1)Bc;I6Vg5w4evyJ7T@O*>*4dtHOCpgjQD$be13v)h^T-4Xti7h^9 z0`Q9JPf5^Qm}UQasHplbz{0NMTTJ*6<~V-yS+}3rbm1ga=&9*kkL-gEuCdRD&n8PE zLy9=6=E__J)3r7f`ct)b=bXk!#LFO;FU|ig5#X!?j!;5lp zrxG!R+@t>vr4MIH_h_2Ml=fVTbU9^^pY{voCA8k{`+;lV;v99U69mf8C0{PV6CgM^ z-E zU~{xr+wTs|PZOsCRCr9~7Dj#Vw*ukTY|A;?pbs{I1yV0u8RC>^)q#k&%wI zhzSU^w~nXu<#O}s-c|5>X~rU&H{|xl_Qzo+g$qZac7_zXEgpaQ#wahMX9bGY$3_Bp z&lsXlhP|7UqsxT53fm2GL&up<*q?P!shKg=b5h6rHw<+@! z&c)P*WedcQ=l$o@o%@HUpi1hVVJ-`UC{Mrb?mbr=V5m)gA#HDC?qIb&{0+PVfe9kjxyZWtJ9cw2G_&DmZ_7Lg>Ldj*is~R2K1kChd zDtOqLDT>gTw*fI;Xv750H~Czz`0`Pa2$NdRlPSF8PSnjUt3^H6N)3j)nDB7u(0x>w zp`Z$t1H#T9lY5tYo5q((bykIf+5z?|40w!O-;AY0!D*KN1XY~7rZ0ASbyBnFJW z3Pi3-tn}pgM2TOPY?>FrI@2G>+horg5MzW(j!VcGcq1VpjK5OMZs@N>Z{P6P*_RN} zvwjf;#<=ValuuYm-Ut9`8x;n0&@{Fd-^Y<_z1%y}pg7eBmI=d{Db-8AFG)uJo^28! z*jl<%1|y=?yY}TGAt`I)RB#JE4Dl$M`9#ImKCf)ZWROjgN|{g=99P=3M)!VXL(d}j z53eqB1Gxs#Ss=C7g;|O4Id0Z-9+-Jq^~?ghUlOf{i^zt)B6d#FZiU1sZ4xP~$~m`b z>45({aQ=O_;7h*X#$PX^PeoKB52uN2h{P|C9PTuYIy5{6cPO;6z6TFp9#Fayc5Lt3 zkf_EQ?6DadR7p`BV7o(pG>bqk&i986D*0fvJ7KA>?|}cTqj^HB@K1uA`6|h{1J2?J zD*?8in7Gg(8uC#40$k$BdXJf?C4?iBoK=Wn;c?8;9GP6>Uf?8;qR(4ki0z*pD|*S9 zSu$<0wjJ87CYN`$$fcAm$hFW$A2P{}MI@444Nt>PiNZT(<|(k0r{{B9&ch$mS6ZH? zh951_C}pr_qvQ)IEFcQKzxSrX_YT%j&=Pw^6|p&HNiXrs zL5T?RU5H;dC9+2L*9Mn}CRAp~h#oPZaBI_yB{X)+Y|<4KhP&=qrJi!q^qHh2DB#GQ zAGM!cI=aon<}M=8EWCnJ7TEe~rXAG#pckEm^5$kl;r@LjDSI?)VRn-Yy$3&X)U{pTI zKka{zBOIKz^FT!G1ap&$e4IY6^EPP2ze2(2h2Cvo_C|?-UO64py)m6iV!S2}y#tY` zwNQ~q&SYPDN%yB88noPNjeE@_y;u&YmU*n0dJP1O>q*jHc-JY0bFD6jxWRWQ%|Ndu zTN;(4U@9^ZjTBK9E4zYOGcxZ3O%Sw9juFWJq`(zb&vW0mz_8*&E-je~ptg>Czj*`( z@|rpidZL1KL`e+HR}NkIs3;IBEQ-rLv)TmL*PO}UFcEP=x~db?G~=XgL_QP(!ago^4SoJlFq4KhQDIful^G+PUYenEO zaciwLV#s4kzO9|fUX>*v(*~fZbN)EH?~r{|EA3AXUcw8X5~eN@SDa}XJCE(k^xLk& z$N}+Wix{U(fIFTge=w*G;aWH z6LU|pChnr*+LG z#YPk!qHne@%8oZX<5jWPd^Z7{Cwm+<5@y1~Ixwf2_< zK-^G)%gJhtL+O4DmFF&5EySLPZiV><={=sV-)%-*7JP>E)*}YNE0p#qRroxhvU44t zd&6Br)V=NUMahylF;5j+9SrHTEHxQb;;A?SgJ}{u>GGcH6i2VFus3S-gy^9lKZNA) z@L+G(4kye5@f&I@MAW0nsmFuPMiPb7KHo)?YYY@6(D6gKnm&j)x#WJM|JMLRmvsMv zg|Hyy5dT1x&2lj)nn^M07CCQ7ORGlLiMq`qFn!~q zzJld-0}5==7p5Jl68b~)y4y`AgjikF5fUQ^1NyZ*Pox$%s^~^5 z#%4B%=L-?R9B3`~b@f1}KjYTyozTt7qKR}JWddBuOnsx8Q(PZ}As_8!j$z_pIWnn~ ziM74KHw8^fY}aQ4h&20Jl+Q`)p=9R6)gtFK*nev894S- z*8tgYE@!v1w=!Cl$%(w8Du(MtMA4kqmL4Chj~hIs$2Z16^*Ur_$tpjSu7n?aO?6M7 z>`PeBj9i=LHlC8kooazTVpT%aRB>?s1q>rpy|UYB53W#J0_vjSZkUl!X)tq#uz^nH zCt9<&$3E+Zi`W zFM)AK-pCit6fN!4;~V^JplTh}`eN>S!xELHXil2Y&kfvYaYSmn54ip^-3RW?syP0i z9(1-H<}29}rYUrcJtQtqOlEu6nBLUmX!&`pxYVMp;SBp@H=D1h@y4IfFrT2z>)ib-stjrg)#py0pUX#)mSW@AJ9Lv zs-7xxX-W_Xc8SuNE3=!u#yL92MlSD5ppVYWPJjV;{G)YRf^TZegq{p*Wa2m)1(un# zHE}*oQMVj4Ya=`?>0<$+R4*CbWxUaJe8?F0nIvfdCzkBP;Q>_+ZuZG(PeKTM$hOyH zPDX20(+^PL;#IcSIu+((`l3}KRRgw317nBq}e9uU3LBJ7Yo>g7W1>qjNb;-7P$$; ziQZ_Z?<-*TM%!`#hDJtgpy+Hs663+k#+w>3>DWzWtX-A-gP$pLlk_@EswW$jPwNW| zdBdUnMdH)rP!kGG}w)57f z&at+&vP3Ogyha#C8*UfYVcl!sb%v$nBJtuqdgebwa;Yp<47>pZO3q%nB=z|XQqS_I zBDXOJhl%QKlWWjw4L350!~kf`S{&YE9RhEJvi+d|#Db|$3WYBc-$ypJDr?jkoPOkJ zNCVWQac$*>2sR*1mS(r!*sFanQ3f`fZGk^@Q&G7R(@pt-d<e((UVi#01SI2 zLVH@SRW=IPDFA+^ADvx7ZXPaWjKdfU!)TlNM;Nr6B5wH3F{=>LpYc( zI03-TYN1nx4!)(I%nf<^#$!KGsBO=kT!ntn7ab$Kpn-LF$M~~>j|_@3S&3HUj-ChR z<=EV_QpEZh-@K7+k4ep^pYT3{qYZcbkNx^ge(uxQ-%^wlC)cv?JK%oWjz6zvrH>bf zzP^8i9>jvfTiIC{uO%8HSvE}(dsx^hRBigAG{30CBqPNY->I$!zfTrbaGOnd927h* zUZx6w*6|fxQzvuM3#M^R$$-(Roe^O70mlup-9JsaV*KTW*=Xe-xE;QA%~-HeMi0To zl~Q+Gy$eXXy8-+5bSN@A$O{PkqP2aT8iIu$HmL4jXA!fDt#CQN|Am*h+rW0%;LHLN zjQtayq29^bm=&|e@_D<*Qw52W@{WXbHI%Giajry{oGX$LCqvmEfLkg#OMvFcy%mqy zzmd*b3aHG%g4-M+4R%tJt6a7)1)TsDnXs5RMej1N=*$+u>Te1Y9(WGBUIU~=Mr&I}xm21n-07q-YD0ylEb)ksz@`H>mD4RviGn9Ay}LNy|zVxw@< zC;q&UTo|ByY-JrNWRpuN17haFsUq8=Ayb&J8mhaS>3wDOeBjQfy)MUuKZ4x_C_J zSZ2{bUL=IAVO)CFHQRe$xiz@9pwr$e_56wZx#HyWbVQncWV_LVx>Q#tzBZU3y`ZOm z^3dDatwINF;jF!Z5&kw6vnosWenFT#Stf@7~@ejH)sWS%el`3%Kw3!Es}IhK!2Cm^J+9J9?;>={1zeG-fT zz00MUB8{4%WDnW&YG6`t2%47Y>+ly>+vh&2=OcwQ$U-Vl)FHO_@cR2>>6 z@l*aqgEy<6O*irU@rQNT9En_`?0W9?jv-nAX_c3;NU}E^R-jyZ@y!%+*T*#eNPoQP zA%&;8*t9W=dU+NYDtq2Bb}h$LouFhZ_xT50J0u;3WS^9-D*Rt$I*&=?6u zj=r!lwle1X-e#~=w~W83xv*Bn(}n4s!JxA>XpXh0X@Mqz2h#~Ni9Y&HBFZS8y&WiI zc@3V5g(=$^26ozn06#K4U|m7+c_~2~#72KTnGN#%nIt&YtM_`Kuezuy&^gpFjR@6` zF<1*LnhQHT>#%BtdRq%^XN?LlJXJa)6N5dSYh*PVZp0;!pCiImRa2 zmjM+G%eP5UsOOVUEJc%JIpNJaA*+k=6F@Qp$t5Zkh`_*egW>05GaJeaxsVriuQ-!m z4FR-J$Ejn#&ZMmgI9QJFtvKK#Y@aTyq9DFg{eTtX=Yd8CWMh&eZ~^CZ83d&B9)OT0 zD#a?CVm(DGgRD1K9QnmNdqEpLcvar=>>c{jacU^)>(g~aHg)`-KUyn5 z?8!lQDN@O`r0h8{>2S+}6%S60{1|wEm9yr20&vT6V*xksJG1S^#@yHvg(VTq6yujg zDPPFDgvN6?xwg3vv7M#Zc0@Wc!kQ)`e^pnbi@Kbe@Z>KLF1{3cXxH|%Wrk{>l{3IpC#6X9eKgjr%(3;HlTjEEg`^NPw z@h$-lFYyRFsm~7uw8k8Ap#dZ?EX%H<$YejC+^>iHl*VZ>Cmqjp|u`1`fDQpj-5>K$^eYKV7Ps}^>q3mC(0?7ld zwHv@SMG&4#(XD{snLWhyzs&io7%->u6(W<-dxwj|NF%sAUBWeiWLul(=jB1`Hvc%p zwwR;dGyA(po&d=YI>Q*p2&b!qRSV5>Z!&yns&^(sInF8>s>VXRr{)LdlaQ=IjdcU- z3R-6lKY~Rc>7l5QZC`zJH4AJNZ*2HWFHH8EyFSSwV}BV)9BhNoUOghpcuqNjOt#@M z6Nv7EBt3fAfF(Emu>!BDs@`)r@(+JbY4|CJ+PI=c{_4aRTL0I9<$rzd>B<7Y-K4=$O$!1zUn#7+UPjoadROI{|+M zB-aIPNw9(5zOOb62(&^guar@V3&T_?&F4eeo9lPJ22|eMu$92`9zkl#TKrOCCvTD! zrzL;l94U&Sa_l@jPH zBK$6ygp85iIs-2Y)A~pOXiQrx_Mo*?U@-Q~#h_J@F4P)YAj}SI!{}o`J~v4GdoWptFaB>=UNL`YNO|izDSkfecdL zes$P&G9m9F|Nb|N;xj#(Ma9>aY);5!Hv=_x-_)OLMxb`pFMzl7TJEf)3F;8btc;H8 zf+?5W714*-8u#Nb0a2ZuhTXE^@X-=m@>&Ms!@8j<*umKS)F1QpO~5)-%|d9+aH_B2y`MN0aOCx%KKR=ORlT`Q^3X zM+3=Gv;y}yyk^s!I7T;84#Z;hBSZ@O5`>Tq#A<`A7OUzu-j!49qw*@A()WK99rcID=>}OE2*c z=Dew>a6^|O0~miZBPd!qUrQm33xHC4kweiGAm34fi@4VlR4I>3N|}Z{bjZEDhstG&aeF>zS*@|AhKQPR^UBeDFNqt?RQDU z2Ssl z&X|LQ8Yv#khYYtqr+=5+t@wmp@a&a5$67k5u`}ZBendVfh`s8>GEZ(zLdsa(+;y{3b5)K<{sj?=6i$2UB(o+o z5+>rY@`PS?Sez>w91yl&FUCSoNW7LFTz0BVBsh`@a&q>p+;4G2f1#F1LM81C9|Kz1 zp-+1Io#uHXq9v2M%}WR)@J)+_pS@gCcc)jCfOz%euFCl;C2!_lGSO8;mT`aLtQyj4+MutSw8^B2PuL zYM6aU9P?Bg3w7<~?+H#aRP_XdygGm^7R{|p_9msJ_R=*!CAsEsQdwtHK3^*fZ;UDT zBaYVJw)fkMx5fd+Fp&uGEqB0c;~GPn6iLs4-N&}eq?Qc(U(NW0Vbu9JV{uI&G>^cq zB?YcE5jlyOGVW-ty~w(pMQTriz9lW_p0^#=S+&{zrAs*mPXO&h#Et}2J?uINaI|Bu1UDuJhAJo|{^{=YfwWc2 z5z#4?01Q`H{-2+$u`#wcyF*Q;Uba1JTk%|Z*49V|NVw7&f%kqRLwBcT>z7_<=s7wI zx3&m8-H5FdwbZ0DH{--~1lD^FwRo~z#R-*|@)a1rIM-j?)#w#~b7Oh4+kKmU9{7u1 zZz1Chcp!@tPsdOK!sb-DvHTp|?r8hU*4RkcFP;3kDKg*G-R~!f!*<5kak&)0QR+x< zTEYgs3w69kN)aMibf_l<&kJq6lOP5=aub)tKN=)?>Bt&D718LOmQvB92$1n;wLXn-E0Q* znE@P*aVb8f`LFg&^E)seP5@d#Y;R3^-kc(Wk=S%ZqAaz-Jro7;+l5RvR{`Bkwj@9{ zr9F9}a$lOZp_1$b8~E-T=+NKV85llAjRjS}L(G_~Z&zENyVuj3=k`plC3W%lFpSJn$vGzGr21NtQ7FV1o` zr@jViBj10{-fRHrLKC8^KHV;Y?i99n=*TDJnLto_Wq?2U<*coC7OV^(SG(B{!aq@L z_s6Y5OAN8$bl?QZpdS`ir1HJUUETTi1`hlhTFSZPnC^<$zp_WR+UL}GplveoS?tbv zZeZ;L`(2J~E`xwR?pFdN?AqB{HMCuDks8!x%2-$}i}&6edr&~$Yd#9&O%y_`1$ext z=c!yBsBX>yK!$11hO^7}aeFF-B=woYAA5m$)Xc~*@D=K&reBo+P0?0Pbj^fcf<80i z?@YDu2xg`;w+y=Yld3pt-6&coWgjs$Fe1wJpMdK_K83rk&8@3XJLOk31n(Kme2SHW zM?S@D%DIxFp+#?qwdZvR=c>d%y4{duBa$dUZS1E(BV2!?+*z9NZS~@uIi{pacw*!( zuj$9|t_Jq4vhu2VlT`ttw}2m<7Ti>(MnF7MuoKmn7xJGK%%yhS6?6*$FtuV5rTfqC zhTj)3)DgG%ms5yidK9}&K-!jtPM2Cwjv1t9IfPG%2$x@)+9P<`Qo&YwsRgnoI!Vpx zGST=vjmb5!NC{To;*X>NcEOqq2W(7anR}_lD-n7CPBqrp%7L6bGcputg@?<8BkNr;IP~q9RH4f@#boBJYEd+( zf72Qc${O)JlT3Dt3Xd6KrrpM?Frz@Kc3?ZT6i6P^(v_6Fp_ZZ7SUgq(cL;^UUL(Ql zU6z3k{dvVYZoe zlgQvW$d@-SmXIvpp_QgJ# z6-1N^@iz(2Bv&PJvE-fOakQmJ2_~cm2~3A~@qDK_54+0%Y{l zrOGFV8fLDlhQTX^Wm|d)mB`xQ?41XoK+9~~nP~!buJWj*$T++C`MhH|soJQ}G8?_K?S}T7CX?Rc95}R9B zajSqd*<6C#3fpu=3<{t-t)M}XTvvqw3y$)67u-01n&hV2Yxl+W`u#G=mYO0>Frni7 z$8r5@h%j{;mu~EzSXQF7LLmIs8y#xqBy@ajlRkk$p3zpCs@!D&P#r6j-{N$b(DD}m zeEnWrbGR4rX(bq1as*8bT<%M~B;h4Ia`3f|(eW(F@w33i#r&ny2%AYR7XXOsLy9_z zt#(QosNqAJrbbM()cqvC@sm7MP7bGH$wqneHXBv@8U#6I{44iw z7m1ZARtiospx%H`9Er&eZN(;KZ+>|1gvZ1UT$C7Wk(uEccnsu%_b%}dF*l^f#TO~L zGq~)Grpr2hT;WgqCes|5I#mc%DAOeX#8bS`O;3imu2OuXguK6m2Wi%y@cr0A7o8VQ z`kKpUAUduNHPhara_nX!e<3?s%m?7x-E`v9Z7@Cz9HxBy!LR`7FXVz4ugJS9^>*(- z^E=c6)Ec$iiB%a##HWlUHh&);@nNKNdclj!G&yMjL*Fqh|MgmsaIYBOq(o-Nz7E|P zvR;X6i8m)th}$z|Zn4*Kg9uWmLXW;y(}&Ww&N--B_%}$rg$gL|XC`JzvhI zfJ1lI$j^SvM#&HXWG4Bsx;3f!U^CMjNI^uL-j+)Yzan;{)u@MTN;^TahP%Dr_GvUG zYFg95K7a;yPO>QRb5xcJ63uUOlQnKjzU%l<>liOg3(9M9gs|fG2!YuMU6|=L8#jZA z5vdZxN3{6)A0X_|5?J!*TQk0Sw7%&xpiORxB}we!km9Mim1E*E@}RIjqrz)0*#l5i z3{NQzO4(onF0!1Su&uHLlOcBCz%eD4IN4BM-knv;GosKxAN~8W7Ud00wYRTQ zi(xis4n?Z0^!pVd)p+YV!a!Ezj;-CU)8_D!6vHOCP?N{bo85yVYt~fU$gV#Y?O;Qb z(#YGT<0j}50na@OuwI6HCcP1X2$QlEgdtCMF6P+esT%zbbt`DAEI8z@Viv-e2$XrC z)&@V_hY`JziP#>Dce<{ZH)9ZlE^jCaU%9RxOE4OH{_IV$F;cq1!7)v@!u@7}9?mVM zz}p~Dsiq7k~D%>T&-xz!ckyzmI7#pC5%EAMqP;1j?NAs%nMA=7paYza?T)lf9R~4Up zW^W}bkOm!29wpVl_(UG>I2@vZ`|Ki~%`JH!(VsR9LOT<>tXkz2%;u1b0Jx=S29LOL zpAnbJg0;N1vldY=CG^J&!xRhf%{3gn>DC=6=Fsi@?FGy1a$;Zh^o3};`JN7l>}V*= zd((i~x%O&efko9z!U5Pg4j-GAOCfed^((~vg;&_j)IOduj6(&*9;+(!d zse{1u@l2byX9=QdY9h)&Fn|B$;FTQYjdW$y-z@I7{kd&a|B}l|*;vc{IHZ2p0x!oL zq-_v*e==>aV6>8+0gw&h{=2qC_s^T>j1XzGcVQn;Q!+`Y6$s^k78}PC7pqme$f5)|DbaqO0KTd>vBSxylC2!a3CY%R zB;$FNj+GbyXECD+qbq9$4nn2Zc5Y1WFp~Iw8{D{_Fs8yUpv=x|byjtm5~+_j9-5wQ z0w{f*y3q}E$Vo#N8)pt7LX>G5nCUI@WhYlJE(AB;i;K{9_p>~zQl=_j%r;wy!HssC zX1$8`JywVuMyJ>609j^^uE)zZcJE!G#{#R>cvvC2+dR`^A->GskX+Rc=i(ZaM+JG^-s06fqO zNj09OpIDdC4dy4mWBoi2ooy_HL602?;+qw73a&(3&tm;@5Q&8KEs-1EaNsu&ui@nE zHIuBmlQbeU93X;9jJ-a6AVItMZw^OrnSv+O565Y@{L7EpYiU6H>ihN*zhufLS=W^G zXwFf*Bw3{2zS8-@xX_ci?VR9oJW4BaZUWa|bLeo5={u*u$Wnz7{#a!XdMPba9f7g% zu_=Et*HjWl0ke(jGL3X{k4K-btTtR@%H<$vs+HqEY(o86*E)S}| zMljcfF)&^=o7!y5y)*v6=6_>x-%T-j--j{gEeO-%1RkBkPUlLMf z+kD6)kYj;no>oVdONvUrBv^1m+AeU;)k=sxfr|D6K+O&J_a*$^zpI?8^^6(M&$P>5y@$qtN5b$#;ef^6Dl=UNn(Dwe7_rB_EUNKPI2r zcY}#aAsWqz8eI>oy0%E!f68jn64Pd7fgYongQ6V4@{2(YRk=J6YrK|FPak?7q?KTd z?(Gyg9JWi%daNj;mxn$6>!hZSms1FAH{Bzrj#L^q&#q;X zrp|=pXEO8qs+ShTW4>a|2&vdKX9aHBJg3*AMI!eGQEyelI!-r5l5f~i7j}D;^{JRv zm}t>QwO2+Pxl!#MhPgUi7wDj{?w5-`gL{3hJ}2nX;sbS0$*Tm+wwyltD6d0X5rnj7 z4jr%oJ9ZJmIuUyD%gNgPz4v^+)0GWQsYC69QS5X$8b%(WchgmV`#WubHjsWBD3th; zCEIqXH8L#t%efqn?%lX7K=6QoWt0%i<3Xjsip9gBDoxekS>tz(W*aM4*N2y2I?@gH zXB`S#FIDG?9{maIF2L1I?PY0eK(_QItlhWfifo1-bIcEEbhd1EmM>?d5kYjoo?N!_`k)!5S`GCUD=RoydhyB&4w(i-y zVwwO);7H^27%oxa`8On#+KW-sXB!Qu%!Gl8UmwZDpDEfUx>r$3qUW2=QA4x;&7~GD-1h>TJoSYUa z&2SNZe4e4Bear{W9-|gUv^O5mP$)BtU7(^-abHy5M3EyW&E63~t9x@0_!-<>Uj_?L z!D9*~@G6Z$HtfA)7UQ@gcUo_R^?YmpLsK`nM0sSd*FA7UHTR<7g}XqSkqNqcmP#zj z{j3X{7kH<0(1)beL%WxDh$AgSW>o14hwlra8iX`troH74llc$~4F%&`rjgoicbc~5 zbfB9`_@*|EW)!}{@C-Mx5B9Ubg7=~e9NgG}w3iK0}&Tyk1;_o*W* zFtd_Rd?lh)iz}+s%EX>AIv3b;D=+@}a@iD*Qh~53WD=IUQi4Z<<$qt2U!xqWcW2MQorVFGCt5@xhoj42FmW6?AEhy z=ZH#(=GkMW*;wJye4@z+6h;O&Y$8pCI9AWP7_`0wjGaA z?kA(i0OagaBfVwwLWW zmbc&O02f>CeEKC-ZU`>3tXkj$F(_6C4?)-lk;(MR+6$pIJ}*uglhji~uQEs0GQ4RM zO;0o$C^Ml(q?qTZBIG|A+5ZToLjoye_n{dqA(wK{CPfCt?7ZmX6~#n!)iG;jiLzIp zmPzrVr)@(W*fYwdY*G#{d~IH;Ihu~1uy8Z$nKU9f6Ni^Tqkj5Y19JdTEmx`*gT`U9l1eCvy6ejzK$r2clNaN!WhgP^G`IF%|p5ewq8k>ZU91qwW;r1T=dH(guI zk5P7*nz_g$CIc}{WeN(HI_oC)-0eB+RBeqSHE^+kAeU1_$c{%HqO)dLnyqUFOUO8< zF0-|Ru$Ah}(vA+Kt$4@{dvbQinSfm%xb56??vEE714BQkR7!HYBR83~?pCbGaFm?> zuyAA2N5Z;X#$q-3CZQc0S(;@vjyt7qa7)tH;gcJ@#L^K$-j9%)21w*bD@g7+C^AR> zd^`5JA3C+|vQ$apHsLj+kRj>lhxf;Fy|?*Z+Xc8izCmSoCox3UzuyuwbDMNmvmePo zRs;m#TexYf*K%q>!sDt;KGe3*A!!J0^*VaqtOldde908`>J~6e<`42}AqzK=4Gl+S zP#T_>dQc@^J-93>)^e9=>-D-G(U2WVRyp%b!FJ0N288hECIo|$E==o0)_p4SjO7*X zZlu~#OQTk=- zVlVp~;Y0O40$D;kRX(CXJB`owUz<|}gx;!#hABrrIX+5Oe0rE2{Jm^jsIZEAq}t^u zG{nYN2Uii^zJ?8)Z|=%X;rV42gW_6MQ9aim=Yc zxg@;g1tMRN`Cz=ZE8k~XYs6RH;L-qV7rMvR387dSrbA{NW1%rb;K_76Ms}X`W&|9- zhZkVYR6j1_N7?=(Hkz`8zA(JAN0}4|fP>nF)V3OvlFwK$+S!NLs{0lzZ}n*s9zB+j zV>XI|Y;%FEUVv6>K)pwERj9A0k@ctvrzP=)Wl*TRb$5#*2Ty!OX8Y!(Lz|yvuo!-s zN4>?5M{N=emP!V(hb?aNmSUo@59s34D#cd68KUjl!x$L2&23oY&x}OLXEHN@b%S z(ncA_tOzz)c~8@+0l~kg7Iiw2uOV)O=#VZBj12LU_2ky(1JCp?Q-R;BeKEE;NZ>Cg zjj5-U3oFw0cK4PVdZj5BBm#EnLqI+)*PiRV3eZrOM}kk}bRUB>g@``ky*my(m`SB& z0`D$oG}H?~d%QHDdUZl7G4GIn-@>pLM`D&hTbOKSY&s*DrYNGQUvS<+$9|sKyA85Y zuDaXAk@3ECg%1%C;{1}*)p5|am8XmhwGWmC)bhvqxurJC z&4b*%NI7%{v==uvQLPJG08i@pCbsG+hni(oGG_(fZyT@tqQR$WBO4H=*9|56GpSG! zV21Og3Z@p%AKUifKpt}{ppahE&aW^aqU4Eke|{)7->E3N(?-{`au&su*XSXT=JbKe zFF%GM<dts;>A^e~ozw`@pCD47qpC+EdCcBm`%@hU}z2H;L6;jrPV4k6(ukVWh0=iYwt5rE%yHEDeN(n%{;=uUd|>sevcq zndgRvwdG4UG$S8ln^kLh^}<>K?5Lp0Ty_KMQk{opAa-@Z=BKA8?Ur8;CO@X}+4Z%cQ380!`C8nffgFfGXU}4V^?73Rgqv3gC~; z1>Kw8Z_5S526j$IJu9kL!DA>CuNYy4uwTynB<3BHLjEJa+?P6?u-%bByKpBjUw>j| zAVqYYh;wQ8IL}}EdpnPZx*@G)Ja!xk@r$-%VS@CkBGB-SG;}4*l!-PB4a8;jZ%XT6x!i@+5K3*d?4L)(m~a*)4y#Pokzy8m$SzqbDjjPZNif0lo(G{biw zf93D(`qxVRb^7n?zdBY127H!pQtscD@!OAoUE^N?{^vRW5$rz$|7Snsf3SNQ{w~+Q zxVHb^qW?E`uY%e)RQR9p-tX}KvHSS>tzGanzRA68_*87nwD@#%-wv^SLx=wxzL$!Q zk(!nfpN7xg!04Me{cQpHZ-(#RfiwboHljvmCZ-Pm`_9s+{`1}6sQ+=PZ-lN9KEq!O z_V-8O@6H$gzc75inb;0SmdfAj^`F?){}}kMSpS3J%f`a+FK93Q_uA5aTm6p=Uj~M6 zfB&Bh-)0cigp(%_ac6(|A0iNDtPY|{4$eb>up2P58yh%3{P4+12*?RGBH|fEA=mrf zjX^U~zB-<6T;n?)y}GQct~P3-Z(IE6{i8!F5JcA(2SWgdhsS~7W0V$w>4B0{)G$&~ z)MUxXmAa*sv%74N#hIf0iIx|Gj*`F;{ucmwK!(2ukf9R<6E$>#1j^dl0HmC)06?H0AvlF%t6+WHyRsS0hDcx!5}C1-yQ*=er4|DWXHqA~aR=L&0ZhSGAb`BM45O2q6FtDt#^fQ;(8|#ka^BFz5Nu^=1PS;fbwhx- zkRrekLh(<2j>ZmPJ10j*N3hicN2Z6zK)$n>jfse@wKd4b$q@zRAwf~F1IQTi<=vV7 zyk1KiTUQ&;zbsR*jfv?44-;oQCRH1-y)#Hs^v@Uwgz|`)ft&#B%-kFt>_7m>9sqJP zHfMTxfr`5w=nv-~FocA+r=6`Gz!bs*fdne+L*^gRR{E41rX$vlFB8Ng%>wsCfRK>q-M046)gHQAa#njZ2=ARqfL4+|vJ z*~$rQ2YLBl7%PCu&=C>_cC>`tr}^JF8-U3K44ILjzY+Ea#L(K-#^kp^4#?}QoE?A9 zf%DI#9z1`8TmU9xu!FG`=r@%6FZ3TQnE%HAnKGn5|8)H$LmiQLMEsYq|!jYz!0MUgdikA_Re4zLq~UOJEw%+Jo9gvIq)x`oKZ3QV8$nzk5^*0LPYxc0XAeZ^O1H#u1GFL$+pno2La5lBI zva)skrvt*-5e#V@Lx)EULfFvG&cW90AF&Tt>tOqkNMt{+;?G1|bUR4O>?$km=*QSXuu>|2MJzhS~mvAwU0(vHyvgKsL%F z%JC;^Wcy1&vvU54+SockrW~X@9$j32x*Tnt|LNlX`@{bJ71@~odbN?&Z|SoEA059l zU}Jgo{6d0_^%4G!2OHa?<2Nd7?7vcR1^tpQ8^^Cyes}$Oy^u4I!S*W^t_KI$`mxB_ zAgsYQknIh2dhAcgtYZQ}`W7;N9x(`6NE)EWs$++cge+0RKZfq#AnPLtwsCpHAq;Im zW`Br0M=L|eM~aX!$z*M4=jden2tlYh+khXI-Y?0sKlG^^7&7Z@oWQ^04;5=`_OQup zewiBV5N04que<+R-H! z9$c0nr+=Jb|JC=;gAn#czmz_Znez_-GQEDmxPF<8Ow{W58R4?5w0*l(Zs zK_u82Iv7II|7Gj|A0&dYm9x<=$fNZ81%%LqtiWIO)PpW?v@`rok#YUk<_10tQb^>l za_M1={zC#j4AK8X$^suI>A#iXA5-+d_%KudOVR=##_PXDE%0H^{#(`p zIk^8**aA73{|{-)$@+h5+lRUP-{SUR2LFe;eHh07EpHzN^MC2vhY|fRjQjsZ;Xcgm z|CYGG|4ZXOjPw6Pw#K}H79RpzE~|uyT^YU&AuO-9g~e)i zhq60SP3L-=%OIvpjM)ntm{bsaNl}<74IZ8HBBnjK9Cp*`$n;xEex2h4CDy$ze8iB~ ze|6en1|l`FB{Cb~ObJ`ZK8#Z&!8Fi@(|(ztKJ_z*vy*kJaT$DWkNa*DLR(7Gd_0P=$Z&!* z+_dh=N*8L(*oN~Ry6Hq2$e+NZchRChAsr4`Za09jf_2^l`yfhFzv`RF*KKs*WZ*|P z_nO|?w>mxS;X-T-b`_{89YW;7WZNa9z#DYfSX6GNIh3ZKlsn>Mlw?mZx|6omM?E$u z=3Q!m90tLst{-v_Zn=@yui3v51pfqvg>@VP>VKqRl5lrEe|c2s*(XlMb9?EXF(++B z^6b_b_jtW)JfZEvq$~iLi$1cV=^aT1TGli|s>$TiUV#FYTM1#n3;M{!UlmkSdE|pLyiUXK)2>Fa zli%00qnaRobEF>Ws*wH0+*g2-ihuf^f~!7*BF(xGOG=)8xRWgcm->64NECVxuZs_7 ze;hyU#^MyXbx+s_EHp6uMajXlfsKR*7bFlrJT{k8TAd@c!;9dmP~JdhkElTe^&~f{ z;mNvIbZp(|$#QdxC9f!-8Ne#XMB$i%2E!wsu9m-ySKkkM_iED4CNBHt#|h?o;9Kkx zaxcaoxv7emN?Hgoxs3@QQg+~0+b@kIaVJ^&(g|u|R}qk=Q(%pa`qD!1>44r#EKiiqrDyn zb!Rri*3*NW7~)xB=L(BX@vj^+$CDf@8=2rsU+ErUZlp4 z0Y+2M$T2V7r>F2{fKXRd>r~eoW%!(Xm2uLZpSMG;{Cf^Q4~KedHL+jVDX|S?KZgta zvXo6*r0L>{B>6EZPM%yoQmFMq-N8rs@C=blUdmo8Uqw3_Nj&Z9d}ds`-FCtSA_Z4RJ7;=>!# z6V$<7Q)&aPyVVzm0!lV4G0&%oT&s8*g{J(dJt8AGuwhN`{I+a-*tIP+PFgdy!FW5y zs;!!Qhkk)Xt;?3FB-jQXei?#L=YFS@O_)E}38lLy&+leR6rew2C2-j-B^ENZVTi4Q z@JE-NzUIge2%mDkhQkP!YCvo?qHe;UTmg7+-lW-aI6tq^swBB#Fw&RH zGC~J06Z;~9UzL4D!7J^KTY0jH$$nkUf{GOoc7VgRJ_SO%%&+db<1G#z5nS|CC#}o_ z--ih6=3y6Jh?xQb?X z(U7^PChRMWR@HeIfNDU~Fn&P`ct?WJj)_!wr&@uG4>s44P)y~*bzUIL6;<_-jwtA! z3NK0hNur;a&D>-Bd`XI7xZLZ?ZHYVw=k$nC%hTiFqx!Pbp&l}`;vmw%f%{KkL@9~;;a3A6?ACiDlb)#n+9~e}q|r5NnL}PoVVPz> zhXNF7-db6GqCZfcj23@kmH(AUijUimE}xZxOAD0_LA~WZTlwGY+d(sBnqQ!Dy`8ilnauncjM28eDg3?YZs5G;nnsHo_3y?Dr z*@>=qk|Ho&HSVK>dN}V~c$CF!IOw2H^8%orp>VAWe{O5Kl40E1K_%v83RH=H<7`eA zjY>GVOvy;sB(@P!E`UtBi8$~wQqy$gYC!JQ4DN6MX-)<3=}dXEg=aik!NL_AZ=rLj zO=44QZgY1_nv?^zSZP%M^bUF?H}rJqh-L?lck%dB*wSUP)Or9p-Mn1zABWVPW zel4IGOZKtTvKQev+R_a~<;-J@hpoesJfcv-Fy(wNT`oJKP-D*Ec|it}?lvWSp=u*G z=yNzgXvq$)2G7eu*X1c4E2k6Ibk_U2Yd6FBTh6@-ve$sgdB=#ll(?s}B(}bA-tIqt zYRxz9iK}Ic$gxhzw6I3-UANJdpW*#2Nrkc*Y0V7JOI>K;m z;6!fb@>9m&n7$LSZio=VaL#cJR7I1i#X zERVaYYWs1P;VYl84}t`tsw{{4bu%sXIWBL*F=~ilIvb6bKE>>}s83$;QNgj06&u)% z%I_P9k|L*N#1`GyD@$nbumeT>R0V-88=G4^<3F1X_t9mbHH@({NfPx^iB3-={l4?C zqu3SmR~Zr}>%>lfF9x&cd|7_$CHoO@c{-cvn7izQHU1e3Y;BhLsWO0+Kke?ko$DOj z9Zgz)j`<_x$DZ0#nnU3fwlAR;S9tIEE!*XvPYV#pBzDO;Gbq_)gb6iQ4a+*gmbnkFb09^aGoI$ueN z&DeP!Pu`x-;buQ3^mRZnd-v4tsCEfKW3n(mdC_vxtK^CphS)^WX3X+1Ez^aW-RAmSSUp4)er1j zM78+pcHg4>7k*7;awX8y>U>E2Og2je6FIy0^q*2wV7IxbH+$D9RKsr^*STU)F)UK{ z_qCFg7g^_k5?`kbd%ndkDDL}m5SGgQjdG50=-g^HXLuH-42vOtDE(!ka+A|xB-$xGb|Fr# z&NV%O8|!7zspi5i+X2N6<{K;z^4SBb%qPVXakgq*lp+=SezRzSjm&!5cgLqSYQx{P z->H;AEo9ol*1jL{C>$p=;dG`fHoR#N8Ba5jc>l?z?>a>KON~<%y>fUD6_i}x>JBWr z!ROqRq4ymkj!X?ASV$w(<$Gc@XXYt@i9-zpmw_PtyTu+$FOnng#_XL)^!n=}RiY(X zGik+*^=oO0f_cT{m1kCY%etoNuQ0p4JASxyHsMV!QH$ffXK`82dy@GnN>JFsV7q%! z*cKP@1w&bQNyLF_rw> z9%BwMyLT~rSAKUsm(xaaqxmh2!8j(qZ%BA7YOJvb4rdQv0IQb3M6n6WGq{M8mjanm z>rY9^5H@%91)hxTi}bu$nO+myzX>)@T|U`M6L)g)e8V@RJiu6@WepNDlNw^Xxl#ezh1(d%UhDu?f{anlWNIwF zk;(j3M`aC<0rkg2GlCGl9s_Uvqo^`^jdXYVb5W)cJNr`(W6TK$rE z#;G|A8Jjm^T&HS6)*OQLRNjpgz7{@Cm94siq^476GGSOJc9W~Q7n;^E8_u1 z=!PdcjnehPKY==%Zmi)|MbtL59?!1saSNeW7Iiz;S)zuNQS^*N~3P zHJxnIF=i=>bBon$M)TzbH)EJfGD_RmTt5B9-tNwx7sPqjNHD0Ox9PdV5&C1+8pZCc z>9m`Uh~kYxyf*M0_^LO@WS#t5Keg6==}Be1vPtbxj$THYjFiW z@cgUASU%KC7oBq^AL$(>JWxs3j`{pZ(n$|vjeq9m^0wFkV0$-gz)HLc?^`KgW-VM0 zsT1o$!iaXAOh1;R7qf`&{wQ3!oKG+R&?Z>zGFIaZlF57Kojen>N#hT0FU;4}Le0-z zK`2gVPpC7S+Lwkdsb4FYzC&!sHsaec;alPkL?v>}VBP2Mh40|IA;fb?wbT3tZ3i>v z_B2At_nMKaXYyIGaZTWlk+WiQCVlhOAac@O@-#SLHUVbbTxu~d*QyO}6vLO1!=lq~ z?`*eB3QAAE8$HK2rM;!fqeysFy&_ev9MAJ(b}@yL<8!RkQy4gSvC;}jxEZsKujR4c zj{FORd&sPDycjz+L2gQK#zz5Cl&MtggSkW*SVVyWR-8Pa`_{9$n{g{GF!Jo`(n4Mb z`i+MoOwjDW_>yI=>Fs|vz!}VLLgetr^zRWIiu4X*$iCma08ihpE*gaz-Wx@CrrYujW)a@L#5%O$x?^pgVm^gik~41?9~R5)0P!oTs&r-;y?H&wcr1+R zb`VSm1@DGD_N7hI0@QYHeg_LAcvv>__V0Jh$751unIueb1`7EWwpDN&JplsEb8 zC?V10OQ4DDY<<7Pzy;!j9ilwbEkAZJ7cIw9)B0>`_WGzSRY+R=6Z%_;aV>KEr~=bVl#Pf z)a+EHCMqeuCn?E%Khez6g{oXyt5bdxoG$(enI*aRP4K>&M%N$48hJ_4CC3Ing`OyJx`rqVa#of|1=61osp6f! zO@}%t;7pie7TjaR0k#WLH7;-ZV4#X)@b+wC+Lu3rKS?^r_m5X?Zkmd+_R%`PnVVrR zPAE34NF#3s_4<88QWG360(t=%#(SP*KAD*-D2nett1UN)c#0v-JGQP zuuh8G6>uzE-tN;z>k`Ct_4*8v?80KExta zO3=r6CmLU}&PAdSWX4p;?hqR#x@}Cmp`F8T`+GzOwXY(r(WMsUg~b+T+FB-)PmM3d zvs%l3V7ny~9!am0?Pwk+OHTX72$xl=Z4M{&pVo{Hc@ded{0!QwsC^xr#l}i}otcg# z*l;(xK!+lTNT(#40bNdCrVi>X1=kaoi5vwk-IGRN6Uy7PMLv_z4^8S>oXs4FkuCDR zg(-kFK{O-dDZGBg+YG z&WBdz9lMqh;elCx!GNT+xBVmHwo9jkj^j%W+ZgGXOJD#=tpMAke9&<^9ZN5@Q<-)8 zr;u=)TeW7nqg8Y!C_kDIT3A*|!sVSSFaxb~qg^3CVHi5rDqnGy0P!g10!r8c^L`jg z)XOMb`tVCsJU}q{ZXdg(2f_PHD_-m<#o8eY=yRK>fdoQd&W4_PC$cV~OW^DgfSdw7M2i7b-}!^S5c z?W$$;4OlwnE856P>b_2VN@FA(5vOJS;{Z`(bWG}dN_=VM`0MOqc044nzUn}k2ED~k zY+NOu#u!0pmH^?Y7a&{vB+SM=WvKil5u*8kj+6n|vtv{$=bd|}Z{lgBEOaQlT$h3f zCTiG3%(AuVNbgqm&^lnqR3sSt1q*dr-_K930& zJ9wn3QrSa3N0kPixfxKX;0KU=xWru79V}N3^G#kvfjtm$tJpD7-$MOSDk>1L@|5Lv zin~0S4(iT%t4B!FMl6O9$3Blf8hh0+@~+6SV#iY(=nKDQd`zf;Bi|0+cmDFtS)C%h zs))yKccV{axh>mPQS7_bTJ>G#WO#l(oC_Av8P}UWWo(BbAGWxevRVpW0QYT)KlS|^ zu0ZNJTDXw6UTb<*Lb}o-{NQy@{$-%I*JKQF=Df+A(`qp4L1V&ooTac`GVcL{v3}}H z(`KdTZzvrl#f$n6ny~^6bdXL zpP>dJ6Q+>A2t9snF{0P*^0R*p_M;izDtbr@Y&xEh?x$%idi78|2C9Z2c~?)?w1ZJ5 zK=Bz%*j#huOSj6b+JL~P&-o_^^U{sP;7JF;}gH*-K54ktUv#>a(Z z$I@~*wbg~~gW4qfjptHv00yDfTIVgbrDK4>j~L{GgU&0?h#M}fqfTVkvyN>qDIMla zhfmX+NsIEK)aYX`v-VhGp5;$0044T}*YS~q<=c10<9V2)ClwEC0dn{{B!b8nW-AI+ z*egi&Em?(cQcw3T>P-BD?N%B;h*n*XH~GBoWMhUo(x9Mt<;|h`jJE1*^6)OTF(%~-?NB~iV4b7vqA7j=Fd^)%utS91l|0bD?%klI zsv!N^m|495{x}%}iwDO124Y|>S(Z#W6XQ^dS ziv9C>ukcOy;Zm!v&r{Q2I@I3q88G9i4b#k65GYX2wt9cBuzl&m5RDWcm-?eF&tko4 zfK{_CFL?Kf$(*{ab3}J_wz;u!T;Sn&+MTbu-O++4ST1gjM8CqCE*D0rUv>rceW>lH zNzA$K$qF9t5V{c+1Pu10B@@1vKQC%r=hb*rOkNv$g+V(4E6<;gVMnjr=awaF+8z{q zjhS8h{H`}Lh5VWEES|zv-C2OGN;Ngg>@xj(X4IBtD{CjZWEcMSearYesXnVi5)S_6 zv>Cn)XxSw{Fa@TMA>9Qm`m08-d7x$!RQwZk8pQZE6%vU)ZCcF6wg*#>j&s z9N+>uLwcg6IiX|E^n-=rOHFtEH{Z)8-lOX}rOBgV^Z^UAipnx!kK*QUU)q2uP| zx?B0XN66M5F2RbFywl%%N}6~9C@s`Z()dL44AASuysC%k@p<@|XNzoO82{jBu@<7G z#M=OikVZP=Z#&REWyxz$RbjF}%wO~I5p;~&^Vx(roPB?dNuoR(%hT$)nJrTvh3Aj# zx8wcnWAXjYQ`Ia?HKs-wKCN&{vmXE64287`tuFN0TTQ7ki@xe=%tx3fy+NnSn6W}#5=?Z-vt z@pXD)yK~&$0pe5yV`!Na#h!auDxQ43fG#EYvZMo`+%u&NK`NlrCLQ@$=9z;bXBFc= zN+)Zs%B&A>q9DHeo$ypj!%TAN+}XTdSErb#1s@m41m)ObM9ztfXr1s{;KYRs*X)O% zihF;~XNJyf`z9;SHmg^L9+)s7GzYXw1p;To2)j zm?w4)B-*l9dRTli5P{Cdry3za%f8KtjY9^eHI05MFXGUB; zn(ukT>=Odl*H1G?n4b1vMKn|G?X&xC3BFFJuq3%qm0OTUPsSH z^G$&yV#X5XXiF_fmN&oqw+=sbY#C7lB&yC58ei8}2LQ90uYyTCuuIY4xA!5U;zurK_3E#ln4@ z?K5yIxfvz*gAP^)zk=7{j-sQQ;R8;V03yw$*Ws#C&3!Y9MA z>5JMaPrQrxo^kIJXba_O=PG7Jz`$uNxSLkFW^bB3;Cd=b%rl36@Y(KJVw9%sVSptp zc|1|x<`h-xSC4f6HrnFp&Q!l=Y4PxZ6^+d>A2#zsgEi&bN@9~V=j65g>)&yl7@~OR z%$LsmY?eZ8qmJ}f(8fq0{cQZ96F#cjik^H-zZuG{+b8YW5Mi7f^>IdZ3`$~m8{(Q! z?^7TBRQeoEn)+y#1E;7W(Vr@MuLgrkIVXEdp1%F8`ZVGTHn|t*DsxYFRf(dsIx5g* z$u$H=%b=1^WJNq~ch%O3M?MyROXAey$&PR)v&1-lW@P4z)tXbBlY;*BMOP8u!6-*F zyaH8+>B;H3u}5PB>|`k zOT{l^F*@(zI79YrUV5;VPO}a-mNfIEzvX=?mvEE#bH!7X`STu?&?Y~0t2K3q-e$+> zDb7$B&1aeJDMJiD7ekTRy5}j_1QTWVX-j%hsG6K+pX%8MIZT!4mlH3#1a<00xs~7x zoaB~%`ZY7BsCR$8x{p#F%2REcKVVWAqEX#ssH#VkfYbh-$muJV4_||0@(9X?8G(L$V=|d>a z8l3S@^!QXM5p~I7&bUe_TY#NZ10)1DFBqAfvqfEnqnlwGXz!XG%qd5 zoNoH8CXz*m%26muuHlTafAMNWZLY5hCaH*cUhmD+%>MD!;Eql5FyBkh;4f()yA?j2P*!bay9|(?MPj?xN+F}T zXhaE_e5YaR<}>#;=l$B5n&iQi1o}D_t(c{vkfZV84y9Mp9eZy?vo^NT5qP9zyI~@W zM+qcHJwqp;pg2S?lGuEaami*PV~9=Qs*crLJ-FHowEGFXzpSmPS>okw*5Z8XQdd>E zb1$4VxzwQY*2sp_K@}ZM97C$dc2+WP&>wN>hmNAgrX~qAvs_eCq=;*C%Z6vd4_(7W zntdi;?2w)m_6<4HFNR#i)!dW>+SEU>3mP&ZBO%Oqr&& z(x^M$v(X$jeNa_j(qV|gr&2qDCZcS1a$Mb0EBIDG1J3NO#+M@STDY?3Y8UcXnF@d~$Zpn!8oXUxKguL2k%W{L3 zCzUeDe81J#9@Ym>Xrt#|GA9d%aT0zEepF8Wqb(+w@WO2i9W<vyZ!EjO~29r27w#69DMSxEOs}!!%0_!@tqoM@w zVYOq}glDdpv2ZyweZeqXeQClV&q6kt{^pVem_M{aqOcjYL9 zbcY%^>vlRRD?3cVRIE+GEBvvPbHCx#mh{T8I-hx=DT*#mrh*@7K&=Gq1!tqp-nw)& z1_eOEe!GKO$rN(ER*lg59o0m+N(`SP&z4q7kS)Z7dfri4XkRmk0lY&`O!n0-Zdbx4 zwAl2hSw7$HW4qSSDam*g#!a4YWJJU+=9iV5^ACWCHRyXB=U2nFYz@My3$%I5u%-<- z*kf5MR1D|&AD`X^AoRI#kHpOH zOTQY)05TF zkuxG?4{R;H-UOmBXA{HHF@ljQ0&s}m6F&KiDSGR^)J_MvF zToj$w-?7DfIwfbWfzo}djjM@3Ha*qdf^dA{;h60q9aq?6LmDiZM#EHP*OEvSLEQw; zpe4Bxe<&NFWjIXU{L!g8V4rohJz~QeqZN)gNq>o#^a>7hFYsCTp!E8@!0EevE~n>m z25VhSpN-mmrxrs8K0UeLYq;RG&y;#o1<&P9Y@B>QTt8Xw-H`;}Euzf{$Wea+%j@Ah zSU4ULjf%wqpqyPLUI@VU$7pi!y`FE?3wvkFK;|D^lH+ULr!gS>o&8(Zs7p$dIE!OX zZXJ!TmqvLOt!9H=i-q;X4EgPA%6cGTXVugQVyrWz&e^*ffqs>?gnX6Bc7vmxckI<7}KUOj&2oi3fh0tKFkLBE2xb_bWGW4yhPCN{4z3a&C;nS zSOYcIh>y{{ggDls=nyc$(iyOQ9L|NsPxqAFp_Pz`Jf-m!5}#FLdrpFjSoWVuE=XSU zcMX2oC4r2>5np)@La~CP=|F zYo(2D2poYyJe((}_a!qxL~tReQys~1>pgcdpxbTKeH_P6P>ct4tIXws1k1`o!-R>* z<`(wbcbo<}0{Lf{!?;qRRwjDtM$N9Db9s--JQKEEOtDjhj84mA;yAaq^L%VdbezXF zZ+scfv-c9ZH#P2e0lL?Bp_KFP)^gG0j8HfX>LtiUD=K`@d!&>v_L&b~i?DXSNXm#v zwZ}|~&ZJeZ73LIn!eYrJ(z_pDUG1QJ#Y4&Esq<9H0XBvZB)}4C5_)XWMdqV!{Bjdv z=oFs{PYow-t4T<{mB?8GNj7r=>9FBmr<=>H=4bL|St8+`>}+^Cf=rM51T{@}W->Ap z?uB=DF^i{ zHfXtCAUR$7zF6vf|Iutj0&N4CaOWt`Czb$VlGEVYFO^TuTmnzb8u?Ll)#NcWVh#$F7N#OpIsN5pR9FGI-+3$ zNT#tEk4z8F0PGqF)h~xE;j6k$1RaLXM1>j3pZQN7*L6JY)v)&-k0d0lO8dOPue(z( zLVTT=^ECsyfqdaRuBO2yZWN#k_?b9k-DQsQ!r;unp*QUUFH(n6RLA_P2zcbRTSN|e zOM?YH^MU)L)s!Npedo#H)DjQV%u{ZbbEXWHIK9Y?tdO;`}f% zMS2>*avoO>&nEb$reNIq6(Rpi!&_Jx^NewwURJFc0}OXHC;_$o^TX?Umbeag3!+e( zDf*|&0isQB4YZUaSui6eNZDVb_w0@#2aj&rPny${xK8&GGp6$D(x|$4XbK*X6=7@M zD7qjM-vgM{XA_PI`@Ge_ds2D(#8|Ia#-&8kv2yuMHoqvtLvgWCdg%;CDHB?ws=xCu zTK4EaxqU)?Q~Zu?30rM;Wng-|dNomtw^x6b6w?Js556jSl^fS$8E&Bdi0ZQquv>I3 z!aJ;>>>C9^u=>^+#;M#Hou{#d7&Jy!-b9{sjv4Qlbzmk}&3YELHUZ+_gdjBh=`xoRK-jFP|rWyU3s7nRTRBpKw!2#VIE( zV|3L2wDJLJ>W%u-C-_x$^jHVDG6=~FGuP28a5aejLZRZR!fOW85*mK5p9*2nEt5B_ zA?hE8kZZDYbS9|FkyY+}N!5l~zcrG-gZ146b{VDW?uf!-=HLo@jO%%Z*y(K z7cI0~t5GpXY9@xk5FF!o!ASYNO5{O6+JhZfnMkfvzj)mto;=J=goi%)jUt@*TR6T6 zqRn^_2N9)lGtR5mz~xe76 zuPFUB?jq*ePjSx(0=+Tv7X2VL3I*J zTzow@aOrqH*8MXK&0$?~bHZoZzal2P2^K>;$3HI=`YaVCi;*>naXFCP{CN$ zV}b5`h#7QQ>Q(ejzUXjvjmxgXyIW-2W4GGE%rnr2Oj9LZ#sGu~D}*ZJvyvvHZSeeC z-zmS}^{#sTB2Tken|I5}*sJfN$vSKs81+7HyW!emF_$KNMAI#s=vDp6J{T(7alj%0 z$N1l`paOBr`FmZL2iZN#?r%XVhN0na@x*D?a{^YuT;f3;V8WO8q3LF|yl(={MHJ1HfmmVv7JZwNgoo3%WWNdU0d1|be|1} zz*wy>2|ZzI`MfHD>1cST5Xgr)&**^sVQ{&-cNXgti%SK!zWAJtGPwwrjP(5;uhE;Z z<`TLQyU^SUH5quSr*_M0)oam_hKiXtKN379r8!Hgd0p-LnNv9A^@Ea|cz>)&;Gi-lVUS#{&np20ig=gvw?(y)ey;% zA`~WP$Eax?qQ7x(~19o@<_ARQ^!YqyR1G!m?Z9ZK4W7*r;PrOGSX5Sk3S@to1u;p#)Z zZ&oE>ljhK6y|>rWv8Hp~Bzt3^cm!y|M)$A1N>DMwX5oq!hFWOoR&&1<=rGx8&y^nL zdUI`tX)dAYK;L2LU$kC}*N*DLV+7>~5>%vPn|jTgEJ=OI=f+&hb=O;uDJESl zqTqx$UQK5;hAcZ<=9uD#@!?s?hF2GyDo~XA$9JgAIJNxbyqC$9{fc$Ip^o*59R8^R;o~YB!0r2hfP#%wt(2B#&UrUVoL-ovttGW{3;K zlXNAN9+Nq73s?L8X5)v{X!zd2RQlamk>yDK;tMc8(#1 z`PRl}D5m7MA+VMarP~Av^SN5L3SCeZty)h07VSYjmx|^qse|3eZ#cN{#eZ`4urn?3 zg)1=Q88EK7&Gn)@-A^9+*8HxCHiewYUJx}x=2k|OfK}}Dsg7)*-TM?|U#pOJAtNJ} z)x*Z7=lp0UhKAn`ci0A>>#p)LrYHC^b#;`LPifGieYh!ryU|lnl#XKhroF173o8%s zNM1Iq^eKOaFlKR<@Qqj^gSRC5Y46>1dUjXzGs)1_w*@Zor`eW?a$E1mX)a$@Mr%w@ ziBV(czEe2e*SBt0?H%w|x^uE6-?cnq_z)nx>e#$z;=^^gup#E3^g|)DiNeEW*WX;P zd^^y_6S2I)fmt7+Gz8x_bu^u`tXx((H4=R5j0-KtFqF>G0aJ38{z8q2TpC2|qii8T!c zW}mV}zt!CGLf=w!UR_E#aj{~cJR6{65+%*qzN}2Tni+7kD5~%XkCOQaYflxkL1e^x zH&-S9{r%1Pwc#QWf2XRe%w%M|$7eDMmmI(ZAKD7;B#{$aDz+;Qk*kUqO?b=AQDs1| z9Ev_X{T_6~cpBmT4PPH2b1gX(>+2?ak$a8v*icxou3g>>BqGF zEE0?-DbG18SrFXpB~?*>ZgcZe5!1eR6moK`H4~Q~3=E)Y;V&AvmvA%s$v&=q_$>%p z?Y)IlCCjqt4GawK?hAK!cXxLm+}+(>26vai-C=-%!QI{6-FeKu=iYbrKJSa~AGjT{ zR&?abs?5r+uFBO?zomG(hSo5HTYU?gmB0p`ITi6WuwCLhaXQj5^mVI?-XI$oR-f`^ z(I*k}AMfs-_cwyJx8B3Jq$IMpYb^pCz^CHGAzzG85n(;vvNDmqheYV_FaU6h8y znQ$hQTdj4JIFCCPO}OhC2cco~s*1tUGtKkGtTCk^|31!=sWQ=y z{2};>YH1J%je_q^Lh8e_Keb9@QEJVCDdVel5v-P>z+`<7KFInNkZ|+sj3_}FmD&U8 z;atCMc+ncRrf|n{M$0*dy)s;f#7Vod2bzAP`jUsxC^OvxD(TDVThs)(RAI=Lq0D685Q+<GPQ~CfG`5os(Y3N7k#jCn7Z!W-^r$* z35wn_6gN|Dx?>HF6vSSACR^$tSrps}v0GbLFpoXgyaMN*w%}@DMRg*wbnDx0f_)*p zu+lBwD8~BswSpaUq~S3UgvDCZ!3U%gUB#r~+;`t^J@z=bq?*)mYpNCZ3!_ZGjda#tF@T1g~I6gwh~y2vJ;H)1|$sjY2{u#V*Q{a zxfONoqkMdpXmh=z;!M0YLmszjO|k<(9kSPm)S7nqnv!N7X`b7TFze8s>jmf9I~s>TqBmE~<5ju|`M3dxT#vGuIG?6d>TN%*Us92=yAR*@{pWT|*g){4(6w+{Z6+IQ5Ig2M zhMaAq=&LK+6buWaW98$1nU`)|Jo2g{OL#>ubb4xV?+C+)g7|_N)P_FjWpcp;q!BTE zQQj;2@|-H#lU4|p4E56yF>KJNLBA2d1E~T04wj$v$4SpLic@a9W&;&##C6-fdK?WG z`IgW?UF}u_N;^mPeL;ri2yL}c)X)0Z205Iv26PG1#^7#Ua^FZn=ZLOEm$Kc(*47yT zKa{$u{9qWvSw|WG@hmCbsNnIBWv;kkj!jml3QS>cEo})a$v{TA0MdjOqAtRmIINmc z%BLuynk@$R`S18x`O(q6wG&p7kQ(}wL3`!6V9pyGb~|Ea#q8N7j@B$ZaEclwW|s0{ zZh`ef6+~39Z9K=?mKt;Po&Y^8Z`~#IiTeDbeSN>~y?V=Bq)BbZ`w(e7jY;iX7ZmTx zhoFl)rML#9+cx3Y?~3p=0_}ZciV8AONS&kRv;DwY%!YMLK%xPGX&^ar7*R{ zU0_^H>2|fF37EL3y)#=jV|pl>$8Rm%<6#o=5q5oV`Q(Xlc1H-DVQ49@6O`5F`N&Ov zv1I1LOy#51@|2O#w%~eB8tbQ@@CsiSm;nC8m^+GFaw+MyR@o4R&O`-$um?i39J##IG%gToId^UY@*i?ki9?S9YNlqIDAb4?r_cEJ@K=5+26RxUM3!5pw&|Yg>D*jO9xg)h*_> z@EE0?o{Mz}6O`#%R-gDNWfk*GFvh6d5@j#H`yLQ>SW;2TEhZCzaL4Ran$a|Q3 z9H3}BCfq~T>^H8+@v0`=w7`Lbjq2KyfX}z+GC34UATn93qSpoLHtgDY)PlTHxoFZ} zZXVwx*Li`i8VDSii^)UYxk)}sM8Db{@#!7e6tYUO1aFYNqiK_vWkx+NR@}6Ufi)O* zeUNAr?s9n`V(cT1erwN_qa1Yq5(Xc8Bt<#xh1(i_bYs5k*uBh2HNW4cJIReq0=7-% zefsPQBtM^@3AsbbX8>)#H(-%0Ah+d5|Bz`+qpA?(z-Hg~u=7uv-@ig1xQ6XF(>&=AiSCJFE3cr5vnocffk zEUO!(fb8h9dQN7$v_ElPd|%a`MLDYzG`5OlRwph`ePhEFC3{GIqG%DC0^KRD=bw~V;%-O>wH;i-G$mwszGoW z4pVM{c9nO6R&;5+Gs{&I$V7yWB}RZeK^=cvvLS17>HWT0|1{Gan+CqRYdQ6ZPm(m4 z9fF1{=`H`fD=sdior)btLadX}(`Z4Tx?u}o*4Hj(& z^A4tK8&C|{kJ|CE!}t4YQmO<5(V*`b-yW2=X{rD~=g3+qx~%kH`Q1u3HbYPSTa46z zVj&$7X7#)f@|DV9?K$1yEunelpC`)F^AtF{Cu;C#jIR6zouQaM$XSHgd>p5KeqHRU zzQ8_~9qhLhJ<*%A3_d23-5UuKGw!9h4BCk18rF|ucA^?6Wu?<}Pb^1VR0aCvYC2?u zZ*1-~cyvdDuzD(GlD)c;voC%fiXW)q%Cxxp>WXjcq<&vrSg6ZsBCMryLTpqaJ-4MomJe^r zdBSPVtK`S-qTrDGY67!+9U^Nb(&93b*9#IDFEj-w%OoldIKgr9LMT{=GwS+3fBj7 z7!Q}B(`su3i4BKZ|N3#x8U_i#+`Ao~^afKS#y3X(J)97;<9Av<(>ey^`2gA5fu_lC zF|B?#SYIw=`rTx34diB zv$D1jOIh3R`C;F9{u!(jTr3{ZXTUQ+&8m&R_ zo8+xPTpKJpCvVu481do;Z=JC1hQEnM(_ElM{5VH-Kw5?JQJF?B=@PQgO#tTC+CGq6yeFDcosN5)w1m zwTBe14+fn|bcALYAhE?0dt3@>Yf5jX`z^|J47Hn2 zBFUd;eE1ZkH4KAgK400(fJigbK@;D#KnW_DZvjjM27HkPJLlTj`-KE(it>f^-jAy- zxy}`%LF|+MFua*jd7&k8Wq0N2`34BJP|8jRozSjh&zhJAy;f&%cHFx)G;WuPOE&s0 z(rnW)y-5L1z)ZQ}{Gs~s6PBK4%-LlE7-{Ho2Tnq05D@|wTRE7`wYiNr+b-z7e_?{# zK=iiYG4R`R?)PWnwpUkG6PR@kaN@%zCsV?{HICaf$A$(30w;t<}UN*(1pGSnk> zCKE&#Tj8eiS#o^VeGIW~WIwT{60fxYtCU#~yP?qozX~Zp#!;s?;NoXpSKGC6r)gKj z@WFksBS3Ev@s2^a>Jq*LW?~w^pwzvu_RKHWt9HTfnT%`&LC}RC^!|pcrc@yMD(R0A zpDwEMYrn%sN$JOnP|7=npL&F81eLSM)EMt0P_YP7-Ud-VqXe^{8!8ltpRBSe^ouY^ z(HlQ8szwZo9j5JX__$e~m%yhWNvilIlX+XDsVsGo2!jY5{wDSeb;#y(`8QD=|)Fa-OArdCLx9&ow?F z#qtsvkt9i(xVOTbKzyWV#Q`S2+t5By;cMM|^0SPFF^ZU-yT;MWLqW$YhEFRYEcUz0 zPSgCvy%Ojog3gelIZ7nE#0}x!?dagkHlp5tYruo0Pf{a`kCggxXVGs$hfw<9?c*as?@P;Q^J_BdJ6@+-d7 z>Y4r6gY!N6uJFt%MNTE@O+ts>JMbpoh2?!1GmxFg`q)l&-UR;DY#d6ZTA`XYy(PNO zFZ+#aV@2e2bpFL~6fh)YwA~q?xx1!7 zf2@?43-T;@x2aK}UTvi;+5o?#qJBMKaQpkjW~;C~$T+ z@-o-5QjuH>+N~(OihY&7{RjFV9YUMZ-*X(#+dn+9%j4r|c*=N1CClG(nRI@Cu|usu zC(!YR0{J0s#qD{ViuQS7#Z%2upyL_gE4N3ZDH2tn$D+2Lm=XS*OaKG-Pg!h2rYG7% z;o?=`l!KHq*0CuV^>&sX_TT-F0{&$ero=Y>I#Mj^uBwvY53*6ja32e5>ta$DvjY}9 z@*_pfD|&8c*Lkic^|3k;4Xv=baSwVG40yu%9(_fkmIGa!&k!wjCk#ueg$fWQo*wJ8 zNr4Jq)@NTySGh~zH7OPh6U5YARJnDve30r^D%t2NV}9>cf6Y49T@tW!02KfdqCBe!T3$YpI)&p8aqYKkbv15 zTfa}$EuSm%0yBGrEdQ!*f+ zo5&&p3xCH;T02=@4XkcDqXyfKfji(=mVzNLZkv-n<`lzOWa2i^)(NG+3i+fe6Yl1u?La8Ph{6 z2Fs>mXg3|zeT7_i`&_aP+_y@^2#{Nc!8~`iElqgIi3Wx4wsUS8K|ERcW&19@m!RPEVrJSI8t~G%5niyF0mq4~Fnac+6`vy??#>tR-sm}y z_vLZirbY7Z>#xdrs4xmGsPXFKFSF<-a;d)nbb z0B~wVC)-W1ylByRb3X^jF~4P0X9m3K-71lsAt%tg2JJ~$AECH+r$3DwE7D!*unMp$#0VHhD_>=^tp=++vHjZBYY$;QO&^VR?wHqMbDfmBNi<=YYA`) z^=lC$L&kaEKj0yk<9@p1I5`nwM3=$Q=4_069g|Q$KVKUdYrMOK^zQZ6r(I$tbOZSV zBcduSN~oLHB?G#;f!`oMQCGwpu|m8HTlfYq*1Rt*Oi@wk>5FTE z&S0-KP`+Q{o*-CLy%5s{P|Au$aI1SRXY(myq)x8U1NvanQV0sS*M2yB=)*0F<9RG* z>OyfU2MHE!p$`ISy#%10wFj>vePx7=V^`^TS3DPozE%)3m^*+PBRgfKkcv5EFmcJi zc1N83y!&ESN>w({jwtu|rJqp)8v6+@{(UKu`XSJ*ia%N&*%4K&U;JdA>b$a#ssTN@ z1GzV!9)hg@0e!YYsUG)cD?oCpf7U2EcO9j6sj_2vSY!-D4uy>R=EL(k<0qqXVcKa7!!^q6BN!=WN%}Q1| z7ske)Od;$|ZVw;Hh(iZIG>YKJ@5WgIR6v!gwb0wH+-) zi|`hktZ}F6r#VqAhY7pd?P;+J3}`$gxtatl2vXGP@;5u>P1tNwQUrL;%+rKzY8C*_2Kjfuv!ySGfii zqtq}&h-nT9{KCp$MYd99k0On)E4u-At?urF!R^L7wCss-ira75_A(BbXt-svW8MAn zoi>+6QPOuK$UTWl{`fEcvvSj8{%O9vr5UksJMJNPTfe75O!p~$Sh?4dEMvUNI)mM% zP-Xki%VyqM^|u|+{e*5Wm(lntUnrk5j)~fAxL&bn7e>2*rIPm|^EbWvqnLu~V+VJ~ z+#CtejbUPa=3GcZJJ5_eMJq+Zw>-b|zNs(-abV|7iTj@|&|`}0gu!7WBpv|j;S^4l zj1R3?C#b-P2ZLOHF5ppv9oDc%R#ZmzyqPdHU||$-ul@*`F4@3_Mqsq$hylc$t?+jF zDd@+)c!^#-UdH??p;;_=t#D0E>JcTZ6=05XN$R8u`CRb}pVvNr1F5E@veoA2;4jCJ zmpChCeR`pD+4qX(CWPwc!mD^OB`SjN>r*Ki_Cr%78gOp?=+uhtVe9Lr%y{(1>|+*z zc8JKZO*EyRZGwoSq4)k8DCNMRHLYTE@aqhQP9enV6kErdi5fX@Hj^uEN|jYf3;SaM z%9JRHH~`<2#34f2=+{~1^)o!7gP$!{)pie6rc7YrQYTPMrTo8AW(1`my%CK2y)!Ec zS#ju&7cjZ3E_(9dN$WP~6;zfA60}*tzSXWR1DQfEq~0%5acXf3xPcu z?9BtayPgNodOz-t{8Zu}324)M&X)Af|?RdKkc*oxErMxmIY9db5X_iTzgwecN zeKk^AKv=#RcqzC!ew>AjE|@$TWghgUrD@fmxSen|BwsCTfmiR7 z{r2ijlxdCGj?dWhLJ{`khu`C{-3<615cN1g^WMMSIM(V8gT*UFtdIxF*w@nhASlmQh*=k<9O8!BF4IMt^)UG5-+ zv;&fHk<)+AdB)Pk#LR147Dt_GM6nQg20)3^7|P5IkTa!;=B}ERcK7b+xtW5EEo**Yf`*lPk^U#V0 z03%hV5lG|=qoJ<`{ir(S=UFWeV>>a=mk#51VtYk3W+`Rv&G~74ZmzfGC^H_8eX)of z6486cmYbeQ(S^bdM!C(t>_@Hj9hXtJ0mwH`Me@mdzaU`rX^daVtR4h%@wPfx4ehcd z&~2&^l?I_`2QykN{uReta=&9rtE&B6Nq+*@jrVu0me3P7U=^|{7p+|Z`{#+l^=|netoS37YU=iXK+4{eqq_IYN(7ej}NGGgUR=G}eN*z(XL(FeXg<1sOV5 zE+I-tMYROWbh2%qtikM&Ch9D-pT4qf+B`pzeWAfOm-1Ah0bkU*&EM%Cz>?93+KTww z&JL|F9GgGY5W-9zzq*nN>tvu{=RBdn&*~(XT_cM&BakWcLbb`DPnxK-PnS zaS>DuHcxU_PziSFhR&Z0B^-kIh_Lf!%u`)~JwFw|!Niy2bE}Lfr0ICAkhLU&f;4$% z)Ma67s0=c(8$BQsg|HP;az&_iWpG6iERBM0g?3e+71=VdtV&<8o1XoWs#CHvewnc9 zZ!opt^!~!o#0uj;fyI-Z6qLJ5j5IAhuQ)wfjJ5T=Qqk+|2g4%N$pya z6*>@O^upP7sN}IoCX5+^LdMMQ8D$RpgW~(R?vA?pAD{B+Yv9CZ*nsE_0 z3M>_!@ZOB6jz7L?glR21Ua7QpFim@a_Kr!TABNX&Jknf{Ih4x094T#d@j_~%3f0X? z9)s#UHZApR7b?lCi{LOW<10Tw>jGt4h{&OeqYo;G|7L3V>xpoB``$mUUXtj zO?PguX67cPTTrUlTnF~%(`ELAk zzV3``xtVf}r5^0BJsvgA|79WZ!qZU>Uiy^lD`%P~cBDW4jVJd-1n+PMSUn$tPm+Y7 z5t5y2V(8oHdaD^cu9UViqFfM&*YQ`mPNkBoHilr*4oRl4oIN$91qn-0^uG6*GbOd$ zh86TF!ws+!U=saMHoYZNVSd&dmy)zG@(ayw2OEPbVSxY>+y?7B&ou3^Drf?tsZlf@ znhj3Dm5C*Vj@Bx>oMcAexft!J1K`f<0YsdW!25;XgWtdm3g&@G`_mtvW9I&~n4T3u z`yd~LjcGrxi=%eH&iZi7Z;>crHSu0qvUL5p3AYZ+^6G&w<#QU{-u>Iz{m`KR8 z-NHV&GkuKdRS%GWzh&43%GE9x`|<$y4o-UN8A0shfLrHykKKdLD^?X*>&Sb^IJhws zy>o~!ixC(N5Ik~6lDoY{CYO(s*N+7b^=?K45mYOR4cnxLk^hc{1lam10`OB{#dY#g zBW?V9#hL4|V&y1LU&2$#$d=2Quk*(I(C(6c<4&c!8;vD!=)F!52d5#1Dvt%BC`YDj zV6;xWNuY6>ZlG6e8b}cYGNp2}q19KL`V3`zmP6K|wdN^$jIJU3%5Tp2%rnPB=V@O` z#5^{w^I-?stRQbwH!7>XXniNFPZERHWBZ9RuSUtucLhyA?pIym;+WLVM6%PbkdJle z(Ed`3F{B}>IZE`iUXfSm*XyTrFL=rS!XD9wd(?0M+SB_Uky-a^M zohpmS>D0Oi^iVmkDOQ_;q*^b?4ftUCnHnkNSPx9Ed77{ol?@kjhL^Nc$p?;+-e?jB z|J?`^7FVWe?p85%d|^4|YFyv{bJ>uUm;NUAX?2)J5RA*6s@VBS+vP`lP|^Zo10-${ zXcP}%2na_7+(Ox;b!W@XeN=mwa-q(>vICKLmQAD8x!mFh*UST6$ox3U&n4#83^!|- zO6q4LaQdRmr!ilJ4`B^@)&_R&l+ePFQ{kpR*wwqN*r~PN@B*0W)uXqZa=Zv2P}uU~ zi)32;Cdn)5=hr)m6`671ZKMeBcZhw5F`4{OwUdz9ZO_5#ADN6c06N|XNne0*`pxh5 z>4e+7@y2OpmH4o4QGM%07rWVWor1%h(7(OPADr$vt{LozQ@e^H;Pv||)`VC{9 zI(RbPlQLD87UDC`Ujjrw7LHs2oz_d)6@k#*y!F zk2zdUv~8ZEu9$)9H5=&ou8I%16mO2q5%p7J?nitijqp?dYeGp@b}_qbi&njh z3ote`T-I?qd{z`b*!fzH42GYyuP2MNXBNm8K1f!sX5+62>O2axI4G>e4|RO1&_t*{a5H2H z@N&B5OoxJK@KL{mZm?X=t|k1$ciDpu8gxIGK~#VOLF`F)-8O@htvhm5 z;B9Gk5mER+PDdao^?D)x%EWawG{qS5`#6r4OqIZ%FYWLbVYXV`~xA&~HlwBr5FW0aWJMZ6s?Dve}BOKtn)S1eozXo?`}+n1!6 zBkj^_1&7h7w1k@T#A;r)2bs_z-YJ>n9=&I`0MSYt$BykoVq&oVmzhcYh$eI<$~%%Z z;mA@(4@3Rw=>mTyj9Y6Lzy*GKKSxDpVb)DHdJ8=#VWacjVavzyV)I&O$y09vJz4tr zrVfdnED=ng7vx>H!AnW?X{2zJODV74`b{#X)WSH3Qy;TF(MB3QY3PKZ7^s`S!sGiw zf+*K&DAKXhvb#NOwJul|mP7ZBwGFOWXz+5CbkE*AnmyBYm+}u)Zv%;nugKZ84`99V zYL9otLO6Skj!xZXJ#$8mE%qo5v{k8eKB;W|G;dm+X2^)OO%X9}U{J}OC_E%2quHd(Z->(+9sE3+^NX+8 zn)7N`^U@U9ZyB$m#G?Vby~zQ*4oC#RXM7V`np0Mi@mNWC8rNH=P1qLHslt2qA+!~c zC2MzSVt}&?JLe2$`utzHW%gu)LrUWg87q1iia={5L2Agz~1BtyO&T z-bP-0V7z(CifM{N=k8D!J>Nt9LO{?=(}D!)X}wE{$RWhxX^ zRm%lR)sW*8ngMF$IUs8`M1nW#cseJigQ$#KUpv26rK~s{-mv_BGh!)e{pQWy!XQQN zL>X=MX7$A#d6*BO*%O#)z$>19IF#|@#^fS==FW?dn|#EOa!6H#-o8cqYcw)mD&9(K zSFz)4tGiFG*0bBgOZNgQ3txpi5SUhTIh5vlEfN0TvwN|FaSCnBt2g`0})x%(y`KM@XIS4yCV^>|Pdhw5udYpb{Ure)7qfsk# zLw(3vvA&0C7dg?#M3*llq;Jr;VG{n;u)TKCSYDn@$}!7v3j>}@TqG8(vng6bLkCVv zJTyo=E&N2)w9kYOj|nURvqF*g-tH?D565|z`+nvsz$IJJ0+waFr` zJN0rbV0D!TmU~FKCu3&_Gde3I2=xx-_QUp-KZEUXJT!mb`d*(R#?ua5D3xx=(gkpu zh$ST+z@aQeQtcx|+)lb0!{}{tkDyn8CQ-QFo3(@v{UYr>+l|`}KwSJH?Laz3p4W;E zFv6Et5GqGytGBmHU-5QBG73VI;FP58AX^>{20mnUT^K$EJ9v#+ra^yco3efTWyc~G zaVT9vt@lmp1QK+bQNyhD@T=~~DI00Fb;dJyne*7!o*aCl8ppYquc-J}cw_e0pd!7+ zaZs&e%CmzrLE5&*5O9JI>I5=BfyvbAEUfDw)t6;PZ$zjx$H7pN6TD8nE(Cv)MkS%I z7EXzFK}!KG>>)V%l2@UCr$s~RgtJIJGdg~*SXj6*HA?=9hVF~rcdqrCZ6kC zuX!?i?zH)7=?TG*444rum(VLymD=4S#80ps!mF2#(x8j@1>rr(=%zWmvdh55bTIq= zgGHTIsZtdP@&iqSWG6DFuk10+?I*G3T>#$9II?4wp(sf%Rl+=cAwkf#T!@>lVB(
    VTfK7J(Xze8P)HSHejfewH`|(2Glbd*OJYSQ5p%%FAn&T|=sqq&*Y2 z2-eJdGL>koxzsT=Rb`mhpL}`$1F)CrKS4wPvU&dlJo^6v>}B}N-TnUnd;fw|{~NIP zFN^iR0DD>g>4WAsB$xFYxcd#~{T60Ay8qJt)%{OGPxo#3*Z5xpzKy;qyZ^KCf7kzG z@IM~@)$?!7Kj(z{x8{Ev{#(WJ4OIOr|JM9(BL-Hc|4_5Ae4}<*SfJS0zVX1{@)wfK z%*^}``P=so#~J=2dB5eKj{lvLmHivl%lzH{jp}7(rT=cfgQEY&>9R9Hu`w|bFno+O)Z_D)U z$G@)euK@q^oc{>+pMn3gAL`%i-oGH|e{pU9eT)8|*u9GC-%#Oy!h65N`^ViUAYki8 zp!rSiWhbC!XQ3mYr~h_{^&2|;-|)TE^h`8#Oa!$2j)unH#OZGfsDCqj{|=-T)VC8e zHa9hM`j4HZRr}|wzfu3=Qr`$&V*jIC9^*XuvAtN$_ZU$IIV z+n73;(SH-ESsDKY?Pd60TRO)71H+e*@!Q}3E5o-LR4wu32~@&0KmkY;(wxmnEZNC* z2oP=qc6MU}7s#K0f|QVga3eB-NepVe@7)9}Gxe+E>Bc>wIFx%Vsa^GmEuYdpabkxl3V%Y3VZ%|ec-VT&A6*z$}iU1rv26Eevk#BMu>;4C; z2#SPn82j>#51jE|h_4_x0sxSjUnDC^3ZPVv3W0BXGfZB5Ix=_n^x!_>^z`Tq zn#1Pw0D_gV83^Pvv9q9Wa|}^8g20We_cz5?kMoD9h-mc!uxR1$A->MKl*rJKiU#K4 z!4X8z144fJYT#jv+k=hN&vqS2Y)w_{sV*wT>d4R&V0dIxPBLZe>JCORh5IQCh``&B zzZBo|@pT-Wb3TA!JOBqq7SmsDeqrAfU-%<@y1>AuqRa~1Zj@f&eOMMy{!jiJCnhIA zzzm!W{>`i|4(}#8Up`;R<7})J5Re+RzE5ZR7AUWQ}PB=!+I{JOu z?XN4i=e=Q{g^xbPPhQC{U5ZdnP4y2%zK^aDA8-tffQt1m7(b=DI^=vtBWuDB{ktkP z=jksj4KyPlm&VUz&DjxzSz-Yelm7*1K$pLeI%fc~F*39L=>R*5gWW(T3ScK=^Z(V- zpAOZBKC}YcfD~*U!4IPca*mn#pH4{k8Cyc;izDRj{_uey&Gq~3irE<3nmlwN3p)qE z(80mb9R+fuK#0i>@PzyeX$+9tA7u_;VzjY!f*b-ssColTZ5>b^?wA?CWDT})c6>np z0Du4{JIFQJnn0Q!@<|{c`!5d*B-Gi;32X;>`Ck|-fXUDi5(aj(gxsh3-#8n9$pj3U zk)Xd3_6Nk!+SbP8w?Gca>#Up|f6syQ&!iqae}h~ACS$OJu@&eyl>0CAA1s*v#{Zcz zq(A?3{Ubvl%ij)Lr$?0aFVy&-BtiDhU>8G2cWXPR zhh};7asK7A0@)b*fWq!>65d{97`XdG*3h517S1XX|cvHphH{)8bv|BbQ#iJ3q)$|K70Cu(H-OF^@8{)yVyIzXlzq&ps6Tz|S8ZJqz= z;{N-?{{9u&nE!gUk=1YMvjHC+zcXNCdG!23f{pbN{*4D4+oR(*Ds1e(QgH?Sk}n&_ zuT*|_{dv8RGmyddD;2H>2iW?t$k`yQ!8VZX4R(6$Pspre0zvu~GJYO02w6xPpvS6X zhmeFUQNurm?%yEmBM7!}dBhuj9Bzv2%SYistf$!va^8tf2eAV{yf|61LUEzP84>R@Q>>E}A1w$Okw*(c zLG;mrWG?nNi6JSAKU$DnB_1tElCK{vNN$pk79=03M+=gR^rHpIMds0h>_2KP~`BE((tpBp1a;3zCb{qXo%D`O$*pqVi}#a#4M>Ai1bLT991SA1z2O8jltv z7tP1D1Ib0}(E|QaGPaLB#m(|i*-j7B&DiNT2kW1qf9$14nC5VPOnfy}_a z9sfK6p=<=HW6(biu>J}EGyI`qZOk5AmLR8poMHdf_s@e6_C~*yK9HI74*)W~e!;kY znawW0;M~8@x?fxmlEDb_!ruo3@IfxPK5QRz$WSwJHvT1(2f^TAX!3`OeFPu$g6+R! zA4U-5HzC7cAol;&h?&E0=Q;i?5J&r8K+fOG={FyQ>i;&Zqy6u@1Nfj7{@Stsaz3bq ze^w{(K`fXUnwkAFX@L(q;eXg~pZGx}*cmz)Lel?b>;NAmg0Ype(J#oO^!o*b(1fhO zU-i_3E^xFn{7sQ@{nzFOJ`7SwDz}9{V$CB|3%?G%wqrFMZ2tK+}qF7RP> z{7dfwA2!E71uu}B{XhHWVR1Zm*l)i{8aq2UK)U3Q-~A!-;xGG;|J(tA+(5=Cvs1Rl zynz-U0$VPtgos@kz6~KPueOE7YIcXRJ5f#NdYa21rb~?33mceJ5PV5dm?;e&o$?~4 zJ-8fp)9J|cTS|VN;{+wvy)Jyjkk@~8+F=GFHL)c!8{td|TgN_(QzXGO(1z1~nV>%P zGl;X3b*pl-@V3oyON;wEz#33|j`cj5*eOaVyX`wngywUKRi^<>9js^^2klg66-m{R zTbKxN^o4LsB;l3&x^{Hhz~W+GW>0qt>q)+J*L#kNU|d2x52@$bU*f;cp1+7tEcK#M zv4i^ASLD~G!smE=H^GP0&5?lX3)@ag^4yX@(S0?@h8Die%CxN6ykSEy_IX@cRb7Rz@&H4qCX)W4q0wD zfU$yg-UIs}N>jh;o5qGH-rBc1J?!B^Yz%f4s3{#nHHYny@YJnUE!Kbbtau05~k=L);zYzrg1crrm90KZp zq+yb9cRqi4ROs0!PR4V4>76krZAJ3z)*1JBy=y$7?ZTui0GZ>d+ycEzinohCvZCo7 zNd{c%Ky8J&5!nJY;czfJR@Iv>k74^_A<@WTHf0!EjISN%;ux>j^--RQ}3bBiUfD4!X?D#t|On1TkwBc867 zzl>Mk4|?}%(#|F>`{u_9=6c{;>=JS>#vi$`?`z67x60o7B`$Kgz0ro6Ul+LAS0D>=Uzwz#dB#4yL zv+q$yUeB!ept5)PGzZ<{^$Tv(K?8MXHpAA_gPa)RSz+f2i%#*c95ct094i}{;9hR0 ziezwQhn1u)PL6iX+9L zSO50(JIJ2XAc7DvqaO7=;}f$t-LK8ABiU8=-z@ZG(&w2q+5aRDa$H_h@Rns_t|dVs zKL~h<<-KEB_7c4v3|_mxgee%-yb~fO#Ne0wgvTb-n5|R}lMh@L221z2z*&2;l%cTX zsj&}NS$p}mt*ybOmWyFP-)3%+ybXZPBKaI;^kG)D_Gdc$&h0L_itgv0OaGTHS8TMJ z>PXI`J5YODCTAyHw-jv-pg`io8`2Zh!Cg~o1FgH&7l#5$HY_pEr-@vvcp8PK{HQ%5 zBRH^OP4N7-Y<$?YEj3PBGqu5ZJI1Q5ntX?Tfkds#mZ>Dz1|EJHf>7svr<6^YKiCPS zyC=`@W=a&GKVv0u*)1g&GPPldt%C4Jmz=)l$PWmga=wPc2$pIN~OuhFU`xodDCYf9K{u}3h{m&-Cj2QL%*B7$F)eMP}5?T%Y{vWdxlUCn}u6%clS z!?ivILc7eb?z!VF4jvI)^i(IU%md$t2!l()gvw>Ac)2{&k73JO}6Wh*8Va zCf_i_5O^G~(wKq=L@YB%0cd<>-26 z*y}~1VN$TrKWo&9yGmp}J-v};s%E9`bI}k9K^+iHzVarWdtx*&OOZN2-1e=X^l2AK zo`A~E_}D4?>lwM;cd565h_>vrim>u>J_vEIsxHM)v-uj6U3=Se5+vV!B_dVO5q?I= zZG7vZw?_6gCWzR|z`;C=$Cs7uf^~0)WkxR48YwAB6GJ1muxGRFPC}_|?}9l+C`8~1 zmXPqbK`C+73Qmw9nuYkvDbgH!G%RZstq{;Z1@ZEoo?Tm7cBZXZ+HO`AJQ5Yl>WuzH zFv{J>f);j0tfh41H0AKAjJD$_K1ME_uKU)a_OziMGPB|!(!hcHPhmtUiTmMK10U?x zdn1#csQ}t3?+T>RHEWqeUQA(`WUAwOFK_JM zetoKvdvHwd+F)vG>HEC~H41f8(uU6=8v+gOJgU#Lg`|ESn=D`Rv+xeXqzAXtk=l=% zU~eqGkiY5lrJ~Yu2R&0O`hn#c`C7?4GW7N8nU|4_7Zl$Hk&g1(OocBUC5h>(z}s>Z z;BZ8T6fJ_%PAaH0v!I%BT#*ZqGZEQ|u6L3mFkLn7ql0=l?_7A4#cMd|pilDxpq`;{ ztqXr{Yr2wQ+}c4U=4A?0iGJg3P8N+yIJr#8NY^B`5mGLIOuC6U@G?@%D*nB<)H>?DZ&#}{m~!5}206!6zKiuUi_FJ3+m zrbV@C(Ohsh)YZESYq;wsJG>)l1do0#pczZ{vD2~_;W*mT4MgS4V~mHb!;(CrP{J_f zd@o%tJEBly&fs}L29oYJC48Z3BR1%BI6!F04z32z%R$%WDIF`P6V`Or`?_m4!}(jz zy$Z6|fXR8sh`N-xr?VuszHr{|KYwb?H|~k6WsAtMPRX>eM(|y?(UqU!{Vhp_vKeX3 z49`nlXyPA}(D@949xL(cL(geE4SnE5Zszh+#^0E}6R~cH5W;ZJg(2rb_bw(JES;0U zW6@Ilg4&i{KU|>HOoYO8$$|AD&xi0eMtsn!I@-_1Js%PFV`U|bXZf8y)@pm5XQRoR zQu*1_XlZESQb3}6;`GI8-}X2UqBks$yQ*sYahBmLpRf;t1fi-dhx&ChE%iArZ^JQa zh+sM!jh8;f?6;^-Uhz@Ev5*xT*p15X8;Fu3r)9(z-PkKjXz;KDMf_9+fh`-GTRh`G zn+^BTWuP^Tu`@{$^-_sWPb2-l^RT1X74uga5+>`!PJb^3v*&zSe(NRs5pa1ro9URl z?1MG_84GM}miehNfRsP&?!2Ar9NZmET7QoDBjm@P+EbcC;S{znp%zzo@Axg-<)2Rr z5Xj^)_`vcqKO-P{f<=^S`B9N&u5HD?7+(qPO0ORwF)fz~kI{zll3}ZNr}zac^*&Rp3mWCKPL2bKrwsw)b6Nu2|;7BFh6urPm2BEO+DPkyTv`MO=b1bs{FLb;ZJr_466_FXwLChH(QkieQG9EPAo zjxc1#GLr+QiAH8D=U`YUMCsKJ>{~>&`094wqWl+rO=WT=(9`OCNc>DTO9c}-yZ7{; zQd3~Jxu`dL*C|xPZyeXTVo)(GQug1J%JnRWzebS!YmpU6C0R3R#f|lAX^Mh*#pIP|R(Z?1rs=ORyS+PpxO6t* zO)gQ3MH-_WgA8uUB@d=@>MiAde%8Ykdeu>1g{(#U-_zh zPNEn(Hf~?^9>K_g35s>P;9TbL)Sez=4l%oTF?&~jcRrWXMslP1EsVi9CcbY-cr0qH zu?G%k4_^SQmcT@@3ClCMh?AEBnNsUdNy!j4cl8CHjO>f_yjYoD6WhNDHcnkW*-I05 za`Ak_H={hjZoz@r`za>VJnVa#aQO!KFq9)i_(n(Qt0CnW9OJH#G@Dc}!Chqy5;Kz; zV!OFg0osMz7{^}h09JyGQO;y)EWVM+{8dL~4UYl!$3x^Q%P#~BMbU$0a*SlnxWSRB z!}U1iJD})KP&_?O2^tf4$z;tKtEr4bc1MsKZkVabll6c0cISUz^H)33;YC_f=g7j40jWe*iuBK4bG;^ZZ z_po*q62X)Ie4c%)jP?Ch-Oq%htIbc}1LPJ|^FN%J8tBJAnHb#l&em-CW8dk>N z7B)(wBU{BzN{*4E!7--i68&t3Rw*dxmE@Iqf-yHGM^_VNJE~jI)&jTUPTd#7h}ahd!%?apdVk@k|cgW4MN+rla9+ha9iakD?)fif{ypPWiH`FhWaV!e-1 zrXb*Ayu3)oc#>M`VHj`en=@_y!CeQuS$(X3bTCeH<1SEeeWZZ_S%y`sEqKto3RGe4 zu_MS=&M@Zh{TRdMR7pFe#oRcCxW2X9yB8dwwDJV?B)oe|Q?wC(D^GmPCCpwMN^};`aIxsOC3gqk;hNX*b7Zt1J+up$U zf#FI)tpU{g{Nc0)L9uh#$>DF?XP{weN>?4qI(xq&5aaDvU<=>1@-1MDL6MIjG~-zc zXcXtAEarWoWc|Q#ed4bdb!XR*j?6WkY|=4iDT;H8)oVucq5eacAZQ=mZKN5i0=L>}myyGw({Jx=w@eC3Prn;I$2XftZ}>;J2pXXN^izT0aBEyRP2MfL>X8_ zfdW>XJfHj4v$>mbD=jec?CR1&UI+S(haybS?7;YvWv=P%e>cDx%x*&D@W=G;5gdy2 z4r0i@-@E`%->xnig&N))MYK0XAHSr!+XV@L0 zJku>db}$z$$5PY!Y-;xUs9Elui?VC3!M0S%g$ip5QW8M&w_}oO<%e2xKWduQ?~6Eu z@!nx%Q|OF{klciOD8jX-On$?XDa}=P`uekU4^E@iAk?#S31&+Nw7?a?nzd3M)p( zeCn?Pm4H`~OHO#Lq9_x?*-$s!EeTzteLwDclzviIWMAS9CCeUm`*BfM6@*u-mC<}5{mPiHCpAf0yoxV+nIw;^wm|_;(W5fZr3sN;MZ~9=MievEhY+~A% zKZ8F>I>+~qS8Z;Zin8|6I>4EmVJ}W7Hmpb^ZwB@HeMC|d94`WT0U5@7o@73mnJXxY z??0<8H;H(PAs0|vMslV2Tm=5{*x9$6K~JICc7RtD zqY7Qs>rV#zY-u~QV7|^qeGgdfRmP@XDOat#7&R=EU3ntGwS=H%DvBXKEA#o?^Uq8i z3@>PPzRT}iw};U-<;L*vG+4YCaa+Mx?!i6gL1ireHW+#t=?cI$Us_{DN^e2rG2rPMc4e6%rZ zSVO*6wM^nkM2aV3RA(CktKmMxB2-Gy$9N|iU$V|cq7Y=pRLJfS8zs7JOuM0-!*Bb0 zLMRA<6PJk`1uor_ zMqd-k+q6YKlh6-M>RFu49Eg!E^1g*BfHgrhBjYO}+$PK)GxIas#UIuiP`D#4&@6H` z6QAES?7uuSi*RAu_GIV}-&Wol$)wFIkmpl_dEFg?pgbk!sNtHXBo=U_u{F7n<-64a zn|p0)N2|3lnBA#2A;K%-iFhN+32n}YR^=VLmJ#8BS$@HQq_nsFBjUD8r-Y86lAk07aE&2Aj94+3my5$;P((qKpyQZ9p@m&vD zBeZvCd&ToE?+SZ(g@}nPlM2JeCm-#qW%LbLI_4|d$V%$IPJBvZBpeZ^W&Ps-QDbyW z>U&CjY32Cq>|%C2B(J{eK$!-;#ZPQpC7;F^L1>l$;i(rOTl*x;#yw@I{3H>g`GAg; z0ob!+R4V74d#7*WX{0Q4D7##jf(RyR*hI{-wdqLjR`$?3V8~P?82beabz0xgPxND? zKos*miJa2qvyc3isCa;X`I8&OOOOWb$_^ed zZI~8AjY3#TN_jyTPNz_YiCUBp?r#%y9eV6|sQuj;e%$Ta+KsQzefh&~CI-G-J_+LI z>E;6UTj$jf8$N8ah)m^itCwa^x_mo$q^eTcLq1292A;VYP^jPskbJnrT-O~eR}J$` zUPOUC5OJ&6F;U+_{ZT3^5U}!;<#vj@Jedya&UvdxNYh3vh7iX-k3JfE)iCm|$gyI_ zQyb_Dzh-<)sDUHj4&QhF^37SDBD|`I$8L9{Ph`0*+g4HRyVY9tUFKwXem$HE7SI{j zn?7Z1han%fxS6tA3SI#BZHYhi{Tr@8>N#4tkhflIdR9WZ(jxrebx;0fptsj#3~}bX z$(++_FzP{L!gZXbuw63m0fVuA>PypRrRQ%b9VNw!`VX3~yz!~VDO-i#XrwiOM)HE| zlnX}d$N*~5Z!k_wF0@M$hQAaFEFhnu1|buskiQ5$er++L*Y5JOe+~Ad8Qv;-NDFK_ zo{;XRX)Jp6P&@{zh9G%YPu8@9Q6@m~8B5q)bL306%BRo$QgHwVq1IaGEw!a%fWeO# z#Pb>f>_Ker@k%Q&i zcgEv+n4>2Z4{HH(_&Owl$QNcS3RT!ENcAmQg>O<%_b%#8{DbXQ8b63uU5_{UyzXRU zhB?xppm^oYq56!r>TL4xF10ZyuWB8h$SpB2_tk92jAo5^kT2Nxv(r!No2T92)mwU0 zX*nA?&aiRuc*=?4f_MG>j_7rkdAfLZtE26hz0AoV$Jk4vVf=jfOUv8ifc$;`QT;)I zIXzX3Gs$GaPlotBH!0_-#Z@ya!|eEZ%p}cK6CXwtM1(;kVV>k&sUSS2i9;Z@{+iZv zA5)ON#FF<(A#c~ruH&2p%gt3e%gZfXy(xX7@~rdaz+&8(Qe!_Ps(!$cv_S1pK3ZU% zqwJz7egH5b?5(gvgx!@q(7*28prooG{o0sWy#W3=83T(4#{33mptwS7?kQdg+Ty@& zMksD&uY2f6z|tB#iO1ZV`+_z4Z6dao0OKL5ZOPGw^%%muU7L(gtZ_|sAIh3|T@G)O zwM#2|Si_RLfePxPo|@6cv1ezgWl)O!^LelEP59wbtFF&e(_lK(-tZYPB10=6d#xRqb|>4y=j0|vn?-p_le1zx~+3WcXhV8v2k4B;dt7eue#mQ zf+tumZjD60!kR7@MyX$R1@(QX?Wal1x$emd9`6vk5fuas_M;^ezL!5QYFy{lcvMVY z8+wI7I|3`upO0ZjuiWRBC2QIq6nu@DUHkm5H!_9%nei;1!dBf`fUQb3HOlNV{d;EA zmSrnzC%R-8{`P&#_&cdSt3whF{^qn9z71&EB|k6)rjH@r1uXijMzOk<)7R_xg44-q zEQM~CC7Nw&6ir{)qn|G7a}LJHgCiW^0y#r^qNO>ZW6$)1h2cw0cl|ft%O&2U>pG>$ zqha&`3$u#KGGUM6=5Jrxm#gV>(#*PB`MXER)*ddwij=(5-+M}$cmXIa)K1d)MDq;L z>%_dOhw1To_?TylY-1Sz;AgQGqNT*!0E>`DI^%CU&^=|zYfx2TvOmmU^YIaMjN0?r zggBgie~n3^JR8f?>baRMQy+!rkLEKD_~Mi@S=a7wct|K1FRwF#{* z^x0hNs+!3kb`AWRS#mRx+qEdeFi)R&qLaRf8t}8FKQB=%Gn}~*s2R7jO65sjJ(egCS=X<3CC#Yp%+y4{xF%zWbflDwg}gFDrCLnEuEo@al@^I@r&p)`J{4K*jcB%W8;W#5R|A1N+pV zLYrln-mdZ-htz)EC}bluvCHOT*a_Y3Cn77%q}R86B4cyOb;W$i)n`7*W{D@SOHrH) zS@B2&jms=_tbjk}_VJiD-O)9NnrV{@F{8G?b*PaPZm;?r4jCxbQUw7%2fbqwS0YmvwVvOG1_H2?TU4xTJt4_0x28rE94vo z;x}5cx=U&GpXm^7&864j zs#4Gi&>scc1e#?-OVXUc<0QQ&irhaLT#gt^jFZvNFe=e{Gk&*s@sa5d`!O? z%B|Zc?b#4voE!CVMs*BIVs{(jno#dkAN^GN98H?~XqE$~s3OsyDtfO5gGxCkdrO|a z{jB;l;tMvp7w9T;Pj^*`qO>|H&}GRr1V_uDl22qsJZ^W@)`>?x7Jp0P)Z@vHa3-_F zIDTei=8M&uQ=F56{`Eyy5#PZmM>D(vRfp-x>AK`yW?>r%eKm^4YLn9ygyDkH>9uXH z`+hEC5uGFgAE}?HfSK!X%kU)us0vHPFJmz}@8LK@_HABzu$4};4mXxG^Q6D!eJPi4 zllXJRQPESh;0v7OmVWv*GpDF`f4#boQXR@uZJIw|QW&C9-DIe$ zN0Wfl{+{JkwDh)7vci(iSyQIX6YUx1GZm*V``axE0sdar7uPs>Mv2RPUIep6pFUNVe+rETT zW(X@Er&SPc!5$vl^d!}0HMk5_^pEhpB3fh;uCOMlhL@$!qe35agO#GH=iyap{MS zqQ<5s2{f}@R8pjfYjewnXTlF%!$q2XCSUB3o)q>CInytOT*THw+vgiQEI-R6A+1^UMNXp8$RM(`k=u@GrT13Y|TYJ8VXPqxu zQ5-RWynAlRi1D1tiFky(+GophgO(?iGRS0^9v!n00Ko{cF$MThD%DmPFfp-vPV*?u1Rh(f|g58@L>N@~W`!9W=STVzHiy@zf^{IyE)fO;4DW%@@!rW<3&!z_CPaQE7dD zcbLq{2`e_ti9dtGnDj)l+Z6ezJhe}IGLm1zPC0s%nQ$B z1)b5t`bz_lTN%-v`8`v_nO=@O5l3WLj&ZLVm$@_c8MMs0 zHqD}OOgaFwmjyrv9wo&|w7(hjQu*#Qj}ChOZr#CNk~KMs`$?t1f&1XeaJ^-d3nv&+ z2X1FK!qs#Sbcu$ugylzWd6ReLD1&r|8aV5AIw>nVOu5O`A8A0X1ndQ8qs`vBbTkG9K*D~zgIdWHa=liK(E1(KM7c@~pCiwfR!Wd9 z#Dsd@QCVnTGl&7aLr+Zh)h=#V!X~uX^r%@r-|l0(*3c=*cofD>o^NDC#4hHSm7DVq zfQU8ddmQIi!?tV-UcA_xp3s77fAELIGlQOHSN*j zo${H6snDwO3sY)#Xpl|wMj4O9%1D(yDiHavIKCS8~-#6YN_ zK>1}rJ}Ce+6tz8Hg_N)sGFpvZ=-Dt7?Y4Ic7VHBdXYjH0tr&mJda)EYl|^~f#I_rh z0c=pHygtsNO5(#pR{W}oE3^^u9jhq{`?GA_=x$#dzGVw)NW_~&J@A(zs)iLClPgw~ z9IM99*~n}87E<)*xs~98*G6K4_u&OUdKvApVZC1kV?T}K+Z?>?ZdUo!Ng>duY$j>h zE9Mon{cKXLc+D5aS8w(HSrh47cBqjvB4rP3E%mSN*VH$GdQu~BC0I3i?2%CH((RR! z*b|9px-MAKpW3H<5o=eshFLxYq$pezoz~y6#e6y?XRd+LeX5PCi9j|z)!l+{eBt4k z?I9gk*keN)ESW~bRAtwaNEAWc1ka!)xesW-go00^FYKTo zwr0Vz<#wMf(e~5l(ZDZkA6e-Tk_58m7UP^WnJnLR`JDLxD+XDzDwtI{>v>F5a3Lc0-wg1|)VO)xA zugrtq_Ik`1=yv|$GpQ8xMjonXYSKkJ7=gisjucvQ6^dl<=-s_+vQf(`GTPPZMuWF9 z6>NfH6HVSLP)jeW*!}fQ3y8|z4{YXM`7Lk@s2z~d4H_)SpTvkRT-W#OYTXLjf6_kA z{uyq~&Kkoe!dD1$DM5G&HRAY8T6KQwf?W=ZA!uP1XQHf$4(5CY>D7n6D|)(kY-}}q zKJ{#OE1_;AWn;1OGQFP#S*CJ$Y|Yl_Xq!cWD?oGKRU0cqC2_1*qYIT`9s$WWR%or{ zYPc(?jYD)y*=f8)(C_>*GHuP$sV7(iHP(oa(Y%B>)}rVTFu~FpuzeiPg~dpi@ zRLKE0h7csc5^559Y|%yLqi+0i6Jh8Sp9)V6CvK}rNWPWGSp!Kna{}qG;a;bk%dF;S z@@82g;hpSkcshbikNX5QO?PH8G868FcXlz0q{|CCXdgM?>f&0Cdd10pSscP3Ui@2p zeEdob;Zkg?xPgz-Z2fTgegv`0o=Ru$ zLxVB^^qp@<%(O3S+C{`u3kzhUYBV-zxn3YSUHZOQ>U{svY(xTW1DSB=D91;vHJP6~5yYQ~hn zjYDL&=aDY&{QRF?7n`4~bxt~>VFE~|u^5j`56%GW8VJ=dhb-Z%x=jQfhR#HV8Oope zPafBGJnhx6_a2WVB&{Hh3eG!z57W$3Zn{+gZs?J$ zakMp9#hXk)Njg!nP&w1>B4tEQpP?{Ygflp|R%BPK}MU!(Wzjv)t+ZrV?p z)04PP_YpIu^6Jv4x_D>`9*`AbYu+fjAQRsMnAK+!jtTp`)xdjFdHcjzuUE#UMAETx z`As&zD8oZ>u~2&H3`QvvTBEAJ^DkQV=s&rALVZ*Gj%^8BZFXf~dc1lyQH!@%f0h)} z1xXLSDtVO~*J2rNp#F&JvkkCYbS=U=tf1^01wpX-))~gB+!>vxv4t2kMpoWLo^*~G z{}^kbMMQJI5~J=Dyuc_$Q)#ziL~hj533gGF(~?r65G#@}=6Ebs^#RNyq*tGCQ%S`sCoN-i)c>^d0cz@v`qL-)Rdw`O2e>i_$qO^r(JOE@ zi2g#M;;F)G2GbH6ey^VjVbCp;H>@G*ABT`@vU7AMsLPR6?tMwshFQNglD~uX-2`?S zrRwg8!eZv&3VV#}d4|~OZ5g(5TgDeHv|FoDF-U4AhQSaV<9ESG`MpZyK|tDr9ax!2 zu2a8w-65Vl%uR%cKKYFzocLQfz6qkuco7E?rExRPtJlEgQl+Vx5L%2^h#2qK@6t?bW>+18pGEB9H_+x@WWV}~cuY8;p=V{zK(>x=jE*I;P9^33duTIp{>4kk{09nv}`Y5rU$Ob`m5=vZrJvVUacs|zsGYrjPU2=8MNE3Gh-ks#`FXA^} zLyHP{?7vtgNAxISs!X3qzYP~)MJoy4tO{)o_H##J_Y~g>HlS z+F~)6CVfQHEt}|7{mDKUD%)|uA_2$v->;woam)F8U6%*hJ552K^l2Gh1-HKVoQ*QM2$qcW{T{E;o3Q2*x)Hn3+zK@rc&ev%%WKtZ(UFFVnKwTYJSL?% zOR9NY?fRKhIOO$%lACyctVrOaU7ZJyhJR}4ltz8wjBfEEoJ``dYQ?g0EHS#W4{L4o z*|n2mGi&~J?DYYm3evNIgo4!&$&n%yCTHa`V<;3_!n^niG;~R)dnZ0tAp-rdr@aA% zrk!>@VO*IhX4Dx(P@_0h<@}_vJIKzVD;;t(`^^{l07f0%$}}JyDc5Vajrueatc4v) z+K3obCWfWTE7}m647%~0(bwVXL%nZSC18{0&}F^1*V3`3bKN9+W1x5hXu?MKuf0l8 zF~erziWY`iXy{gRzZK{(*=o;~9_D&;ZH8$sq3A&0Vd!79UW?a`>ce9U8#o)cDb--f zmb<~%o;UzU+Do`i;Rk80MKU>%VUesxu^G+CoHn*@eF11iU;6iu-gNfsmnfy+Zm_Vs z_|U-WqRwqu$x63Z5PV;yU2u;zwMN>#3KDaC(?)enx>`%CRBd>q6_B1$n9CAW zq+^?U&6_MqeaYv>T*`IVTaPIwT`i*Egg9PJXElZ_J6qZ17n~|kl={ba zsLVLE{N%is$(8+zb@gx8@AmD8QBFX?883&fLjC6peMIdKbD`~Ks!KfVW*Ny;-E{HFY!+Gj3N zwqvwZOpkptUJBDdKjHA%*$Y#%3s{<}0a#-N$b@xbVe)a`vz@E%AjbFyk38uDQ+iqCDMC9{Se&u8B5}#-``|XeNe+-wt=!2A}J$@-n6;_%d~Ml$B3u z(4u{~DS*4tQ&5zSV)~}Ns-g=k5AaA{Hmvk1e}*t-ahC9nSR#YBB>HLZ-E?|(SM)Q< z(AKvFF7c zyl3LWb-1u0=AZOKA+w3X!)4dsT(5jP(8d$7yuyK5AE7h^-#2wMowKZ5Ryj2ieCv!0 zEypmF&d~u=a+dx=jfmw+)-|?d!g$Ig{S7*|+RHDrlW=0iFkjC*SDSJ7PPvL;gL1{> zDuio9pUnqYS{)+cPv0eS5!i_}4FzVOvPHku-10)-QgmKjN;z?{VxT-5pkopx&Dp-J zOuCvGaI`3@@Cc8R`3P%I6|+HP#CtbaCI9{X&H1(AA`yS5s;kUoWW2{`G76U*zyu%K z3hpG46I&{_D-MyXiWg0I%gs?`K(HK&K0N&%bi;TW;r$I?A0cxsITY*bCVP>4jq})0 zSg@{L-U}om*QOYEuRcBQnesEj_aQ72j3+72IV)KZ-0US)QGaf8^HLGhzIPOIa;!BI zmmdraplRVR8n~BmGy2Isu6_6|2wK^ZFSNOcVf0{XZrg*jJ=Il6-38<%vPdr`%@^Km z@4|#`3^snN+D7?BX{!Etd-n4}Z}ff29VR7C^xe2YIRWfQB!&ZH6%h@3_30F(MnX-^Wuf!UtPs%!@2l&qVjOok}~Kdld17e>(-8I)b4LTj$9lC$*QO zqV|w$Gp&?xDEEc=E7`~Z7AZgVNlX8G#h9*H*h}4NGn)onr43tRLC~|o9|GR-07SR^ z(?%hwD_8d>=+(t6aa0bxN$a~*dKO@{SlN0*%rr086(;-a{fy5j zTc%y!x1Ab9BvwYccZqm5+zTR_0TWLS4mi=1$V^et!r3aA`QBno8EH(2TbQ`BYhVOV zlh2Y5@wPodRShppK>K)5TR2waDUamjn4eMOf=XU#sEt(@h%;RyS& z#e~e(Y>6&&Ag`VJu4j5u{Fz0zzX!GfUtZ9LVn2K!Co3%8i|0YyRnNA3-GduV&oy00 zK1Mbs(&Z(ke{|%6O}v1Ouc!~Xix0IniqLR`*SVL+Qd-DEsaMaFAc!FquJ$$8c3=h0 z46?uE$ChW2RqkKf3OqBQnscxmO96TJ$RQsVWL;VdHN8QfOUE9B^0jhy@PjUT@vt}q+{E*ZQD*dPDdTv zw$pLPHafQ5NyoNr+j(#IJTvdiGw-*)e_+m9_pWp6)<)IYXIHI*{X?tLX2lT+V-VPi zXD`f(H^v{3GT5hAc=3qwgZaj*{KY=f$7hK?#|!l1=v^mAbNU6WMFZ$!P?p=v(%(7& zne3%>W+!bBj4qoaAOJIuD2HjS?9{O9lRi@5eq`Wbr#&~V<`JV!j4&p2SKdWn7ho;s zSGIcF#-cNaZ)zFcp7VagWG46w*^3F39phrrN$N2Ux*<78MN`!Oc5o_Y7(gv5$hrc+l@T(|X#>-IK_&PJy-TVhV5wcGk71qG+;@O|HZ zZnu~N1YZV28@I(~vY{HOeV%K`**21~s-jiNuplZ%G4`i<$=1aqzZ!~^SJXm>rzY=? zD6BY$FPK41=!0Gc4{Sgx3DYOlz0yz5DdIh;1z<_g-Hu3MgGTlG4Fv7T^$>S(g5*C= zdZtmGa^ke=Y1kvKTkkbu>3Ar&LtgB^a7*hkq{thB zyS`BReiL?%=uB`a-Cb;HnGy0st)0pXhBcgZq$4DqC1)5FKK`-H6Fbbc$?jB+CCaO< zBV{EW$gB`R9^XvTNt_*vT|G+u6e&`@#pFK!od7#8Dyp|;!b%!SQ=d9$uM7{&d1J$F zN20WdGppFqnvD-$S+m&8QZdXeux_ZFga)pa?^wrDbB@uIP!HSt%M!*!UEa~YzF*f~ zon;R4q>kf#h%CP5q)v_ts&~ah(8ZlfY(4UAt7y!3Wdu5*w!SfCCAmoCj#2a3eqe1@ z!&(*~@qoZokZeQu62w^6+Qq`E(W~kSLXHR_Oe55&_soZeRzs}!9HOOi#9HCk&m~mV zisypA28nnv^ulQGW1=@oU~7sx!FX8G>}p2ivGCA(XSQs{^iZ{q-!cB)#>-S->KlLR9+SfqG^4(Ks_vwG<5Wj|m>g|qm1bK(%@-+##!#!_+ zy3MguwQ?ffuocGf!vn7yvGs;r-`V3PTGkdaal?QxKa1P;VikfJ?~M6alA*ICK8%@h zT}5mq=JjRNvG!US%abjvUCe3bGfF)@m*^BFD*a+vb>gFvS;RNN9R2y0By0KI_kg(F zl7?1cF^L3(H+rYSjILfSDUf;M09D5^{vN7&zhOmzUoHNo86E;`^ouQRjM~62^}BW+H6X7vE?V@Lo5wdvwO*jB20{ns5{giFZqkoZQLlDK0(wU_1?)0x z!5b9s=sIL(8Ig~R7rfZCxJFo3b&8?Z2Ny~cS9$XN#7rh6+2J%Np7a`$ z4Up-CW-H3`78wzo%&R)EUOx1fb1ADdroFMbpSXofp|z~TIiqbBs@hg zQR=!Y)`D&B50=(msKd-D^w+(1d5XX&&t-S^)dI7f{Am_69%Bjk;mO@7(XK--@xxAA z;Aj#XaxppC=Sx#+FVv4x41!B>S#t7qD!t>iqe|kO*{&KvCL(k#F#{BdY6V-94B3-P z?)T05r&;DWGzm0Z%VD_r zMX`5En)6pOj^VuXZv6DEw`e_>cQ9SsfM&{i)QO83zTa1uQ6nOX0)5B)`k=Z^S4jwT zj-s9Xg`E*x(5-l5GxXHI*+?BI2Fej}R?iDDPo)&jp4%P45{7U7d7?BeSBbl8qMBgF z=*nN%8JgvTl1+re$8o9~e6h3Y0_RwMu-{VrL~qhE_?Sd~ZzM>fOXd{YeSU-~0 ziDsaLok7bzp$utJ4G6&1bVv{1*xaf2=!ybi_f*NCcy*=ZTm&DA8>r^Vu($$uC9rkU zxUVY6Um5Q3%@TX>r8Xnr|CAuX2n+vdO5pkL$4ZIG}XfCs?PK zFVr01QY;h;oB7)P@DXD5!WvW7*WDh$)$p<>zcV(3zKM zF_N(eXQ>(=6IsB>YpIm!!=HQ}f13R&{js|!JmkI_&+1-_#9o2CxQy)ef(*tFL&eQD ziAD!ObeyyhN>eZ{>h9KwyIT8J4u||f&SkMlZY|M^0WB)#chRLDwaX09TVz&fa2{y%jB1YcE1~(FAoaiZW4qhN}c&|q+(nsdpi>ran$$iUM7p5 zLJ>1GPUyyu1pB5_=H4-ynvwy+3c$paE1$h>SB8>PI{bgah4%-x`mHOibBjf>!F+VG zNzkC#KSXJQpbsLS4^!$`)Ptq5vp17WUbfSH$M=cdZY1OoOfBGv{JuJ0o;4J5D9vC_ z@-riT6a6ky6#;v;wkj$#N|Wdp*;~G(4p>xn?yxB-(!~w_I&tfbU?ZKDxlps@akkok z((2baEOw^DNDI3*H+_qApP_L!`Fg~{5M*$SbYo^Da^Ct(B^tsD63?;CwwUldU_QtS zU#AmusE~aqJHuESS!Ev9gOp;B`0#^|pZyzDZrXZF2yqWp@LQmq#>B`shl`zyoB`XbdLZ_B9 zb7CI!T7${iaqrg9xJ@oL$>_USlTG{dCKUt`EA@u+huTLs93$PBv&#f9^3dfDyp+fw z5+ojuYA}auQ!8nfUC@31!UV5@_-+1U;Me7x@6U|Wk|=yX5yAGYaMXI@_JgCx3TGBg z*x446Dl*w5A-zLY+RJw2Xh-Z!CP*%}!c7%36$GsNm}1;0y0NDcuC)nQsWTyWL!$qOWy z8QBVgWC%a#{RLk|twaJY?T;CkCjR;7e!Gu~%8wV3a8;1NQc?Gl)@o zSB%S7qMaxdJWKp?mmR>Kt9?R>6s6L?ktJr}-HLJp36P@~1(^J5MF*h5*SZ80XPFJ7 zm9aW@jiZ)_f{s@VpH@QH?01))rUglRr7*~ZouS0DRmgTp>%+g>+1a1+iUHYTg&exi zXzJ@zD8Z=&Zev6NTMANRwxE*w03|*(J%ODibDH|qAj5G%eJ?BH!_2p!Z)y8h&HyP6 z^4PjG;;e8QBOpyKAW6&7Ml&+Z~vFe0~O;i$?Bh0Xk zU>7qI7sO5QBtJ_8TaM*w#YFvTIedD8X{c*JD&GpPq&7O0M?SwALRguhYL7A-*U{Rt zXf^Rse4m2=}O5&m~%baC@^U6@y0?hM@2Ju~*3Ag0>P)K{|2S=&Q)m30Mn_ zD+lG%bdLh`1kORf(ko5GBw0kAV(-5etJo3C| z1miCGY=_ozz*R(HakSR84^?kBzoJX+p4pE*c;B<{O3&;vlr+-bWDFR+18<6**xrZH16c{| zk8L#PjS%2wJUZm^4kj%ev@rRlxmFo|0KMZjtDLc;bs`>wyLl zc^bxS35l|7f-;JyVVuWmeYZ6tBAtzdT)hfn|Mh}8 zKXOjw#eZ3HG3ZiFKqc>+W?pHIsu|dwmVWJWrA{eSm|{#uv=Y?2fYrChCh9Ly@ge(; zL?%g#2pzj))E`WR&TF3YuVSBV7=bC6yeBsxWXLq-H@#PGneKL`#baFXs;Prd&-@U| zkCjodUaGX=>z-{3;XqeA?)uHj;o?| z!j(bG>vduoK)rJ_mBQVWBOy55C`(;SOT=<4=(i&A%lAL`?LRR7XcyU({hsZ3-uB^% zQx+FT$5+ZPE?xGP!=n4^lO0+)29d5eG{_H0D_+mz6m-Ca6;E|Xq4sA)a9)oFQ)HSz zk3}6l2_u3zxd0~KZh0JHmM8i{(V|u0W{5Auk={-vjY}`o*wJ;iGfO=)@NVIS9yyOw5S#g<0Uj))Of#W`ykh@RB$j< zME}}vzfc(U0T$2VDiiOon;3i6U;7N<(&7|RsDY(^y%+=zxEcWwd%T+06&U&&&+K9y zb3oIY&iqx(pHaCs3MW~?kcib9N54XF3o$3fOp1olyAveom3Cn!O{iw2F} zK-ZW#re|PAZ9C>`d5ZrEmUKWs7l}m#Ho=aUtWJ`mI#}&=dNqz66K}xJOeI5LyjCZD ztSP3mZwcE#TPM^Gdui&wmNaCMP*6+vkyY;JsBLMTFWq4CcGDZEnO* zN-!vJzdZ?oF0Nb6qc9;U3d8V%7m#DyZ{-9@=AG-&dT6tqfmSerW1L@la-qU&%lTZq zU1TgGMN|%SjfCo%E4~mpvfc?(FAu~mZ*XYy6Ip%45o2VGNgx9zS zMp4c-9lnkj!jnnK1u*qid z=@$tmnIw99FHXw!S!15HW%iA26zn4Bz>dTPNQbF7+7Pz>sjzywX+5+(+sUTEWhr2F zAW!Q6q5O~h!JQhgZI=6If&zAEwcVnrb?+{;r_$OKG$E>4fzoEeNWnrZwRBlG9*WSQ zaK^RLev|%imYcTHgw~~g$4L|f|H!KIrwdD>QLts)EiQ%_s=3dff~OyX8r8wh0&>rs zFT_=R`B-e?-t}^Wc!F3zt;hS8_>GmrpH_Xs13S3>KKWJp-H=6tnK5UPX`50TaYTUZ zC9(-P%?jpHweXocdBmbQdo3PbsctP|WXL%8`v(Hla%{Idu9FinW>hIWefGw<*D)FG z^YgWVvF5v5Nbg>6UFs!vd>4>EFcO;5qLhYtZ4zN;7w{Vt0Ch#W5hKFCuti|-V$J{3 z%n})ymbR#Oa^kOk!_SrR0-oa(nirjuU>I!Txyw8&l!(OuTSltCM;rs*WN>DS^h=)1)0|iI(bQukwWIXrF+x)GKVZz3tJLA$Yz0V9_0Jkb<*cLDELF5G z4~va~D4dMV}M+DLLy3 zq+zcEt2oUS{gU9$V+zmL;Tqosp;Z^}_iiPIq)bEdu993yPA_3wDa=w(ajOOiyiE&e zAfn%a-ERBjYxpf=Z1PLCpjHLDf(vs)H%ka-quawr64KDY56wb&%DZv4fX|>RRoa{! zDVsdMK9}e)T^xP*dIT$ds<9m{K#%a2n5=fE>8CqUD}xQY+U;q!3JhpCB)gg-EeR}S!%d+|zp26VVM%)gSF{}0=r5-s4urN}biX!J0_yOE z^XL9y;UXf0?f~lZo!q8u>If7)NBw)h))R&&9dn`GRTR8lzuHdMikHum;-?d`9zTHz z!6jV^!xfnV%H`Qdr^{$c<&C4#Y5LXXk`n9_xoRLp@Pr&!9cDAvoz|dFjs(-!mwt^?f0&TR#NM7+WWdwCkoWl$zoJ zHp>%3&zd^k4s@HEMIRWAxvmQJpN&$&kRYeIqzDQsf|WT+R6Pnc!B=(z?poa42ZP&; zcj!42VwJbwvh3v?u+Z^J<;S}E<2r0E3nOLkMo@YZRQw5E{AU%W$NW=$`AgDc;CI|Z z@V9^w_C0a&%m39QX%b>~kpO?3>ryrBF+VH$$ z(=Uv60m~%qeaqYQ>W^dzs*4%i9dmOe!Z3!7@tJcW3vEX?>JYCG3*Ykm&j0qADToUv zcS_R#Y=IF=Qa2192QlG*unu17RK@tvihY6xjC3%_wR-`d7VNN^^ILhvx1Ki>mU?W= zLf+LMA=AYhI53FJmR!+<(Pu0CoqkICaW7ut7mt_GKa1%W^IywdQxbc`iED&dBV7_Z zXhHxhei887=WQTY7gw~{bPxV?40(yQV%299IhTJgZ)!xWS}wSXlTe`{`o2DuoNhlf zMWzYw){jA}>>jqhZpw2Fe@JxZgXYynr2-;dlXy$LgXd7lFKXgHh@8QhvM+I~Z7Rh9HE9 zdu`lU2+3zdb#lue^U*}vOV=V`PX>GQ!0v7s++S*3x9rqc)O}a)1*)fKwGZY6V0yt0 zrf++kGKSV`phqW(5Xj^ULTf`0nla zI}P~9-tJ|*(y6K=PBiG2$(}^fz1e*=lbb=P?VqiQbM&-}6Eh^%F$cDaDjl?qmT9VFC zZf<~*B~?6U)vTnecVBG7RY;AH6 zm35d&;<_iwp32pTnVYI>`2O9(iXAOId}cz_yq8R@hgpVHS$vseF?zC`*uD7V8u;fs zj=Z^`MXM*Er;y$glq49x_9zrSdhq~YC9J*K-PY&kdRvZi zezM-r4;XzK6I3y)1A$t+tqN90zbp=Pn<_w~LoD3Eic*hz#r2lnZ=cexY&%!cpTKkD z|5c+c^2AHH3e}i{-ll}py&|iyN^mP2F6x~x+t2vglk#mn9#K(e(>|%;n&DLtE)k6* zIWTmC%-Jwd;NZY&4Q#pZjpG_qgu5bxel7OxM!bbW^)2@VU34&654+cXK~IztvBxN{ zfx^FuriyqPd%;`iA&_hs3%2co0)r=q7&WB4N{Ved$+l14VD?B0Z5GB)U)45sUXaAT zz~HM(S&GPjFIw&9ue1+f=_n)}WrA&Ihn5$v&2DwXFq21ccYkwKlPv+W@+d2|Cumkr zB?M^3b*Pr8TjcDlUJ-5IEJIDGIxsLEqVmC}N#1fA;m$9i^XDS*haf&;oc!tYG*@8H zPxl%GjTLO53+C?J)n?;a1>DUM5uM9^F$IYje>54 zc2=Dg+A^`N%3g7rp8b@mRk1UEnXu}wH?`sR{>0SC4(mXL&6kxJl(S2UJS{t~JUv;2 zz4g3O-s|iK%Bg}$vV#(M)8$-2>sp){IuLF2!rgh~?Zp$mK}95Qz!Pv}Rt377gU%JR z-mfR1J8{RqJ9r13oSop25WeWv)NW-R&G#*j@K#uYplzj#@V-H~*rZvTokG@ElY~B` zIlW&BxzRMtH$-7e##mP$-01+;BgVgip@jl?CMX(DW@K>kb_b&}b^e-_Ll7V6q2LQo zNiOU%M|q)`{gs76X{&g)eK|_vjEm4wV2SvI_hxie-0@WdY)k3!N`7jh#_=$GuIG3b5{GDFlMcJ73BD1z?8dcE>&G>M_SXsSo? zvoInR=!Wp9>blHoJSl&Ss(tDINw@>0w6=+2>AET`@bGtow=R@enV(rYu)@ zcpwDEeQ^WvME@18<&25sz!2?w@rgC{FLQg5qPWB4j)fAITrYbmil?&4aSp}Bdw6zxp~?J*JKji@kR0sTRZ*tfyp7PJXq6S%i?I^;CMLf zE0+|sOJP#KHi_~4YNY%j;s-&>S#(hDB{%c#_h|OesPj_|+KKF@d1BkG z-P`z4I#~=45m|D|^in}sdEkXr@5TY~x+AvvX38~&cCf$Zc+@!Wr-jrDUwauu$y1K6 zf@!YAk^cBszMK~^{KFk!jXX#JX)>Y)C{CV4tArR4#=h^Rb*-#t>`*b*vEX89z#^~d<<5!9m zkX6o!_^mPBm?WJ)NJuCk(^G-CQt_EM#c*028Z^rlk%cnoj0}4$R1qu$>t%cB*b;bh zTvHV#-}Pwx7Z!E#a}UKGcKKOY$SAYi!ajI2e2f{@4v>MrrrQN7)GQbI@&We_PI~GY zLGI&1SZ8~W-Gk06R~B09DtgE{xG@#Jb4e~s5E%^+J@S5|bbI@jR5ngoHx@Y5yBQHg zRHZC2Y?Bs7`6~*F(AH0xkRTaOQa2Cnn~i_3Bx@aZi~{xPOLz(e#c~ikmY&^Q>_&xjL6p(T|Pm0ysf7D=6Ev4XSD{+TV%m5+z{tIJ!~i z)v0*}u3(5L{i@1c9247E$aeab^04n5+Foifhcu;pf(DDcZ^lw)PtO%oTPao}Oi@RN6__`owW8cpI7yc=P`;mI}5-72SyFDxftjqCdZmJL~X>2GwO z)_`pQ!Mxn5jG3RbU4FC&CC?`{K;{*LLG=KJgmhHGE09lIcedQzN3(Y+6Y1D1JrIjy z+ca98%PD$r%{btP%8R82EHSrcxLM6oTsIqm+ZSm*jrA&e2xri6L+U$>#o~vi zlZe7;dk$9j$YQiXsOycG_z4)d-~4W$LA2Eyf1GYsMF8g(&9_c`v5PauDLBjtXB7^; zEM-k0YCBf`53ThcKk)^{_}=5pU$E9G)2C!(AZ%o;e(x1sLi)^9=81PlQUN7S=TORy zk~{d13Pi*TdSFbCsv*U`XYB)ETzMY%Si@x`+vdp{%IRoczBE6ak-2fTu5Z$)e&YDJ zR=`wGQGo%EU4H(YsKE!iD{-KR5~m}UV0Oq6t!*G)p{i8geFSnmcoyoNv)`$47T$Tw z9SV<&rU&^rL6uUUu(ueRyIBmW(-Y@eUEm6N? z-;+phF!BbX(*GvYomIT=rj)^d7hhI-fC7H9cy=He;#fy8Izf0wjW8gzTlZDaVJ|#vU>6Bb6IgCc8##f&wRPl2>$b}B^Pst_q=smjy zh*#J+wr?Mjl7jWW%uEtQG-9w&-;u3}ek)=2Fw~!(&i7}*ytQ^AydX&H=PK_g$h^tI zXlCRlZgAc^Z2mZ2Y+CCme(H^9q(~dz)Frc%CxH$0g1YN6cqy(r{U#deQo`@Iev^bH zvoH?g)W@n%vXRP29y(zt0qW+j^!UCIFV3?XihS&}>~0TN^#v>w+o5a6+6K=oGN=K7_vca#Eg_6UpJ&A zIDSyb(vA8^qTby*!5h>8Vnx%-mP!FdZzC@OF#cRsscCa*Xg-mv|8Gh!=l`RdKt%p^nUL>*=IX7$M(Wmo{Q$rG4mz$=b%IF$M0#^fS= z=FW?lmvY3AdPq%-(Y{#+JPHLr1%IWbv&eC_#oZ@I``PW`rE39=O`u#62u!=F3|=pc zI040gSQ{xzf$$2lAaB8mu&Zlv#e)Pve{hH)}Z<6lw{aGMKT$xjG=HZlW z?){!s!xp6sJ}K&Y(+?0B`Nc}nE}Q*bmP2Yd1CmAi)f%~KUvZ}WY;3!oRS$z*)^72b zG7wG-=FZv{jiMhTb-4YkzF1}@Mx$03hWb!75`7QTE(+q02`-<=$lqY_!leAG;Ck(% zu)RDRRil?<7Y00+c*rc+XOp#uh7O#T_~?-NngvO!=%0xn9^=^pW<{he-iD$AaoP^E z5q)UxuMfNGP-aM06yhj&t4Cnsnt{8eyMwA=ZQM zTx3*(Zd~1Bq$;LWwZ468#;w6uu1OMe-KkSx1FNk#u-rq=IT<@coY7q&L#%Tsvmdsv z=nl5S_0alt>wA5Q6h}XBp;EFT&k(?EB9WMI0FS!xjb3E10Mg101nJlwL&6I`Xb{PD15;=)SXkFW zX)Mc)-iXm?jf0^k#e1E4T?luRM>)b(QdXiuq((vMhO@~$GdluS zEG*cV8l`+i$M8k{5&g&p+o%406W{f%*F1?cXWIO<^*o+wrO_ItR z`63s~*oSMSvPJXXiz8Coc&=;<>GMoY=}KKE=(Jw(rINh6rOCHSq}~Y^$vf{2Gv-&- zq7S|0>sB**;})r^kd-P83Cr{k78DOV9@>p`%!kC)_BpFsmMwJG0QdW^Eny5y>xYLS zPpexG1&Wd_@2zeOT(RmMZk?Dk z*~oc-!gzAPq-*o2X^?FlZ>N{<*Ddb7&|N5N*W@`=;2EfDYpu{NaeK0+6W%Nlvh>zK33xi<<7&7^`jyy|SIZc5DGeG?c_WVEXWDo@Q%~tbD)J(PlT5~^ zFUy+ER^S?#3Zwz0mzf1B%~50)r0yS|7C*836GAKMiqw?uV>clip7Z*QC_Nj_gIZ*> zX~wJ-bB%+;B`cY4>X)wSMqtE&R7%?y_PEt%ajtC_-&tV&#>%(Ni~jk zOz9oQxL`K;&deH;wtk)lDI z_@Hp8J+A`41q)K~6g1jRk_&>!eOHg@gDf1g_~6sdd&hXvCrgW;O!~oNBMt}e$_vcR ze8#uYx+INzN0NJ$G;?Ma8yER+;=t8OU0tBF@ghM@U*B1$GrkxYW;P^RMhJK@TYE;) z0-v;6hkjOXonoygE{uCIvbC)v!$?DuT*VX^lI@I_*>Nv)&EBq9&oriU6NFtg^|>7D zjtK6mQY3CK1(TVq&n#GL@+!Tb9`XXR&%I-S>aS3FjE6xK-hykxnun~S-4SOY?&B8no5}dlQ#y&7 zVmVyFKffY0ZgPN)-sIblAnBNzsm=K*egz3ifxSI%&SHq!l z%G?~Vk{wV3ts!;AEWo(2*bI6tbme+Ccv3gdSW92#!;*c@@GvhP59cuqqgp^xqHq{Z ziWqW9dJWmwBZ!(Z%|M7HO=Q3K3blLGDwI_acftHWz_hj;V4I#?4j{1d*)PR~Y2_xtOg9{_yyABa;xuUP*7 z!KK!*T=1WWV8uMM17N(qeghKWF|HIs5lee%LJVTkHua5!8`w3%8iX(qS^7+4q=^rQl&kxXlM$rHK0R89m`JW%4 z|I8i#^8@ssiSvJcfM))`vFWd1sAynn>`2HUWB%0QPp`n41C7_kRY5-{dsovbyM)Y@-rWEC7W@Kgs0NuHm*w|Rv z80i?5 z$M4kO_x7)2&dv^~@NY6McFzCuGoUET-~G(a!NT$P`T%2y^WXa7&h%8w zJpdGXWglJdy76 z$HK@=!^pxdVxgm9XJlhxp=W1i`aF+T@H5lT?HmB?|GM*ATibj}^k2UJ zTb(cUf6FOlWNqST3ZVZ_2qI>dp9KKWidcSDMaam&#?a_14+lqkBRwk^m$bGNX&bx& zxB>TdkKf!9kmbf~J+AEi;syXdbvE=rf-`~usz|MlPUYpSx7#US`QT`5_mP=?eb+gP@$QPYN?DaIR#@MrK#YV z5sK%_fS(L^f$b~|H6NZWFdtZ5&UEfilQMaoqGTVY&xfR)-_kEgVCPy8ks&<1Z!8X~ zNs$d;hF*qa$`qJ?6K6iBeqRf8PJDx-f0c2|48o~taF(o2>_dUX&*up6#8i+W2}XRd ziKKnM+wZFLzwsAJ^ib*=nrL!T)!+=`3b{sx+~hr(Zm$2kPbZdetf+gF`RlB;zTOls z?SZ{yx3}~6 zgOQQ<%(t7fb?y^ht~4KxMy1d&=DArF9He&<$JS8UygH5&()Z$OCZVHiApQ8?eRKP-j}{XGdBn_3vZ?}DzD-$O`tIKV7GWqp4z z2L0?Cb#$qIdT6EHWif}e=aqjY!B300-T?yPN|5Q}KUz*+sE}cjS!489?ZwD*{F0$V zw(AI8(R#UDg}xxUPgr}$Y6GFt=2&rzBysCQE+G5(ax84$L(LZWeSg)hfi?0PKeynK z;!uv*riExj?M;hn?~i84iTqA9Vw(SBCxZTr@6 z5p}Gu3km}q!aVtn*c|)zO7G`GC|H&Wsp}6}7Sk|Sr1Tx8`M1qDB6-C4pGL^q+;nPJ zt2T6JXwN^=OtgO1Z|B>MP-zipp|JRea1x!E90sHL4hZHz+eZi@0geD#PQREUdPz@9gFFRU zPg8}(GKRw^VzG!>EB3yY=;LhNJ?-pq)XjDHb3e4b6_0q$E07X(@JCjZcDNzl0iUY+wjWOfyZYCIj8KqGCm zwH|)}Js+K&C>=FYjTP4@=Uos~tZ+Mo^fy8wa5nN!-`DFBhH*4%1ayDw^$AJ5yoc_* zj7z}PpPGY~1js4YbP~9Ef~mc;U23>#>lw#pMkZ5!Tr+Qe|CA6h)k$55zgv|lq*$vY zs~Hdo2naKq^iLAMYGn03Fyd}#b&*DZweaNI1zL(GDHeVinb;zQuP|BFNag$N@Aj9} z?{QHFGZSmqFZTbKkqb9kL#b=cA(HhH=!Cf4!;$mN@UPlVZq$nCdPX$k(9Nok@r5Jh5x* zH;iJ7k^H#gVCoDydl0D3-e$9ENf8$~oofgy&^^Esxi`0;NPuor9ps61kNxT%+Kjwy5__WSd{8zsnm$C zh&{^4Jf!{A#c&DUA*wRj|V?IN~Ej zB!Np|((f}a78RgDm!-8NMvV6j!f-<>5@MeWRh9Vw;{cW1kkQyt^cMe}2TYs(D z;b-Z4tZu8zC}%|WVZb;Z)u`TvHuFSfN3rekCLEG6E(q^ z2pkNamkm&#Xm_6Y>fs;yc`VeGLqakjv(=rmF89s6%-maxaX|?0^SB}D95<8cLbsw~ z+TYTV^H`_;=3;~o{v~?&m_He|o2KY~IKhEQa9Ee$7P-NX-x9HYnT@V=V_@*%=9IgP z1bh+#6zTPv()dF7iXL=hvfFRFRrWgdQrpA~KAif~=Z9%yd}`bbYR8Q6X+grkiQwTB zUAjtHP5878XKe7c{n`+)n*mGgv0fij+#q7~!F28eCw4@d8dX91{S-q)E-lUAY7_%J zPEGBUi`J(5qp2Sp9WUNmtNzRoPjQdIj7$xd(C@BB(F=Qqgmq)tlw)}QQdV^uU_QIV zh{|=;RxQ7e%Zb<{%{K93I@)kqi<7ABY5Gh?(7Wa-Oy;jj?j2B>v1Avg6Wa*8ys(P%t|aG(u#VQ-nN(kEV`|Bk+Jm=@HCE48m;axR^gsRnKke&R zOUuB*&ir2z@K4|S1!@4a{tx!m%KwY8e0Jq*^vnQO7J2{+D?8w`rv|VxG67iWSpUKP zcV=d#2YmM3|BNr6ufgz%F|f14FtM=%{=egEA0|fTfAIhA^JT-v#t2|%|2*gO%zykp z_xK0@-0yRS`ENX5Iz~3Sf5w;ozxIp_EdTg@VgEnl|HSp*bH3)UeVAAn{uy8EuQ*xQ z8UM|hi50-Y!t%*7*gow($ES{w`7>uaMwl<#f9l2lS(~pKeaWA;|Lnm)_fK8^$@5G1 zIq2!=K9&CwnAzF>8#K&U5MP1+H>j`mS7E=3_22oksQ=h}$^Y1W%FOitCjM2Rf9yVs z^)-Lt{}$@s`M=m#FkiZ_@!yMJ|Geb?E!zM4Q2Z}l{Fh&TkwzgS2LpRETSuEOKJ+Qe z=vjR+PickEr|pX%nK%HLVE)kv{L9g(KNF^AV5SGKvwfD2neK~|iGT7`GXs8W6HBAd zCy$oj!QhMVeJN?b_Wu&TTwp#Kt*w}mnTe_6SMffzs{fIgzl5*jKdG4!fc_t9_a8vd z%FNOgKrU!wW&7)kwMl=nHG6A{Pu}WiWTpIhasNv}|8LUwc@qq5ECAYnDImjVt=T?r z=Re{XVgAn&!1x&u!)K=dKin|90bCAed72pp7^I7&%V=`*9C3AxyA#O2r3DfSzKev< zucf6$5Tt93yD|8+r|AH1ut{gmT^NnAw4r>x#r+ood$k`!dRBA=SY1QIY!gGnATlx)dWXMYZ&pEMDxgc8NlU5@yr}?71P~5CD?&ceOD;8R3!k`H z5})7ToxS5@y5bE-fo)6v>{5i_v_Hq{pO_4G9V=>+; z99PTd9ssncYi0~p;{;S`Tf+jf1lm~$8b@LTbX^ZP`H)I@ngCPp+(-n~%`tg4Y3Wem zwKg<Hl3>%u{Z048-@tGHiLh((2XW6{tGs#LOnFQk6 zqTT27Ny;Jwg1W!7dBA}^8?(g4J;WXTfv;ugvI^lbmNPHX&7!Ofun>iGc@*^{G`

    fQFEJFUr!KMk5(~d)fUtD&E=lo40hf1+ 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 diff --git a/benchmark_tests/plots/lassen_bruck.pdf b/benchmark_tests/plots/lassen_bruck.pdf deleted file mode 100644 index b7700637e05e65e38b7d6e50a7bd475fb0cbf935..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130306 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%0AhD6+H_q(-t@+ucwo&P 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≺+!vyvDjU+WYzJXrW{} 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

    T?dp$b%ys-~u^AdZMK{ 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 diff --git a/benchmark_tests/plots/plot.py b/benchmark_tests/plots/plot.py deleted file mode 100644 index db18a10b2..000000000 --- a/benchmark_tests/plots/plot.py +++ /dev/null @@ -1,220 +0,0 @@ -file_in = "../dane/microbench.out" -folder_out = "dane" - -import os -if not os.path.exists(folder_out): - os.makedirs(folder_out) - -file = open(file_in) - -class Timings: - sizes = "" - times = "" - - def __init__(self): - self.sizes = list() - self.times = list() - - def add_timing(self, size, time): - self.sizes.append(size) - self.times.append(time) - -class PingPong: - on_socket = "" - off_socket = "" - off_node = "" - - def __init__(self): - self.on_socket = Timings() - self.off_socket = Timings() - self.off_node = Timings() - -class MultiProcTimings: - active_procs = "" - multi_proc_timings = "" - - def __init__(self): - self.active_procs = list() - self.multi_proc_timings = list() - - def add_active_procs(self, n_active): - self.active_procs.append(n_active) - self.multi_proc_timings.append(Timings()) - - def add_timing(self, size, time): - self.multi_proc_timings[-1].add_timing(size, time) - -class MultiProcPingPong: - on_socket = "" - off_socket = "" - on_node = "" - on_socket_all = "" - off_node_all = "" - - def __init__(self): - self.on_socket = MultiProcTimings() - self.off_socket = MultiProcTimings() - self.off_node = MultiProcTimings() - self.on_socket_all = MultiProcTimings() - self.off_node_all = MultiProcTimings() - -standard = PingPong() -multiproc = MultiProcPingPong() -multimsg = PingPong() -matching = PingPong() - -group = "" -test = "" - -## Collect Standard Data -for line in file: - if "MultiProc" in line: - group = multiproc - elif "Multi" in line: - group = multimsg - elif "Matching" in line: - group = matching - elif "Ping-Pong" in line: - group = standard - - if "On-Socket" in line: - if "All Sockets Active" in line: - test = group.on_socket_all - else: - test = group.on_socket - elif "Off-Socket" in line: - test = group.off_socket - elif "Off-Node" in line: - if "Even Sockets" in line: - test = group.off_node_all - else: - test = group.off_node - - if "Active Procs" in line: - active = (int)((line.rsplit('\n')[0]).rsplit(' ')[-1]) - test.add_active_procs(active) - - if "Size" in line: - data = (line.rsplit('\n')[0]).rsplit(':') - size = (int)(data[0].rsplit(' ')[-1]) - time = (float)(data[1].rsplit(' ')[-1]) - test.add_timing(size, time) - -import pyfancyplot as plot -import matplotlib.backends - - -## Plot Standard On-Socket vs Off-Socket vs Off-Node -plot.add_luke_options() -plot.line_plot(standard.on_socket.times, x_data = standard.on_socket.sizes, tickmark = '.-', color = 'blue', label = "On-Socket") -plot.line_plot(standard.off_socket.times, x_data = standard.off_socket.sizes, tickmark = '.-', color = 'red', label = "Off-Socket") -plot.line_plot(standard.off_node.times, x_data = standard.off_node.sizes, tickmark = '.-', color = 'black', label = "Off-Node") -plot.add_anchored_legend() -plot.set_scale('log', 'log') -plot.add_labels("Message Size (Bytes)", "Measured Time (Seconds)"); -plot.save_plot("%s/standard.pdf"%folder_out) - - -## Plot On-Socket PPN -plot.add_luke_options() -plot.set_palette(n_colors=len(multiproc.on_socket.active_procs)) - -for i in range(len(multiproc.on_socket.active_procs)): - n_proc = multiproc.on_socket.active_procs[i] - data = multiproc.on_socket.multi_proc_timings[i] - plot.line_plot(data.times, x_data = data.sizes, tickmark = ".-", label = "%d Active"%(n_proc)) -plot.add_anchored_legend(ncols=3) -plot.set_scale('log', 'log') -plot.add_labels("Message Size (Bytes)", "Measured Time (Seconds)"); -plot.save_plot("%s/multiproc-on-socket.pdf"%folder_out) - -## Plot Off-Socket PPN -plot.add_luke_options() -plot.set_palette(n_colors=len(multiproc.off_socket.active_procs)) - -for i in range(len(multiproc.off_socket.active_procs)): - n_proc = multiproc.off_socket.active_procs[i] - data = multiproc.off_socket.multi_proc_timings[i] - plot.line_plot(data.times, x_data = data.sizes, tickmark = ".-", label = "%d Active"%(n_proc)) -plot.add_anchored_legend(ncols=3) -plot.set_scale('log', 'log') -plot.add_labels("Message Size (Bytes)", "Measured Time (Seconds)"); -plot.save_plot("%s/multiproc-off-socket.pdf"%folder_out) - -## Plot Off-Node PPN -plot.add_luke_options() -plot.set_palette(n_colors=len(multiproc.off_node.active_procs)) - -for i in range(len(multiproc.off_node.active_procs)): - n_proc = multiproc.off_node.active_procs[i] - data = multiproc.off_node.multi_proc_timings[i] - plot.line_plot(data.times, x_data = data.sizes, tickmark = ".-", label = "%d Active"%(n_proc)) -plot.add_anchored_legend(ncols=3) -plot.set_scale('log', 'log') -plot.add_labels("Message Size (Bytes)", "Measured Time (Seconds)"); -plot.save_plot("%s/multiproc-off-node.pdf"%folder_out) - - -## Plot On-Socket PPN, All Active -plot.add_luke_options() -plot.set_palette(n_colors=len(multiproc.on_socket_all.active_procs)) - -for i in range(len(multiproc.on_socket_all.active_procs)): - n_proc = multiproc.on_socket_all.active_procs[i] - data = multiproc.on_socket_all.multi_proc_timings[i] - plot.line_plot(data.times, x_data = data.sizes, tickmark = ".-", label = "%d Active"%(n_proc)) -plot.add_anchored_legend(ncols=3) -plot.set_scale('log', 'log') -plot.save_plot("%s/multiproc-on-all-sockets.pdf"%folder_out) - -## Plot Off-Node PPN, Even Sockets -plot.add_luke_options() -plot.set_palette(n_colors=len(multiproc.off_node_all.active_procs)) - -for i in range(len(multiproc.off_node_all.active_procs)): - n_proc = multiproc.off_node_all.active_procs[i] - data = multiproc.off_node_all.multi_proc_timings[i] - data_orig = multiproc.off_node.multi_proc_timings[i+1] - ctr = plot.color_ctr - plot.line_plot(data.times, x_data = data.sizes, tickmark = ".-", label = "%d Active"%(n_proc*2)) - plot.color_ctr = ctr - plot.line_plot(data_orig.times, x_data = data.sizes, tickmark = ":") - -plot.add_anchored_legend(ncols=3) -plot.set_scale('log', 'log') -plot.save_plot("%s/multiproc-off-node-even.pdf"%folder_out) - -# Plot Multiple Messages -plot.add_luke_options() -plot.line_plot(multimsg.on_socket.times, x_data = multimsg.on_socket.sizes, tickmark=".-", label = "On-Socket") -plot.line_plot(multimsg.off_socket.times, x_data = multimsg.off_socket.sizes, tickmark=".-", label = "Off-Socket") -plot.line_plot(multimsg.off_node.times, x_data = multimsg.off_node.sizes, tickmark=".-", label = "Off-Node") -plot.add_anchored_legend(ncols=3) -plot.set_scale('log', 'log') -plot.save_plot("%s/multimsg.pdf"%folder_out) - - -# Plot Queue Searches -plot.add_luke_options() - -ctr = plot.color_ctr -plot.line_plot(multimsg.on_socket.times, x_data = multimsg.on_socket.sizes, tickmark=":", label = "On-Socket") -plot.color_ctr = ctr -plot.line_plot(matching.on_socket.times, x_data = matching.on_socket.sizes, tickmark="-") - -ctr = plot.color_ctr -plot.line_plot(multimsg.off_socket.times, x_data = multimsg.off_socket.sizes, tickmark=":", label = "Off-Socket") -plot.color_ctr = ctr -plot.line_plot(matching.off_socket.times, x_data = matching.off_socket.sizes, tickmark="-") - -ctr = plot.color_ctr -plot.line_plot(multimsg.off_node.times, x_data = multimsg.off_node.sizes, tickmark=":", label = "Off-Node") -plot.color_ctr = ctr -plot.line_plot(matching.off_node.times, x_data = matching.off_node.sizes, tickmark="-") - -plot.add_anchored_legend(ncols=3) -plot.set_scale('log', 'log') -plot.add_labels("Number of Messages (Each 1 Byte)", "Measured Time (Seconds)"); -plot.save_plot("%s/matching.pdf"%folder_out) - - diff --git a/benchmark_tests/plots/plot_allgathers.py b/benchmark_tests/plots/plot_allgathers.py deleted file mode 100644 index 663f8c21b..000000000 --- a/benchmark_tests/plots/plot_allgathers.py +++ /dev/null @@ -1,97 +0,0 @@ -import numpy as np - -import profile -import glob - -import matplotlib -from matplotlib import pyplot as plt -from pyfancyplot import plot - -class Test(): - procs = 0 - ppn = 0 - mpi = "" - standard = "" - locality = "" - hierarchical = "" - multilane = "" - sizes = "" - - def __init__(self, procs, ppn): - self.procs = procs - self.ppn = ppn - self.mpi = list() - self.standard = list() - self.locality = list() - self.hierarchical = list() - self.multilane = list() - self.sizes = list() - - def list_append(self, size): - self.sizes.append(size) - self.mpi.append(np.inf) - self.standard.append(np.inf) - self.locality.append(np.inf) - self.hierarchical.append(np.inf) - self.multilane.append(np.inf) - - def add_time(self, i, line): - time = (float)((line.rsplit('\n')[0]).rsplit(' ')[-1]) - if "MPI" in line: - if time < self.mpi[i]: - self.mpi[i] = time - elif "mult_hier" in line: - if time < self.multilane[i]: - self.multilane[i] = time - elif "hier" in line: - if time < self.hierarchical[i]: - self.hierarchical[i] = time - elif "loc" in line: - if time < self.locality[i]: - self.locality[i] = time - else: - if time < self.standard[i]: - self.standard[i] = time - - -tests = list() -folder = "../%s"%profile.computer -for i in range(len(profile.procs)): - tests.append(Test(profile.procs[i], profile.ppn[i])) - for file in glob.glob("%s/%s_n%d_ppn%d.*.out"%(folder, profile.filename, profile.procs[i], profile.ppn[i])): - idx = -1 - f = open(file) - for line in f: - if "Testing Size" in line: - if idx + 1 == len(tests[-1].sizes): - tests[-1].list_append((int)((line.rsplit('\n')[0]).rsplit(' ')[-1])) - idx = idx + 1 - elif "Time" in line: - tests[-1].add_time(idx, line) - f.close() - - -# Plot Size = 1 -plot.add_luke_options() -standard = [t.standard[1] for t in tests] -hierarchical = [t.hierarchical[1] for t in tests] -multilane = [t.multilane[1] for t in tests] -locality = [t.locality[1] for t in tests] -mpi = [t.mpi[1] for t in tests] -plot.barplot(np.arange(len(tests)), [standard, hierarchical, multilane, locality], ["Bruck", "Hierarchical", "Multi-lane", "Locality"]) - -width = 0.4 -ax = plot.get_ax() -xticks = ax.get_xticks() - -for i in range(len(xticks)): - plt.plot([xticks[i] - width, xticks[i] + width], [mpi[i], mpi[i]], '--', color='black') -plot.add_labels("", "Measured Time (Seconds)") -labels = list() -for i in range(len(profile.ppn)): - ppn = profile.ppn[i] - nodes = profile.procs[i] / ppn - labels.append("%d PPN\n%d Nodes"%(ppn, nodes)) - -plot.set_xticklabels(labels, rotation='horizontal') -plot.save_plot("%s_%s.pdf"%(profile.computer, profile.filename)) diff --git a/benchmark_tests/plots/pyfancyplot.py b/benchmark_tests/plots/pyfancyplot.py deleted file mode 100644 index c1d8a2899..000000000 --- a/benchmark_tests/plots/pyfancyplot.py +++ /dev/null @@ -1,564 +0,0 @@ -import numpy as np -import matplotlib.pylab as plt -import pandas as pd -import seaborn as sns - -sns.set(style="whitegrid", palette="muted") -font_style = 'serif' -linewidth = 3 -xscale = 'linear' -yscale = 'linear' -palette_name = "tab20" -palette_n_colors = 7 -palette_desat = None -color_ctr = 0 -lgd = None -fig = None - -################################ -### Set Default Options -### Serif Font, Size 24 -### Initial standard color palette -################################ -def default_options(): - fontsize = 24 - fig_width_pt = 700.0 - inches_per_pt = 1.0/72.27 # Convert pt to inch - golden_mean = (np.sqrt(5)-1.0)/2.0 # Aesthetic ratio - fig_width = fig_width_pt*inches_per_pt # width in inches - fig_height = fig_width*golden_mean # height in inches - fig_size = [fig_width, fig_height] - params = {'backend': 'ps', - 'font.family': 'serif', - 'font.serif': 'cm', - 'font.sans-serif': 'arial', - 'axes.labelsize': fontsize, - 'font.size': fontsize, - 'axes.titlesize': fontsize, - 'legend.fontsize': fontsize-2, - 'xtick.labelsize': fontsize, - 'ytick.labelsize': fontsize, - 'text.usetex': True, - 'figure.figsize': fig_size, - 'lines.linewidth': 4, - 'hatch.linewidth': 3.0} - plt.rcParams.update(params) - - global font_style - global linewidth - global xscale - global yscale - global palette_name - global palette_n_colors - global palette_desat - global color_ctr - global ldg - global fig - - font_style = 'serif' - linewidth = 3 - xscale = 'linear' - yscale = 'linear' - palette_name = "tab20" - palette_n_colors = 7 - palette_desat = None - color_ctr = 0 - lgd = None - - fig = plt.figure(figsize=(fig_width, fig_height)); - plt.gcf() - -################################ -### The reason you're -### using this script -################################ -def add_luke_options(): - from matplotlib import rc - font = {'family' : 'serif', - 'size' : 22} - rc("font", **font) - rc("lines", linewidth=3) - - ax = plt.gca() - ax.xaxis.grid(False) - #plt.grid(True) - sns.despine(left=True, right=True) - -################################ -### Clear previously plotted data -### Reset all global variables -### Initialize default options -################################ -def clear(): - plt.clf() - plt.close('all') - default_options() - -# Automatically set default options -default_options() -add_luke_options() ## add these by default, too - - -################################ -### Set color palette -### Palette options: -### - Seaborn color palette name -### - list of colors -### - list of RGB values -################################ -def set_palette(palette = "tab20", n_colors = None, desat = None): - global palette_name - global palette_n_colors - global palette_desat - global color_ctr - - palette_name = palette - palette_n_colors = n_colors - palette_desat = desat - color_ctr = 0 - -################################ -### Returns Seaborn color palette -### If num colors is passed, will -### override palette_n_colors -### For other options, first -### call set_palette -################################ -def get_palette(num_colors = None): - global palette_name - global palette_n_colors - global palette_desat - if not num_colors is None: - palette_n_colors = num_colors - return sns.color_palette(palette_name, palette_n_colors, palette_desat) - -################################ -### Returns next color in -### Seaborn palette -################################ -def next_color(): - global palette_name - global palette_n_colors - global palette_desat - global color_ctr - - color_palette = sns.color_palette(palette_name, palette_n_colors, palette_desat) - color = color_palette[color_ctr]; - color_ctr = (color_ctr + 1) % len(color_palette) - return color - -################################ -### Set scale of x and y dimensions -### Either 'linear' or 'log' -################################ -def set_scale(xscale, yscale): - ax = plt.gca() - ax.set_xscale(xscale) - ax.set_yscale(yscale) - ax.set_autoscaley_on(False) - -def get_ax(): - return plt.gca() - -################################ -### Set figure size (by inches) -################################ -def set_figure_size(dim_inches): - global fig - fig.set_size_inches(dim_inches) - -################################ -### Set figure size (by dpi) -################################ -def set_figure_dpi(dpi): - fig.set_dpi(dpi) - -################################ -### Add standard matplotlib legend -################################ -def add_legend(ncol = 1, - loc = 'best', - frameon = False, - fontsize = 20, - **kargs): - - plt.legend(loc = loc, ncol = ncol, frameon = frameon, fontsize = - fontsize, **kargs) - -################################ -### Add multi column legend -### Anchored about plot by default -################################ -def add_anchored_legend(ncol = 2, - loc = "upper center", - anchor = (0., 1.10, 1.,.102), - frameon = False, - fontsize = 22, - **kargs): - - global lgd - lgd = plt.legend(loc = loc, ncol = (int)(ncol), bbox_to_anchor = anchor, - frameon = frameon, fontsize = fontsize, **kargs) - -################################ -### Adds legend for barplots -### By selecting rectangles -### Adds multi column legend -### Anchored about plot by default -################################ -def barplot_legend(labels, positions, ax, n_cols = 0, **kargs): - import matplotlib.patches as patches - objs = ax.findobj(match=patches.Rectangle) - legend_lines = list() - - for i in range(len(labels)): - idx = positions[i] - legend_lines.append(objs[idx]) - if n_cols <= 0: - n_cols = ((len(labels) - 1) / 2) + 1 - if (n_cols < 2): - n_cols = 2 - add_anchored_legend(handles=legend_lines, labels=labels, ncol=n_cols, - **kargs) - -################################ -### Add a title -################################ -def add_title(title): - plt.title(title) - -################################ -### Add labels for x and y dims -################################ -def add_labels(xlabel, - ylabel): - plt.xlabel(xlabel) - plt.ylabel(ylabel) - - - -################################ -### Set limits for x dimension -################################ -def set_xlim(xmin = None, - xmax = None): - ax = plt.gca() - if xmin is None: - xmin, _ = ax.get_xlim() - if xmax is None: - _, x1 = ax.get_xlim() - xmax = x1 + (x1 / 20.0) - ax.set_xlim((xmin, xmax)) - -################################ -### Set limits for y dimension -################################ -def set_ylim(ymin = None, - ymax = None): - ax = plt.gca() - ax.set_autoscaley_on(False) - if ymin is None: - y0, _ = ax.get_ylim() - ymin = y0 - (y0 / 50.0) - if ymax is None: - _, ymax = ax.get_ylim() - ax.set_ylim(ymin, ymax) - ax.axis('tight') - - - -################################ -### Sets x-tick labels -################################ -def set_xticklabels(xticklabels, - rotation = 'vertical', - fontsize = 16, - **kargs): - ax = plt.gca() - ax.set_xticklabels(xticklabels, rotation = rotation, - fontsize = fontsize, **kargs) - -################################ -### Sets x-ticks and corresponding labels -################################ -def set_xticks(xdata, xticklabels, rotation = 'horizontal', **kargs): - ax = plt.gca() - ax.set_xticks(xdata) - ax.set_xticklabels(xticklabels, rotation = rotation, **kargs) - - -################################ -### Sets y-tick labels -################################ -def set_yticklabels(yticklabels, - rotation = 'horizontal', - fontsize = 16, - **kargs): - ax = plt.gca() - ax.set_yticklabels(yticklabels, rotation = rotation, - fontsize = fontsize, **kargs) - -################################ -### Set y-ticks and corresponding labels -################################ -def set_yticks(ydata, - yticklabels, - rotation = 'horizontal', - **kargs): - ax = plt.gca() - ax.set_yticks(ydata) - ax.set_yticklabels(yticklabels, rotation = rotation, **kargs) - - - -################################ -### Standard line plot -################################ -def line_plot(y_data, - x_data = None, - tickmark = '-', - alpha = 1.0, - linewidth=3, - ax = plt, - color = None, - **kargs): - if x_data is None: - x_data = np.arange(0, len(y_data)) - if color is None: - color = next_color() - return ax.plot(x_data, y_data, tickmark, - color = color, clip_on = False, - alpha = alpha, linewidth = linewidth, **kargs) - - -def violin_plot(x_data, y_data, labels = None, add_legend = True, ax = None,**kargs): - if ax is None: - ax = plt.gca() - - if labels is None: - vplot = sns.barplot(x=x_data, y=y_data) - else: - pd_dict = dict() - pd_dict['x'] = x_data - for i in range(len(labels)): - pd_dict[labels[i]] = y_data[i] - df = pd.DataFrame(pd_dict) - df = df.melt(id_vars=['x'], var_name='measure', value_vars=labels, - value_name='time') - vplot = sns.violinplot(data=df, x='x', y='time', hue='measure', ax = - ax, palette = get_palette(), edgecolor='black', **kargs) - - return vplot - -################################ -### Standard scatter plot -################################ -def scatter_plot(x_data, - y_data, - marker = 'o', - color = None, - **kargs): - if color is None: - color = next_color() - return plt.scatter(x_data, y_data, c = color, edgecolors='none', - clip_on = False, marker = marker, **kargs) - -################################ -### Spy of Matrix -################################ -def spy(A, color = 'black', markersize = None): - plt.spy(A, rasterized=True, markersize=markersize) - -################################ -### Creates a standard barplot -################################ -def barplot(x_data, - y_data, - labels = None, - ax = None, - add_legend = True, - color = None, - **kargs): - if ax is None: - ax = plt.gca() - - bplot = "" - if labels is None: - if color is None: - color = next_color() - bplot = sns.barplot(x=x_data, y=y_data, color=color, ax = ax, - edgecolor='black', **kargs) - else: - pd_dict = dict() - pd_dict['x'] = x_data - for i in range(len(labels)): - pd_dict[labels[i]] = y_data[i] - df = pd.DataFrame(pd_dict) - df = df.melt(id_vars=['x'], var_name='measure', value_vars=labels, - value_name='time') - bplot = sns.barplot(data=df, x='x', y='time', hue='measure', ax = - ax, palette = get_palette(), edgecolor='black', **kargs) - if add_legend: - positions = [i * len(x_data) for i in range(len(labels))] - barplot_legend(labels, positions, ax) - - - return bplot - -################################ -### Creates a stacked barplot -################################ -def stacked_barplot(x_data, # simple list - y_data, #list of lists (each of len(x_data)) - labels, #list of labels corresponding to y_data - ax = None, - **kargs): - if ax is None: - ax = plt.gca() - bplots = list() - - new_y_data = list() - for i in range(len(y_data)): - new_y_data.append(list()) - for j in range(len(y_data[i])): - new_y_data[i].append(y_data[i][j]) - - for i in range(len(labels)): - for j in range(len(labels)-1, i, -1): - for k in range(0, len(new_y_data[i])): - new_y_data[i][k] += new_y_data[j][k] - - colors = get_palette(len(labels)) - plots = list() - for i in range(len(labels)): - pd_dict = dict() - pd_dict['x'] = x_data - pd_dict[labels[i]] = new_y_data[i] - df = pd.DataFrame(pd_dict) - df = df.melt(id_vars=['x'], var_name='measure', - value_vars=[labels[i]], - value_name='time') - bplots.append(sns.barplot(ax = ax, data=df, x='x', y='time', - color=colors[i], edgecolor='black', **kargs)) - positions = [i * len(x_data) for i in range(len(labels))] - barplot_legend(labels, positions, ax) - return bplots - -################################ -### Create a partially stacked barplot -### Stacking some bars, but not all -### All bars share the same x_data -################################ -def partially_stacked_barplot(x_data, # simple list - y_data, # list of data and lists e.g. - # [y0, y1, [y2, y3], y4] would leave - # y0, y1, and y4 as simple bars - # but would stack y3 on top of y2 - labels, # labels in same format as y_data - # e.g. [l0, l1, [l2, l3], l4] corresponds - # to example y_data - ax = None, - **kargs): - if ax is None: - ax = plt.gca() - bplots = list() - max_stack_size = 1 - num_bars = len(y_data) - num_colors = 0 - bar_num_stacked = list() - stacked_bars = list() - positions = list() - for i in range(num_bars): - if type(labels[i]) == type("string"): - bar_num_stacked.append(1) - positions.append(i) - else: - stack_size = len(y_data[i]) - bar_num_stacked.append(stack_size) - stacked_bars.append(i) - for j in range(stack_size): - positions.append(i) - if stack_size > max_stack_size: - max_stack_size = stack_size - num_colors += bar_num_stacked[-1] - indices = np.zeros((num_colors, ), dtype = 'int') - - colors = get_palette(num_colors) - iter_colors = list() - iter_ctrs = list() - iter_labels = list() - iter_y_data = list() - - ctr = 0 - for i in range(num_bars): - stack_size = bar_num_stacked[i] - if stack_size == 1: - iter_labels.append(labels[i]) - iter_y_data.append(y_data[i]) - else: - iter_labels.append(labels[i][-1]) - iter_y_data.append(y_data[i][0]) - for j in range(1, stack_size): - for k in range(0, len(iter_y_data[i])): - iter_y_data[i][k] += y_data[i][j][k] - iter_colors.append(colors[ctr + stack_size - 1]) - iter_ctrs.append(ctr + stack_size - 1) - ctr += stack_size - - # Bar plot for each stack - for i in range(max_stack_size): - set_palette(iter_colors, len(iter_colors)) - barplot(x_data, iter_y_data, iter_labels) - for idx in stacked_bars: - stack_size = bar_num_stacked[idx] - if stack_size <= i+1: - continue - iter_ctrs[idx] -= 1 - indices[iter_ctrs[idx]] = i+1 - iter_labels[idx] = labels[idx][stack_size-i-2] - for k in range(len(iter_y_data[idx])): - iter_y_data[idx][k] -= y_data[idx][stack_size-i-1][k] - iter_colors[idx] = colors[iter_ctrs[idx]] - - label_list = list() - for i in range(num_bars): - stack_size = bar_num_stacked[i] - if stack_size == 1: - label_list.append(labels[i]) - else: - for j in range(stack_size): - label_list.append(labels[i][j]) - for i in range(num_colors): - positions[i] = (indices[i]*num_bars*len(x_data)) + (positions[i]*len(x_data)) - barplot_legend(label_list, positions, ax) - - -################################ -### Save the plot to a file -### Clears all data after by default -################################ -def save_plot(filename, - clear_plot = True, - **kargs): - global lgd - - if lgd is None: - plt.savefig(filename, bbox_inches = "tight", - transparent=True, **kargs) - else: - plt.savefig(filename, bbox_extra_artists=(lgd,), bbox_inches = "tight", - transparent=True, **kargs) - - if clear_plot: - clear() - -################################ -### Display your plot -################################ -def display_plot(): - plt.show() - - - - - diff --git a/benchmark_tests/plots/quartz_bruck_allgather.pdf b/benchmark_tests/plots/quartz_bruck_allgather.pdf deleted file mode 100644 index 648cb0661e48889f51771a165895b2712499a625..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99908 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%0AhD6+H_q(-t@+ucwo&P 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≺+!vyvDjU+WYzJXrW{} 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 ee-+JNh2-F Date: Fri, 14 Nov 2025 09:00:01 -0600 Subject: [PATCH 11/15] Add initial Doxygen documentation for all MPIL structs and functions, and most internal functions (#49) * Documented locality_aware.h * Outlined new README * Updated make_test to default to mpirun and allow the variable to be tweaked from CMake command line * Commented out openmp code that was untested * General code organization and cleanup * Updated HIP build * Fixed mis-aligned gpu to hip function * Moved mircobenchmark out of main builds --- CMakeLists.txt | 28 +- Doxyfile | 2863 +++++++++++++++++ README.md | 348 +- benchmarks/CMakeLists.txt | 2 +- .../{ => unsupported}/microbenchmarks.cpp | 0 include/locality_aware.h | 269 +- library/include/collective/alltoall.h | 204 +- library/include/collective/alltoallv.h | 71 +- library/include/communicator/MPIL_Comm.h | 66 +- library/include/communicator/MPIL_Info.h | 2 + library/include/communicator/comm_data.h | 14 +- library/include/communicator/comm_pkg.h | 12 + library/include/communicator/locality_comm.h | 18 +- library/include/heterogeneous/gpu_alltoall.h | 53 +- library/include/heterogeneous/gpu_alltoallv.h | 6 +- library/include/heterogeneous/gpu_utils.h | 16 + library/include/heterogeneous/utils_hip.h | 2 +- library/include/neighborhood/MPIL_Topo.h | 14 + library/include/persistent/MPIL_Request.h | 53 +- library/include/utils/utils.h | 44 +- .../collective/alltoallv/alltoallv_batch.c | 18 +- library/source/heterogeneous/CMakeLists.txt | 8 +- .../{device_repack.c => device_repack.cpp} | 0 library/source/heterogeneous/gpu_alltoall.c | 421 ++- library/source/heterogeneous/gpu_alltoallv.c | 8 +- .../{gpu_repack.c => gpu_repack.cpp} | 0 .../locality/neighbor_locality.cpp | 1 - 27 files changed, 4144 insertions(+), 397 deletions(-) create mode 100644 Doxyfile rename benchmarks/{ => unsupported}/microbenchmarks.cpp (100%) rename library/source/heterogeneous/{device_repack.c => device_repack.cpp} (100%) rename library/source/heterogeneous/{gpu_repack.c => gpu_repack.cpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 60ea3700f..76318c4a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ project(locality_aware VERSION 2.0.0 LANGUAGES C CXX) # Set C,CXX standards set(CMAKE_CXX_STANDARD 11) -set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD 11) # Set default build type if(NOT CMAKE_BUILD_TYPE) @@ -37,16 +37,6 @@ set(MPIRUN "mpirun" CACHE STRING "MPIRUN command") find_package(MPI REQUIRED) set(EXTERNAL_LIBS "MPI::MPI_CXX") -if(NOT USE_HIP) - # Find OpenMP (not required) - find_package(OpenMP) - if (${OpenMP_CXX_FOUND}) - list(APPEND EXTERNAL_LIBS "OpenMP::OpenMP_CXX") - add_definitions(-DOPENMP) - endif() -endif() - - # Set default language of .cpp files set(locality_aware_LANG CXX) add_library(locality_aware "") @@ -68,8 +58,9 @@ if (USE_CUDA OR USE_HIP) target_compile_definitions(locality_aware PUBLIC CUDA) elseif (USE_HIP) - enable_language(HIP) - set(locality_aware_LANG HIP) + find_package(hip REQUIRED) + target_link_libraries(locality_aware PUBLIC hip::host) + target_compile_options(locality_aware PUBLIC $<$:-x hip>) message(STATUS "HIP support enabled") target_compile_definitions(locality_aware PUBLIC HIP) endif() @@ -77,7 +68,14 @@ endif() if (ENABLE_UNIT_TESTS) enable_testing() - set(TEST_PROCS "16" CACHE STRING "Number of processes to use when running ctests") + set(TEST_PROCS "16" CACHE STRING "Number of processes to use when making ctests") + function(make_test file) + #get file name for unique handle + get_filename_component(exec_name ${file} NAME_WE) + add_executable(${exec_name} ${file}) + target_link_libraries(${exec_name} locality_aware ${EXTERNAL_LIBS}) + add_test(NAME ${exec_name}_Test COMMAND ${MPIRUN} -n ${TEST_PROCS} ./${exec_name}) + endfunction() endif() # Flag == TRUE means TEST @@ -109,7 +107,7 @@ add_subdirectory(include) add_subdirectory(library) target_link_libraries(locality_aware PUBLIC ${EXTERNAL_LIBS}) -if (USE_GPU) +if (USE_CUDA) get_property(GPU_SOURCES_GLOBAL GLOBAL PROPERTY GPU_SOURCES_GLOBAL) set_source_files_properties(${GPU_SOURCES_GLOBAL} PROPERTIES LANGUAGE ${locality_aware_LANG}) endif() diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 000000000..08e824bd7 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,2863 @@ +# Doxyfile 1.9.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Locality Aware" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = A collection of MPI alltoall and alltoallv algorithms with support for various optimizations + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. + +CASE_SENSE_NAMES = SYSTEM + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = include/ library/ README.md + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, +# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php, +# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be +# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cxxm \ + *.cpp \ + *.cppm \ + *.c++ \ + *.c++m \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.ixx \ + *.l \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = benchmarks library/tests/ + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = README.md + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" + +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" + +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" + +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. +# The default value is: YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag UML_LOOK is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# https://www.graphviz.org/)). +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, +# gif, gif:cairo, gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, +# png:cairo, png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate +# files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. +# The default value is: YES. + +DOT_CLEANUP = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/README.md b/README.md index 1da4bc354..e67f769c2 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,322 @@ -# Locality-Aware MPI -This repository performs locality-aware optimizations for standard MPI collectives as well as neighborhood collectives. - -## Collective Optimizations -The collective optimizations are within the folder src/collective. - -### Allgather : -The file allgather.c contains methods for performing the bruck allgather, the ring allgather, and point-to-point communication (all processes perform Isends and Irecvs with each other process). Each version also contains a locality-aware optimization. - -### Alltoall : -The file alltoall.c contains methods for performing the bruck alltoall algorithm and point-to-point communication (all processes perform Isends and Irecvs with each other process). This file contains locality-aware aggregation for the p2p version, and a locality-aware bruck alltoall is in progress. - -### Alltoallv : -The file alltoallv.c contains point-to-point communication for the all-to-allv operation, and a locality-aware optimization for this. A persistent version of the locality-aware alltoallv is in progress to improve load balancing without significant overheads. - -## Neighborhood Collectives : -The neighborhood collective operations are within the folder src/neighborhood. - -### Dist Graph Create : -To use the MPI Advance optimizations for neighborhood collectives, create the topology communicator with MPIX_Dist_graph_create_adjacent (in dist_graph.c). - -### Neighbor Alltoallv : -A standard neighbor alltoallv and locality-aware version are both implemented in neighbor.c. To use these, call the dist graph create adjacent method above, followed by MPIX_Neighbor_alltoallv_init(). - -### Neighbor Alltoallv : -A standard neighbor alltoallw version is implemented in neighbor.c. To use this, call the dist graph create adjacent method above, followed by MPIX_Neighbor_alltoallw_init(). +# Overview +The locality_aware repository offers locality-aware optimizations for the standard and neighborhood MPI Alltoall collective. This library also provides these optimizations for the the "v" variants of these collectives. + + +# Building Instructions +The locality_aware library is a straightforward CMake projects with modest dependencies. + +## Software Dependencies +- CMake 3.21 +- C 11 +- C++ 11 +- MPI +- HIP or CUDA for GPU-based optimizations, if enabled + +## Building +``` +mkdir build +cd build +cmake .. +``` +### Build options +- `-DGPU_AWARE` (Default `ON`)
    + Allow the library to build with supplied GPU type if available (see USE_CUDA and USE_HIP) +- `-DUSE_CUDA` (Default `OFF`)
    + Build the library with CUDA support. In order to use this option you may need to provide the target GPU architecture if it is not set in the environment. The specific architecture of an NVIDIA GPU can be found by searching for the GPU model on NVIDIA's [compute capability list](https://developer.nvidia.com/cuda-gpus) and supplying the compute capability via `-DCMAKE_CUDA_ARCHITECTURES`. You should ignore the decimal when supplying the architecture. For example: a GeForce RTX 4080 has a compute capability of 8.9 thus to build for it you would use `-DCMAKE_CUDA_ARCHITECTURES=89`. +- `-DUSE_HIP` (Default `OFF`)
    + Build the library with HIP support. In order to use this option you may need to provide the target GPU architecture if it is not set in the environment. The specific architecture of an AMD GPU can be found by searching for the GPU model at [ROCM capability list](https://rocm.docs.amd.com/en/latest/reference/gpu-arch-specs.html) and supplying the compute capability via `-DCMAKE_HIP_ARCHITECTURES`. For example: `-DCMAKE_HIP_ARCHITECTURES=gfx942` +- `-DBENCHMARKS` (Default `ON`)
    + This option enables the building of the benchmarks in the top level of the repo. The benchmark executable are built and available for use. Input files for the benchmarks can be found in `/test_data` +- `-DENABLE_UNIT_TESTS` (Default `ON`)
    + This option enables ctest support for quick testing of proper functionality of the library. Tests can be run by either `make test` or by running `ctest` in the build directory. +- `-DTEST_PROCS`(Default `16`)
    + Controls the number of processes used during ctests. +- `-DMPIRUN` (Default `mpirun`)
    + Set the command to used to run ctests. + + +# Using the Library + +All user-facing structs and APIs can be found in repository file: `include/locality_aware.h`. +The produced library file will be called `liblocality_aware.`. This project will also install a CMake module file to enable CMake to find the locality_aware library for use in other projects. + +## APIs +This library offers multiple algorithms to modify the behavior of MPI_Alltoall and MPIL_Alltoallv collective operations. A majority of these functions share a common interface with the official functions in the MPI standard and simply utilize our MPIL prefix, whiles require a few more inputs and/or MPIL-specific objects. The latter APIs do not have an existing entry in the MPI standard to describe the input parameters, so please see internal documentation for function details. + +Each of the MPIL collective operations contain a few options of algorithms to run internally. The algorithm used is defined in an variable external to the function and can be changed at the user level to swap the algorithm being run underneath. The set of available algorithm options available for each call, and how to select them, are shown below. + +### Selecting an algorithm +A majority of the API calls are wrappers that invoke internal functions to complete the operation. The API calls follow the same parameter setup as the associated MPI function. +Each wrapper looks for a set global variable to determine which algorithm to use. Users can set these global variables using the `MPIL_Set_<>` functions, after which all calls of the associated alltoall operation will use the new algorithm. If no algorithm is selected or an invalid enum is provided, each function uses the default algorithm noted below. + +### Alltoall +```c +int MPIL_Alltoall(const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm); + +int MPIL_Set_alltoall_algorithm(enum AlltoallMethod algorithm); + +enum AlltoallMethod{ + ALLTOALL_GPU_PAIRWISE, // Default + ALLTOALL_GPU_NONBLOCKING, + ALLTOALL_CTC_PAIRWISE, + ALLTOALL_CTC_NONBLOCKING, + ALLTOALL_PAIRWISE, + ALLTOALL_NONBLOCKING, + ALLTOALL_HIERARCHICAL_PAIRWISE, + ALLTOALL_HIERARCHICAL_NONBLOCKING, + ALLTOALL_MULTILEADER_PAIRWISE, + ALLTOALL_MULTILEADER_NONBLOCKING, + ALLTOALL_NODE_AWARE_PAIRWISE, + ALLTOALL_NODE_AWARE_NONBLOCKING, + ALLTOALL_LOCALITY_AWARE_PAIRWISE, + ALLTOALL_LOCALITY_AWARE_NONBLOCKING, + ALLTOALL_MULTILEADER_LOCALITY_PAIRWISE, + ALLTOALL_MULTILEADER_LOCALITY_NONBLOCKING, + ALLTOALL_PMPI +}; +``` + +### Alltoallv +```c +int MPIL_Alltoallv(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm); + +int MPIL_Set_alltoallv_algorithm(enum AlltoallvMethod algorithm) + +enum AlltoallvMethod{ + ALLTOALLV_GPU_PAIRWISE, // Default + ALLTOALLV_GPU_NONBLOCKING, + ALLTOALLV_CTC_PAIRWISE, + ALLTOALLV_CTC_NONBLOCKING, + ALLTOALLV_PAIRWISE, + ALLTOALLV_NONBLOCKING, + ALLTOALLV_BATCH, + ALLTOALLV_BATCH_ASYNC, + ALLTOALLV_PMPI +} +``` + +### Compressed-row Storage Alltoall Variants + +The locality_aware library also offers an alltoall and alltoallv variant that has been modified to work specifically with compressed row storage layout. These calls do require additional parameters than the normal alltoall call (see the header file for more details). + +```c +int MPIL_Alltoall_crs(const int send_nnz, + const int* dest, + const int sendcount, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int** src_ptr, + int recvcount, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIL_Info* xinfo, + MPIL_Comm* xcomm); + +int MPIL_Set_alltoall_crs(enum AlltoallCRSMethod algorithm); + +enum AlltoallCRSMethod{ + ALLTOALL_CRS_RMA, + ALLTOALL_CRS_NONBLOCKING, + ALLTOALL_CRS_NONBLOCKING_LOC, + ALLTOALL_CRS_PERSONALIZED, // Default + ALLTOALL_CRS_PERSONALIZED_LOC +} + +int MPIL_Alltoallv_crs(const int send_nnz, + const int send_size, + const int* dest, + const int* sendcounts, + const int* sdispls, + MPI_Datatype sendtype, + const void* sendvals, + int* recv_nnz, + int* recv_size, + int** src_ptr, + int** recvcounts_ptr, + int** rdispls_ptr, + MPI_Datatype recvtype, + void** recvvals_ptr, + MPIL_Info* xinfo, + MPIL_Comm* comm); + +int MPIL_Set_alltoallv_crs(enum AlltoallvCRSMethod algorithm); + +enum AlltoallvCRSMethod{ + ALLTOALLV_CRS_NONBLOCKING, + ALLTOALLV_CRS_NONBLOCKING_LOC, + ALLTOALLV_CRS_PERSONALIZED, // Default + ALLTOALLV_CRS_PERSONALIZED_LOC + +} +``` + +### Neighborhood Collectives +For neighborhood collectives, you first need to create a neighborhood communicator before the opration. This can be done by using `MPIL_Dist_graph_create_adjacent`, in the same way as its MPI counterpart. In addition to `MPIL_Neighbor_alltoallv`, the locality_aware library offers a variant on the neighbor alltoall that takes a normal (MPIL) communicator and an additional object that specifies the topology. This object (`MPIL_Topo`) allows for a first-class topology object that is not tied to a communicator, and thus could be easily adjusted or regenrated without needing a new communicator. If you have already created a neighborhood communicator, you can use `MPIL_Topo_from_neighbor_comm` to copy the topology object inside the communicator. + +```c +int MPIL_Dist_graph_create_adjacent(MPI_Comm comm_old, + int indegree, + const int sources[], + const int sourceweights[], + int outdegree, + const int destinations[], + const int destweights[], + MPIL_Info* info, + int reorder, + MPIL_Comm** comm_dist_graph_ptr); + +int MPIL_Topo_from_neighbor_comm(MPIL_Comm* comm, MPIL_Topo** mpil_topo_ptr); + +int MPIL_Neighbor_alltoallv(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm); +int MPIL_Neighbor_alltoallv_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm); + +int MPIL_Set_alltoallv_neighbor_alogorithm(enum NeighborAlltoallvMethod algorithm); + +enum NeighborAlltoallvMethod +{ + NEIGHBOR_ALLTOALLV_STANDARD, // Default + NEIGHBOR_ALLTOALLV_LOCALITY +}; +``` +#### Persistent Neighborhood Alltoallv +There are also persistent versions of the nieghborhood alltoallv collectives: + +```c +int MPIL_Neighbor_alltoallv_init(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); +int MPIL_Neighbor_alltoallv_init_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); +int MPIL_Neighbor_alltoallv_init_ext(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); +int MPIL_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, + const int sendcounts[], + const int sdispls[], + const long global_sindices[], + MPI_Datatype sendtype, + void* recvbuf, + const int recvcounts[], + const int rdispls[], + const long global_rindices[], + MPI_Datatype recvtype, + MPIL_Topo* topo, + MPIL_Comm* comm, + MPIL_Info* info, + MPIL_Request** request_ptr); + +int MPIL_Set_alltoallv_neighbor_init_alogorithm(enum NeighborAlltoallvInitMethod algorithm); + +enum NeighborAlltoallvInitMethod +{ + NEIGHBOR_ALLTOALLV_INIT_STANDARD, // DEFAULT + NEIGHBOR_ALLTOALLV_INIT_LOCALITY +}; +``` + +### Structs and Classes. +The library provides the following opaque structs. Limited access and control of the interior of these structs is available through API calls. For more information, please see the doxygen documentation. +- MPIL_Comm +- MPIL_Info +- MPIL_Topo +- MPIL_Request + +For functions provided by the library the provided functions should be used in place of the standard MPI structs of the similar name. + +### Support Functions +Functions available to user to interact with structs +```c +int MPIL_Alloc(void** pointer, const int bytes); +int MPIL_Free(void* pointer); +int MPIL_Comm_init(MPIL_Comm** xcomm_ptr, MPI_Comm global_comm); +int MPIL_Comm_free(MPIL_Comm** xcomm_ptr); +int MPIL_Comm_device_init(MPIL_Comm* xcomm); +int MPIL_Comm_device_free(MPIL_Comm* xcomm); +int MPIL_Comm_win_init(MPIL_Comm* xcomm, int bytes, int type_bytes); +int MPIL_Comm_win_free(MPIL_Comm* xcomm); +int MPIL_Comm_leader_init(MPIL_Comm* xcomm, int procs_per_leader); +int MPIL_Comm_leader_free(MPIL_Comm* xcomm); +int MPIL_Comm_req_resize(MPIL_Comm* xcomm, int n); +int MPIL_Comm_update_locality(MPIL_Comm* xcomm, int ppn); +int MPIL_Comm_tag(MPIL_Comm* comm, int* tag); +int MPIL_Comm_topo_init(MPIL_Comm* xcomm); +int MPIL_Comm_topo_free(MPIL_Comm* xcomm); +int MPIL_Info_init(MPIL_Info** info); +int MPIL_Info_free(MPIL_Info** info); +``` +# Repository Layout +The locality_aware repository is laid out as follows: +- *Include* +- *Benchmarks* Contains source code for several benchmarks used in publications. +- *Test Data* Contains input files for testing of Benchmarks. +- *Library* Contains the source code for the library. It is divided into four parts: + - **bindings:** contains the implementations of all the user facing functions. These commonly call functions deeper inside the library. + - **include:** contains any internal headers necessary for building. + - **source:** contains implementations of internal functions used by the bindings. + - **test:** contains functions and code for conducting the unit tests. + + +# Acknowledgements +This work has been partially funded by ... + +############################################################ diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index e131759b1..7bd8d4c24 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -4,7 +4,7 @@ set(CPP_SOURCES "") ## Most benchmarks will go in just fine list(APPEND CPP_SOURCES "alltoall_crs.cpp") list(APPEND CPP_SOURCES "alltoallv_crs.cpp") -list(APPEND CPP_SOURCES "microbenchmarks.cpp") +#list(APPEND CPP_SOURCES "microbenchmarks.cpp") list(APPEND CPP_SOURCES "p2p_alltoall.cpp") list(APPEND CPP_SOURCES "p2p_alltoallv.cpp") diff --git a/benchmarks/microbenchmarks.cpp b/benchmarks/unsupported/microbenchmarks.cpp similarity index 100% rename from benchmarks/microbenchmarks.cpp rename to benchmarks/unsupported/microbenchmarks.cpp diff --git a/include/locality_aware.h b/include/locality_aware.h index 1ddf18235..9944715f9 100644 --- a/include/locality_aware.h +++ b/include/locality_aware.h @@ -13,7 +13,28 @@ typedef struct _MPIL_Info MPIL_Info; typedef struct _MPIL_Topo MPIL_Topo; typedef struct _MPIL_Request MPIL_Request; -/* Enums for listing of implemented algorithms */ +/** \defgroup alg_enum Algorithm enumerations + * @brief Enumerations of implemented algorithms + * @details Each member has + * one or more descriptors after the main function + * that change the underlying algorithm. + * When supplied to the algorithm selection function. + *
    STANDARD: Uses standard collective operation + *
    PAIRWISE: Uses Pairwise communication pattern. + *
    NONBLOCKING: Uses non-blocking communication internally. + *
    HIERARCHICAL: Single leader aggregates messages before redistribution + *
    MULTILEADER: Group of leaders aggregates message among themselves before distribution. + *
    LOCALITY_AWARE|LOCALITY|LOC: Uses hardware architecture based *communication tree for operations. + *
    NODE_AWARE: Partial locality awareness (limited to node level locality) + *
    BATCH: Aggregate messages for bulk send + *
    ASYNC: Uses RMA for one-sided communication + *
    GPU: Offload communication to GPU + *
    CTC: Copy messages from gpu to cpu before sending. + *
    INIT: Persistent Communication used internally. + *
    PMPI: Calls the underlying MPI implementation. +**/ + +/** @brief Enumeration of implemented alltoall algorithms @ingroup alg_enum**/ enum AlltoallMethod { #if defined(GPU) && defined(GPU_AWARE) @@ -38,6 +59,7 @@ enum AlltoallMethod }; +/** @brief Enumeration of implemented alltoallv algorithms @ingroup alg_enum**/ enum AlltoallvMethod { #if defined(GPU) && defined(GPU_AWARE) @@ -53,18 +75,21 @@ enum AlltoallvMethod ALLTOALLV_PMPI }; +/** @brief Enumeration of implemented neighborhood alltoall algorithms @ingroup alg_enum**/ enum NeighborAlltoallvMethod { NEIGHBOR_ALLTOALLV_STANDARD, NEIGHBOR_ALLTOALLV_LOCALITY }; +/** @brief Enumeration of implemented neighborhood alltoallv algorithms @ingroup alg_enum**/ enum NeighborAlltoallvInitMethod { NEIGHBOR_ALLTOALLV_INIT_STANDARD, NEIGHBOR_ALLTOALLV_INIT_LOCALITY }; +/** @brief Enumeration of implemented alltoall compressed row storage algorithms @ingroup alg_enum**/ enum AlltoallCRSMethod { ALLTOALL_CRS_RMA, @@ -74,7 +99,8 @@ enum AlltoallCRSMethod ALLTOALL_CRS_PERSONALIZED_LOC }; -enum AlltoallvCRSMethod +/** @brief Enumeration of implemented alltoallv compressed row storage algorithms @ingroup alg_enum**/ +enum AlltoallvCRSMethod { ALLTOALLV_CRS_NONBLOCKING, ALLTOALLV_CRS_NONBLOCKING_LOC, @@ -82,15 +108,27 @@ enum AlltoallvCRSMethod ALLTOALLV_CRS_PERSONALIZED_LOC }; -/* Create global variables for algorithm selection. */ +/** \defgroup globals Algorithm_switches + *@brief Global variables used to select which algorithms to use inside MPIL API calls. + *@{ +*/ extern enum AlltoallMethod mpil_alltoall_implementation; extern enum AlltoallvMethod mpil_alltoallv_implementation; extern enum NeighborAlltoallvMethod mpil_neighbor_alltoallv_implementation; extern enum NeighborAlltoallvInitMethod mpil_neighbor_alltoallv_init_implementation; extern enum AlltoallCRSMethod mpil_alltoall_crs_implementation; extern enum AlltoallvCRSMethod mpil_alltoallv_crs_implementation; + /**@}*/ + -/* Algorithm selection functions. */ +/** \defgroup global_setters algorithm_set_functions + * @brief Functions to set global settings to call a chosen algorithm. + * @details + * Each accepts an enumerated value from one of the Algorithm enumerations. + * If an invalid value is provided, called MPIL functions will use their default algorithm. + * + * @{ +*/ int MPIL_Set_alltoall_algorithm(enum AlltoallMethod algorithm); int MPIL_Set_alltoallv_algorithm(enum AlltoallvMethod algorithm); int MPIL_Set_alltoallv_neighbor_alogorithm(enum NeighborAlltoallvMethod algorithm); @@ -98,34 +136,128 @@ int MPIL_Set_alltoallv_neighbor_init_alogorithm( enum NeighborAlltoallvInitMethod algorithm); int MPIL_Set_alltoall_crs(enum AlltoallCRSMethod algorithm); int MPIL_Set_alltoallv_crs(enum AlltoallvCRSMethod algorithm); + /**@}*/ -// Functions to control various versions of the MPIL_Comm object--------------------- +/**@brief Allocate and initialize MPIL_Comm object at xcomm using global_comm as a parent. + * @details + * Will set xcomm->global_comm to global_comm and + * sets tag value up to 126 based on MPI_Comm_world + * + * @param [in] global_comm parent communicator + * @param [out] xcomm newly initilizied MPIL_Comm object. + * @return MPI_Success upon successful completion. +.**/ int MPIL_Comm_init(MPIL_Comm** xcomm_ptr, MPI_Comm global_comm); + +/** @brief Free MPIL_Comm and all contained structs. + * @param [in, out] xcomm_ptr pointer to xcomm object to delete. + * @return MPI_Success upon successful completion. +.**/ int MPIL_Comm_free(MPIL_Comm** xcomm_ptr); +/** @brief Create comm for communication between processes on the same node. + * @details + * Sets up LocalityComm by gathering arrays for get_node, get_local, and get_global methods. + * These arrays allow for these methods to work with any ordering. + * No longer relying on SMP ordering of processes to nodes! + * This functions assumes all nodes have the same number of processes per node. + * + * @param [in, out] xcomm MPIL_comm to modify + * @return MPI_Success upon successful completion. +.**/ int MPIL_Comm_topo_init(MPIL_Comm* xcomm); + +/** @brief Delete local_comms + * @details + * Called by MPIL_Comm_free. + * Deletes MPIL_topo object stored by xcomm. + * @param [in, out] xcomm MPIL_comm to modify + * @return MPI_Success upon successful completion. +*/ int MPIL_Comm_topo_free(MPIL_Comm* xcomm); +/** @brief Generates MPIL_Topo from using comm object returned from MPIL_Dist_graph_create_adjacent. + * @details + * Uses neighbor_comm inside comm to generate MPIL_Topo object. + * Calls MPI_Dist_graph_neighbors_count and MPI_Dist_graph_neighbors. + * + * @param [in] comm MPIL_comm object with set neighbor_comm + * @param [out] mpil_topo_ptr pointer to generated MPIL_Topo struct + * @return MPI_Success upon successful completion. +*/ +int MPIL_Topo_from_neighbor_comm(MPIL_Comm* comm, MPIL_Topo** mpil_topo_ptr); + +/** @brief Subdivides an MPIL_Comm into subgroups and assigns communication leader for each subgroup. + * @details + * Global communicator is split into sub-communicators of maximum size proc_per_leader. + * Calls MPIL_Comm_topo_init if MPIL_Comm::local_comm is not set. + * Leader is selected from each sub-communicator and added to MPIL_Comm::leader_group_comm + * + * @param [in, out] xcomm MPIL_comm to divide + * @param [in] procs_per_leader maximum number of processes per leader + * @return MPI_Success upon successful completion. +.**/ int MPIL_Comm_leader_init(MPIL_Comm* xcomm, int procs_per_leader); + +/** @brief Free MPI communicators created as part of hierarchical or multi-leader setups. + * @details + * Called by MPIL_Comm_free + * @param [in, out] MPIL_Comm + * @return MPI_Success upon successful completion. +.**/ int MPIL_Comm_leader_free(MPIL_Comm* xcomm); +/** @brief Create a window for one-sided communication. + * @details + * Allocate memory and create MPI_Window. + * + * @param [in, out] xcomm + * @param [in] bytes size of windows in bytes + * @param [in] type_bytes local_unit_size for displacements, in bytes + * @return MPI_Success upon successful completion. +.**/ int MPIL_Comm_win_init(MPIL_Comm* xcomm, int bytes, int type_bytes); + +/** @brief Delete window and free allocated memory, called by MPIL_Comm_free**/ int MPIL_Comm_win_free(MPIL_Comm* xcomm); +/** @brief Initialize GPU stream inside the communicator + * @details + * GPU Stream is mapped based on selected support. + * If at least one gpu is detected, creates gpuStream and binds to xcomm. + * If not built with GPU support, this function is a no-op + * + * @param [in,out] xcomm MPIL_Comm to initialize stream for GPU. + * @return MPI_Success upon successful completion. +.**/ + int MPIL_Comm_device_init(MPIL_Comm* xcomm); +/** @brief Destroys the gpustream operated by xcomm + * @details + * If not built with GPU support, this function is a no-op + * + * @param [in,out] xcomm MPIL_Comm object holding handle of gpustream to deallocate. + * @return MPI_Success upon successful completion. +.**/ int MPIL_Comm_device_free(MPIL_Comm* xcomm); +/** @brief Resize xcomm number of requests and status arrays to be size n.**/ int MPIL_Comm_req_resize(MPIL_Comm* xcomm, int n); + +/** @brief Wrapper around update_locality, see update_locality().**/ int MPIL_Comm_update_locality(MPIL_Comm* xcomm, int ppn); -/** @brief get current tag and increment tag in the comm.**/ +/** @brief Get current tag in communicator and then increment tag by 1. See get_comm()**/ int MPIL_Comm_tag(MPIL_Comm* comm, int* tag); // Functions to initialize and free the MPI_Info object +/** @brief Initializes MPIL_Info object**/ int MPIL_Info_init(MPIL_Info** info); + +/** @brief Deletes MPIL_Info object**/ int MPIL_Info_free(MPIL_Info** info); -// Functions to control the MPIL_Topo object +/** @brief Initializes and returns pointer to ::MPIL_Topo object **/ int MPIL_Topo_init(int indegree, const int sources[], const int sourceweights[], @@ -134,15 +266,29 @@ int MPIL_Topo_init(int indegree, const int destweights[], MPIL_Info* info, MPIL_Topo** mpil_topo_ptr); -int MPIL_Topo_from_neighbor_comm(MPIL_Comm* comm, MPIL_Topo** mpil_topo_ptr); + +/** @brief deletes ::MPIL_topo object **/ int MPIL_Topo_free(MPIL_Topo** topo); -// Functions to control the MPIL_Request object +/**@brief Start processing the request. + * @details + * Query request::start_function and call it to activate the request. +**/ int MPIL_Start(MPIL_Request* request); + +/**@brief Wait for the request to complete. + * @details + * Query request::wait_function and call it to wait for requests to complete. +**/ int MPIL_Wait(MPIL_Request* request, MPI_Status* status); + +/**@brief Deallocates MPIL_Request object and any internal structures **/ int MPIL_Request_free(MPIL_Request** request); + +/** @brief Set reorder value of request to value **/ int MPIL_Request_reorder(MPIL_Request* request, int value); +/** @brief Wrapper around MPI_Dist_graph_create_adjacent. */ int MPIL_Dist_graph_create_adjacent(MPI_Comm comm_old, int indegree, const int sources[], @@ -155,6 +301,21 @@ int MPIL_Dist_graph_create_adjacent(MPI_Comm comm_old, MPIL_Comm** comm_dist_graph_ptr); // Main MPIL Functions +/** @defgroup collective_func Algorithm APIs + * @brief Wrapper functions around algorithms to fit MPI semantics + * @details + * Use switch statements and global variables to call + * internal algorithm to complete the associated + * + * Parameters are the same as MPI_Alltoall, just adapted to work + * with objects extended by the library. +**/ + +/** @brief Wrapper around MPI_Alltoall. + * @details + * Defaults to AllTOALL_PMPI + * @ingroup collective_func + */ int MPIL_Alltoall(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -162,6 +323,12 @@ int MPIL_Alltoall(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief Wrapper around MPI_Alltoallv. + * @details + * Defaults to AllTOALLV_PMPI + * @ingroup collective_func + */ int MPIL_Alltoallv(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -172,6 +339,9 @@ int MPIL_Alltoallv(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); +/** @brief Wrapper around MPI_Neighbor_alltoallv + * @ingroup collective_func + */ int MPIL_Neighbor_alltoallv(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -181,6 +351,10 @@ int MPIL_Neighbor_alltoallv(const void* sendbuf, const int rdispls[], MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief Wrapper around MPI_Neighbor_alltoallv that accepts an already generated topology. + * @ingroup collective_func + */ int MPIL_Neighbor_alltoallv_topo(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -192,6 +366,9 @@ int MPIL_Neighbor_alltoallv_topo(const void* sendbuf, MPIL_Topo* topo, MPIL_Comm* comm); +/** @brief Wrapper around persistent versions MPI_Neighbor_alltoallv. + * @ingroup collective_func + */ int MPIL_Neighbor_alltoallv_init(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -203,6 +380,10 @@ int MPIL_Neighbor_alltoallv_init(const void* sendbuf, MPIL_Comm* comm, MPIL_Info* info, MPIL_Request** request_ptr); + +/** @brief Extended version of neighbor alltoallv that allows you to provide global indices. + * @ingroup collective_func + */ int MPIL_Neighbor_alltoallv_init_ext(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -216,6 +397,10 @@ int MPIL_Neighbor_alltoallv_init_ext(const void* sendbuf, MPIL_Comm* comm, MPIL_Info* info, MPIL_Request** request_ptr); + +/** @brief Wrapper around persistent version MPI_Neighbor_alltoallv that accepts an already generated topology object. + * @ingroup collective_func + */ int MPIL_Neighbor_alltoallv_init_topo(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -228,6 +413,10 @@ int MPIL_Neighbor_alltoallv_init_topo(const void* sendbuf, MPIL_Comm* comm, MPIL_Info* info, MPIL_Request** request_ptr); + +/** @brief Extended version of MPIL_Neighbor_alltoallv_init_topo that allows you to provide global indices. + * @ingroup collective_func + */ int MPIL_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -243,6 +432,29 @@ int MPIL_Neighbor_alltoallv_init_ext_topo(const void* sendbuf, MPIL_Info* info, MPIL_Request** request_ptr); +/** @brief Extended version of MPI_Alltoall optimized for compressed row storage layout. + * @details + * Sets up dynamic communication tree based on row sparsity in the supplied matrix. + * + * + * + * Defaults to ALLTOALL_CRS_PERSONALIZED; + * @ingroup collective_func + * + * @param [in] send_nnz Number of dynamic sends. + * @param [in] dest Destination of the messages + * @param [in] sendcount Send per message count + * @param [in] sendtype Datatype being sent + * @param [in] sendvals Data to be sent. + * @param [in,out] recv_nnz Number of dynamic recvs + * @param [out] src_ptr Destinations of messages to be recieved + * @param [out] recvcount Receives per-message count + * @param [out] recvtype Datatype being recieved + * @param [out] recvvals_ptr Data to receive + * @param [in] xinfo + * @param [in] xcomm + * @return MPI_Success + */ int MPIL_Alltoall_crs(const int send_nnz, const int* dest, const int sendcount, @@ -255,6 +467,28 @@ int MPIL_Alltoall_crs(const int send_nnz, void** recvvals_ptr, MPIL_Info* xinfo, MPIL_Comm* xcomm); + +/** @brief Extended version of MPI_Alltoallv optimized for compressed row storage layout. + * @details + * Defaults to ALLTOALLV_CRS_PERSONALIZED; + * + * @ingroup collective_func + * @param [in] send_nnz Number of dynamic sends. + * @param [in] dest Destination of the messages + * @param [in] sendcount Send per message count + * @param [in] sdispls Displacements of sent messages. + * @param [in] sendtype Datatype being sent + * @param [in] sendvals Data to be sent. + * @param [in, out] recv_nnz Number of dynamic receives + * @param [in, out] size of dynamic receives + * @param [out] src_ptr Destination for received message + * @param [out] recvcount Receives per-message count + * @param [out] recvtype Datatype being received + * @param [out] recvvals_ptr Data to receive + * @param [in] xinfo + * @param [in] xcomm + * @return MPI_Success + */ int MPIL_Alltoallv_crs(const int send_nnz, const int send_size, const int* dest, @@ -272,8 +506,23 @@ int MPIL_Alltoallv_crs(const int send_nnz, MPIL_Info* xinfo, MPIL_Comm* comm); -// Utility functions (used in some of the crs tests, may move internal + +/** @brief Dynamically allocates enough space for char[bytes] and returns pointer to the new allocation. + * @details + * Will cause error if bytes < 0 + * Will return null pointer if bytes == 0 + * @param [out] pointer pointer to allocated space. + * @param [in] bytes number of chars to make space + * @return MPI_Success on a successful return. + */ int MPIL_Alloc(void** pointer, const int bytes); + +/** @brief Frees space at pointer. + * @details + * Does nothing if supplied with nullptr. + * @param [in, out] pointer to allocated space. + * @return MPI_Success on a successful return. + */ int MPIL_Free(void* pointer); #ifdef __cplusplus diff --git a/library/include/collective/alltoall.h b/library/include/collective/alltoall.h index 0d8a67a6d..a2b5fe85c 100644 --- a/library/include/collective/alltoall.h +++ b/library/include/collective/alltoall.h @@ -10,8 +10,36 @@ extern "C" { #endif +/** @brief Function pointer to alltoall implemenation + * @details + * Uses the parameters of standard MPI_Alltoall API, except replacing MPI_Comm with MPIL_Comm + * most of the behavior is derived from internal parameters in MPIL_Comm. + * MPIL_API alltoall switch statement targets one of these. + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + **/ typedef int (*alltoall_ftn)( const void*, const int, MPI_Datatype, void*, const int, MPI_Datatype, MPIL_Comm*); + +/** @brief Function pointer to alltoall helper function.. + * @details + * Uses the parameters of standard MPI_Alltoall API, plus a tag for additional options. + * usually invoked by a function of type alltoall_ftn. + * + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + * @param [in] tag unique tag for matching messages. + **/ typedef int (*alltoall_helper_ftn)(const void*, const int, MPI_Datatype, @@ -23,7 +51,17 @@ typedef int (*alltoall_helper_ftn)(const void*, //** External Wrappers //**//---------------------------------------------------------------------- -/** @brief set tag and call pairwise_helper **/ +/** @brief Call the pairwise implementation. + * @details calls get_tag() then call pairwise_helper() with the same input parameters plus the found tag. + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + * @return returns value of the pairwise_helper call. + */ int alltoall_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -32,7 +70,18 @@ int alltoall_pairwise(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief set message tag and call pairwise_helper **/ +/** @brief Call the non-blocking implemenation. + * @details calls get_tag then call nonblocking_helper() with the same input parameters plus the found tag. + * + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + * @return returns value of the nonblocking_helper call. + */ int alltoall_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -41,7 +90,16 @@ int alltoall_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief call alltoall_hiearchical passing pairwise_helper**/ +/** @brief call alltoall_hiearchical passing pairwise_helper() + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + * @return returns value of the nonblocking_helper call. +**/ int alltoall_hierarchical_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -50,7 +108,7 @@ int alltoall_hierarchical_pairwise(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief call alltoall_hiearchical passing nonblocking_helper**/ +/** @brief call alltoall_hiearchical passing nonblocking_helper() **/ int alltoall_hierarchical_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -59,7 +117,7 @@ int alltoall_hierarchical_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief call alltoall_hiearchical passing pairwise_helper, nleaders=4**/ +/** @brief call alltoall_hiearchical() passing pairwise_helper(), nleaders=4**/ int alltoall_multileader_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -68,7 +126,7 @@ int alltoall_multileader_pairwise(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief call alltoall_hiearchical passing nonblocking_helper, nleaders=4**/ +/** @brief call alltoall_hiearchical() passing nonblocking_helper(), nleaders=4**/ int alltoall_multileader_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -77,7 +135,7 @@ int alltoall_multileader_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief call node_aware with pairwise helper **/ +/** @brief call alltoall_node_node_aware() with pairwise_helper() **/ int alltoall_node_aware_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -86,7 +144,7 @@ int alltoall_node_aware_pairwise(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief call node_aware with nonblocking helper **/ +/** @brief call alltoall_node_aware() with nonblocking_helper() **/ int alltoall_node_aware_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -95,7 +153,7 @@ int alltoall_node_aware_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief call locality_aware with pairwise helper, groups_per_node=4**/ +/** @brief call alltoall_locality_aware() with pairwise_helper(), groups_per_node=4**/ int alltoall_locality_aware_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -104,7 +162,7 @@ int alltoall_locality_aware_pairwise(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief call locality_aware with nonblocking helper, groups_per_node=4**/ +/** @brief call alltoall_locality_aware() with nonblocking_helper(), groups_per_node=4**/ int alltoall_locality_aware_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -113,7 +171,7 @@ int alltoall_locality_aware_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief calls multileader_locality with pairwise helper **/ +/** @brief calls alltoall_multileader_locality() with pairwise_helper() **/ int alltoall_multileader_locality_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -122,7 +180,7 @@ int alltoall_multileader_locality_pairwise(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief calls multileader_locality with nonblocking helper **/ +/** @brief calls alltoall_multileader_locality() with nonblocking_helper() **/ int alltoall_multileader_locality_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -133,7 +191,7 @@ int alltoall_multileader_locality_nonblocking(const void* sendbuf, //** Intermediate Wrappers //**//----------------------------------------------------------------- -/** @brief calls alltoall_locality_aware with groups_per_node=1**/ +/** @brief calls alltoall_locality_aware() with groups_per_node=1**/ int alltoall_node_aware(alltoall_helper_ftn f, const void* sendbuf, const int sendcount, @@ -143,7 +201,7 @@ int alltoall_node_aware(alltoall_helper_ftn f, MPI_Datatype recvtype, MPIL_Comm* comm); -/** @brief wrapper around alltoall_multileader, nleaders=1)**/ +/** @brief wrapper around alltoall_multileader() with nleaders=1**/ int alltoall_hierarchical(alltoall_helper_ftn f, const void* sendbuf, const int sendcount, @@ -153,9 +211,41 @@ int alltoall_hierarchical(alltoall_helper_ftn f, MPI_Datatype recvtype, MPIL_Comm* comm); +/** @brief Sets up messaging groups based on topology + * @param [in] f helper function to do underlying ptp communication. + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + * @param [in] groups_per_node number of groups per node + * @return value from call to f + **/ +int alltoall_locality_aware(alltoall_helper_ftn f, + const void* sendbuf, + const int sendcount, + MPI_Datatype sendtype, + void* recvbuf, + const int recvcount, + MPI_Datatype recvtype, + MPIL_Comm* comm, + int groups_per_node); + //** Core Helper functions //**//------------------------------------------------------------------ -/** @brief Uses Sendrecv to do the alltoall**/ +/** @brief Uses Sendrecv to do the alltoall + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + * @param [tag] tag int flag used for message matching. + * @return returns MPI_Success + **/ int pairwise_helper(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -165,7 +255,19 @@ int pairwise_helper(const void* sendbuf, MPI_Comm comm, int tag); -/** @brief Uses Isend and Irecv to do the alltoall**/ +/** @brief Nonblocking point to point implementation of alltoall. + * @details + * Uses Isend and Irecv to do the alltoall + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + * @param [tag] tag int flag used for message matching. + * @return returns MPI_Success + **/ int nonblocking_helper(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -175,7 +277,24 @@ int nonblocking_helper(const void* sendbuf, MPI_Comm comm, int tag); -/** @brief ??? \todo fill**/ +/** @brief Uses n_leaders to aggregate messages and distribute to other processes. + * @details + * Number of leaders controlled by n_leader parameter. + * Communication occurs in three stages: + * - Each leader gathers from its grouping + * - Leaders exchange using supplied helper function. + * - Each leader scatters back among its group. + * + * @param [in] f pointer to helper function allows additional functionality + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + * @param [in] n_leaders number of leader processes. + * @return returns MPI_Success +*/ int alltoall_multileader(alltoall_helper_ftn f, const void* sendbuf, const int sendcount, @@ -186,18 +305,18 @@ int alltoall_multileader(alltoall_helper_ftn f, MPIL_Comm* comm, int n_leaders); -/** @brief complex returns locality_helper **/ -int alltoall_locality_aware(alltoall_helper_ftn f, - const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm, - int groups_per_node); - -/** @brief ??? \todo fill**/ +/** @brief Groups messages based on topology, uses supplied helper function to do ptp communication. + * @param [in] f helper function to do underlying ptp communication. + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + * @param [in] tag tag forwarded to helper function. + * @return MPI_SUCCESS + **/ int alltoall_locality_aware_helper(alltoall_helper_ftn f, const void* sendbuf, const int sendcount, @@ -211,7 +330,30 @@ int alltoall_locality_aware_helper(alltoall_helper_ftn f, MPI_Comm group_comm, int tag); -/** @brief ??? \todo fill**/ +/** @brief Multileader alltoall algorithm with topology aware grouping. + * @details + * Uses multiple leaders (currently set to 4) to gather messages based on topology + * to create communication map. + * Actual communications are implemented by the supplied helper function. + * Steps: + * - Does a node-wide gather + * - repack for sends (assumes SMP ordering) + * - invoke helper function to perform alltoall between leaders + * - repacks if necessary + * - invoke helper function to perform alltoall on node + *

    + * Currently assumes full nodes and equal procs_per_leader per node + * + * @param [in] f pointer to helper function allows additional functionality + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + * @param [tag] tag int flag used for message matching. + * @return returns MPI_Success + **/ int alltoall_multileader_locality(alltoall_helper_ftn f, const void* sendbuf, const int sendcount, @@ -221,7 +363,7 @@ int alltoall_multileader_locality(alltoall_helper_ftn f, MPI_Datatype recvtype, MPIL_Comm* comm); -// Calls underlying MPI implementation +/** @brief calls underlying PMPI_Alltoall implementation **/ int alltoall_pmpi(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, diff --git a/library/include/collective/alltoallv.h b/library/include/collective/alltoallv.h index 587381c39..3796e3a14 100644 --- a/library/include/collective/alltoallv.h +++ b/library/include/collective/alltoallv.h @@ -10,7 +10,19 @@ #ifdef __cplusplus extern "C" { #endif - +/** @brief Function pointer to alltoallv implementation + * @details + * Uses the parameters of standard MPI_Alltoallv API, except replacing MPI_Comm with MPIL_Comm. + * Most of the behavior is derived from internal parameters in MPIL_Comm. + * + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + **/ typedef int (*alltoallv_ftn)(const void*, const int*, const int*, @@ -20,8 +32,16 @@ typedef int (*alltoallv_ftn)(const void*, const int*, MPI_Datatype, MPIL_Comm*); - -// Helper Functions + +/** @brief Uses Sendrecv to do the alltoallv + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + **/ int alltoallv_pairwise(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -31,6 +51,16 @@ int alltoallv_pairwise(const void* sendbuf, const int rdispls[], MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief Uses Isend and Irecv to do the alltoallv + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + **/ int alltoallv_nonblocking(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -40,6 +70,22 @@ int alltoallv_nonblocking(const void* sendbuf, const int rdispls[], MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief Groups messages before sending them as a group using nonblocking operations. + * @details + * Has internal tuning parameter nb_stride, which controls the number + * of messages between waits. + * + * Fires off nb_stride messages before waiting on completion. + * + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + **/ int alltoallv_batch(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -49,6 +95,23 @@ int alltoallv_batch(const void* sendbuf, const int rdispls[], MPI_Datatype recvtype, MPIL_Comm* comm); + + +/** @brief Groups messages before sending them as a group using blocking, but does not wait for completion of stride. + * @details + * Has internal tuning parameter nb_stride, which controls the number + * of messages between waits. + * + * Fires off nb_stride messages then rotates and fires new messages as requests complete. + * + * @param [in] sendbuf buffer containing data to send + * @param [in] sendcount int number of items in sendbuff + * @param [in] sendtype MPI_Datatype in sendbuff + * @param [out] recvbuf buffer to receive messages + * @param [in] recvcount int number of items expected in recvbuff + * @param [in] recvtype MPI_Datatype in recvbuff + * @param [in] comm MPIL_Comm used for context + **/ int alltoallv_batch_async(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -58,6 +121,8 @@ int alltoallv_batch_async(const void* sendbuf, const int rdispls[], MPI_Datatype recvtype, MPIL_Comm* comm); + +/**@brief calls underlying PMPI_Alltoallv implementation **/ int alltoallv_pmpi(const void* sendbuf, const int sendcounts[], const int sdispls[], diff --git a/library/include/communicator/MPIL_Comm.h b/library/include/communicator/MPIL_Comm.h index 762077556..72e34d919 100644 --- a/library/include/communicator/MPIL_Comm.h +++ b/library/include/communicator/MPIL_Comm.h @@ -6,57 +6,113 @@ #ifdef __cplusplus extern "C" { #endif - +/** @brief Struct capable of maintaining multiple request and communicators for library operations. + * @details + * Protected struct, external user access must be through MPIL APIs. + * Supported operations include: + * - Buffers for MPI_Windows + * - MPI_comms for locality, multileader, and neighborhoods + * Contains preprocessor locked GPU aware components. +**/ typedef struct _MPIL_Comm { + /**@brief Global MPI comm for reference, usually MPI_COMM_WORLD**/ MPI_Comm global_comm; + /**@brief communicator containing neighbor processes**/ MPI_Comm neighbor_comm; // For hierarchical collectives + /**@brief Communicator for communicating inside the node**/ MPI_Comm local_comm; - MPI_Comm group_comm; + /**@brief Communicator containing leader process on each node**/ + MPI_Comm group_comm; + /**@brief Communicator containing a single leader and its subordinates**/ MPI_Comm leader_comm; + /**@brief Communicator containing all leaders **/ MPI_Comm leader_group_comm; + /**@brief Communicator containing all leaders on a single node**/ MPI_Comm leader_local_comm; + /**@brief Number of nodes in comm**/ int num_nodes; + /**@brief Rank of process in comm**/ int rank_node; + /**@brief Processes per node**/ int ppn; + /**@brief MPI_window if using sync**/ MPI_Win win; + /**@brief Buffer for MPI_window**/ char* win_array; + /**@brief Size of win_array in bytes**/ int win_bytes; + /**@brief Size of the datatype in win_array in bytes**/ int win_type_bytes; + /**@brief Internal array of requests made during a blocking collective**/ MPI_Request* requests; + /**@brief Status the requests in requests**/ MPI_Status* statuses; + + /**@brief Size of requests and statuses + @details + requests and statuses should always be the same size. + can be updated through MPIL_Comm_req_resize; + **/ int n_requests; - + /** @brief Unique identifier for any requests using this comm (defaulting to 126)**/ int tag; + /** @brief Maximum size of tag allowed by the system.**/ int max_tag; + /** @brief Maps rank in global_comm to rank in local_comm **/ int* global_rank_to_local; + /** @brief Maps rank in global_comm to node id (0 based) **/ int* global_rank_to_node; + /** @brief Orders ranks bases on node, node*ppn+local **/ int* ordered_global_ranks; #ifdef GPU - + /** @brief Number of gpus on the node**/ int gpus_per_node; + /** @brief Rank running on the gpu**/ int rank_gpu; - // actual type is gpuStream_t, changed to void* to assist compiling. + /** @brief Pointer to gpuStream_t + @details + Changed to void* to assist compiling. + Actual type is gpuStream_t, changed to void* to assist compiling. + */ void* proc_stream; #endif } MPIL_Comm; +/** @brief Returns the node that process proc is on(data->global_rank_to_node[proc]**/ int get_node(const MPIL_Comm* data, const int proc); + +/** @brief Return the rank of proc in local communicator (using MPIL_Comm::global_rank_to_local)**/ int get_local_proc(const MPIL_Comm* data, const int proc); + +/** @brief Given a node and a rank of a process, get its rank in the global communicator**/ int get_global_proc(const MPIL_Comm* data, const int node, const int local_proc); // For testing purposes (manually set PPN) +/** @brief Recreates internal communicators given the number of processes per node + @details + Frees and resets local_com and group_comm. + Splits MPIL_Comm::global_comm into local_comms of size ppn or smaller. + Remaps rank to rank in local_comms, each node, and reorders_global +**/ int update_locality(MPIL_Comm* xcomm, int ppn); +/** @brief Gets current tag from xcomm then increments MPIL_Comm::tag + @details + Invoked externally by MPIL_Comm_get_tag + @param [in, out] xcomm communicator to query and updated + @param [out] tag value of xcomm->tag before the operations + @return MPI_SUCCESS +**/ int get_tag(MPIL_Comm* xcomm, int* tag); #ifdef __cplusplus diff --git a/library/include/communicator/MPIL_Info.h b/library/include/communicator/MPIL_Info.h index 2976a24f3..bc2427c82 100644 --- a/library/include/communicator/MPIL_Info.h +++ b/library/include/communicator/MPIL_Info.h @@ -5,6 +5,8 @@ extern "C" { #endif +/** @brief Struct for containing number of initialized columns and overall size +**/ typedef struct _MPIL_Info { int crs_num_initialized; diff --git a/library/include/communicator/comm_data.h b/library/include/communicator/comm_data.h index 244bc14a3..cb3885aef 100644 --- a/library/include/communicator/comm_data.h +++ b/library/include/communicator/comm_data.h @@ -6,22 +6,34 @@ #ifdef __cplusplus extern "C" { #endif - +/** @brief Structure containing metadata about a message**/ typedef struct _CommData { + /** @brief Number of messages sent between sender and receiver **/ int num_msgs; + /** @brief size of the message sent between sender and receiver **/ int size_msgs; + /** @brief Size of the datatype used in the communication **/ int datatype_size; + /** @brief Number of processes involved in the communication **/ int* procs; + /** @brief pointer to index **/ int* indptr; + /** @brief indexes for message **/ int* indices; + /** @brief buffer containing copy of message **/ char* buffer; } CommData; +/** @brief ::CommData constructor that sets all values to 0, except for CommData::datatype_size **/ void init_comm_data(CommData** comm_data_ptr, MPI_Datatype datatype); +/** @brief ::CommData destructor that frees allocated memory **/ void destroy_comm_data(CommData* data); +/** @brief Sets the CommData::num_msgs to provided value */ void init_num_msgs(CommData* data, int num_msgs); +/** @brief Sets CommData::size_msgs and allocates CommData::indices for indexing messages **/ void init_size_msgs(CommData* data, int size_msgs); +/** @brief Allocates CommData::buffer to the size of `(CommData::size_msgs * CommData::datatype_size)` bytes **/ void finalize_comm_data(CommData* data); #ifdef __cplusplus diff --git a/library/include/communicator/comm_pkg.h b/library/include/communicator/comm_pkg.h index 241081cfe..0d4ef6a92 100644 --- a/library/include/communicator/comm_pkg.h +++ b/library/include/communicator/comm_pkg.h @@ -3,18 +3,30 @@ #include "comm_data.h" +/** @brief Struct for storing directional message data for a group of processes. + * @details + * One ::CommPkg per process. + * One ::CommData object for each process send to and received from by that process. + **/ typedef struct _CommPkg { + /** @brief Information on outgoing messages **/ CommData* send_data; + /** @brief Information on incoming messages **/ CommData* recv_data; + /** @brief Tag value to use for communications (see ::get_tag()). **/ int tag; } CommPkg; +/** @brief Allocate and initialize a ::CommPkg **/ void init_comm_pkg(CommPkg** comm_ptr, MPI_Datatype sendtype, MPI_Datatype recvtype, int _tag); +/** @brief Calls finalize_comm_data() on CommPkg::send_data and CommPkg::recv_data **/ void finalize_comm_pkg(CommPkg* comm); +/** @brief ::CommPkg destructor that cleans up CommPkg::send_data and CommPkg::recv_data + * **/ void destroy_comm_pkg(CommPkg* comm); #endif diff --git a/library/include/communicator/locality_comm.h b/library/include/communicator/locality_comm.h index 2a8f38746..678179a7b 100644 --- a/library/include/communicator/locality_comm.h +++ b/library/include/communicator/locality_comm.h @@ -10,23 +10,37 @@ #ifdef __cplusplus extern "C" { #endif - +/** @brief Struct for messaging metadata for the different groups of processes**/ typedef struct LocalityComm { + /** @brief Metadata about local (on-node) communications. **/ CommPkg* local_L_comm; + /** @brief Metadata about original messages before aggregation. **/ CommPkg* local_S_comm; + /** @brief Metadata to redistribute aggravated messages. **/ CommPkg* local_R_comm; + /** @brief Metadata on messages sent between nodes. **/ CommPkg* global_comm; - + /** @brief Pointer to MPIL_Comm used in the locality mapping. **/ MPIL_Comm* communicators; } LocalityComm; +/** @brief Constructor for ::LocalityComm. Datatypes used to create message metadata. */ void init_locality_comm(LocalityComm** locality_ptr, MPIL_Comm* comm, MPI_Datatype sendtype, MPI_Datatype recvtype); +/** @brief Finalize the ::CommPkg objects inside this object. */ void finalize_locality_comm(LocalityComm* locality); +/** @brief Destructor for a ::LocalityComm object. */ void destroy_locality_comm(LocalityComm* locality); +/** @brief Collect the maximum number of local and non-local messages. + * @details This method is currently collective over MPI_COMM_WORLD, using + * MPI_Allreduce(MPI_MAX). For all ranks in that communicator, the first two values are + * derived from LocalityComm::local_L_comm, LocalityComm::local_S_comm, and + * LocalityComm::local_R_comm. The last two outputs are derived from + * LocalityComm::global_comm. + */ void get_local_comm_data(LocalityComm* locality, int* max_local_num, int* max_local_size, diff --git a/library/include/heterogeneous/gpu_alltoall.h b/library/include/heterogeneous/gpu_alltoall.h index 77495b6bc..f1808449f 100644 --- a/library/include/heterogeneous/gpu_alltoall.h +++ b/library/include/heterogeneous/gpu_alltoall.h @@ -8,6 +8,12 @@ extern "C" { #endif +/** @brief A GPU-aware wrapper around provided ::alltoall_ftn + * @details This function assumes that the provided ::alltoall_ftn is GPU aware, and + * can handle GPU buffers. No extra behavior from the normal ::atlltoall_ftn. allocates + * host memory for send and recv buffers. has same arguments as alltoall_helper function. + * @returns The result from the ::alltoall_ftn. + **/ int gpu_aware_alltoall(alltoall_ftn f, const void* sendbuf, const int sendcount, @@ -16,6 +22,14 @@ int gpu_aware_alltoall(alltoall_ftn f, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); + +/** @brief A GPU buffer variant wrapper around provided ::alltoall_ftn + * @details Unlike the GPU-aware variant, this version first allocates memory on the + * hosts, copies the data from the CPU to the host, performs the requests all-to-all, then + * copies the final result back to the GPU. All parameters have the same requirements as + * the ::alltoall_ftn . + * @returns The result from the ::alltoall_ftn. + **/ int copy_to_cpu_alltoall(alltoall_ftn f, const void* sendbuf, const int sendcount, @@ -25,6 +39,7 @@ int copy_to_cpu_alltoall(alltoall_ftn f, MPI_Datatype recvtype, MPIL_Comm* comm); +/** @brief Calls ::gpu_aware_alltoall with ::pairwise_helper function**/ int gpu_aware_alltoall_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -32,6 +47,7 @@ int gpu_aware_alltoall_pairwise(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); +/** @brief Calls ::gpu_aware_alltoall with ::nonblocking_helper function**/ int gpu_aware_alltoall_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -39,6 +55,7 @@ int gpu_aware_alltoall_nonblocking(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); +/** @brief Calls ::copy_to_cpu_alltoall with ::pairwise_helper function**/ int copy_to_cpu_alltoall_pairwise(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -46,6 +63,7 @@ int copy_to_cpu_alltoall_pairwise(const void* sendbuf, const int recvcount, MPI_Datatype recvtype, MPIL_Comm* comm); +/** @brief Calls ::copy_to_cpu_alltoall with nonblocking_helper function**/ int copy_to_cpu_alltoall_nonblocking(const void* sendbuf, const int sendcount, MPI_Datatype sendtype, @@ -54,25 +72,24 @@ int copy_to_cpu_alltoall_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -#ifdef OPENMP -#include - -int threaded_alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm); +// #ifdef OPENMP +// #include +// int threaded_alltoall_pairwise(const void* sendbuf, +// const int sendcount, +// MPI_Datatype sendtype, +// void* recvbuf, +// const int recvcount, +// MPI_Datatype recvtype, +// MPIL_Comm* comm); -int threaded_alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm); -#endif +// int threaded_alltoall_nonblocking(const void* sendbuf, +// const int sendcount, +// MPI_Datatype sendtype, +// void* recvbuf, +// const int recvcount, +// MPI_Datatype recvtype, +// MPIL_Comm* comm); +// #endif #ifdef __cplusplus } diff --git a/library/include/heterogeneous/gpu_alltoallv.h b/library/include/heterogeneous/gpu_alltoallv.h index 04e279efe..3d1af4557 100644 --- a/library/include/heterogeneous/gpu_alltoallv.h +++ b/library/include/heterogeneous/gpu_alltoallv.h @@ -110,8 +110,8 @@ int copy_to_cpu_alltoallv_batch_async(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm); -#ifdef OPENMP -#include +//#ifdef OPENMP +//#include int threaded_alltoallv_pairwise(const void* sendbuf, const int sendcounts[], const int sdispls[], @@ -131,7 +131,7 @@ int threaded_alltoallv_nonblocking(const void* sendbuf, const int rdispls[], MPI_Datatype recvtype, MPIL_Comm* comm); -#endif +//#endif #ifdef __cplusplus } diff --git a/library/include/heterogeneous/gpu_utils.h b/library/include/heterogeneous/gpu_utils.h index 7e0d2cb83..339d2fa3f 100644 --- a/library/include/heterogeneous/gpu_utils.h +++ b/library/include/heterogeneous/gpu_utils.h @@ -15,11 +15,13 @@ extern "C" { // If using GPU, specific gpu methods (for either NCCL or HIP) #ifdef GPU +/** @brief Kernel for copying from send_buf into recv_buf using all GPU threads. **/ __global__ void device_repack(char* __restrict__ sendbuf, char* __restrict__ recvbuf, int size_x, int size_y, int size_z); + void get_mem_types(const void* sendbuf, const void* recvbuf, gpuMemoryType* send_type, @@ -27,9 +29,23 @@ void get_mem_types(const void* sendbuf, void get_memcpy_kind(gpuMemoryType send_type, gpuMemoryType recv_type, gpuMemcpyKind* memcpy_kind); + +/** @brief Set dims (currently 8,8,8) then launch ::device_repack() kernel **/ void gpu_repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf); + +/** @brief Prints error message if ierr is not gpuSuccess **/ void gpu_check(int ierr); +/** @brief Copy contents from sendbuf into recvbuf + * @details Checks the type of memory involved in order to make sure the correct method + * for copying the data is called. `memcpy` is used for CPU transfers; `::gpuMemcpy` is + * used for GPU transfers. + * @param [in] size_i if GPU + * @param [in] size_j if GPU + * @param [in] size_k + * @param [out] sendbuf + * @param [in] recvbuf + **/ void repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf); #endif diff --git a/library/include/heterogeneous/utils_hip.h b/library/include/heterogeneous/utils_hip.h index 7d798ce8e..ab24220af 100644 --- a/library/include/heterogeneous/utils_hip.h +++ b/library/include/heterogeneous/utils_hip.h @@ -12,7 +12,7 @@ #define gpuSetDevice hipSetDevice // Data allocation -#define gpuMallocHost hipHostMalloc +#define gpuMallocHost hipMallocHost #define gpuMalloc hipMalloc #define gpuFree hipFree #define gpuFreeHost hipHostFree diff --git a/library/include/neighborhood/MPIL_Topo.h b/library/include/neighborhood/MPIL_Topo.h index 0c71d3c69..3df5d3557 100644 --- a/library/include/neighborhood/MPIL_Topo.h +++ b/library/include/neighborhood/MPIL_Topo.h @@ -5,14 +5,28 @@ extern "C" { #endif +/** @brief Struct for caching parameters given to MPI_Dist_graph_create_adjacent. + * @details Structure containing the same arguments as MPI_Dist_graph_create_adjacent. + * Information is cached to avoiding the need to rebuild communicators when tweaking a + * topology. Instead of the topology being attached to the communicator, the topology it + * can now be a standalone object and then given to the collective call itself. See MPI + * standard: MPI_Dist_graph_create_adjacent for more details on the members. + **/ typedef struct _MPIL_Topo { + /** @brief size of sources and source weights **/ int indegree; + /** @brief rank for which the process is a a destination **/ int* sources; + /** weights of the edges into the calling process**/ int* sourceweights; + /** @brief size of destinations and destweights **/ int outdegree; + /** @brief ranks of processes for which the calling process is a source**/ int* destinations; + /** @brief weights of the edges outof the calling process**/ int* destweights; + /** @brief the ranks may be reordered (true if 0) false otherwise**/ int reorder; } MPIL_Topo; diff --git a/library/include/persistent/MPIL_Request.h b/library/include/persistent/MPIL_Request.h index c5c0ad04f..b4244a76e 100644 --- a/library/include/persistent/MPIL_Request.h +++ b/library/include/persistent/MPIL_Request.h @@ -9,52 +9,69 @@ extern "C" { #endif +/** @brief A custom MPI_Request struct used for the library's persistent collectives + * @details For external users, there is limited direct access to class members through + * API calls. Contains multiple requests and buffers to manage complex communication. + * Contains function pointer to appropriate start and wait functions. + */ typedef struct _MPIL_Request { - // Message counts - // Will only use global unless locality-aware + // Message counts; Will only use global unless locality-aware + /** @brief Intra-node message count **/ int local_L_n_msgs; + /** @brief Sent message count **/ int local_S_n_msgs; + /** @brief Received message count **/ int local_R_n_msgs; + /** @brief Number of inter-node messages **/ int global_n_msgs; - // MPI Request arrays - // Will only use global unless locality-aware + // MPI Request arrays; Will only use global unless locality-aware + /** @brief Requests to manage of intra-node messages **/ MPI_Request* local_L_requests; + /** @brief Requests to control sent messages **/ MPI_Request* local_S_requests; + /** @brief Requests to control received messages **/ MPI_Request* local_R_requests; + /** @brief Requests to manage of inter-node messages **/ MPI_Request* global_requests; - // Pointer to locality communication, only for locality-aware + /** @brief Pointer to locality communication information if using locality-aware + * variants **/ LocalityComm* locality; - // Pointer to sendbuf and recvbuf - const void* sendbuf; // pointer to sendbuf (where original data begins) - void* recvbuf; // pointer to recvbuf (where final data goes) + /** @brief Pointers to the user's original send buffer */ + const void* sendbuf; + /** @brief Pointer to the user's original receive buffer */ + void* recvbuf; - // Number of bytes per receive object (for locality-aware) + /** @brief Number of bytes per receive object, locality-aware only **/ int recv_size; - - // Block size : for strided/blocked communication + /** @brief Block size for strided/blocked communication **/ int block_size; - int tag; int reorder; - // For allocating cpu buffers for heterogeneous communication #ifdef GPU - void* cpu_sendbuf; // for copy-to-cpu - void* cpu_recvbuf; // for copy-to-cpu + /** @brief Allocated cpu-based send buffers for copy-to-cpu algorithms **/ + void* cpu_sendbuf; + /** @brief Allocated cpu-based receive buffers for copy-to-cpu algorithms **/ + void* cpu_recvbuf; #endif - - // Keep track of which start/wait functions to call for given request + /** @brief Function pointer to MPIL_Start or MPIL_neighbor_start **/ int (*start_function)(struct _MPIL_Request* request); + /** @brief Function pointer to MPIL_Wait or MPIL_neighbor_wait **/ int (*wait_function)(struct _MPIL_Request* request, MPI_Status* status); } MPIL_Request; +/** @brief Constructor for ::MPIL_Request. Initializes most members to 0. */ void init_request(MPIL_Request** request_ptr); + +/** @brief Allocate enough space for n MPI_Requests + @param [in] n_request how many requests need space + @param [out] request_ptr pointer to start of allocated memory +**/ void allocate_requests(int n_requests, MPI_Request** request_ptr); -void destroy_request(MPIL_Request* request); #ifdef __cplusplus } diff --git a/library/include/utils/utils.h b/library/include/utils/utils.h index 20242c93a..094b2d41a 100644 --- a/library/include/utils/utils.h +++ b/library/include/utils/utils.h @@ -6,44 +6,36 @@ extern "C" { #endif // General utility methods (that use C++ functions) /** @brief wrapper around std::sort - @param [in] n_objects number of objects to short - @param [in, out] array of indexes - @param [in] array of values -**/ + * @param [in] n_objects number of objects to short + * @param [in, out] array of indexes + * @param [in] array of values + **/ void sort(int n_objects, int* object_indices, int* object_values); /** @brief wrapper around std::rotate, - * @details - * Rotates such that new_first_byte is first in array - * Divides recvbuf into two parts [first, middle] and (middle, last) - * then swaps their positioning. - * Example: A = 0, 1, 2, 3, 4, 5 - * std::rotate(A*, 2, A*+6) would split into (0, 1) and (2, 3, 4, 5) - * and after running A = 2, 3, 4, 5, 0, 1 + * @details + * Rotates such that new_start_byte is first in array. Divides recvbuf into two parts + * [first, middle] and (middle, last) then swaps their positioning. Example: A = 0, 1, 2, + * 3, 4, 5 std::rotate(A*, 2, A*+6) would split into (0, 1) and (2, 3, 4, 5) and after + * running A = 2, 3, 4, 5, 0, 1 * - * @param [in, out] recvbuf buffer of elements to rotate - * @param [in] new_first_byte index immediately after the split point. - * @param [in] index of last element in the effected range + * @param [in, out] recvbuf buffer of elements to rotate + * @param [in] new_start_byte index immediately after the split point. + * @param [in] index of last element in the effected range **/ void rotate(void* ref, int new_start_byte, int end_byte); /** @brief reverses order of elements in recv_buffer - * @details - * Divides recvbuf into two parts [first, middle] and (middle, last) - * then swaps their positioning. - * Example: A = 0, 1, 2, 3, 4, 5 - * std::rotate(A*, 2, A*+6) would split into (0, 1) and (2, 3, 4, 5) - * and after running A = 2, 3, 4, 5, 0, 1 + * @details Divides recvbuf into two parts [first, middle] and (middle, last) then swaps + * their positioning. Example: A = 0, 1, 2, 3, 4, 5 std::rotate(A*, 2, A*+6) would split + * into (0, 1) and (2, 3, 4, 5) and after running A = 2, 3, 4, 5, 0, 1 * - * @param [in, out] recvbuf buffer of elements to rotate - * @param [in] new_first_byte index immediately after the split point. - * @param [in] index of last element in the effected range - * \todo why this instead of std::reverse? + * @param [in, out] recvbuf buffer of elements to rotate + * @param [in] new_first_byte index immediately after the split point. + * @param [in] index of last element in the effected range **/ void reverse(void* recvbuf, int n_bytes, int var_bytes); -void repack(int size_i, int size_j, int size_k, char* sendbuf, char* recvbuf); - #ifdef __cplusplus } #endif diff --git a/library/source/collective/alltoallv/alltoallv_batch.c b/library/source/collective/alltoallv/alltoallv_batch.c index 34dad7876..70ce02af7 100644 --- a/library/source/collective/alltoallv/alltoallv_batch.c +++ b/library/source/collective/alltoallv/alltoallv_batch.c @@ -19,15 +19,15 @@ int alltoallv_batch(const void* sendbuf, int nb_stride = 5; if (nb_stride >= num_procs) { - alltoallv_nonblocking(sendbuf, - sendcounts, - sdispls, - sendtype, - recvbuf, - recvcounts, - rdispls, - recvtype, - comm); + return alltoallv_nonblocking(sendbuf, + sendcounts, + sdispls, + sendtype, + recvbuf, + recvcounts, + rdispls, + recvtype, + comm); } int tag; diff --git a/library/source/heterogeneous/CMakeLists.txt b/library/source/heterogeneous/CMakeLists.txt index 1bef5ef2e..05e3e888a 100644 --- a/library/source/heterogeneous/CMakeLists.txt +++ b/library/source/heterogeneous/CMakeLists.txt @@ -5,10 +5,10 @@ set_property(GLOBAL APPEND PROPERTY GPU_SOURCES_GLOBAL ${CMAKE_CURRENT_SOURCE_DIR}/gpu_alltoall.c ${CMAKE_CURRENT_SOURCE_DIR}/gpu_check.c ${CMAKE_CURRENT_SOURCE_DIR}/repack.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/device_repack.c + ${CMAKE_CURRENT_SOURCE_DIR}/device_repack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/get_memcpy_kind.c ${CMAKE_CURRENT_SOURCE_DIR}/gpu_alltoallv.c - ${CMAKE_CURRENT_SOURCE_DIR}/gpu_repack.c + ${CMAKE_CURRENT_SOURCE_DIR}/gpu_repack.cpp ) target_sources(locality_aware PRIVATE @@ -16,8 +16,8 @@ target_sources(locality_aware PRIVATE gpu_alltoall.c gpu_check.c repack.cpp - device_repack.c + device_repack.cpp get_memcpy_kind.c gpu_alltoallv.c - gpu_repack.c + gpu_repack.cpp ) diff --git a/library/source/heterogeneous/device_repack.c b/library/source/heterogeneous/device_repack.cpp similarity index 100% rename from library/source/heterogeneous/device_repack.c rename to library/source/heterogeneous/device_repack.cpp diff --git a/library/source/heterogeneous/gpu_alltoall.c b/library/source/heterogeneous/gpu_alltoall.c index 14f1fdbd4..e728065ea 100644 --- a/library/source/heterogeneous/gpu_alltoall.c +++ b/library/source/heterogeneous/gpu_alltoall.c @@ -14,27 +14,7 @@ int gpu_aware_alltoall(alltoall_ftn f, MPI_Datatype recvtype, MPIL_Comm* comm) { - int num_procs; - MPI_Comm_size(comm->global_comm, &num_procs); - - int send_bytes, recv_bytes; - MPI_Type_size(sendtype, &send_bytes); - MPI_Type_size(recvtype, &recv_bytes); - - int total_bytes_s = sendcount * send_bytes * num_procs; - int total_bytes_r = recvcount * recv_bytes * num_procs; - - char* cpu_sendbuf; - char* cpu_recvbuf; - gpuMallocHost((void**)&cpu_sendbuf, total_bytes_s); - gpuMallocHost((void**)&cpu_recvbuf, total_bytes_r); - - int ierr = f(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm); - - gpuFreeHost(cpu_sendbuf); - gpuFreeHost(cpu_recvbuf); - - return ierr; + return f(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm); } int gpu_aware_alltoall_nonblocking(const void* sendbuf, @@ -150,203 +130,202 @@ int copy_to_cpu_alltoall_nonblocking(const void* sendbuf, comm); } -#ifdef OPENMP -int threaded_alltoall_pairwise(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - int ierr = 0; - - int rank, num_procs; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - int send_bytes, recv_bytes; - MPI_Type_size(sendtype, &send_bytes); - MPI_Type_size(recvtype, &recv_bytes); - - int total_bytes_s = sendcount * send_bytes * num_procs; - int total_bytes_r = recvcount * recv_bytes * num_procs; - - char* cpu_sendbuf; - char* cpu_recvbuf; - gpuMallocHost((void**)&cpu_sendbuf, total_bytes_s); - gpuMallocHost((void**)&cpu_recvbuf, total_bytes_r); - - // Copy from GPU to CPU - ierr += gpuMemcpy(cpu_sendbuf, sendbuf, total_bytes_s, gpuMemcpyDeviceToHost); - - memcpy(cpu_recvbuf + (rank * recvcount * recv_bytes), - cpu_sendbuf + (rank * sendcount * send_bytes), - sendcount * send_bytes); - -#pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) - { - MPI_Status status; - int tag; - get_tag(comm, &tag); - int send_proc, recv_proc; - int send_pos, recv_pos; - - int n_msgs = num_procs - 1; - int thread_id = omp_get_thread_num(); - int num_threads = omp_get_num_threads(); - - int n_msgs_per_thread = n_msgs / num_threads; - int extra_msgs = n_msgs % num_threads; - int thread_n_msgs = n_msgs_per_thread; - if (extra_msgs > thread_id) - { - thread_n_msgs++; - } - - if (thread_n_msgs) - { - int idx = thread_id + 1; - for (int i = 0; i < thread_n_msgs; i++) - { - send_proc = rank + idx; - if (send_proc >= num_procs) - { - send_proc -= num_procs; - } - recv_proc = rank - idx; - if (recv_proc < 0) - { - recv_proc += num_procs; - } - send_pos = send_proc * sendcount * send_bytes; - recv_pos = recv_proc * recvcount * recv_bytes; - - MPI_Sendrecv(cpu_sendbuf + send_pos, - sendcount, - sendtype, - send_proc, - tag, - cpu_recvbuf + recv_pos, - recvcount, - recvtype, - recv_proc, - tag, - comm->global_comm, - &status); - - idx += num_threads; - } - } - } - - ierr += gpuMemcpy(recvbuf, cpu_recvbuf, total_bytes_r, gpuMemcpyHostToDevice); - - gpuFreeHost(cpu_sendbuf); - gpuFreeHost(cpu_recvbuf); - - return ierr; -} - -int threaded_alltoall_nonblocking(const void* sendbuf, - const int sendcount, - MPI_Datatype sendtype, - void* recvbuf, - const int recvcount, - MPI_Datatype recvtype, - MPIL_Comm* comm) -{ - int num_procs, rank; - MPI_Comm_rank(comm->global_comm, &rank); - MPI_Comm_size(comm->global_comm, &num_procs); - - int send_bytes, recv_bytes; - MPI_Type_size(sendtype, &send_bytes); - MPI_Type_size(recvtype, &recv_bytes); - - int total_bytes_s = sendcount * send_bytes * num_procs; - int total_bytes_r = recvcount * recv_bytes * num_procs; - - char* cpu_sendbuf; - char* cpu_recvbuf; - gpuMallocHost((void**)&cpu_sendbuf, total_bytes_s); - gpuMallocHost((void**)&cpu_recvbuf, total_bytes_r); - - int ierr = 0; - ierr += gpuMemcpy(cpu_sendbuf, sendbuf, total_bytes_s, gpuMemcpyDeviceToHost); - - memcpy(cpu_recvbuf + (rank * recvcount * recv_bytes), - cpu_sendbuf + (rank * sendcount * send_bytes), - sendcount * send_bytes); - -#pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) - { - int tag; - get_tag(comm, &tag); - int send_proc, recv_proc; - int send_pos, recv_pos; - - int n_msgs = num_procs - 1; - int thread_id = omp_get_thread_num(); - int num_threads = omp_get_num_threads(); - - int n_msgs_per_thread = n_msgs / num_threads; - int extra_msgs = n_msgs % num_threads; - int thread_n_msgs = n_msgs_per_thread; - if (extra_msgs > thread_id) - { - thread_n_msgs++; - } - - if (thread_n_msgs) - { - MPI_Request* requests = - (MPI_Request*)malloc(2 * thread_n_msgs * sizeof(MPI_Request)); - - int idx = thread_id + 1; - for (int i = 0; i < thread_n_msgs; i++) - { - send_proc = rank + idx; - if (send_proc >= num_procs) - { - send_proc -= num_procs; - } - recv_proc = rank - idx; - if (recv_proc < 0) - { - recv_proc += num_procs; - } - send_pos = send_proc * sendcount * send_bytes; - recv_pos = recv_proc * recvcount * recv_bytes; - - MPI_Isend(cpu_sendbuf + send_pos, - sendcount, - sendtype, - send_proc, - tag, - comm->global_comm, - &(requests[i])); - MPI_Irecv(cpu_recvbuf + recv_pos, - recvcount, - recvtype, - recv_proc, - tag, - comm->global_comm, - &(requests[thread_n_msgs + i])); - idx += num_threads; - } - - MPI_Waitall(2 * thread_n_msgs, requests, MPI_STATUSES_IGNORE); - - free(requests); - } - } - - ierr += gpuMemcpy(recvbuf, cpu_recvbuf, total_bytes_r, gpuMemcpyHostToDevice); - gpuFreeHost(cpu_sendbuf); - gpuFreeHost(cpu_recvbuf); - - return ierr; -} - -#endif +// #ifdef OPENMP +// int threaded_alltoall_pairwise(const void* sendbuf, +// const int sendcount, +// MPI_Datatype sendtype, +// void* recvbuf, +// const int recvcount, +// MPI_Datatype recvtype, +// MPIL_Comm* comm) +// { +// int ierr = 0; + +// int rank, num_procs; +// MPI_Comm_rank(comm->global_comm, &rank); +// MPI_Comm_size(comm->global_comm, &num_procs); + +// int send_bytes, recv_bytes; +// MPI_Type_size(sendtype, &send_bytes); +// MPI_Type_size(recvtype, &recv_bytes); + +// int total_bytes_s = sendcount * send_bytes * num_procs; +// int total_bytes_r = recvcount * recv_bytes * num_procs; + +// char* cpu_sendbuf; +// char* cpu_recvbuf; +// gpuMallocHost((void**)&cpu_sendbuf, total_bytes_s); +// gpuMallocHost((void**)&cpu_recvbuf, total_bytes_r); + +// // Copy from GPU to CPU +// ierr += gpuMemcpy(cpu_sendbuf, sendbuf, total_bytes_s, gpuMemcpyDeviceToHost); + +// memcpy(cpu_recvbuf + (rank * recvcount * recv_bytes), +// cpu_sendbuf + (rank * sendcount * send_bytes), +// sendcount * send_bytes); + +// #pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) +// { +// MPI_Status status; +// int tag; +// get_tag(comm, &tag); +// int send_proc, recv_proc; +// int send_pos, recv_pos; + +// int n_msgs = num_procs - 1; +// int thread_id = omp_get_thread_num(); +// int num_threads = omp_get_num_threads(); + +// int n_msgs_per_thread = n_msgs / num_threads; +// int extra_msgs = n_msgs % num_threads; +// int thread_n_msgs = n_msgs_per_thread; +// if (extra_msgs > thread_id) +// { +// thread_n_msgs++; +// } + +// if (thread_n_msgs) +// { +// int idx = thread_id + 1; +// for (int i = 0; i < thread_n_msgs; i++) +// { +// send_proc = rank + idx; +// if (send_proc >= num_procs) +// { +// send_proc -= num_procs; +// } +// recv_proc = rank - idx; +// if (recv_proc < 0) +// { +// recv_proc += num_procs; +// } +// send_pos = send_proc * sendcount * send_bytes; +// recv_pos = recv_proc * recvcount * recv_bytes; + +// MPI_Sendrecv(cpu_sendbuf + send_pos, +// sendcount, +// sendtype, +// send_proc, +// tag, +// cpu_recvbuf + recv_pos, +// recvcount, +// recvtype, +// recv_proc, +// tag, +// comm->global_comm, +// &status); + +// idx += num_threads; +// } +// } +// } + +// ierr += gpuMemcpy(recvbuf, cpu_recvbuf, total_bytes_r, gpuMemcpyHostToDevice); + +// gpuFreeHost(cpu_sendbuf); +// gpuFreeHost(cpu_recvbuf); + +// return ierr; +// } + +// int threaded_alltoall_nonblocking(const void* sendbuf, +// const int sendcount, +// MPI_Datatype sendtype, +// void* recvbuf, +// const int recvcount, +// MPI_Datatype recvtype, +// MPIL_Comm* comm) +// { +// int num_procs, rank; +// MPI_Comm_rank(comm->global_comm, &rank); +// MPI_Comm_size(comm->global_comm, &num_procs); + +// int send_bytes, recv_bytes; +// MPI_Type_size(sendtype, &send_bytes); +// MPI_Type_size(recvtype, &recv_bytes); + +// int total_bytes_s = sendcount * send_bytes * num_procs; +// int total_bytes_r = recvcount * recv_bytes * num_procs; + +// char* cpu_sendbuf; +// char* cpu_recvbuf; +// gpuMallocHost((void**)&cpu_sendbuf, total_bytes_s); +// gpuMallocHost((void**)&cpu_recvbuf, total_bytes_r); + +// int ierr = 0; +// ierr += gpuMemcpy(cpu_sendbuf, sendbuf, total_bytes_s, gpuMemcpyDeviceToHost); + +// memcpy(cpu_recvbuf + (rank * recvcount * recv_bytes), +// cpu_sendbuf + (rank * sendcount * send_bytes), +// sendcount * send_bytes); + +// #pragma omp parallel shared(cpu_sendbuf, cpu_recvbuf) +// { +// int tag; +// get_tag(comm, &tag); +// int send_proc, recv_proc; +// int send_pos, recv_pos; + +// int n_msgs = num_procs - 1; +// int thread_id = omp_get_thread_num(); +// int num_threads = omp_get_num_threads(); + +// int n_msgs_per_thread = n_msgs / num_threads; +// int extra_msgs = n_msgs % num_threads; +// int thread_n_msgs = n_msgs_per_thread; +// if (extra_msgs > thread_id) +// { +// thread_n_msgs++; +// } + +// if (thread_n_msgs) +// { +// MPI_Request* requests = +// (MPI_Request*)malloc(2 * thread_n_msgs * sizeof(MPI_Request)); + +// int idx = thread_id + 1; +// for (int i = 0; i < thread_n_msgs; i++) +// { +// send_proc = rank + idx; +// if (send_proc >= num_procs) +// { +// send_proc -= num_procs; +// } +// recv_proc = rank - idx; +// if (recv_proc < 0) +// { +// recv_proc += num_procs; +// } +// send_pos = send_proc * sendcount * send_bytes; +// recv_pos = recv_proc * recvcount * recv_bytes; + +// MPI_Isend(cpu_sendbuf + send_pos, +// sendcount, +// sendtype, +// send_proc, +// tag, +// comm->global_comm, +// &(requests[i])); +// MPI_Irecv(cpu_recvbuf + recv_pos, +// recvcount, +// recvtype, +// recv_proc, +// tag, +// comm->global_comm, +// &(requests[thread_n_msgs + i])); +// idx += num_threads; +// } + +// MPI_Waitall(2 * thread_n_msgs, requests, MPI_STATUSES_IGNORE); + +// free(requests); +// } +// } + +// ierr += gpuMemcpy(recvbuf, cpu_recvbuf, total_bytes_r, gpuMemcpyHostToDevice); +// gpuFreeHost(cpu_sendbuf); +// gpuFreeHost(cpu_recvbuf); + +// return ierr; +// } +// #endif diff --git a/library/source/heterogeneous/gpu_alltoallv.c b/library/source/heterogeneous/gpu_alltoallv.c index 219c552f4..3d4079897 100644 --- a/library/source/heterogeneous/gpu_alltoallv.c +++ b/library/source/heterogeneous/gpu_alltoallv.c @@ -271,7 +271,8 @@ int threaded_alltoallv_pairwise(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm) { - int ierr = 0; + printf("FUNCTION UNSUPPORTED\n"); + /* int ierr = 0; int rank, num_procs; MPI_Comm_rank(comm->global_comm, &rank); @@ -365,7 +366,7 @@ int threaded_alltoallv_pairwise(const void* sendbuf, gpuFreeHost(cpu_sendbuf); gpuFreeHost(cpu_recvbuf); - return ierr; + return ierr; */ } int threaded_alltoallv_nonblocking(const void* sendbuf, @@ -378,6 +379,8 @@ int threaded_alltoallv_nonblocking(const void* sendbuf, MPI_Datatype recvtype, MPIL_Comm* comm) { + printf("FUNCTION UNSUPPORTED\n"); + /* int ierr = 0; int rank, num_procs; @@ -480,6 +483,7 @@ int threaded_alltoallv_nonblocking(const void* sendbuf, gpuFreeHost(cpu_recvbuf); return ierr; + */ } #endif diff --git a/library/source/heterogeneous/gpu_repack.c b/library/source/heterogeneous/gpu_repack.cpp similarity index 100% rename from library/source/heterogeneous/gpu_repack.c rename to library/source/heterogeneous/gpu_repack.cpp diff --git a/library/source/neighborhood/locality/neighbor_locality.cpp b/library/source/neighborhood/locality/neighbor_locality.cpp index 54fcafa95..fcfd71215 100644 --- a/library/source/neighborhood/locality/neighbor_locality.cpp +++ b/library/source/neighborhood/locality/neighbor_locality.cpp @@ -142,7 +142,6 @@ void init_locality(const int n_sends, // Copy to pointer for return request->locality = locality_comm; - request->tag = locality_comm->global_comm->tag; } #ifdef __cplusplus } From c5816c9389e9614587b1a1a51000596635058913 Mon Sep 17 00:00:00 2001 From: Derek Schafer Date: Fri, 14 Nov 2025 09:18:15 -0700 Subject: [PATCH 12/15] Tweaked CMake file to not install twice --- CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 76318c4a4..bbab9c66d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,15 +112,12 @@ if (USE_CUDA) set_source_files_properties(${GPU_SOURCES_GLOBAL} PROPERTIES LANGUAGE ${locality_aware_LANG}) endif() -install(TARGETS locality_aware) - ### Installation Requirements for Spack Package ### include(CMakePackageConfigHelpers) # Install the actual target(s) and register them for export install(TARGETS locality_aware - EXPORT locality_awareTargets - DESTINATION lib) + EXPORT locality_awareTargets) # Create the configuration file configure_package_config_file( From 22364f29ad99581927ec970992cd2be772b4b67f Mon Sep 17 00:00:00 2001 From: Derek Schafer Date: Mon, 17 Nov 2025 12:10:41 -0700 Subject: [PATCH 13/15] Updated README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e67f769c2..eb2ab25c0 100644 --- a/README.md +++ b/README.md @@ -317,6 +317,6 @@ The locality_aware repository is laid out as follows: # Acknowledgements -This work has been partially funded by ... +This work has been funded by the National Science Foundation through grants 2514054, 2450093, 2412182, CNS2450092, CCF2338077, CCF2151022. This work was also performed with support from the U.S. Department of Energy's National Nuclear Security Administration (NNSA) under the Predictive Science Academic Alliance Program (PSAAP-III), Award DE-NA0003966. -############################################################ +Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation or the U.S. Department of Energy's National Nuclear Security Administration. From ab9affffa4f280bb1cbaa8d9bff8e04b85eebf34 Mon Sep 17 00:00:00 2001 From: Derek Schafer Date: Wed, 19 Nov 2025 08:29:12 -0700 Subject: [PATCH 14/15] Removed stale requirement in CMake file --- cmake/locality_aware-config.cmake.in | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/locality_aware-config.cmake.in b/cmake/locality_aware-config.cmake.in index a0e668292..a1df7b15e 100644 --- a/cmake/locality_aware-config.cmake.in +++ b/cmake/locality_aware-config.cmake.in @@ -2,5 +2,4 @@ include("${CMAKE_CURRENT_LIST_DIR}/locality_awareTargets.cmake") -find_package(OpenMP) find_package(MPI REQUIRED) \ No newline at end of file From bf6d1b944132b0105e2dcf4a05af4f993173be3b Mon Sep 17 00:00:00 2001 From: Derek Schafer Date: Wed, 19 Nov 2025 08:46:17 -0700 Subject: [PATCH 15/15] Updated testing workflow to have lower time out on tests, use more precise CMake tesing flags, and removed stale branch targets --- .github/workflows/testing.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index c18dba87f..0d68d6978 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -1,8 +1,6 @@ name: MPI Advance Ctest on: - push: - branches: [master, cleanup] pull_request: branches: [master] @@ -28,22 +26,22 @@ jobs: sudo apt-get install -y cmake build-essential - name: Configure CMake - timeout-minutes: 10 + timeout-minutes: 5 run: | mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=Release .. + cmake -DTEST_PROCS=4 -DCMAKE_BUILD_TYPE=Release .. cd .. - name: Build project - timeout-minutes: 10 + timeout-minutes: 5 run: | cd build - make + make -j4 cd .. - name: Run tests with CTest - timeout-minutes: 10 + timeout-minutes: 5 run: | cd build ctest --output-on-failure

    kAGlF?q!lu(@{+_0Y^i4Tm$<^yApTu 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%0AhD6+H_q(-t@+ucwo&P#$22pC5bhl^*rU*KBx;r z)u}|Q61RJ_VynC<8wY*AzI=S}dZ|Zk9vxA=&I=~M{_&w2hJDI9kiOv5)1i->ePo!R z@WWLzfF{B1cw&3IKP`5SdW#%vQrg91kBt=$HJ>-c@g8=$KK_t*}%kR1E1cCn2 zmWrqRCsJ0RL6MB)oj_~Op^y@~)HzgSu1AE?uaF`>t36&=vk|~_%wUlEBzgb=cfBzt z`rb%YSISJrFb=}H!$}1w2!6$pTZNOJ`}U-#n>R9o9_BOD(|9+L*}&B%zDtM(%|3v- z#^SbJaVyOgmNe{6Qzod7v2Nk(S%?E=3pJvHm+k4B1^(x6OvO-zAaxaB_s)eDFle?4 zDVCA||G;6~1pwxQL88{5M1yIOd@dN^zJH0--BLj2r>JDv>}c4-zKWxF>wnC}>zJ6L zM4)Cx>GmD+Va^7`H1JHgF02sN`S*`R<|y~Oc6qrrJh z0rn|?~SX~i5qHB#7yXgm;3D_aDYUu-Y)I-z zw^DW`X=hVEbt3lYY(o#DvjRZ2_F`$AvV^r$@S$>zg!EZ4v}bAC*!r3b{4LA&pSC3E zJQ@aMJw@aTXjK^RE(^o&M?rtiLF0C;niQtgSOP+MV|0`9#S28Qoib7!MRkMkI4xBg8bSWbhY{%4}hN#Rl$eC^lKEttHW`ZzDh0eOWy?2pxi?5x- z%5#nTu8bg7J&-=0rD|5Q?_w1WgpO)7*b427e@F#3bX4+fO-~b?3C=5tgf~lt?ew^c zaB*Qu%=PTXkj<|Yp@!JX%r~^0@t$7YHS~a~dHEJbx!Ys^4;59%%x^Zsvr$27nnBMyNm(3m@=AkPbLo9LfT-iG=LC~+gE z`h1$i<11~yabFMHY1B1c_n|v$NG}hFBJ}5~m?4bn;^%2KP{cb~F*@{~Ns_fngk}hK z_ksqXF$SGt4%i>Fl#9eHeq$XB4Q71KM;EsT3t1!Zz8EH%+9HbNjPAZzU;XWlgf`Sg z_68WG>`+cmS6x`b2exA2XzU94=`$2| z!hh%JsQp3q9Mxw^q5XA$c4z-)R5qU0{mU?gEx>4hEbfaiZuR0Ni-&y%@JxPbl(ErC zauPltzahtgrbFq=gbezB(vW{8!C9O zxfN7gTji{Wz02}3;F!y(>F)Hm1QBsttC+fOlo%I{mSpOX) za|-gsnh|&;5P}Q}FlOifrz?bviFg(7sGm4ZrKP#4sF=!JO--(&CHV8+DTp;B#!4Vm zu%KG62j}mp09YLh_z<%n7Ga<3sJLjtYh%N&3H+Bc;?*QQ73441qnvE$9<&B^kWQZP zhiG@RPEgl1PD^U3z2fa#AFy+6EIPUc%wD94qRLqfQNIh|2H^krB4QoT|4iL8F;YUq zL%ESfXV_qH`Gl`nnkwcX&&C*3FMH;ae0hpGl#Bhpf0>65HV}s*&i;h4!*ZSkD+1|p zOs!9A%Sbw1hV(JSf$2ddPn9)k7*2|mHTNw1_yJZV@KN&#ZqO&&YTquT!WjRgt;r4;;6wKRXBbgH(1W;+Ktq5p0vJ{9r(AsD-_3_5(gjX`Nu^7BEaP`ddYPud>zW#QUh^M2$cVUM9Z zY}I~mfvI93IJjIAtvA=DuL$M01=kD{utHj$+CcvU_O}~MXJfIsbm!id*?)mqlKpK` z#g2DI_we-JkVn;<$y|8IqLXN5D3a%7?se&3=Qb+AzANX(sh2(EMkX>q@#X(!7Cr~5 z%QTonNC)7ALxr-A8D}aXwXTo2gtL8YwHDjF;K6M?r$fju{t!P;G-Xqdkx@%qHz?^1 z9J)nvHVTY;msp`CofE1RvV8o{cO@7*Jz+OPxGik%+a1DEAxgi z>0r~K-YdSAZWOF0&8}MQ1w->!rF_JjbEVw`q|b+LO-> zeLKDdu|8f318Xpz3oJFS|ACe*X$mpxfyMW(0147Z4?l|oS*nZ%+`Jr}FBrL9G6^a> z?uj!tCdCcnRiW0qH!W{MsEN5uz^-$KGwo1o1}O^c1HbsB2sSKjxd^&>X@&G(qJJX0 z9uOd^Hn$dRRx|(9C>cq*TV%L&y$k)NFngthYrSg>Z!OR*f2gKQ?`N;Ba~NP>J4)=zS=N!mSs31{k6)@WYSb_#MEOow2hhlXq}MNFMEV zkztJU$OQfi?toR6!GbO=Q=?8P++VD>QAdPg@MB2phYcF{66*ayDoM;dQJNzk-m=C* zMz6|Y#Kb)j;fEgrwqxsdGi3R+BlKn?P+|b@eAd=qj;L#qZbD7wQaAe|$ojo5`jnBgABeIZfdFRPw;SH94ou#HFzCAj=_jO~fI*>fRp)Ky+Jr|4=enY?Cb-(#* zrRo5?C;R>bNsUm50m!7&vlx50M32Jxy_Ku?hHlxmEh6}q@n1{Z3taO5d4~YOq>D`g z!|d^Xn+pO-NrjUbB93QiXD~2RAPtXxFHL$e*ZW6M6L!%fPFc4iW%I(~WC(DoPW+AI zk26Gc%>j3IQFCgM5M5;*7kKcm!BD5(enzzurKDe{pCD(`g8&*7|H4`q%-IB66vYIk zp2hG+PS{2cEa+e^>>LFK}y{xPQ!8u^C17)F$%$G(RRs7+4b$faynu9AVd+{ zlb##*T-Ws!IEz#kC|W;!U*fgQbSsuDeGQMU4O_TnOcoBgpc6_2dRo?t!wHor@frq5 zMn)yg#w0+?Ej52~-(m;CeN}SpCk!`E@`(rL9glr-4jGKPX8vXBo5?=P1<%KW2Ka|Y z-HJXG?v6ndiN5Xmw23lL4t&>{3B?Z!z*^M-;_aV^8k3%aN^skvFMS1`J+iQM)RM!RK5${d(|>cv0@{Owi{$&mWUY;j67jM0r3;B-0Q=ntC1$p%LiYpZLs$xUcqWm>%8!c1( zZ-jo?zfir7P za7_n$sxzC{bQKmL{-uwb*DrV;zO6Z@;j~%`z*-Lj8CnKouf{5Eqna9cEqMW!DW!^# z<7@%U2eC0Z;v-%RFIfg=e)U^FfToFyd42HomMKw%;nv>CqFxpI!#UU`ubwtp+I6Ul zB7vqsd3$^bs>bgx^qX)-{t}!puxW|hwf~AR{icszY>zKK-fbMk4b_{ERqzJaHlktf zFdF?n)7ckgdQhFeEaWka-jV^+J@(NtgADA?zxuNJKG&>q(%SMLdEfy(k}l1Jkh#f4 z5wrGRP6ms*9TbNlAl7!NV)Q*NNVa*5q=AZ;uc;o5R@UAETn1xi!}MPX89f(;G1etd+j1|RK2V#KAoPB?vSe&RcI+11Sgd8Z0(@EXQ!A$5*MQ{u& z5N2bC!)vtQrm}~{Mn*V%T&eO|PrcWHq7e>#kBH&6Cv$L%Ab<~H|3#GXQSzh%ifm2D zfPM8mp`T(!l9(Lii=wh8BrSi_9#gdKD1+N*_=Xz85>R>ap_;KrMM8x>-v!?+i-rRZ zqiW{VrlGuMYddXx48sw()rg~cp4awP7$y*w}i;rrF>vfQ1EJ8GpX zW3sNe%=&W~M$j+3efdE+L>qlqL=!W655%!Y{3=k)n#TFM%b%^rWd}lRIr!G$s-%uQ z{_|}DBYT2&ay=;%eP@vebmyuIeok%*OYJzv&e%Z8a+j+V&2?-{qjJbEAQha01UY;| zV@(GpE0W(w{q@g8-`)W+fMj7McBe6m?aQpRb_utO8ACCccZawDG}X^Q$i%&%wRqS> zS%+;E?#mB1OFcJF&dYcrB7_O`=xV6vn?MEXm5VMs;Hyu%zdU)7`ztRcMY!v;O1@TX z>~JlTQEq7X=kJe^6<1zSlEf~cnlyr$Sz63<`_D6d-q#dGU)*(U@`v#IA+u2?2^w81 z%&xUozTlE>Hf!Z*e4Lq1b;L-a9cixkq&&2aw>!Dmuk=zZXgb$OU=p`%Zs+pH043pD z8H!Y&Fw(iD0ZDLl#Wkk_8G)4|($50oAh0dU=~KCq`cZwOUJ?pk<_>hr67*XNV3`FT z8U(ZByzS*CUCRQ~0>j|BlB7?3l}HUSW6p6LsV9mA=`MLvfa#a)l-p#{FFb#2OT_O9 zNW{~0A;}%sACALJJ4`@#9gCXY74I>hiQSjd%-k%y@1^{;^7dYLbXWn^aVaFU*wi4# zw@!Q_<4{883{Vk=AsywrYT7FB7{I#ga)m}weG$eqm^R@rbT-$QqbdB+ ze9@nrKp{5jOb}y}CPWQW8C%J1?#)uL0=!pg8EZUh_LSz(*CZWo_S2pH(7CeqddlT` ziH3>J38!Ca%up#t}OsqQG^&AUtj%RP@m-vDFFESwgOPM5djMF&6{G@NyQH;uvnuo?U8JC0quwsOC{YmO(X{w5yj~`H(D_L7s(Vi!IN}#)S ziIByh6|gR@=sd}}BKK-B)m9DyVQ^+a>^XE&o!${0M!N;~ z2W&VxR!XgXv{-o>UXVC6?DO%R%pfe{2#RC& zJ?8(j%)*?J+Vj{FoHB`EL>`HYl0d7i4W7w<8_Ip=ULnb^p*6OZU?N;Ld|~rW%z7^3 zGlu21W2|1B#Q^sDKgFd#)QOr851<(uRzN(w(%Q!@kR|kk&GrJ)3DZR3J~-V{iXz0n zL`Km;aCh>?E(5oH)oat+nrtG(L*-(uczHu{EULP@_c? zLFIb(R(6#Wacz#-hY?TEJXoztKd)tammy<{hxO?l#8C6v=EtO|&wSjy#2+TxG}NC> zDa$8$@^9{wohCGlUoSQd-?9vu^Sgw8P6Q6f2qKCeMUTRnB639Q^r-D@RubWj&RmRb z!M%kxBd`vShMZ1+^Jn(7&M{bayZ60FF6>J3ng=EkAwq|AX2SyU;?4u!3eL0a1a;8bnkCL`)c&gEN_6W zC(~O(_Pa$2i5_d4KG0Q6iyDG|jlJRz(M5%RxOFcoCDP*&z0}gLfaJ#~kUcGrKe0I`#+D^fqQgG4I|z5*7_xyJq6YD*!;yNU+1+ z9h2(70)pv*u9L_MV_Xf5khViIiRnvZ0$Yzi$o4#%K{kLRj~JPv`{`yH#mnZsq%i4u z&1y*2*O~y>-%i&2hHs3q=hu)n8eGEI$cPGKk9Ogex4s5WM^fw_({&Liwvz;UoE!DM zRcg?9ib1A6rfRY}Uk4C1<0Ji=z*{W1Q|((%?QMJb zz2(=;KY}->XPFY^qc%Z1RVT(M0&Y+5%dlJ?ZF)eks-wG1dbZD5MEy!(W+MptK3dLI z8c6dL*$)$Eg&|K{>O@3>)P)XQGr2TQtjhz@=aWl0`7s}KuV`Dslc=NbHE)ySF;+gM zhZ?{mh{yA$@UTqwo}0UfR*;^fY|(^zV-r9vr;Z?X(aV(|Axc1Kegino2{D@>KAC_j zV4Z)cQQ<6;r%pZZGP7NvenQS=gJ2w4UNByd{$qvw{U87PDG!HEp#*Y_lN3SloxNcf zUvox`2(dK%MoV<+n&|cMQ~!≺+!vyvDjU+WYzJXrW{}m5$$A4#$VQgtzYlhn5a~ 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�JpiW~Yix7sbG=TwiNI^_2_)pBsZ*2e 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 diff --git a/benchmark_tests/plots/dane/multiproc-on-socket.pdf b/benchmark_tests/plots/dane/multiproc-on-socket.pdf deleted file mode 100644 index bd01ed413da5822c11e03b1d2d3eb7f9761bcb1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99760 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