1. 声明

本文档提供的信息属于上海燧原科技有限公司和/或其子公司(以下统称“燧原科技”)所有,且燧原科技保留不经通知随时对本文档信息或对任何产品和服务做出修改的权利。本文档所含信息和本文档所引用燧原科技其他信息均“按原样”提供。燧原科技不担保信息、文本、图案、链接或本文档内所含其他项目的准确性或完整性。燧原科技不对本文档所述产品的可销售性、所有权、不侵犯知识产权、准确性、完整性、稳定性或特定用途适用性做任何暗示担保、保证。燧原科技可不经通知随时对本文档或本文档所述产品做出更改,但不承诺更新本文档。

在任何情况下,燧原科技不对因使用或无法使用本文档而导致的任何损害(包括但不限于利润损失、业务中断和信息损失等损害)承担责任。燧原科技不承担因应用或使用本文档所述任何产品或服务而产生的任何责任。

本文档所列的规格参数、性能数据和等级需使用特定芯片或计算机系统或组件来测量。经该等测试,本文档所示结果反映了燧原科技产品的大概性能。系统配置及软硬件版本、环境变量等的任何不同会影响实际性能,产品实际效果与文档描述存在差异的,均属正常现象。燧原科技不担保测试每种产品的所有参数。客户自行承担对产品适合并适用于客户计划的应用以及对应用程序进行必要测试的责任。客户产品设计的脆弱性会影响燧原科技产品的质量和可靠性并导致超出本文档范围的额外或不同的情况和/或要求。

燧原科技和燧原科技的标志是上海燧原科技有限公司申请和/或注册的商标。本文档并未明示或暗示地授予客户任何专利、版权、商标、集成电路布图设计、商业秘密或任何其他燧原科技知识产权的权利或许可。

本文档为版权所有并受全世界版权法律和条约条款的保护。未经燧原科技的事先书面许可,任何人不可以任何方式复制、修改、出版、上传、发布、传输或分发本文档。为免疑义,除了允许客户按照本文档要求使用文档相关信息外,燧原科技不授予其他任何明示或暗示的权利或许可。

燧原科技对本文档享有最终解释权

2. 版本历史

文档版本

版本

作者

V2.0

ECCL 用户使用手册

Enflame Tech

3. ECCL简介

燧原公司集合通信库(Enflame Collective Communications Library, 简称为ECCL)在基于公司自研的芯片GCU上提供了卡间通信的功能。

其主要提供了以下多个通信原语:

  • ecclAllReduce:将多卡上的数据进行规约操作,并将得到的结果保存在每张卡上

  • ecclBroadcast:将一张卡上的数据广播给多张卡

  • ecclReduceScatter:将多卡上的数据进行规约,并将得到的结果均匀分片后分发到每张卡上

  • ecclAllGather:将多卡上的数据聚合一起,每张卡上的原始数据大小需一致,并将得到的结果保存在每张卡上

  • ecclAllGatherv:将多卡上的数据聚合一起,每张卡上的原始数据大小允许不同,并将得到的结果保存在每张卡上

ECCL同样支持点对点通信功能:

  • ecclSend:将卡上的数据发送给另一张指定的卡

  • ecclRecv:从指定的卡上接收数据到本卡

  • ecclGather:将多张卡上的数据聚合到一张卡上

  • ecclScatter:将一张卡上的数据均匀分片后分发到每张卡上

  • ecclAllToAll:每张卡都将传输独立的数据给其他的卡

4. ECCL用户使用手册

以下内容描述了如何使用ECCL进行分布式训练并提供了相关的测试用例

4.1. ECCL使用前置准备

  • Openmpi,当前支持的版本为4.0.5, 训练容器自带

  • 燧原TopsRider安装包里的Enflame TopsRider SDK

以上是使用ECCL时所需最基础的依赖软件包。

4.2. ECCL安装

openmpi

首先确保使用的是燧原发布的训练专用的docker镜像,启动容器后自带openmpi-4.0.5 ,如下:

mpirun -version

+ mpirun.real -vv --allow-run-as-root --version
mpirun.real (OpenRTE) 4.0.5

TopsRider

参考《TopsRider软件栈安装手册》安装好Enflame TopsRider SDK软件包。

eccl

ECCL的安装过程请参考《TopsRider软件栈安装手册》,完成TopsRider即完成了ECCL的安装。

4.3. ECCL使用方式

以下内容描述了如何使用ECCL进行集合通信操作。包括了ECCL接口描述、使用概述以及使用示例介绍

ECCL对外接口

描述了ECCL集合通信库的对外接口以及相关的参数信息。包括了通信域生成与管理类接口、通信算子类接口、组操作接口以及点对点通信接口。详情可见《ECCL_API参考》文档。

ECCL使用

目前ECCL仅支持单线程上单个GCU卡的运行情况。下列代码将会展示如何使用MPI创建一个通信域并在通信域上运行集合通信算子。

  • 检索有关线程的MPI信息:

int pid = -1, np = -1;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &pid);
MPI_Comm_size(MPI_COMM_WORLD, &np);
  • 根GCU卡将会生成一个通信域的UniqueId,并把Id广播给域内其余的卡:

ecclUniqueId id;
if (pid == 0) assert(ecclGetUniqueId(&id) == ecclSuccess);
MPI_Bcast((void *)&id, sizeof(id), MPI_BYTE, 0, MPI_COMM_WORLD);
  • 根据以上信息可以生成一个通信域:

ecclComm_t worldComm;
assert(ecclCommInitRank(&worldComm, np, id, pid) == ecclSuccess);
  • 在生成的通信域上调用相应的通信算子进行集合通信(以ecclAllReduce为例):

ecclAllReduce(..., worldComm);
  • 集合通信结束时,释放相应的资源并摧毁通信域:

assert(ecclCommDestroy(worldComm) == ecclSuccess);

ECCL使用示例

ECCL可以配合不同版本的runtime使用,以下提供了基于runtime2.0和runtime3.0版本的完整ECCL测试用例。

目前ECCL已经默认使能runtime3.0,如需基于runtime2.0进行测试,可使用临时的环境变量ECCL_RUNTIME_3_0_ENABLE(该环境变量后续会删除):

export ECCL_RUNTIME_3_0_ENABLE=false

基于runtime3.0测试:

export ECCL_RUNTIME_3_0_ENABLE=true

以下代码段描述了基于runtime2.0的单进程单设备测试用例:

#include <stdio.h>
#include "eccl.h"
#include "mpi.h"
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>

// runtime相关头文件
#include "gcu/2_0/runtime/platform.h"
#include "gcu/2_0/runtime/stream.h"
#include "gcu/2_0/runtime/device_memory.h"

#define MPICHECK(cmd) do {                          \
  int e = cmd;                                      \
  if( e != MPI_SUCCESS ) {                          \
    printf("Failed: MPI error %s:%d '%d'\n",        \
       __FILE__,__LINE__, e);                       \
    exit(EXIT_FAILURE);                             \
  }                                                 \
} while(0)

#define ECCLCHECK(call) do {                              \
  ecclResult_t res = (ecclResult_t)call;                  \
  if (res != ecclSuccess) {                               \
    printf("%s:%d : Test ECCL failure '%s'\n",            \
            __FILE__, __LINE__, ecclGetErrorString(res)); \
    return res;                                           \
  }                                                       \
} while (0)

int main(int argc, char* argv[])
{
  // 初始化测试相关基础变量
  int count = 32*1024*1024;
  int pid = -1, np = -1;
  int size = count * sizeof(float);
  int myRank, nRanks;

  // 初始化MPI
  MPICHECK(MPI_Init(&argc, &argv));
  MPICHECK(MPI_Comm_rank(MPI_COMM_WORLD, &myRank));
  MPICHECK(MPI_Comm_size(MPI_COMM_WORLD, &nRanks));
  MPI_Barrier(MPI_COMM_WORLD);

  ecclUniqueId id;
  ecclComm_t comm;

  // 0号进程生成通信域的独有身份信息并广播至其他的进程上
  if (myRank == 0) ECCLCHECK(ecclGetUniqueId(&id));
  MPICHECK(MPI_Bcast((void *)&id, sizeof(id), MPI_BYTE, 0, MPI_COMM_WORLD));

  // 初始化通信域
  ECCLCHECK(ecclCommInitRank(&comm, nRanks, id, myRank));

  // 分配测试相关的资源,包括stream和memory
  auto SrvDeviceCnt_ = efrt::Platform::GetInstance().VisibleDeviceCount();
  auto deviceId_ = myRank % SrvDeviceCnt_;
  efrt::StreamExecutor *streamExe_ = efrt::Platform::GetInstance().ExecutorForDevice(deviceId_);
  efrt::Stream *stream = efrt::Stream::CreateStream(streamExe_);
  auto inputMem = efrt::DeviceMemory::CreateLocalDeviceMemory(streamExe_, size, 0);
  auto outputMem = efrt::DeviceMemory::CreateLocalDeviceMemory(streamExe_, size, 0);
  float *hostSend = new float[count];
  float *hostRecv = new float[count];

  // 在host端生成测试数据
  for (int i = 0; i < count; i++) {
    hostSend[i] = i * 0.000001;
    hostRecv[i] = 0;
  }

  // 将数据复制到device端
  stream->MemcpyH2DAsync(inputMem, hostSend, size);
  stream->MemcpyH2DAsync(outputMem, hostRecv, size);
  stream->Synchronize();

  // 调用集合通信接口并将结果复制到host端
  ECCLCHECK(ecclAllReduce((void *)inputMem, (void *)outputMem, count,
                          ecclFloat32, ecclSum, comm, stream));

  stream->MemcpyD2HAsync(hostRecv, outputMem, size);

  // 在stream进行一次同步操作以确保以上操作已经完成
  stream->Synchronize();

  // 释放上面分配的相应的资源
  efrt::DeviceMemory::DestroyDeviceMemory(inputMem);
  efrt::DeviceMemory::DestroyDeviceMemory(outputMem);
  delete[] hostSend;
  delete[] hostRecv;
  efrt::Stream::DestroyStream(stream);

  // 摧毁通信域
  ecclCommDestroy(comm);

  MPI_Barrier(MPI_COMM_WORLD);
  // 终止MPI
  MPI_Finalize();

  printf("[MPI Rank %d] Success \n", myRank);
  return 0;
}

以下代码段描述了基于runtime3.0的单进程单设备测试用例:

#include <stdio.h>
#include "eccl.h"
#include "mpi.h"
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>

// runtime相关头文件
#include "tops/tops_runtime_api.h"
#include "tops/tops_ext.h"

typedef enum {
  testSuccess = 0,
  testInternalError = 1,
  testEcclError = 2,
  testEcclRandError = 3
} testResult_t;

#undef CHECK
#define CHECK(call)                                                      \
  {                                                                      \
    const topsError_t error = call;                                      \
    if (error != topsSuccess) {                                          \
      printf("Error: %s:%d\n", __FILE__, __LINE__);                      \
      std::cout << "@@id = " << std::this_thread::get_id() << std::endl; \
      exit(1);                                                           \
    }                                                                    \
  }

#define MPICHECK(cmd) do {                          \
  int e = cmd;                                      \
  if( e != MPI_SUCCESS ) {                          \
    printf("Failed: MPI error %s:%d '%d'\n",        \
        __FILE__,__LINE__, e);                      \
    exit(EXIT_FAILURE);                             \
  }                                                 \
} while(0)

#define ECCLCHECK(call) do {                              \
  ecclResult_t res = (ecclResult_t)call;                  \
  if (res != ecclSuccess) {                               \
    printf("%s:%d : Test ECCL failure '%s'\n",            \
          __FILE__, __LINE__, ecclGetErrorString(res));   \
    return testEcclError;                                 \
  }                                                       \
} while (0)

static uint64_t getHostHash(const char* string) {
  uint64_t result = 5381;
  for (int c = 0; string[c] != '\0'; c++){
    result = ((result << 5) + result) ^ string[c];
  }
  return result;
}

int main(int argc, char* argv[]) {
  // 初始化测试相关基础变量
  int count = 32*1024*1024;
  int pid = -1, np = -1;
  int device_count = 0;
  int myRank, nRanks;
  ecclResult_t ret;

  // 初始化MPI
  MPICHECK(MPI_Init(&argc, &argv));
  MPICHECK(MPI_Comm_rank(MPI_COMM_WORLD, &myRank));
  MPICHECK(MPI_Comm_size(MPI_COMM_WORLD, &nRanks));
  MPI_Barrier(MPI_COMM_WORLD);

  topsSetDevice(myRank);
  ecclUniqueId id;
  ecclComm_t comm;

  // 0号进程生成通信域的独有身份信息并广播至其他的进程上
  if (myRank == 0) assert(ecclGetUniqueId(&id) == ecclSuccess);
  MPI_Bcast((void *)&id, sizeof(id), MPI_BYTE, 0, MPI_COMM_WORLD);

  // 初始化通信域
  assert(ecclCommInitRank(&comm, nRanks, id, myRank) == ecclSuccess);

  void *inputMem, *outputMem, *host_input, *host_output;
  topsStream_t stream = nullptr;

  // 分配测试相关的资源,包括stream和memory
  CHECK(topsHostMalloc(&host_input, count * sizeof(float)));
  CHECK(topsHostMalloc(&host_output, count * sizeof(float)));
  CHECK(topsMalloc(&inputMem, count * sizeof(float)));
  CHECK(topsMalloc(&outputMem, count * sizeof(float)));
  CHECK(topsStreamCreate(&stream));

  // 在host端生成测试数据
  for (int i = 0; i < count; i++) {
    (reinterpret_cast<float *>(host_input))[i] = 1;
  }

  // 将数据复制到device端
  CHECK(topsMemcpy(inputMem, host_input, count * sizeof(float),
                    topsMemcpyHostToDevice));

  // 调用集合通信接口
  ECCLCHECK(ecclAllReduce((const void*)inputMem, (void*)outputMem, count, ecclFloat, ecclSum,
            comm, stream));

  // 在stream进行一次同步操作以确保以上操作已经完成
  CHECK(topsStreamSynchronize(stream));

  // 将数据复制到host端
  memset(host_output, 0, count * sizeof(float));
  CHECK(topsMemcpy(host_output, outputMem, count * sizeof(float), topsMemcpyDeviceToHost));

  printf("\n================================= the first 16: \n");
  for (int i=0; i< 16; i++) {
    float* outPtr = (float*)host_output;
    printf("%f ", outPtr[i]);
  }

  printf("\n================================= the last 16: \n");
  for (int i=0; i< 16; i++) {
    float* outPtr = (float*)host_output;
    printf("%f ", outPtr[count -1 -i]);
  }
  printf("\n");

  // 释放上面分配的相应的资源
  CHECK(topsFree(inputMem));
  CHECK(topsFree(outputMem));
  CHECK(topsHostFree(host_input));
  CHECK(topsHostFree(host_output));
  CHECK(topsStreamDestroy(stream));

  // 摧毁通信域
  ECCLCHECK(ecclCommDestroy(comm));

  MPI_Barrier(MPI_COMM_WORLD);
  // 终止MPI
  MPI_Finalize();

  printf("[MPI Rank %d] Success \n", myRank);
  return 0;
}

4.4. ECCL环境变量

ECCL允许客户通过控制环境变量来为一些特定的用途进行微调

ECCL_MAX_NCHANNELS

当使用ECCL进行集合通信操作时,此环境变量表示最大支持使用几个ring来分担通信任务,会影响多卡分布式性能。

允许传入的值

取值范围是1至6,默认值为2。

使用示例

同时使用2个ring来分担allreduce任务:

export ECCL_MAX_NCHANNELS=2

ECCL_NIC_AFFINITY_GCUS

该变量代表了一组能通过PCIe switch直接访问网卡的GCU,即这些GCU和网卡亲和。通过此环境变量可以限制参与节点间网络通信的GCU范围,可能会影响多机分布式性能。

允许传入的值

该变量接受一个被双引号包含的字符串,中间用”,”隔开,取值范围是0至nRanks-1。默认所有GCU都与网卡亲,例如8卡服务器的默认值为”0,1,2,3,4,5,6,7”。

使用示例

设置GCU0、GCU1、GCU2、GCU3与网卡亲和:

export ECCL_NIC_AFFINITY_GCUS="0,1,2,3"

ECCL_SOCKET_NTHREADS

该变量用于设置使用socket连接时CPU创建的线程个数,有可能会提高socket传输性能,以cpu利用率为代价。该环境变量会影响socket通信的性能。

允许传入的值

与ECCL_NSOCKS_PERTHREAD配合使用,取值范围是1至16,默认值为4。

使用示例

使用socket连接时CPU创建的线程个数2:

export ECCL_SOCKET_NTHREADS=2

ECCL_NSOCKS_PERTHREAD

该变量用于设置使用socket连接时单个线程创建的socket个数。在单个socket传输速率限制的情况下,设置>1可能会提高性能。该环境变量会影响socket通信的性能。

允许传入的值

与ECCL_SOCKET_NTHREADS配合使用,默认值为1。总的socket个数不超过64(即ECCL_NSOCKS_PERTHREAD*ECCL_SOCKET_NTHREADS<=64)。

使用示例

使用socket连接时CPU的单个线程创建的socket个数2:

export ECCL_NSOCKS_PERTHREAD=2

ECCL_SOCKET_IFNAME

该变量用于设置socket通信使用的ip interface。

允许传入的值

用于筛选ip interface的字符串前缀。”^”用于排除,”=”用于精确选择。默认值为空。

使用示例

选择所有以eth开头的ip interface,例如eth0、eth1等:

export ECCL_SOCKET_IFNAME="eth"

只选择eth0:

export ECCL_SOCKET_IFNAME="=eth0"

不选择所有以eth开头的ip interface:

export ECCL_SOCKET_IFNAME="^eth"

不选择eth0:

export ECCL_SOCKET_IFNAME="^=eth0"

ECCL_IB_HCA

该变量用于设置RDMA传输使用的网口。

允许传入的值

用于筛选RDMA网口的字符串,存在多个筛选条件时用”,”隔开。默认值为空。 接受以下三种形式的字符串(以网卡mlx5_1为例):

  • 网卡名(例:”mlx5_1”)。

  • 网卡名的部分前缀(例:”mlx5”)。

  • 网卡名加物理口序号,网卡与物理口之间用”:”隔开(例:”mlx5_1:1”表示网卡mlx5_1的第一个口)。

字符串中可增加”^”用于排除,”=”用于精确选择。在字符串为网卡名时,”=”可忽略。

使用示例

选择所有以mlx5开头的口,例如mlx5_0、mlx5_1等:

export ECCL_IB_HCA="mlx5"

选择网卡mlx5_1上的所有的口,例如mlx5_1:1、mlx5_1:2等:

export ECCL_IB_HCA="mlx5_1"ECCL_IB_HCA="=mlx5_1"

使用网卡mlx5_0、mlx5_1的第一个口:

export ECCL_IB_HCA="=mlx5_0:1,mlx5_1:1"

不选择mlx5_1上的口:

export ECCL_IB_HCA="^mlx5_1"ECCL_IB_HCA="^=mlx5_1"

不选择mlx5_1上的第一个口:

export ECCL_IB_HCA="^=mlx5_1:1"

ECCL_DEBUG

该变量用于设置ECCL打印的log级别,WARN、INFO、TRACE三种级别。WARN级别在业务逻辑出现异常时打印,INFO级别用于用户行为留痕、调用链跟踪,TRACE级别用于记录ECCL代码内部重复性的调用信息。其中INFO需与定义子模块的环境变量ECCL_DEBUG_SUBSYS配合使用,WARN和TRACE会打印所有子模块。

允许传入的值

  • NONE:关闭ECCL log打印

  • WARN:打开WARN级别log

  • INFO:打开INFO级别log,默认值

  • TRACE:打开TRACE级别log

使用示例

打印INFO级别的初始化以及环境变量相关log(即默认值):

export ECCL_DEBUG=INFO
export ECCL_DEBUG_SUBSYS=INIT,ENV

关闭ECCL所有log打印:

export ECCL_DEBUG=NONE

打印WARN级别的log:

export ECCL_DEBUG=WARN

打印TRACE级别的所有log:

export ECCL_DEBUG=TRACE
export ECCL_DEBUG_SUBSYS=ALL

ECCL_DEBUG_SUBSYS

用于设置需要打印的子模块,主要包括初始化、环境变量、调用等等子模块,可以设置多个子模块,多个子模块间用”,”隔开,默认值为”INIT,ENV”。关闭子模块,在子模块前加”^”。ECCL_DEBUG设置为INFO时需与ECCL_DEBUG_SUBSYS配合使用。

允许传入的值

  • INIT:初始化阶段信息,默认值

  • ENV:环境变量信息,默认值

  • CALL:ECCL对外接口调用信息

  • ALLOC:ECCL内部资源分配信息

  • TRANSPORT:ECCL内部transport信息

  • NET:网络相关信息,调用socket、ibverbs接口信息

  • TOPO:ECCL内部拓扑相关信息

  • P2P:ECCL点对点通信相关信息

  • ALL:ECCL所有模块信息

使用示例

打印INFO级别的初始化以及环境变量相关log:

export ECCL_DEBUG=INFO
export ECCL_DEBUG_SUBSYS=INIT,ENV

打印TRACE级别的所有log:

export ECCL_DEBUG=TRACE
export ECCL_DEBUG_SUBSYS=ALL

关闭初始化模块打印:

export ECCL_DEBUG_SUBSYS=^INIT

5. eccl-tests

用来测试检查ECCL操作的性能和正确性。

5.1. 环境构建

在调用ECCL相关测试前需要安装好ECCL所依赖的软件包,安装规则可见ECCL使用前置准备处

5.2. 测试使用

eccl-tests运行在多张GCU卡上,通过mpirun命令启动程序并指定启动的进程的个数,每个进程对应着一个唯一rank。目前支持一个rank管理一块GCU。在一个rank(即进程)上支持一个自己的线程。

5.3. 使用方法

eccl-tests可以用mpirun中的参数np来控制参与的卡数,并支持多种通信算子的性能测试,包括AllReduce、Broadcast、ReduceScatter、AllGather、Reduce、Send/Recv、Gather、Scatter和AlltoAll。其中集合通信可以支持单机和多机(包括AllReduce、Broadcast、ReduceScatter、AllGather和Reduce),T20/I20/T21点对点通信(包括Send/Recv、Gather、Scatter和AlltoAll)中Send/Recv此版本仅支持单机不支持多机,Gather、Scatter和AlltoAll支持单机和多机,S系列加速卡点对点通信仅支持单机,不支持多机。

下面提供了测试各种通信算子的命令格式,所用的测试数据大小均为1KB到128MB。

AllReduce

单机8卡:

mpirun -np 8 eccl_all_reduce_perf -b 1k -e 128M -f 2

两机16卡:

mpirun -np 16 -hostfile hostfile --allow-run-as-root -bind-to none
-map-by slot --mca plm_rsh_args "-p 2223" --mca btl_tcp_if_include interface
eccl_all_reduce_perf -b 1k -e 128M -f 2

两台机器需要保持docker环境一致,且互相可ssh免密登录;其中“-np 16“中的16表示测试16张卡;”–mca btl_tcp_if_include interface“表示使用名为interface的ip interface作为mpi tcp的通信端口,需保证interface为两台机器的可用端口;”-hostfile hostfile“中第二个hostfile为设置所用机器文件名,需要包含点分十进制表示的两台机器的ipv4地址,如下以192.168.0.1和192.168.0.2为例,slots=8表示使用该机器的八张卡:

192.168.0.1 slots=8
192.168.0.2 slots=8

以上以两机16卡AllReduce为例子,Broadcast等等也类似,详见下面章节;也可以根据测试要求设置“-np“的参数以及hostfile进行更多卡的测试。

多机测试初始化阶段需要查找可用ip interface用于建立tcp连接,进行不同机器、rank之间的数据传输,默认会选择查找到的第一个端口,当不同机器的ip interface配置不同,会造成初始化失败,建议参考环境变量ECCL_SOCKET_IFNAME的使用,设置选择特定的ip interface。

多机测试中的不同机器之间优先使用RDMA链路,需要保证每台机器上的RDMA网卡数目、配置相同;若数目、配置不同,参考环境变量ECCL_IB_HCA的使用,设置选择、排除对应的网卡。

Broadcast

单机8卡:

mpirun -np 8 eccl_broadcast_perf -b 1k -e 128M -f 2

两机16卡:

mpirun -np 16 -hostfile hostfile --allow-run-as-root -bind-to none
-map-by slot --mca plm_rsh_args "-p 2223" --mca btl_tcp_if_include interface
eccl_broadcast_perf -b 1k -e 128M -f 2

ReduceScatter

单机8卡:

mpirun -np 8 eccl_reduce_scatter_perf -b 1k -e 128M -f 2

两机16卡:

mpirun -np 16 -hostfile hostfile --allow-run-as-root -bind-to none
-map-by slot --mca plm_rsh_args "-p 2223" --mca btl_tcp_if_include interface
eccl_reduce_scatter_perf -b 1k -e 128M -f 2

AllGather

单机8卡:

mpirun -np 8 eccl_all_gather_perf -b 1k -e 128M -f 2

两机16卡:

mpirun -np 16 -hostfile hostfile --allow-run-as-root -bind-to none
-map-by slot --mca plm_rsh_args "-p 2223" --mca btl_tcp_if_include interface
eccl_all_gather_perf -b 1k -e 128M -f 2

Reduce

单机8卡:

mpirun -np 8 eccl_reduce_perf -b 1k -e 128M -f 2

两机16卡:

mpirun -np 16 -hostfile hostfile --allow-run-as-root -bind-to none
-map-by slot --mca plm_rsh_args "-p 2223" --mca btl_tcp_if_include interface
eccl_reduce_perf -b 1k -e 128M -f 2

注:此通信算子性能测试需要指定拓扑结构为ring。

Send/Recv

单机8卡:

mpirun -np 8 eccl_send_recv_perf -b 1k -e 128M -f 2

Gather

单机8卡:

mpirun -np 8 eccl_gather_perf -b 1k -e 128M -f 2

两机16卡:

mpirun -np 16 -hostfile hostfile --allow-run-as-root -bind-to none
-map-by slot --mca plm_rsh_args "-p 2223" --mca btl_tcp_if_include interface
eccl_gather_perf -b 1k -e 128M -f 2

Scatter

单机8卡:

mpirun -np 8 eccl_scatter_perf -b 1k -e 128M -f 2

两机16卡:

mpirun -np 16 -hostfile hostfile --allow-run-as-root -bind-to none
-map-by slot --mca plm_rsh_args "-p 2223" --mca btl_tcp_if_include interface
eccl_scatter_perf -b 1k -e 128M -f 2

AlltoAll

单机8卡:

mpirun -np 8 eccl_all_to_all_perf -b 1k -e 128M -f 2

两机16卡:

mpirun -np 16 -hostfile hostfile --allow-run-as-root -bind-to none
-map-by slot --mca plm_rsh_args "-p 2223" --mca btl_tcp_if_include interface
eccl_all_to_all_perf -b 1k -e 128M -f 2

5.4. 命令参数

GCU数量

表 5.4.1 GCU及线程数量

参数名称

使用方法

默认值

参数说明

-t

--nthreads <num threads>

1(仅支持1)

运行在每一个进程上的线程数

-g

--ngcus <GCUs per thread>

1(仅支持1)

运行在每一个线程上的GCU个数

Attention

目前仅支持一个进程对应一个GCU启动

需要扫描的尺寸

表 5.4.2 需要扫描的尺寸

参数名称

使用方法

默认值

参数说明

-b

--minbytes <min size in bytes>

32M

启动时的最小尺寸

-e

--maxbytes <max size in bytes>

32M

结束时的最大的尺寸

-i

--stepbytes <increment size>

1M

在一定的尺度等量选择增量

-f

--stepfactor <increment factor>

不使用

乘法的使用

Attention

-i和-f只使用一个,默认使用-i

ECCL操作参数

表 5.4.3 ECCL操作参数

参数名称

使用方法

默认值

参数说明

-o

--op <sum/prod/min/max/all>

sum

指定要执行的规约操作类型。仅与AllReduce、Reduce或ReduceScatter等源操作相关

-d

--datatype <eccltype/all>

float

指定数据类型

-r

--root <root/all>

0

指定 root GCU,应用于Broadcast或者Reduce相关操作

表现

表 5.4.4 表现

参数名称

使用方法

默认值

参数说明

-n

--iters <iteration count>

20

循环迭代次数

-w

--warmup_iters <warmup iteration count>

5

预热迭代次数(不是时间)

-a

--average <0/1/2/3>

1

报告所有rank的平均性能(仅MPI=1)<0=Rank0,1=Avg,2=Min,3=Max>

测试操作

表 5.4.5 测试操作

参数名称

使用方法

默认值

参数说明

-c

--check <0/1>

1

检查结果的正确性。在大量GCU上可能较慢,默认检查

-z

--blocking <0/1>

0

进行ECCL集合通信阻塞,即让CPU在每次集体阻塞后等待和同步

5.5. 结果

表 5.5.1 eccl-tests性能相关名词解释

参数名称

参数说明

size

数据的大小(S)

count

数据的计数

type

数据的类型

redop

操作的类型

time

操作的执行时间(t)

algbw

算法带宽,根据数据大小以及操作执行时间计算得到,algbw=S/t

busbw

总线带宽,根据数据链路上实际传输的数据大小以及操作执行时间计算得到。实际是根据操作类型,将算法带宽乘以一个系数,详见下表

表 5.5.2 不同操作类型总线带宽与算法带宽的关系

操作的类型

计算关系

AllReduce

busbw=algbw*2*(n-1)/n

ReduceScatter

busbw=algbw*(n-1)/n

AllGather

busbw=algbw*(n-1)/n

Broadcast

busbw=algbw

Reduce

busbw=algbw

5.6. 注意事项

打开额外的日志开关(如ENFLAME_LOG_DEBUG_MOD=”ECCL”)会增加下发耗时,可能导致eccl-tests测得的性能低于实际性能,建议在进行eccl-tests性能测试时不要设置日志相关环境变量或者执行其他可能影响性能的操作。