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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Please check all the platforms and/or backends this PR affects (i.e., code is to
- [ ] MPICH
- [ ] NCCL/RCCL
- [ ] MCCL
- [ ] CNCL

## Performance Impact

Expand Down Expand Up @@ -116,6 +117,7 @@ See `CONTRIBUTING.md` § Pull Requests for the official testing requirements and
- [ ] MPICH
- [ ] NCCL/RCCL
- [ ] MCCL
- [ ] CNCL

---

Expand Down
27 changes: 26 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 ---
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ cmake .. -DWITH_NVIDIA=ON -DWITH_OMPI=ON
| `WITH_MPICH` | Enable MPICH backend | `OFF` |
| `WITH_NCCL` | Enable NCCL/RCCL backend | `OFF` |
| `WITH_MCCL` | Enable MCCL backend | `OFF` |
| `WITH_CNCL` | Enable CNCL backend | `OFF` |
| **Miscellaneous** |||
| `AUTO_DETECT_DEVICES` | Automatically detect available devices and enable corresponding support | `ON` |
| `AUTO_DETECT_BACKENDS` | Automatically detect available communication backends and enable corresponding support | `OFF` |
Expand Down Expand Up @@ -357,6 +358,7 @@ export LD_LIBRARY_PATH=${INFINI_INSTALL}/lib:$LD_LIBRARY_PATH
| **MPICH** | Full | `WITH_MPICH=ON` | Requires the MPICH development package.|
| **NCCL** | Partial | `WITH_NCCL=ON` | Requires NVIDIA or Iluvatar NCCL, or HYGON RCCL. Currently available when `WITH_NVIDIA=ON`, `WITH_ILUVATAR=ON`, or `WITH_HYGON=ON`.|
| **MCCL** | Partial | `WITH_MCCL=ON` | Requires MetaX or Moore MCCL. Currently available when `WITH_METAX=ON` or `WITH_MOORE=ON`.|
| **CNCL** | Partial | `WITH_CNCL=ON` | Requires Cambricon CNCL. Available only when `WITH_CAMBRICON=ON`.|

</details>

Expand Down
2 changes: 1 addition & 1 deletion include/comm.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
extern "C" {
#endif

#define INFINICCL_UNIQUE_ID_BYTES 128
#define INFINICCL_UNIQUE_ID_BYTES 136

typedef void *infinicclComm_t;

Expand Down
3 changes: 3 additions & 0 deletions scripts/gen_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}

# =================================================================
Expand Down
10 changes: 10 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 ---
# =========================================================
Expand Down
5 changes: 5 additions & 0 deletions src/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ struct BackendPriority<BackendType::kMccl> {
static constexpr int value = 10;
};

template <>
struct BackendPriority<BackendType::kCncl> {
static constexpr int value = 10;
};

} // namespace infini::ccl

#endif // INFINI_CCL_BACKEND_H_
4 changes: 4 additions & 0 deletions src/backend_device_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ template <>
struct IsSupportedCombination<BackendType::kMccl, Device::Type::kMoore>
: std::true_type {};

template <>
struct IsSupportedCombination<BackendType::kCncl, Device::Type::kCambricon>
: std::true_type {};

}; // namespace infini::ccl

#endif // INFINI_CCL_BACKEND_DEVICE_MAP_H_
139 changes: 139 additions & 0 deletions src/backends/ccl/cncl/api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#ifndef INFINI_CCL_BACKENDS_CCL_CNCL_API_H_
#define INFINI_CCL_BACKENDS_CCL_CNCL_API_H_

#include <cncl.h>

#include <cstddef>

#include "backends/ccl/common/api.h"
#include "devices/cambricon/checks.h"
#include "logging.h"
#include "return_status_impl.h"
#include "runtime.h"

namespace infini::ccl {

template <Device::Type device>
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<device>::Stream;

private:
// CNCL does not support a `nullptr` queue argument, so we need to manage a
// default queue for synchronous operations.
struct DefaultQueue {
Stream queue = nullptr;
int device_id = -1;

~DefaultQueue() {
if (queue != nullptr) {
INFINI_CHECK_CNRT(cnrtQueueDestroy(queue));
}
}

void SetDevice(int new_device_id) {
if (queue != nullptr) {
INFINI_CHECK_CNRT(cnrtQueueDestroy(queue));
}
INFINI_CHECK_CNRT(cnrtQueueCreate(&queue));
device_id = new_device_id;
}
};

static Stream GetDefaultQueue() {
thread_local DefaultQueue default_queue;

int device_id = 0;
INFINI_CHECK_CNRT(cnrtGetDevice(&device_id));
if (default_queue.device_id != device_id) {
default_queue.SetDevice(device_id);
}
return default_queue.queue;
}

using PointToPointOp = Result (*)(void*, size_t, DataType, int, Comm, Stream);

static Result PointToPoint(PointToPointOp operation, void* buffer,
size_t count, DataType data_type, int peer,
Comm comm, Stream stream) {
// Up to CNCL 1.30.8, a `nullptr` queue argument is unsupported.
// Synchronize the fallback queue to preserve the synchronous behavior of
// the `nullptr` path; explicit queues stay async. Future CNCL versions may
// remove this compatibility path.
const bool is_default_queue = stream == nullptr;
if (is_default_queue) {
stream = GetDefaultQueue();
}

Result result = operation(buffer, count, data_type, peer, comm, stream);
if (is_default_queue) {
INFINI_CHECK_CNRT(cnrtQueueSync(stream));
}
return result;
}

public:
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 InitComms(Comm* comms, int num_comm, const int* dev_list,
const int* rank_list, int nrank,
UniqueId* clique_id) {
return cnclInitComms(comms, num_comm, dev_list, rank_list, nrank,
clique_id);
}

static Result CommInitRank(Comm* comm, int nranks, UniqueId id, int rank) {
using Rt = Runtime<device>;

int device_id = 0;
INFINI_CHECK_CNRT(Rt::GetDevice(&device_id));
return InitComms(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);
}

static Result Send(const void* send_buff, size_t count, DataType data_type,
int peer, Comm comm, Stream stream) {
return PointToPoint(cnclSend, const_cast<void*>(send_buff), count,
data_type, peer, comm, stream);
}

static Result Recv(void* recv_buff, size_t count, DataType data_type,
int peer, Comm comm, Stream stream) {
return PointToPoint(cnclRecv, recv_buff, count, data_type, peer, comm,
stream);
}
};

} // namespace infini::ccl

#endif // INFINI_CCL_BACKENDS_CCL_CNCL_API_H_
15 changes: 15 additions & 0 deletions src/backends/ccl/cncl/cambricon/api.h
Original file line number Diff line number Diff line change
@@ -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<BackendType::kCncl, Device::Type::kCambricon>
: CnclApi<Device::Type::kCambricon> {};

} // namespace infini::ccl

#endif // INFINI_CCL_BACKENDS_CCL_CNCL_CAMBRICON_API_H_
31 changes: 31 additions & 0 deletions src/backends/ccl/cncl/checks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef INFINI_CCL_BACKENDS_CCL_CNCL_CHECKS_H_
#define INFINI_CCL_BACKENDS_CCL_CNCL_CHECKS_H_

#include <cncl.h>

#include <iostream>

#include "return_status_impl.h"

#define INFINI_CHECK_CNCL(result) \
::infini::ccl::detail::CheckCnclImpl((result), __FILE__, __LINE__)

namespace infini::ccl {

namespace detail {

inline ReturnStatus CheckCnclImpl(cnclResult_t cncl_result, const char *file,
int line) {
if (cncl_result != CNCL_RET_SUCCESS) {
std::cerr << "backend(cncl) CNCL error code: " << cncl_result << " at line "
<< line << " in " << file << std::endl;
std::abort();
}
return ReturnStatus::kSuccess;
}

} // namespace detail

} // namespace infini::ccl

#endif // INFINI_CCL_BACKENDS_CCL_CNCL_CHECKS_H_
17 changes: 17 additions & 0 deletions src/backends/ccl/cncl/impl/all_gather.h
Original file line number Diff line number Diff line change
@@ -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 <Device::Type device>
class AllGatherImpl<BackendType::kCncl, device>
: public CclAllGatherImpl<BackendType::kCncl, device> {};

template <>
struct BackendEnabled<AllGather, BackendType::kCncl> : std::true_type {};

} // namespace infini::ccl

#endif // INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_ALL_GATHER_H_
17 changes: 17 additions & 0 deletions src/backends/ccl/cncl/impl/all_reduce.h
Original file line number Diff line number Diff line change
@@ -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 <Device::Type device>
class AllReduceImpl<BackendType::kCncl, device>
: public CclAllReduceImpl<BackendType::kCncl, device> {};

template <>
struct BackendEnabled<AllReduce, BackendType::kCncl> : std::true_type {};

} // namespace infini::ccl

#endif // INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_ALL_REDUCE_H_
17 changes: 17 additions & 0 deletions src/backends/ccl/cncl/impl/comm_destroy.h
Original file line number Diff line number Diff line change
@@ -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 <Device::Type device>
class CommDestroyImpl<BackendType::kCncl, device>
: public CclCommDestroyImpl<BackendType::kCncl, device> {};

template <>
struct BackendEnabled<CommDestroy, BackendType::kCncl> : std::true_type {};

} // namespace infini::ccl

#endif // INFINI_CCL_BACKENDS_CCL_CNCL_IMPL_COMM_DESTROY_H_
Loading
Loading