diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b006ce..48181fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ option(WITH_OMPI "Enable OpenMPI backend" OFF) option(WITH_MPICH "Enable MPICH backend" OFF) option(WITH_NCCL "Enable NCCL backend" OFF) option(WITH_MCCL "Enable MCCL backend" OFF) +option(WITH_CNCL "Enable CNCL backend" OFF) # ========================================================= # --- MISC. BUILD OPTIONS --- @@ -327,10 +328,23 @@ if(AUTO_DETECT_BACKENDS) else() message(STATUS "No suitable device environment, skipping MCCL detection.") endif() + + # Detect CNCL Dependencies + if(WITH_CAMBRICON) + find_path(AUTO_CNCL_INC NAMES cncl.h HINTS "$ENV{NEUWARE_HOME}" /usr/local/neuware PATH_SUFFIXES include QUIET) + find_library(AUTO_CNCL_LIB NAMES cncl HINTS "$ENV{NEUWARE_HOME}" /usr/local/neuware PATH_SUFFIXES lib lib64 QUIET) + + if(AUTO_CNCL_INC AND AUTO_CNCL_LIB) + set(WITH_CNCL ON) + message(STATUS "Auto-detected CNCL backend.") + else() + message(STATUS "CNCL library/headers not found in Cambricon paths.") + endif() + endif() endif() # Fallback: If no backends are enabled or auto-detected, fall back to OpenMPI as the default bootstrap profile. -if(NOT WITH_OMPI AND NOT WITH_MPICH AND NOT WITH_NCCL AND NOT WITH_MCCL) +if(NOT WITH_OMPI AND NOT WITH_MPICH AND NOT WITH_NCCL AND NOT WITH_MCCL AND NOT WITH_CNCL) set(WITH_OMPI ON) message(STATUS "No backend specified or detected. Defaulting to `WITH_OMPI=ON`") endif() @@ -535,6 +549,17 @@ if(WITH_MCCL) include_directories(${MCCL_INC}) endif() +if(WITH_CNCL) + if(NOT WITH_CAMBRICON) + message(FATAL_ERROR "CNCL backend requires Cambricon device support. Please enable `WITH_CAMBRICON`.") + endif() + + find_library(CNCL_LIB NAMES cncl HINTS "${NEUWARE_HOME}" "$ENV{NEUWARE_HOME}" /usr/local/neuware PATH_SUFFIXES lib lib64 REQUIRED) + find_path(CNCL_INC NAMES cncl.h HINTS "${NEUWARE_HOME}" "$ENV{NEUWARE_HOME}" /usr/local/neuware PATH_SUFFIXES include REQUIRED) + + include_directories(${CNCL_INC}) +endif() + # Python is required for code generation. find_package(Python3 REQUIRED) diff --git a/include/comm.h b/include/comm.h index ec16c92..e36f2ac 100644 --- a/include/comm.h +++ b/include/comm.h @@ -10,7 +10,7 @@ extern "C" { #endif -#define INFINICCL_UNIQUE_ID_BYTES 128 +#define INFINICCL_UNIQUE_ID_BYTES 136 typedef void *infinicclComm_t; diff --git a/scripts/gen_bridge.py b/scripts/gen_bridge.py index 211080b..59d4dd2 100644 --- a/scripts/gen_bridge.py +++ b/scripts/gen_bridge.py @@ -32,16 +32,19 @@ "mpich": ["backends/mpi/ompi/impl"], "nccl": ["backends/ccl/nccl/impl"], "mccl": ["backends/ccl/mccl/impl"], + "cncl": ["backends/ccl/cncl/impl"], } BACKEND_COMMON_HEADERS = { "nccl": ["backends/ccl/nccl/type_map.h"], "mccl": ["backends/ccl/mccl/type_map.h"], + "cncl": ["backends/ccl/cncl/type_map.h"], } CCL_PROVIDER_BACKENDS = { "nccl": "backends/ccl/nccl", "mccl": "backends/ccl/mccl", + "cncl": "backends/ccl/cncl", } # ================================================================= diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 84ffbcf..c191b68 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -283,6 +283,16 @@ if(WITH_MCCL) target_link_libraries(infiniccl PRIVATE ${MCCL_LIB}) endif() +# CNCL +if(WITH_CNCL) + list(APPEND BACKEND_LIST "cncl") + file(GLOB_RECURSE CNCL_SRCS "backends/ccl/cncl/*.cc" "backends/ccl/cncl/*.cpp") + + target_sources(infiniccl PRIVATE ${CNCL_SRCS}) + target_include_directories(infiniccl PRIVATE ${CNCL_INC}) + target_link_libraries(infiniccl PRIVATE ${CNCL_LIB}) +endif() + # ========================================================= # --- File Generation --- # ========================================================= diff --git a/src/backend.h b/src/backend.h index 0b02662..7ebad6d 100644 --- a/src/backend.h +++ b/src/backend.h @@ -63,6 +63,11 @@ struct BackendPriority { static constexpr int value = 10; }; +template <> +struct BackendPriority { + static constexpr int value = 10; +}; + } // namespace infini::ccl #endif // INFINI_CCL_BACKEND_H_ diff --git a/src/backend_device_map.h b/src/backend_device_map.h index 7cf46b0..8c65d82 100644 --- a/src/backend_device_map.h +++ b/src/backend_device_map.h @@ -33,6 +33,10 @@ template <> struct IsSupportedCombination : std::true_type {}; +template <> +struct IsSupportedCombination + : std::true_type {}; + }; // namespace infini::ccl #endif // INFINI_CCL_BACKEND_DEVICE_MAP_H_ diff --git a/src/backends/ccl/cncl/api.h b/src/backends/ccl/cncl/api.h new file mode 100644 index 0000000..b2b8ca2 --- /dev/null +++ b/src/backends/ccl/cncl/api.h @@ -0,0 +1,67 @@ +#ifndef INFINI_CCL_BACKENDS_CCL_CNCL_API_H_ +#define INFINI_CCL_BACKENDS_CCL_CNCL_API_H_ + +#include + +#include + +#include "backends/ccl/common/api.h" +#include "logging.h" +#include "return_status_impl.h" +#include "runtime.h" + +namespace infini::ccl { + +template +struct CnclApi { + static constexpr BackendType kBackendType = BackendType::kCncl; + static constexpr Device::Type kDeviceType = device; + + using Comm = cnclComm_t; + using UniqueId = cnclCliqueId; + using Result = cnclResult_t; + using DataType = cnclDataType_t; + using RedOp = cnclReduceOp_t; + using Stream = typename Runtime::Stream; + + static ReturnStatus Check(Result result) { + if (result != CNCL_RET_SUCCESS) { + LOG(cnclGetErrorStr(result)); + return ReturnStatus::kSystemError; + } + return ReturnStatus::kSuccess; + } + + static Result GetUniqueId(UniqueId* id) { return cnclGetCliqueId(id); } + + static Result CommInitRank(Comm* comm, int nranks, UniqueId id, int rank) { + using Rt = Runtime; + + int device_id = 0; + if (Rt::GetDevice(&device_id) != cnrtSuccess) { + return CNCL_RET_ERR_MLU_RUNTIME; + } + + return cnclInitComms(comm, 1, &device_id, &rank, nranks, &id); + } + + static Result CommDestroy(Comm comm) { return cnclFreeComm(comm); } + + static Result AllReduce(const void* send_buff, void* recv_buff, size_t count, + DataType data_type, RedOp op, Comm comm, + Stream stream) { + return cnclAllReduce(send_buff, recv_buff, count, data_type, op, comm, + stream); + } + + static Result AllGather(const void* send_buff, void* recv_buff, + size_t send_count, DataType data_type, Comm comm, + Stream stream) { + return cnclAllGather(send_buff, recv_buff, send_count, data_type, comm, + stream); + } +}; + +} // namespace infini::ccl + +#endif // INFINI_CCL_BACKENDS_CCL_CNCL_API_H_ diff --git a/src/backends/ccl/cncl/cambricon/api.h b/src/backends/ccl/cncl/cambricon/api.h new file mode 100644 index 0000000..865fc0e --- /dev/null +++ b/src/backends/ccl/cncl/cambricon/api.h @@ -0,0 +1,15 @@ +#ifndef INFINI_CCL_BACKENDS_CCL_CNCL_CAMBRICON_API_H_ +#define INFINI_CCL_BACKENDS_CCL_CNCL_CAMBRICON_API_H_ + +#include "backends/ccl/cncl/api.h" +#include "devices/cambricon/runtime_.h" + +namespace infini::ccl { + +template <> +struct CclApi + : CnclApi {}; + +} // namespace infini::ccl + +#endif // INFINI_CCL_BACKENDS_CCL_CNCL_CAMBRICON_API_H_ diff --git a/src/backends/ccl/cncl/impl/all_gather.h b/src/backends/ccl/cncl/impl/all_gather.h new file mode 100644 index 0000000..6ab1dad --- /dev/null +++ b/src/backends/ccl/cncl/impl/all_gather.h @@ -0,0 +1,17 @@ +#ifndef INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_ALL_GATHER_H_ +#define INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_ALL_GATHER_H_ + +#include "backends/ccl/common/impl/all_gather.h" + +namespace infini::ccl { + +template +class AllGatherImpl + : public CclAllGatherImpl {}; + +template <> +struct BackendEnabled : std::true_type {}; + +} // namespace infini::ccl + +#endif // INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_ALL_GATHER_H_ diff --git a/src/backends/ccl/cncl/impl/all_reduce.h b/src/backends/ccl/cncl/impl/all_reduce.h new file mode 100644 index 0000000..490f609 --- /dev/null +++ b/src/backends/ccl/cncl/impl/all_reduce.h @@ -0,0 +1,17 @@ +#ifndef INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_ALL_REDUCE_H_ +#define INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_ALL_REDUCE_H_ + +#include "backends/ccl/common/impl/all_reduce.h" + +namespace infini::ccl { + +template +class AllReduceImpl + : public CclAllReduceImpl {}; + +template <> +struct BackendEnabled : std::true_type {}; + +} // namespace infini::ccl + +#endif // INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_ALL_REDUCE_H_ diff --git a/src/backends/ccl/cncl/impl/comm_destroy.h b/src/backends/ccl/cncl/impl/comm_destroy.h new file mode 100644 index 0000000..efc8899 --- /dev/null +++ b/src/backends/ccl/cncl/impl/comm_destroy.h @@ -0,0 +1,17 @@ +#ifndef INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_COMM_DESTROY_H_ +#define INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_COMM_DESTROY_H_ + +#include "backends/ccl/common/impl/comm_destroy.h" + +namespace infini::ccl { + +template +class CommDestroyImpl + : public CclCommDestroyImpl {}; + +template <> +struct BackendEnabled : std::true_type {}; + +} // namespace infini::ccl + +#endif // INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_COMM_DESTROY_H_ diff --git a/src/backends/ccl/cncl/impl/comm_init_rank.h b/src/backends/ccl/cncl/impl/comm_init_rank.h new file mode 100644 index 0000000..8ca3f59 --- /dev/null +++ b/src/backends/ccl/cncl/impl/comm_init_rank.h @@ -0,0 +1,17 @@ +#ifndef INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_COMM_INIT_RANK_H_ +#define INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_COMM_INIT_RANK_H_ + +#include "backends/ccl/common/impl/comm_init_rank.h" + +namespace infini::ccl { + +template +class CommInitRankImpl + : public CclCommInitRankImpl {}; + +template <> +struct BackendEnabled : std::true_type {}; + +} // namespace infini::ccl + +#endif // INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_COMM_INIT_RANK_H_ diff --git a/src/backends/ccl/cncl/impl/get_unique_id.h b/src/backends/ccl/cncl/impl/get_unique_id.h new file mode 100644 index 0000000..fac71f2 --- /dev/null +++ b/src/backends/ccl/cncl/impl/get_unique_id.h @@ -0,0 +1,17 @@ +#ifndef INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_GET_UNIQUE_ID_H_ +#define INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_GET_UNIQUE_ID_H_ + +#include "backends/ccl/common/impl/get_unique_id.h" + +namespace infini::ccl { + +template +class GetUniqueIdImpl + : public CclGetUniqueIdImpl {}; + +template <> +struct BackendEnabled : std::true_type {}; + +} // namespace infini::ccl + +#endif // INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_GET_UNIQUE_ID_H_ diff --git a/src/backends/ccl/cncl/type_map.h b/src/backends/ccl/cncl/type_map.h new file mode 100644 index 0000000..04f6eee --- /dev/null +++ b/src/backends/ccl/cncl/type_map.h @@ -0,0 +1,73 @@ +#ifndef INFINI_CCL_BACKENDS_CCL_CNCL_TYPE_MAP_H_ +#define INFINI_CCL_BACKENDS_CCL_CNCL_TYPE_MAP_H_ + +#include + +#include "backends/ccl/common/api.h" +#include "comm_impl.h" +#include "data_type_impl.h" + +namespace infini::ccl { + +inline bool DataTypeToCnclType(DataType dtype, cnclDataType_t* cncl_dtype) { + switch (dtype) { + case DataType::kInt8: + *cncl_dtype = cnclInt8; + return true; + case DataType::kInt16: + *cncl_dtype = cnclInt16; + return true; + case DataType::kInt32: + *cncl_dtype = cnclInt32; + return true; + case DataType::kInt64: + *cncl_dtype = cnclInt64; + return true; + case DataType::kUInt8: + *cncl_dtype = cnclUint8; + return true; + case DataType::kUInt16: + *cncl_dtype = cnclUint16; + return true; + case DataType::kUInt32: + *cncl_dtype = cnclUint32; + return true; + case DataType::kUInt64: + *cncl_dtype = cnclUint64; + return true; + case DataType::kFloat16: + *cncl_dtype = cnclFloat16; + return true; + case DataType::kBFloat16: + *cncl_dtype = cnclBfloat16; + return true; + case DataType::kFloat32: + *cncl_dtype = cnclFloat32; + return true; + default: + return false; + } +} + +template <> +struct CclTypeMap { + using Api = CclApi; + + static bool ToBackendDataType(DataType dtype, + typename Api::DataType* backend_dtype) { + return DataTypeToCnclType(dtype, backend_dtype); + } + + static bool ToBackendRedOp(ReductionOpType red_op, + typename Api::RedOp* backend_op) { + if (red_op == ReductionOpType::kAvg) { + return false; + } + *backend_op = static_cast(red_op); + return true; + } +}; + +} // namespace infini::ccl + +#endif // INFINI_CCL_BACKENDS_CCL_CNCL_TYPE_MAP_H_ diff --git a/src/backends/ccl/common/impl/comm_init_rank.h b/src/backends/ccl/common/impl/comm_init_rank.h index 2b7adee..aced7b5 100644 --- a/src/backends/ccl/common/impl/comm_init_rank.h +++ b/src/backends/ccl/common/impl/comm_init_rank.h @@ -1,6 +1,7 @@ #ifndef INFINI_CCL_BACKENDS_CCL_COMMON_IMPL_COMM_INIT_RANK_H_ #define INFINI_CCL_BACKENDS_CCL_COMMON_IMPL_COMM_INIT_RANK_H_ +#include #include #include "backends/ccl/common/api.h" @@ -19,12 +20,18 @@ class CclCommInitRankImpl { using Api = CclApi; using CommInstance = CclCommInstance; - const auto *backend_id = - reinterpret_cast(id.internal); + if (comm && comm->intra_comm()) { + // TODO(lzm): change to use `glog`. + LOG("Invalid communicator handle for `CommInitRank`."); + return ReturnStatus::kInvalidArgument; + } + + typename Api::UniqueId backend_id{}; + std::memcpy(&backend_id, id.internal, sizeof(backend_id)); typename Api::Comm ccl_handle{}; auto status = - Api::Check(Api::CommInitRank(&ccl_handle, nranks, *backend_id, rank)); + Api::Check(Api::CommInitRank(&ccl_handle, nranks, backend_id, rank)); if (status != ReturnStatus::kSuccess) { return status; } diff --git a/src/backends/mpi/ompi/impl/comm_init_all.h b/src/backends/mpi/ompi/impl/comm_init_all.h index e834622..3217655 100644 --- a/src/backends/mpi/ompi/impl/comm_init_all.h +++ b/src/backends/mpi/ompi/impl/comm_init_all.h @@ -18,11 +18,10 @@ class CommInitAllImpl { ListGetBest(ActiveDevices{}); using Rt = Runtime; - if (!comm) { + if (comm && comm->inter_comm()) { // TODO(lzm): change to use `glog`. - LOG("Failed to initialize OpenMPI communicator: invalid " - "communicator pointer."); - return ReturnStatus::kInternalError; + LOG("Invalid communicator handle for `CommInitAll`."); + return ReturnStatus::kInvalidArgument; } int rank, size; diff --git a/src/base/comm_init_all.h b/src/base/comm_init_all.h index e737306..c37d781 100644 --- a/src/base/comm_init_all.h +++ b/src/base/comm_init_all.h @@ -13,12 +13,10 @@ struct CommInitAllImpl; class CommInitAll : public Operation { public: - template - static ReturnStatus Execute(void **comm_handle, Args &&...args) { - Communicator *&comm = *reinterpret_cast(comm_handle); - if (comm && comm->inter_comm()) { - // TODO(lzm): change to use `glog`. + template + static ReturnStatus Execute(void** comm_handles, int n_dev, + const int* dev_list) { + if (!comm_handles || n_dev <= 0) { LOG("Invalid communicator handle for `CommInitAll`."); return ReturnStatus::kInvalidArgument; } @@ -26,6 +24,8 @@ class CommInitAll : public Operation { constexpr Device::Type kDev = ListGetBest(ActiveDevices{}); + Communicator *&comm = *reinterpret_cast(comm_handle); + if (!comm) { comm = new Communicator(kDev, 0); } diff --git a/src/base/comm_init_rank.h b/src/base/comm_init_rank.h index 46f93f2..e4391d4 100644 --- a/src/base/comm_init_rank.h +++ b/src/base/comm_init_rank.h @@ -14,11 +14,9 @@ class CommInitRank : public Operation { public: template - static ReturnStatus Execute(void **comm_handle, Args &&...args) { - Communicator *&comm = *reinterpret_cast(comm_handle); - if (comm && comm->intra_comm()) { - // TODO(lzm): change to use `glog`. - LOG("Invalid communicator handle for `CommInitRank`."); + static ReturnStatus Execute(void **comm_handle, Args &&...args) { + if (!comm_handle) { + LOG("Invalid communicator handle for `CommInitAll`."); return ReturnStatus::kInvalidArgument; } @@ -29,6 +27,8 @@ class CommInitRank : public Operation { int current_dev = 0; CHECK_STATUS(Rt, Rt::GetDevice(¤t_dev)); + Communicator *&comm = *reinterpret_cast(comm_handle); + if (!comm) { comm = new Communicator(kDev, current_dev); } else {