CUDA和远程GPU

只要手边有Nvidia的视频卡,CUDA对每个人都有益。 但是,如果您喜欢的笔记本电脑上没有Nvidia显卡,该怎么办? 还是需要在虚拟机中进行开发?


我将在本文中尝试考虑诸如rCUDA(远程CUDA)框架之类的解决方案,该解决方案在存在Nvidia图形卡时会有所帮助,但未安装在本应启动CUDA应用程序的计算机中。 对于那些感兴趣的人,欢迎光临。


TLDR

rCUDA (远程CUDA)-实现CUDA API的框架,允许您使用远程视频卡。 它处于有效的Beta版,仅在Linux下可用。 rCUDA的主要目标是与CUDA API完全兼容,您无需以任何方式修改代码,只需设置特殊的环境变量即可。


什么是rCUDA


rCUDA (远程CUDA)是实现CUDA API的框架,允许您使用位于远程计算机上的视频卡进行CUDA计算,而无需对代码进行任何更改。 由瓦伦西亚理工大学( rcuda-team )开发。


局限性


当前仅支持GNU / Linux系统,但是开发人员承诺将来会支持Windows。 rCUDA的当前版本18.03beta与CUDA 5-8兼容,即不支持CUDA 9。 开发人员宣布除图形外,与CUDA API完全兼容。


可能的用例


  1. 当不方便或无法转发视频卡时(例如,当视频卡被主机占用时,或有多个虚拟机时),在虚拟机中运行CUDA应用程序。
  2. 没有独立显卡的笔记本电脑。
  3. 希望使用多个视频卡(群集)。 从理论上讲,您可以使用团队中所有可用的视频卡,包括联合使用。

简要说明


测试配置


对以下配置进行了测试:


伺服器:
Ubuntu 16.04,GeForce GTX 660


客户:
笔记本电脑上装有Ubuntu 16.04的虚拟机,没有独立的图形卡。


获取rCUDA


最困难的阶段。 不幸的是,目前,获取此框架副本的唯一方法是在官方网站上填写适当的请求表 。 但是,开发人员承诺在1-2天内做出回应。 就我而言,他们是在同一天向我发送了一份分配。


安装CUDA


首先,您需要在服务器和客户端上安装CUDA Toolkit(即使客户端没有nvidia视频卡)。 为此,您可以从官方网站下载它或使用存储库。 最主要的是使用不高于8的版本。在此示例中, 使用官方网站的.run安装程序。


chmod +x cuda_8.0.61_375.26_linux.run ./cuda_8.0.61_375.26_linux.run 

重要! 在客户端上,您应该拒绝安装nvidia驱动程序。 默认情况下,CUDA工具包将位于/ usr / local / cuda /。 安装CUDA示例,您将需要它们。


安装rCUDA


我们会将从开发人员那里收到的归档文件解压缩到服务器和客户端上的主目录中。


 tar -xvf rCUDA*.tgz -C ~/ mv ~/rCUDA* ~/rCUDA 

您需要在服务器和客户端上都执行这些操作。


在服务器上启动rCUDA守护程序


 export PATH=$PATH/usr/local/cuda/bin export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64:/home/<XXX>/rCUDA/lib/cudnn cd ~/rCUDA/bin ./rCUDAd 

将<XXX>替换为您的用户名。 如果要查看详细的输出,请使用./rCUDAd -iv。


客户端设置


让我们在客户端上打开终端,以后我们将在其中运行CUDA代码。 在客户端,我们需要用rCUDA库“替换”标准CUDA库,为此我们向环境变量LD_LIBRARY_PATH添加了适当的路径。 我们还需要指定服务器的数量及其地址(在我的示例中,将是一台)。


 export PATH=$PATH/usr/local/cuda/bin export LD_LIBRARY_PATH=/home/<XXX>/rCUDA/lib/:$LD_LIBRARY_PATH export RCUDA_DEVICE_COUNT=1 #    (),     export RCUDA_DEVICE_0=<IP  >:0 #     

组装和发射


让我们尝试构建并运行一些示例。


例子1


让我们从一个简单的deviceQuery示例开始,该示例仅显示兼容设备(在我们的情况下为远程GTX660)的CUDA设置。


 cd <YYY>/NVIDIA_CUDA-8.0_Samples/1_Utilities/deviceQuery make EXTRA_NVCCFLAGS=--cudart=shared 

重要! 没有EXTRA_NVCCFLAGS =-cudart =共享奇迹将不起作用
在安装CUDA时,将<YYY>替换为为CUDA Samples指定的路径。


运行组装的示例:


 ./deviceQuery 

如果您正确执行了所有操作,则结果将如下所示:


结果
 ./deviceQuery Starting... CUDA Device Query (Runtime API) version (CUDART static linking) Detected 1 CUDA Capable device(s) Device 0: "GeForce GTX 660" CUDA Driver Version / Runtime Version 9.0 / 8.0 CUDA Capability Major/Minor version number: 3.0 Total amount of global memory: 1994 MBytes (2090991616 bytes) ( 5) Multiprocessors, (192) CUDA Cores/MP: 960 CUDA Cores GPU Max Clock rate: 1072 MHz (1.07 GHz) Memory Clock rate: 3004 Mhz Memory Bus Width: 192-bit L2 Cache Size: 393216 bytes Maximum Texture Dimension Size (x,y,z) 1D=(65536), 2D=(65536, 65536), 3D=(4096, 4096, 4096) Maximum Layered 1D Texture Size, (num) layers 1D=(16384), 2048 layers Maximum Layered 2D Texture Size, (num) layers 2D=(16384, 16384), 2048 layers Total amount of constant memory: 65536 bytes Total amount of shared memory per block: 49152 bytes Total number of registers available per block: 65536 Warp size: 32 Maximum number of threads per multiprocessor: 2048 Maximum number of threads per block: 1024 Max dimension size of a thread block (x,y,z): (1024, 1024, 64) Max dimension size of a grid size (x,y,z): (2147483647, 65535, 65535) Maximum memory pitch: 2147483647 bytes Texture alignment: 512 bytes Concurrent copy and kernel execution: Yes with 1 copy engine(s) Run time limit on kernels: Yes Integrated GPU sharing Host Memory: No Support host page-locked memory mapping: Yes Alignment requirement for Surfaces: Yes Device has ECC support: Disabled Device supports Unified Addressing (UVA): Yes Device PCI Domain ID / Bus ID / location ID: 0 / 1 / 0 Compute Mode: < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) > deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 9.0, CUDA Runtime Version = 8.0, NumDevs = 1, Device0 = GeForce GTX 660 Result = PASS 

我们应该看到的最重要的事情是:


设备0 = GeForce GTX 660
结果=通过

太好了! 我们设法在没有独立显卡的计算机上构建并运行CUDA应用程序,为此目的,它使用了安装在远程服务器上的视频卡。


重要! 如果应用程序输出以以下形式的行开头:


 mlock error: Cannot allocate memory rCUDA warning: 1007.461 mlock error: Cannot allocate memory 

这意味着有必要在服务器和客户端上的“ /etc/security/limits.conf”文件中添加以下行:


 * hard memlock unlimited * soft memlock unlimited 

因此,您将允许所有用户(*)无限(无限)阻塞内存(memlock)。 最好用所需的用户替换*,而不是无限选择较少的胖权限。


例子2


现在让我们尝试一些更有趣的事情。 我们将使用共享内存和同步来测试矢量的标量积的实现方式(“ CUDA技术示例” Sanders J. Kendrot E. 5.3.1)。


在此示例中,我们计算了尺寸为33 * 1024的两个向量的标量积,并将答案与CPU上获得的结果进行了比较。


dotProd.cu
 #include <stdio.h> #define imin(a,b) (a<b?a:b) const int N = 33 * 1024; const int threadsPerBlock = 256; const int blocksPerGrid = imin(32, (N+threadsPerBlock-1) / threadsPerBlock); __global__ void dot(float* a, float* b, float* c) { __shared__ float cache[threadsPerBlock]; int tid = threadIdx.x + blockIdx.x * blockDim.x; int cacheIndex = threadIdx.x; float temp = 0; while (tid < N){ temp += a[tid] * b[tid]; tid += blockDim.x * gridDim.x; } // set the cache values cache[cacheIndex] = temp; // synchronize threads in this block __syncthreads(); // for reductions, threadsPerBlock must be a power of 2 // because of the following code int i = blockDim.x/2; while (i != 0){ if (cacheIndex < i) cache[cacheIndex] += cache[cacheIndex + i]; __syncthreads(); i /= 2; } if (cacheIndex == 0) c[blockIdx.x] = cache[0]; } int main (void) { float *a, *b, c, *partial_c; float *dev_a, *dev_b, *dev_partial_c; // allocate memory on the cpu side a = (float*)malloc(N*sizeof(float)); b = (float*)malloc(N*sizeof(float)); partial_c = (float*)malloc(blocksPerGrid*sizeof(float)); // allocate the memory on the gpu cudaMalloc((void**)&dev_a, N*sizeof(float)); cudaMalloc((void**)&dev_b, N*sizeof(float)); cudaMalloc((void**)&dev_partial_c, blocksPerGrid*sizeof(float)); // fill in the host memory with data for(int i=0; i<N; i++) { a[i] = i; b[i] = i*2; } // copy the arrays 'a' and 'b' to the gpu cudaMemcpy(dev_a, a, N*sizeof(float), cudaMemcpyHostToDevice); cudaMemcpy(dev_b, b, N*sizeof(float), cudaMemcpyHostToDevice); dot<<<blocksPerGrid, threadsPerBlock>>>(dev_a, dev_b, dev_partial_c); // copy the array 'c' back from the gpu to the cpu cudaMemcpy(partial_c,dev_partial_c, blocksPerGrid*sizeof(float), cudaMemcpyDeviceToHost); // finish up on the cpu side c = 0; for(int i=0; i<blocksPerGrid; i++) { c += partial_c[i]; } #define sum_squares(x) (x*(x+1)*(2*x+1)/6) printf("GPU - %.6g \nCPU - %.6g\n", c, 2*sum_squares((float)(N-1))); // free memory on the gpu side cudaFree(dev_a); cudaFree(dev_b); cudaFree(dev_partial_c); // free memory on the cpu side free(a); free(b); free(partial_c); } 

构建并运行:


 /usr/local/cuda/bin/nvcc --cudart=shared dotProd.cu -o dotProd ./dotProd 

这个结果告诉我们一切都很好:


GPU-2.57236e + 13
CPU-2.57236e + 13

例子3


运行另一个标准的CUDA-matrixMulCUBLAS测试(矩阵乘法)。


 cd < YYY>/NVIDIA_CUDA-8.0_Samples/0_Simple/matrixMulCUBLAS make EXTRA_NVCCFLAGS=--cudart=shared ./matrixMulCUBLAS 

结果

[矩阵乘以CUBLAS]-开始...
GPU设备0:具有计算能力3.0的“ GeForce GTX 660”


矩阵A(640,480),矩阵B(480,320),矩阵C(640,320)
使用CUBLAS计算结果...完成。
性能= 436.24 GFlop / s,时间= 0.451毫秒,大小= 196608000 Ops
使用主机CPU的计算结果...完成。
比较CUBLAS矩阵与CPU结果:PASS


注意:CUDA示例不用于性能测量。 启用GPU Boost后,结果可能会有所不同。


我们感兴趣的是:


性能= 436.24 GFlop / s,
比较CUBLAS矩阵与CPU结果:PASS

安全性


我没有在rCUDA的文档中提及任何授权方法。 我认为目前最简单的方法是仅从特定地址打开对所需端口(8308)的访问。


使用iptables,它将看起来像这样:


 iptables -A INPUT -m state --state NEW -p tcp -s < > --dport 8308 -j ACCEPT 

对于其余的内容,我将安全性问题超出了本文的范围。


来源和链接

[1] http://www.rcuda.net/pub/rCUDA_guide.pdf
[2] http://www.rcuda.net/pub/rCUDA_QSG.pdf
[3] C.Reaño,F。Silla,G。Shainer和S. Schultz,“本地和远程GPU与EDR 100G InfiniBand的性能相似”,在国际中间件大会的会议记录中,加拿大不列颠哥伦比亚省温哥华,2015年12月。
[4] C.Reaño和F. Silla,“ CUDA远程GPU虚拟化框架的性能比较”,在美国伊利诺伊州芝加哥举行的国际集群计算会议上,2015年9月。

Source: https://habr.com/ru/post/zh-CN416127/


All Articles