通信抽象库用法#

通信抽象库 (CAL) 是 cuSOLVERMp 库的辅助模块,使其能够高效地执行不同 GPU 之间的通信。cuSOLVERMp 网格创建 API 接受 cal_comm_t 通信器对象,并要求在任何 cuSOLVERMp 调用之前创建它。目前,CAL 仅支持每个参与进程使用单个 GPU,并且每个参与 GPU 只能由单个进程使用的用例。


通信抽象库#

通信描述#

为了初始化通信器句柄 cal_comm_t,您需要遵循引导过程 - 请参阅相应的 cal_comm_create() 函数或示例 如何使用 MPI 创建通信器句柄

cuSOLVERMp 使用的主要通信后端是模块化 OpenUCC 库。OpenUCC 传输模块(例如 OpenUCX、NCCL 等)将在运行时使用,它们的配置可以通过各种方式进行控制(即,影响 NCCL 的环境变量:https://docs.nvda.net.cn/deeplearning/nccl/user-guide/docs/env.html,影响 OpenUCX 的环境变量:https://openucx.readthedocs.io/en/master/faq.html)- 如果平台提供商提供这些依赖项的已知优化设置,请咨询他们。否则,cuSOLVERMp 和 UCC 将使用默认值。

根据底层通信的性质,在使用 cuSOLVERMp 时需要记住一些限制

  • 在进程的任何时间点,只能有一个 cuSOLVERMp 例程在运行。但是,可以创建并保留多个通信/cuSOLVERMp 句柄,但用户有责任确保 cuSOLVERMp 例程的执行不重叠。

  • 如果您正在使用 NCCL 通信库,则 NCCL 集体调用不应与 cuSOLVERMp 调用重叠,以避免可能的死锁。

配置通信子模块#

有一些环境变量可以更改通信模块的行为。

变量

描述

CAL_LOG_LEVEL

通信模块的详细程度,0 表示没有输出,6 表示最大细节量。默认 - 0

UCC_CONFIG_FILE

UCC 库的自定义配置文件。它可以控制底层传输和集体参数。有关配置语法的详细信息,请参阅 UCC 文档。默认值 - 内置 cuSOLVERMp UCC 配置。

默认情况下,cuSOLVERMp 将使用发行包中提供的 UCC 配置文件:share/ucc.conf,库将使用相对于 cusolverMp 共享对象位置的路径加载它。


使用 MPI 创建通信器句柄#

如果您的应用程序使用消息传递接口 (MPI) 进行分布式通信,则此处介绍如何使用它来引导通信抽象库通信器

#include "cal.h"
#include "cusolverMp.h"
#include "mpi.h"

calError_t allgather(void* src_buf, void* recv_buf, size_t size, void* data, void** request)
{
    MPI_Request req;
    int err = MPI_Iallgather(src_buf, size, MPI_BYTE, recv_buf, size, MPI_BYTE, (MPI_Comm)data, &req);
    if (err != MPI_SUCCESS)
    {
        return CAL_ERROR;
    }
    *request = (void*)req;
    return CAL_OK;
}

calError_t request_test(void* request)
{
    MPI_Request req = (MPI_Request)request;
    int         completed;
    int         err = MPI_Test(&req, &completed, MPI_STATUS_IGNORE);
    if (err != MPI_SUCCESS)
    {
        return CAL_ERROR;
    }
    return completed ? CAL_OK : CAL_ERROR_INPROGRESS;
}

calError_t request_free(void* request)
{
    return CAL_OK;
}

calError_t cal_comm_create_mpi(MPI_Comm mpi_comm, int rank, int nranks, int local_device, cal_comm_t* comm)
{
    cal_comm_create_params_t params;
    params.allgather = allgather;
    params.req_test = request_test;
    params.req_free = request_free;
    params.data = (void*)mpi_comm;
    params.rank = rank;
    params.nranks = nranks;
    params.local_device = local_device;
    return cal_comm_create(params, comm);
}

void main()
{
    // Initialize MPI, create some distribute data
    MPI_Init(...);
    int rank, size;
    MPI_Comm mpi_comm = MPI_COMM_WORLD;
    MPI_Comm_rank(mpi_comm, &rank);
    MPI_Comm_size(mpi_comm, &size);

    cal_comm_t cal_comm;

    // creating communicator handle with MPI communicator
    cal_comm_create_mpi(mpi_comm, rank, size, &cal_comm);

    // using cuSOLVERMp with cal_comm handle
    cusolverMpCreateDeviceGrid(..., cal_comm, ...);

    // destroying communicator handle
    cal_comm_destroy(cal_comm);

    MPI_Finalize();
}

为方便起见,这些带有 MPI 的辅助函数也以源代码形式在发行包的 src 文件夹中提供。

通信抽象库数据类型#

calError_t#

来自通信抽象库 API 的返回值。这些值在下表中描述

描述

CAL_OK

成功。

CAL_ERROR

通用错误。

CAL_ERROR_INVALID_PARAMETER

接口函数的无效参数。

CAL_ERROR_INTERNAL

无效错误。

CAL_ERROR_CUDA

CUDA 运行时或驱动程序 API 中的错误。

CAL_ERROR_IPC

系统 IPC 通信调用中的错误。

CAL_ERROR_UCC

UCC 调用中的错误。

CAL_ERROR_NOT_SUPPORTED

不支持请求的配置或参数。

CAL_ERROR_BACKEND

通用后端依赖项中的错误,以详细日志级别运行以查看详细错误消息

CAL_ERROR_INPROGRESS

操作仍在进行中


cal_comm_t#

cal_comm_t 存储设备端点和与通信相关的资源。它必须分别使用 cal_comm_create()cal_comm_destroy() 函数创建和销毁。

cal_comm_create_params_t#

typedef struct cal_comm_create_params
{
    calError_t (*allgather)(void* src_buf, void* recv_buf, size_t size, void* data, void** request);
    calError_t (*req_test)(void* request);
    calError_t (*req_free)(void* request);
    void* data;
    int   nranks;
    int   rank;
    int   local_device;
} cal_comm_create_params_t;
cal_comm_create_params_t 结构是通信模块创建函数的参数。此结构必须由用户在调用 cal_comm_create() 之前填写。此结构的字段描述

字段

描述

allgather

指向在主机内存上实现 allgather 功能的函数的指针。此函数可以相对于主机异步 - 在这种情况下,函数应创建可以通过相应的 req_testreq_free 函数寻址的处理程序。指向此处理程序的指针应写入 request 参数

req_test

如果 allgather 函数是异步的,则此函数将用于查询是否已交换数据,并且可以由通信器使用。如果交换完成,则应返回 0,否则返回 CAL_ERROR_INPROGRESS

req_free

如果 allgather 函数是异步的,则在 allgather 函数完成数据交换后,将使用此函数来释放 request 句柄使用的资源

data

指向将在调用时提供给 allgather 函数的附加数据的指针

nranks

将要创建的通信器中参与的 ranks 数量

rank

将分配给新通信器中调用进程的 Rank。应为 0nranks 之间的数字

local_device

cusolverMp 使用此通信器将使用的本地设备。请注意,用户应在 CAL 或 cusolverMp 调用中使用此设备之前创建设备上下文。


通信抽象库 API#

cal_comm_create#

calError_t cal_comm_create(
    cal_comm_create_params_t params,
    cal_comm_t* new_comm)
使用此函数创建的处理程序是使用 cuSOLVERMp API 所必需的。请注意,用户应在 CAL 或 cuSOLVERMp 调用中使用之前,为创建参数中指定的设备创建设备上下文。确保创建设备上下文的最简单方法是调用 cudaSetDevice(device_id); cudaFree(0)。有关如何填写此结构的说明,请参阅 cal_comm_create_params_t 文档。您可以在 MPI 示例cuSOLVERMp 示例 中查看如何创建 CAL 通信器。

参数

描述

params

一个结构体,其中包含初始化通信器所需的参数。请参阅 cal_comm_create_params_t

new_comm

用于存储新通信器句柄的指针。

有关返回值的描述,请参阅 calError_t


cal_comm_destroy#

calError_t cal_comm_destroy(
    cal_comm_t comm)
释放与提供的通信器句柄关联的资源

参数

描述

comm

要释放的通信器句柄。

有关返回值的描述,请参阅 calError_t


cal_stream_sync#

calError_t cal_stream_sync(
    cal_comm_t comm,
    cudaStream_t stream)
阻止调用线程,直到 stream 中所有未完成的设备操作都完成。使用此函数代替 cudaStreamSynchronize,以便为通信器推进可能的未完成的通信操作。

参数

描述

comm

通信器句柄。

stream

要同步的 CUDA 流。

有关返回值的描述,请参阅 calError_t


cal_comm_get_size#

calError_t cal_comm_get_size(
    cal_comm_t comm,
    int* size )
检索提供的通信器中处理元素的数量。

参数

描述

comm

通信器句柄。

size

处理元素的数量。

有关返回值的描述,请参阅 calError_t


cal_comm_get_rank#

calError_t cal_comm_get_rank(
    cal_comm_t comm,
    int* rank )
检索分配给通信器的处理元素 rank(base-0)。

参数

描述

comm

通信器句柄。

rank

调用进程的 Rank Id。

有关返回值的描述,请参阅 calError_t