mpi 与 nccl 多进程多卡单机示例结果验证

做了部分注释,比较乱

本示例结构:

1,源代码

#include <stdio.h>
#include "cuda_runtime.h"
#include "nccl.h"
#include "mpi.h"
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/time.h>#define SOCKET_SIZE 1#if SOCKET_SIZE
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <fcntl.h>
#include <poll.h>
#endifusing namespace std;#define MPI_CHECK(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 CUDA_CHECK(cmd) do {                         \cudaError_t e = cmd;                              \if( e != cudaSuccess ) {                          \printf("Failed: Cuda error %s:%d '%s'\n",             \__FILE__,__LINE__,cudaGetErrorString(e));   \exit(EXIT_FAILURE);                             \}                                                 \
} while(0)#define NCCL_CHECK(cmd) do {                         \ncclResult_t r = cmd;                             \if (r!= ncclSuccess) {                            \printf("Failed, NCCL error %s:%d '%s'\n",             \__FILE__,__LINE__,ncclGetErrorString(r));   \exit(EXIT_FAILURE);                             \}                                                 \
} while(0)/* DJB2a是一种简单的哈希算法,由计算机科学家Daniel J. Bernstein设计。* 它被广泛用于哈希表等数据结构中。该算法通过遍历输入字符串的每个字符,* 并结合一个常数(通常是33),来计算字符串的哈希值。* 它在计算速度和哈希碰撞方面表现良好,但不适用于加密目的。
**/
static uint64_t getHostHash(const char* string) {// Based on DJB2a, result = result * 33 ^ charuint64_t result = 5381;for (int c = 0; string[c] != '\0'; c++){result = ((result << 5) + result) ^ string[c];}return result;
}static void getHostName(char* hostname, int maxlen) {gethostname(hostname, maxlen);//本函数声明于 /usr/include/unistd.hfor (int i=0; i< maxlen; i++) {if (hostname[i] == '.') {hostname[i] = '\0';return;}}
}float max__(float x, float y)
{return x>y? x:y;
}float sum__(float x, float y)
{return x + y;
}void print_vector(float* A, int n)
{for(int i=0; i<n; i++)printf("%.2f ", A[i]);
}void init_dev_vectors(float* A_d, float* B_d, int n, int rank, long long seed)
{float * A = (float*)malloc(n*sizeof(float));float * B = (float*)malloc(n*sizeof(float));//float * M = (float*)malloc(n*sizeof(float));//max[i] = max(A[i], B[i]);//float * S = (float*)malloc(n*sizeof(float));//sum[i] = sum(A[i], B[i]);srand(seed);for(int i=0; i<n; i++){A[i] = (rand()%100)/100.0f;B[i] = (rand()%100)/100.0f;//M[i] = max__(A[i], B[i]);//S[i] = sum__(A[i], B[i]);}printf("\nrank = %d, sendbuff =\n", rank);  print_vector(A, n);
//  printf("\nrank = %d, Sum =\n", rank);  print_vector(S, n);cudaMemcpy(A_d, A, n*sizeof(float), cudaMemcpyHostToDevice);cudaMemcpy(B_d, B, n*sizeof(float), cudaMemcpyHostToDevice);free(A);free(B);
}void fetch_dev_vector(float* A_d, int n, int rank)
{float* A = (float*)malloc(n*sizeof(float));cudaMemcpy(A, A_d, n*sizeof(float), cudaMemcpyDeviceToHost);printf("rank = %d, recvbuff =\n", rank);print_vector(A, n);
}void  get_seed(long long &seed)
{struct timeval tv;gettimeofday(&tv, NULL);seed = (long long)tv.tv_sec * 1000*1000 + tv.tv_usec;//only second and usecond;printf("useconds:%lld\n", seed);
}int main(int argc, char* argv[])
{int size = 16*16;//32*1024*1024;int myRank, nRanks, localRank = 0;//initializing MPIprintf("argc = %d\n", argc);MPI_CHECK(MPI_Init(&argc, &argv));//本行之后便进入多线程状态,线程数由 mpirun -np 4 ./a.out 的这个4来指定MPI_CHECK(MPI_Comm_rank(MPI_COMM_WORLD, &myRank));// 本线程的线程序号:myRank = 0, 1, 2, 4-1;MPI_CHECK(MPI_Comm_size(MPI_COMM_WORLD, &nRanks));// 本次启动mpi 程序的总线程数 nRanks==4;cout<< "nRanks="<< nRanks<<endl;//calculating localRank based on hostname which is used in selecting a GPUuint64_t hostHashs[nRanks];//每个rank的主机名字的hash值,占据一个 uint64_t 元素,存储于 hostHashs[myRank] 中;cout<<"nRanks = "<<nRanks<<endl;char hostname[1024];getHostName(hostname, 1024);//cout<<"Host Name is "<<hostname<<endl;hostHashs[myRank] = getHostHash(hostname);printf("myRank = %d, hostHash = %lx\n", myRank, hostHashs[myRank]);MPI_CHECK(MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, hostHashs, sizeof(uint64_t), MPI_BYTE, MPI_COMM_WORLD));//if(myRank==0)if(1){for(int i=0; i<nRanks; i++)printf("myRank = %d, hostHash[%d] = %lx\n", myRank, i, hostHashs[i]);}for (int p=0; p<nRanks; p++) {if (p == myRank) break;if (hostHashs[p] == hostHashs[myRank]) {printf("p=%d\n", p);localRank++;//本进程适合持有本地的第几张 gpu 卡}}printf("myRank = %d, localRank-- = %d\n", myRank, localRank);
/* TCP RDMA (Remote Direct Memory Access) GDR (GPU Direct RDMA) 是一种技术,* 它允许在使用RDMA的网络上进行高性能的GPU内存之间的直接数据传输。* 这种技术可以通过网络直接在GPU之间传输数据,而无需将数据先传输到主机内存。* 这有助于减少数据传输的延迟和CPU的参与,从而提高了数据传输的效率。
**/ncclUniqueId id;ncclComm_t comm;float *sendbuff, *recvbuff;cudaStream_t s;//get NCCL unique ID at rank 0 and broadcast it to all othersif (myRank == 0){cout<<"start:  id is"<<endl;for(int i=0; i<128; i++){if(id.internal[i]=='\0')break;printf("%d",id.internal[i]);}cout<<"start end"<<endl;ncclGetUniqueId(&id);//ncclGetUniqueId 是获得 an Internet socket address, 即,当前机器的ip和port,作为servercout<<"  end:  id is "<<endl;for(int i=0; i<128; i++){if(id.internal[i]=='\0')break;printf("%d",id.internal[i]);}cout<<"end end"<<endl;
#if SOCKET_SIZEcout<<"sizeof(sockaddr_in6) = "<<sizeof(sockaddr_in6)<<endl;
#endif}MPI_CHECK(MPI_Bcast((void *)&id, sizeof(id), MPI_BYTE, 0, MPI_COMM_WORLD));//将进程0,即root,的 socket 地址,广播给其他进程;//printf("LL:: MPI_Bcast()\n");fflush(stdout);//picking a GPU based on localRank, allocate device buffersCUDA_CHECK(cudaSetDevice(localRank));//每个进程都set一个自己的gpu设备,并从中分配两块显存空间 sendbuff和 recvbuff;CUDA_CHECK(cudaMalloc(&sendbuff, size * sizeof(float)));CUDA_CHECK(cudaMalloc(&recvbuff, size * sizeof(float)));CUDA_CHECK(cudaStreamCreate(&s));//创建本线程自己的streamlong long  seed = 0;get_seed(seed);
//void init_dev_vectors(float A_d, int n, float* B_d, int rank, int seed)init_dev_vectors(sendbuff, recvbuff, size, myRank, seed);//initializing NCCLNCCL_CHECK(ncclCommInitRank(&comm, nRanks, id, myRank));//创建一个新的通信子,多线程多进程场景使用。/*********************************************************************************************************** ncclResult_t ncclCommInitRank(ncclComm_t* comm, int nranks, ncclUniqueId commId, int rank)* Creates a new communicator (multi thread/process version).* rank must be between 0 and nranks-1 and unique within a communicator clique.* Each rank is associated to a CUDA device, which has to be set before calling ncclCommInitRank.* ncclCommInitRank implicitly synchronizes with other ranks,* hence it must be called by different threads/processes or use ncclGroupStart/ncclGroupEnd.**********************************************************************************************************///communicating using NCCLNCCL_CHECK(ncclAllReduce((const void*)sendbuff, (void*)recvbuff, size, ncclFloat, /*ncclMax*/ ncclSum, comm, s));//completing NCCL operation by synchronizing on the CUDA streamCUDA_CHECK(cudaStreamSynchronize(s));if(myRank == 1)fetch_dev_vector(recvbuff, size, myRank);//free device buffersCUDA_CHECK(cudaFree(sendbuff));CUDA_CHECK(cudaFree(recvbuff));//finalizing NCCLncclCommDestroy(comm);//finalizing MPIMPI_CHECK(MPI_Finalize());printf("[MPI Rank %d] Success \n", myRank);return 0;
}

2,构建

2.1 Makefile


LD_FLAGS := -lnccl -L/usr/local/cuda/lib64 -lcudart -I/usr/local/cuda/includeMPI_FLAGS := -I /usr/lib/x86_64-linux-gnu/openmpi/include -L /usr/lib/x86_64-linux-gnu/openmpi/lib -lmpi -lmpi_cxxEXE :=  multiProcess_multiDevice_oneServer_allreduce
#singleProcess_multiDevice_oneServer_allreduce
all: $(EXE)singleProcess_multiDevice_oneServer_allreduce: singleProcess_multiDevice_oneServer_allreduce.cppg++ -g $< -o $@ $(LD_FLAGS)
# singleProcess_multiDevice_oneServer_allreducemultiProcess_multiDevice_oneServer_allreduce: multiProcess_multiDevice_oneServer_allreduce.cppg++ -g $< -o $@ $(LD_FLAGS) $(MPI_FLAGS)
# ../../ex_openmpi/local/bin/mpirun -np 2 ./oneServer_multiDevice_multiThreadmpi_test: mpi_test.cppg++ -g $< -o $@ $(LD_FLAGS) $(MPI_FLAGS).PHONY: clean
clean:-rm $(EXE)

2.2 构建

$ make

3,运行

../../ex_openmpi/local/bin/mpirun -np 2 ./multiProcess_multiDevice_oneServer_allreduce

4,效果

allreduce ncclSum的数学效果,每个进程的recvbuff都满足:

 recvbuff[i] = sendbuff_rank0[i] + sendbuff_rank1[i] + ... + sendbuff_rankn-1[i]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/229921.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何正确使用docker搭建靶场--pikachu

在Linux中搭建靶场——pikachu 1.开启docker systemctl start docker 2.查看docker状态 systemctl status docker 3.查看docker存在那些镜像 docker images 4.拉取镜像&#xff0c;这里是以pikachu为例因此需要一个php5的版本 &#xff08;1&#xff09;打开代理&#xff…

照片怎么抠图换背景?不会的伙伴看这里!

随着数字技术的日新月异&#xff0c;抠图换背景技术已成为图像处理的翘楚。它是指将图片中的主体与背景分离&#xff0c;再将其置于新的背景之上。这一技艺在广告、摄影和设计等领域中占据着举足轻重的地位。本文将为您揭示三种抠图换背景的秘籍&#xff0c;助您轻松掌握这一技…

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的图像剪切(ROI)功能(C#)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的图像剪切&#xff08;ROI&#xff09;功能&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机的图像剪切&#xff08;ROI&#xff09;功能的技术背景CameraExplorer如何使用图像剪切&#xff08;ROI&#xff09;…

electron预加载脚本

webPreferences 指定预加载脚本,可以使用部分node脚本 webPreferences: {preload: path.join(__dirname, "preload.js"),},创建preload.js 中 测试文件读取功能 const fs require(fs) const text fs.readFileSync(package.json, utf-8)console.log(text)报错,为了…

基于果蝇算法优化的Elman神经网络数据预测 - 附代码

基于果蝇算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于果蝇算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于果蝇优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针…

Windows搭建RTSP视频流服务(EasyDarWin服务器版)

文章目录 引言1、安装FFmpeg2、安装EasyDarWin3、实现本地\虚拟摄像头推流服务4、使用VLC或PotPlayer可视化播放器播放视频5、RTSP / RTMP系列文章 引言 RTSP和RTMP视频流的区别 RTSP &#xff08;Real-Time Streaming Protocol&#xff09;实时流媒体协议。 RTSP定义流格式&am…

Apache Commons VFS(虚拟文件系统)使用详解

第1章&#xff1a;Apache Commons VFS简介 大家好&#xff0c;我是小黑&#xff0c;今天我们来聊聊Apache Commons VFS&#xff08;虚拟文件系统&#xff09;。想必很多朋友都听说过或者用过Apache Commons的其他库&#xff0c;但是VFS可能还有点陌生。那么&#xff0c;什么是…

Tomcat Notes: Deployment File

This is a personal study notes of Apache Tomcat. Below are main reference material. - YouTube Apache Tomcat Full Tutorial&#xff0c;owed by Alpha Brains Courses. https://www.youtube.com/watch?vrElJIPRw5iM&t801s 1、Tomcat deployment1.1、Two modes of …

test ui-02-UI 测试组件之 Appium 入门介绍

Appium简介 正如主页所述&#xff0c;Appium的目标是支持许多不同平台&#xff08;移动、Web、桌面等&#xff09;的UI自动化。 不仅如此&#xff0c;它还旨在支持用不同语言&#xff08;JS、Java、Python等&#xff09;编写的自动化代码。 将所有这些功能组合到一个程序中是…

贝叶斯推断:细谈贝叶斯变分和贝叶斯网络

1. 贝叶斯推断 统计推断这件事大家并不陌生&#xff0c;如果有一些采样数据&#xff0c;我们就可以去建立模型&#xff0c;建立模型之后&#xff0c;我们通过对这个模型的分析会得到一些结论&#xff0c;不管我们得到的结论是什么样的结论&#xff0c;我们都可以称之为是某种推…

数据结构【线性表篇】(一)

数据结构【线性表篇】(一&#xff09; 文章目录 数据结构【线性表篇】(一&#xff09;前言为什么突然想学算法了&#xff1f;为什么选择码蹄集作为刷题软件&#xff1f; 目录一、顺序表(一)、顺序表的定义(二)、顺序表的插入删除(三)、顺序表的查找 二、完整代码(一)、顺序表的…

骑砍战团MOD开发(29)-module_scenes.py游戏场景

骑砍1战团mod开发-场景制作方法_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Cw411N7G4/ 一.骑砍游戏场景 骑砍战团中进入城堡,乡村,战斗地图都被定义为场景,由module_scenes.py进行管理。 scene(游戏场景) 天空盒(Skyboxes.py) 地形(terrain code) 场景物(scene_…

《3D数学基础-图形和游戏开发》阅读笔记 | 3D数学基础 (学习中)

文章目录 3D数学基础矢量/向量概述 - 什么是向量单位矢量&#xff1a;只关注方向不关注大小 数学运算矢量的加法与减法减法的几何意义计算一个点到另一个点的位移矢量的点积与叉积 矩阵矩阵的几何意义 3D数学基础 矢量/向量 在笔记中 变量使用小写字母表示&#xff0c;a由于…

Springboot集成RabbitMq二

接上一篇&#xff1a;Springboot集成RabbitMq一-CSDN博客 1、搭建项目-消费者 与之前一样 2、创建配置类 package com.wym.rabbitmqconsumer.utils;import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.spring…

2023-12-16 LeetCode每日一题(统计区间中的整数数目)

2023-12-16每日一题 一、题目编号 2276. 统计区间中的整数数目二、题目链接 点击跳转到题目位置 三、题目描述 给你区间的 空 集&#xff0c;请你设计并实现满足要求的数据结构&#xff1a; **新增&#xff1a;**添加一个区间到这个区间集合中。 **统计&#xff1a;**计算…

华为服务器安装银河麒麟V10操作系统(IBMC安装)

iBMC是华为面向服务器全生命周期的服务器嵌入式管理系统。提供硬件状态监控、部署、节能、安全等系列管理工具&#xff0c;标准化接口构建服务器管理更加完善的生态系统。 服务器BMC IP&#xff1a;192.168.2.100 一、准备工作 1、确保本机和服务器BMC管理口在同一网络 2、银…

用Audio2Face驱动UE - MetaHuman

新的一年咯&#xff0c;很久没发博客了&#xff0c;就发两篇最近的研究吧。 开始之前说句话&#xff0c;别轻易保存任何内容&#xff0c;尤其是程序员不要轻易Ctrl S 在UE中配置Audio2Face 先检查自身电脑配置看是否满足&#xff0c;按最小配置再带个UE可能会随时崩&#x…

设计模式--适配器模式

适配器模式 适配器模式&#xff08;Adapter&#xff09;&#xff0c;将一个类的接口转换为客户希望的另一个接口&#xff0c;Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 系统的数据和行为都正确&#xff0c;但接口不符合时&#xff0c;我们应该…

19、BLIP-2

简介 github 通过利用预训练的视觉模型和语言模型来提升多模态效果和降低训练成本&#xff0c;预训练的视觉模型能够提供高质量的视觉表征&#xff0c;预训练的语言模型则提供了强大的语言生成能力。 实现过程 为了弥合模态差距&#xff0c;提出了一个分两个阶段预训练的 Qu…

YOLOv5算法进阶改进(10)— 更换主干网络之MobileViTv3 | 轻量化Backbone

前言:Hello大家好,我是小哥谈。MobileViTv3是一种改进的模型架构,用于图像分类任务。它是在MobileViTv1和MobileViTv2的基础上进行改进的,通过引入新的模块和优化网络结构来提高性能。本节课就给大家介绍一下如何在主干网络中引入MobileViTv3网络结构,希望大家学习之后能够…