cuDSS 高级功能#

混合主机/设备内存模式#

混合主机/设备内存模式的主要思想是克服默认(非混合)模式的限制,即 L 和 U 因子(它们构成 cuDSS 消耗的总设备内存的最大部分)必须适合设备内存。为此,混合内存模式允许在任何给定时刻仅将 L 和 U 的(较小)部分保存在设备内存中,同时仅将整个因子保存在主机内存中。

虽然混合内存模式会带来额外的从主机到设备和从设备到主机的内存传输成本(因此如果因子适合设备内存,则会比默认模式慢),但它可以帮助解决在默认模式下无法处理的更大系统。

用户可以通过调用 cudssConfigSet() 和枚举 cudssConfigParam_t 中的设置 CUDSS_CONFIG_HYBRID_MODE 来启用或禁用混合内存模式。由于使用混合模式意味着在分析阶段需要进行更改,因此必须在调用带有 CUDSS_PHASE_ANALYSIScudssExecute() 之前完成启用混合模式。

一旦启用混合模式,就有几种使用方法

  • 第一种方法是依赖 cuDSS 的内部启发式方法,它可以确定混合模式可以消耗多少设备内存。在这种情况下,cuDSS 将假定它可以使用整个 GPU 内存,并根据设备属性设置设备内存限制(这可能不是准确的估计,因为驱动程序或其他应用程序可能会在 GPU 上保留空间)。

  • 第二种方法允许用户更好地控制设备内存消耗。用户可以通过调用 cudssConfigSet() 和枚举 cudssConfigParam_t 中的 CUDSS_CONFIG_HYBRID_DEVICE_MEMORY_LIMIT 设置来设置设备内存限制。 可选地,用户还可以通过调用带有枚举 cudssDataParam_t 中的 CUDSS_DATA_HYBRID_DEVICE_MEMORY_MINcudssDataGet() 来查询混合内存模式所需的最小设备内存量。设备内存限制可以在分析阶段之后设置,但必须在因式分解阶段之前设置。

    注意:CUDSS_CONFIG_HYBRID_DEVICE_MEMORY_LIMITCUDSS_DATA_HYBRID_DEVICE_MEMORY_MIN 考虑的是 cuDSS 所需的总设备内存,而不仅仅是因子。

局限性

  • 因子 L 和 U(以及所有必要的内部数组)必须适合主机内存。

  • 混合内存模式必须在分析阶段之前启用。

  • 目前,混合内存模式在所有阶段都增加了 CUDA 流的额外同步。

  • 默认情况下,混合内存模式使用 cudaHostRegister()/cudaHostUnregister()(如果设备支持)。由于有时这可能比不使用主机注册内存更慢,因此有一个设置 CUDSS_CONFIG_USE_CUDA_REGISTER_MEMORY 来启用/禁用 cudaHostRegister() 的使用。

  • 目前,当 CUDSS_ALG_1CUDSS_ALG_2 用于重新排序,或者当 CUDSS_ALG_1 用于因式分解时,不支持混合内存模式。

混合主机/设备执行模式#

混合执行模式允许 cuDSS 在 GPU 和 CPU 上执行计算。目前,它用于加速并行化能力较低的执行部分。就目前而言,我们建议将此功能用于小型矩阵的因式分解和求解。

用户可以通过调用 cudssConfigSet() 和枚举 cudssConfigParam_t 中的设置 CUDSS_CONFIG_HYBRID_EXECUTE_MODE 来启用或禁用混合内存模式。由于使用混合模式意味着在分析阶段需要进行更改,因此必须在调用带有 CUDSS_PHASE_ANALYSIScudssExecute() 之前完成启用混合执行模式。

一旦启用混合执行模式,输入矩阵、右手边和解可以是主机内存指针。对于输入 CSR 矩阵,csr_offsetcsr_columnscsr_values 可以是主机或设备内存指针,彼此独立。 例如,当 csr_values 是主机内存时,csr_offsetscsr_columns 可以是设备内存。

局限性

  • 混合执行模式必须在分析阶段之前启用。

  • 目前,混合执行模式在所有阶段都增加了 CUDA 流的额外同步。

  • 目前,当使用 CUDSS_CONFIG_HYBRID_MODEMGMN 模式,或者当 batchCount 大于 1,或者当 CUDSS_CONFIG_REORDERING_ALG 设置为 CUDSS_ALG_1CUDSS_ALG_2 时,不支持混合执行模式。

  • 主机内存数据仅支持 nrhs = 1。

多 GPU 多节点 (MGMN) 模式#

多 GPU 多节点 (MGMN) 模式的思想是通过使用多个进程的分布式计算来允许在 cuDSS 中使用多个 GPU 设备。为了灵活性,MGMN 模式通过将所有特定于通信的原语抽象到一个小的、单独构建的 shim 通信层中来启用。用户可以使用他们自己实现的通信层以及他们选择的通信后端(MPI、NCCL 等)。

由于 shim 通信层抽象出了所有特定于通信的操作,并且仅在请求 MGMN 模式时在运行时加载,因此在 cuDSS 中启用的 MGMN 执行不需要对不使用 MGMN 模式的应用程序进行任何更改。

在用户应用程序代码中启用 MGMN 执行包括两个步骤

  • 首先,用户应通过调用 cudssSetCommLayer() 来设置通信层库。这可以是 cuDSS 包中的预构建通信层之一,也可以是用户自定义构建的库。如果使用 NULL 代替通信层库名称调用 cudssSetCommLayer(),则此例程会尝试从环境变量 CUDSS_COMM_LIB 中读取名称。

    注意:通信层库是为库句柄设置的,并用于所有涉及修改后的句柄的执行调用。

  • 其次,用户应通过调用带有 CUDSS_DATA_COMM 参数名称的 cudssDataSet() 来设置 cuDSS MGMN 模式要使用的通信器。通信器的类型必须与底层通信后端匹配。例如,如果使用 OpenMPI,则通信器应为 OpenMPI 通信器,否则很可能发生崩溃。

    注意:由于 MGMN 模式可以支持不同的(包括用户实现的)通信层,因此使用特定底层通信后端(例如 MPI 或 NCCL)的限制适用。

局限性

  • cuDSS 通信层底层的通信后端必须是 GPU 感知的,因为所有分布式接口 API 实现都必须接受设备内存缓冲区并遵守流排序(大多数通信层 API 都接受 cudaStream_t 类型的参数,请参阅 cudssDistributedInterface_t 的定义)。

  • MGMN 模式目前仅支持输入矩阵、右手边和解向量未分布且完整大小的数组分配在根进程(应具有等于 0 的秩)上的情况。 此外,非根进程必须具有正确的矩阵形状(但从上面可以看出,如果传递给 矩阵创建例程,则数据缓冲区指针将被忽略)。

  • MGMN 模式不能与 混合模式 一起使用。

  • CUDSS_ALG_1CUDSS_ALG_2 用于重新排序时,不支持 MGMN 模式。

cuDSS 中的通信层库#

cuDSS 中通信层的目的是抽象出所有通信原语,并使用一小组仅必要的操作构建到一个独立的共享库中,该库在为 cuDSS 启用 MGMN 模式时在运行时加载。

在支持 MGMN 模式的平台上,cuDSS 的分发包包括 OpenMPI 和 NCCL 的预构建通信库 libcudss_commlayer_openmpi.solibcudss_commlayer_nccl.so。 还包括实现的源代码 cudss_commlayer_openmpi.cucudss_commlayer_nccl.cu,以及一个脚本 cudss_build_commlayer.sh,其中包含如何构建通信层库的示例。 提供这些源文件和脚本用于演示目的,并且可以用作开发自定义实现的指南。

一旦通信层被实现并构建到小型独立的共享库中,应用程序应通过上面提到的 步骤 为 cuDSS 启用 MGMN 模式。

注意:如果通信层依赖于其他共享库,则此类依赖项必须在链接时或运行时可用(取决于通信层实现)。 例如,为 OpenMPI 预构建的 libcudss_commlayer_openmpi.so 依赖于在链接时找到 libmpi.so.40,因此如果应用程序使用此通信层用于 cuDSS,则应额外链接到 OpenMPI 共享库。

cuDSS 中的通信层(分布式接口)API#

cuDSS 中的通信层可以被认为是 cuDSS MGMN 模式所需的所有必要通信原语的包装器。虽然支持用户定义的实现,但分布式接口 API 是固定的。 API 分隔到头文件 cudss_distributed_interface.h 中。

具体而言,分布式接口 API 编码在类型 cudssDistributedInterface_t 的定义中,有效的通信层实现必须定义名称为 cudssDistributedInterface 的此类型的符号。 如果未找到此类符号,则调用 cudssSetCommLayer() 将导致错误。

多线程 (MT) 模式#

多线程模式的思想是允许在 cuDSS 中使用多个 CPU 线程。为了灵活性,MT 模式通过将所有特定于线程的原语抽象到一个小的、单独构建的 shim 线程层中来启用。用户可以使用他们自己实现的线程层以及他们选择的线程后端(OpenMP、pthreads、TBB 等)。

在用户应用程序代码中启用 MT 执行

用户应通过调用 cudssSetThreadingLayer() 来设置线程层库。这可以是 cuDSS 包中的预构建线程层之一,也可以是用户自定义构建的库。如果使用 NULL 代替线程层库名称调用 cudssSetThreadingLayer(),则此例程会尝试从环境变量 CUDSS_THREADING_LIB 中读取名称。

注意:线程层库是为库句柄设置的,并用于所有涉及修改后的句柄的执行调用。

注意:由于 MT 模式可以支持不同的(包括用户实现的)线程层,因此使用特定底层线程后端(例如 OpenMP 或 pthreads)的限制适用。

cuDSS 中的线程层库#

cuDSS 中线程层的目的是抽象出所有多线程原语,并使用一小组仅必要的操作构建到一个独立的共享库中,该库在为 cuDSS 启用 MT 模式时在运行时加载。

在支持 MT 模式的平台上,cuDSS 的分发包包括 GNU OpenMP 的预构建线程库 libcudss_mtlayer_gomp.so。 还包括实现的源代码 cudss_mtlayer_gomp.cu,以及一个脚本 cudss_build_mtlayer.sh,其中包含如何构建线程层库的示例。 提供这些源文件和脚本用于演示目的,并且可以用作开发自定义实现的指南。

一旦线程层被实现并构建到小型独立的共享库中,应用程序应通过上面提到的 步骤 为 cuDSS 启用 MT 模式。

注意:如果线程层依赖于其他共享库,则此类依赖项必须在运行时可用(取决于线程层实现)。

cuDSS 中的线程层 API#

cuDSS 中的线程层可以被认为是 cuDSS MT 模式所需的所有必要线程原语的包装器。虽然支持用户定义的实现,但线程接口 API 是固定的。 API 分隔到头文件 cudss_threading_interface.h 中。

具体而言,线程接口 API 编码在类型 cudssThreadingInterface_t 的定义中,有效的线程层实现必须定义名称为 cudssThreadingInterface 的此类型的符号。 如果未找到此类符号,则调用 cudssSetThreadingLayer() 将导致错误。