CUDA dan Remote GPU

CUDA baik untuk semua orang, selama ada kartu video dari Nvidia. Tapi apa yang harus dilakukan ketika tidak ada kartu grafis Nvidia di laptop favorit Anda? Atau apakah Anda perlu melakukan pengembangan di mesin virtual?


Saya akan mencoba untuk mempertimbangkan dalam artikel ini solusi seperti kerangka kerja rCUDA (Remote CUDA), yang akan membantu ketika ada kartu video Nvidia, tetapi tidak dipasang di mesin tempat aplikasi CUDA seharusnya diluncurkan. Bagi yang berminat, selamat datang ke kucing.


TLDR

rCUDA (Remote CUDA) - kerangka kerja yang mengimplementasikan API CUDA, memungkinkan Anda untuk menggunakan kartu video jarak jauh. Itu dalam versi beta yang berfungsi, hanya tersedia di Linux. Tujuan utama rCUDA adalah kompatibilitas penuh dengan API CUDA, Anda tidak perlu memodifikasi kode dengan cara apa pun, cukup tetapkan variabel lingkungan khusus.


Apa itu rCUDA


rCUDA (Remote CUDA) adalah kerangka kerja yang mengimplementasikan API CUDA, memungkinkan Anda untuk menggunakan kartu video yang terletak di mesin jarak jauh untuk komputasi CUDA tanpa membuat perubahan apa pun pada kode Anda. Dikembangkan di Universitas Politeknik Valencia ( tim -uda ).


Keterbatasan


Hanya sistem GNU / Linux yang saat ini didukung, namun, pengembang menjanjikan dukungan Windows di masa depan. Versi rCUDA saat ini, 18.03beta, kompatibel dengan CUDA 5-8, artinya, CUDA 9 tidak didukung. Pengembang menyatakan kompatibilitas penuh dengan API CUDA, dengan pengecualian grafis.


Kemungkinan menggunakan case


  1. Menjalankan aplikasi CUDA di mesin virtual ketika meneruskan kartu video tidak nyaman atau tidak mungkin, misalnya, ketika kartu video ditempati oleh host, atau ketika ada lebih dari satu mesin virtual.
  2. Laptop tanpa kartu grafis tersendiri.
  3. Keinginan untuk menggunakan beberapa kartu video (pengelompokan). Secara teoritis, Anda dapat menggunakan semua kartu video yang tersedia di tim, termasuk bersama-sama.

Instruksi singkat


Konfigurasi tes


Pengujian dilakukan pada konfigurasi berikut:


Server:
Ubuntu 16.04, GeForce GTX 660


Pelanggan:
Mesin virtual dengan Ubuntu 16.04 pada laptop tanpa kartu grafis diskrit.


Mendapatkan rCUDA


Tahap yang paling sulit. Sayangnya, saat ini, satu-satunya cara untuk mendapatkan salinan kerangka kerja ini adalah dengan mengisi formulir permintaan yang sesuai di situs web resmi. Namun, pengembang berjanji untuk merespons dalam 1-2 hari. Dalam kasus saya, mereka mengirimi saya distribusi pada hari yang sama.


Instal CUDA


Pertama, Anda perlu menginstal CUDA Toolkit di server dan klien (bahkan jika klien tidak memiliki kartu video nvidia). Untuk melakukan ini, Anda dapat mengunduhnya dari situs resmi atau menggunakan repositori. Hal utama adalah menggunakan versi yang tidak lebih tinggi dari 8. Dalam contoh ini, installer .run dari situs resmi digunakan .


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

Penting! Pada klien, Anda harus menolak untuk menginstal driver nvidia. Secara default, CUDA Toolkit akan tersedia di / usr / local / cuda /. Instal Sampel CUDA, Anda akan membutuhkannya.


Instal rCUDA


Kami akan membongkar arsip yang diterima dari pengembang ke direktori home kami di server dan di klien.


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

Anda perlu melakukan tindakan ini baik di server maupun di klien.


Mulai daemon rCUDA di server


 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 

Ganti <XXX> dengan nama pengguna Anda. Gunakan ./rCUDAd -iv jika Anda ingin melihat keluaran verbose.


Pengaturan klien


Mari kita buka terminal pada klien, di mana kita akan menjalankan kode CUDA di masa depan. Di sisi klien, kita perlu "mengganti" pustaka CUDA standar dengan pustaka rCUDA, yang dengannya kita menambahkan jalur yang sesuai ke variabel lingkungan LD_LIBRARY_PATH. Kita juga perlu menentukan jumlah server dan alamatnya (dalam contoh saya, itu akan menjadi satu).


 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 #     

Perakitan dan peluncuran


Mari kita coba membangun dan menjalankan beberapa contoh.


Contoh 1


Mari kita mulai dengan contoh perangkat sederhana Contoh yang hanya menampilkan pengaturan CUDA untuk perangkat yang kompatibel, yaitu, dalam kasus kami, remote GTX660.


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

Penting! Tanpa EXTRA_NVCCFLAGS = - cudart = membagikan keajaiban tidak akan berfungsi
Ganti <YYY> dengan jalur yang Anda tentukan untuk Sampel CUDA saat memasang CUDA.


Jalankan contoh rakitan:


 ./deviceQuery 

Jika Anda melakukan semuanya dengan benar, hasilnya akan seperti ini:


Hasil
 ./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 

Hal terpenting yang harus kita lihat:


Device0 = GeForce GTX 660
Hasil = LULUS

Hebat! Kami berhasil membangun dan menjalankan aplikasi CUDA pada mesin tanpa kartu grafis diskrit, menggunakan kartu video yang dipasang pada server jarak jauh untuk tujuan ini.


Penting! Jika output aplikasi dimulai dengan garis-garis bentuk:


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

itu berarti bahwa perlu menambahkan baris-baris berikut ke file /etc/security/limits.conf di server dan di klien:


 * hard memlock unlimited * soft memlock unlimited 

Dengan demikian, Anda akan mengizinkan semua pengguna (*) tidak terbatas (tidak terbatas) memblokir memori (memlock). Akan lebih baik untuk mengganti * dengan pengguna yang diinginkan, dan alih-alih tanpa batas pilih hak yang kurang gemuk.


Contoh 2


Sekarang mari kita coba sesuatu yang lebih menarik. Kami akan menguji implementasi produk skalar vektor menggunakan memori bersama dan sinkronisasi ("Teknologi CUDA dalam Contoh" Sanders J. Kendrot E. 5.3.1).


Dalam contoh ini, kami menghitung produk skalar dari dua vektor dimensi 33 * 1024, membandingkan jawaban dengan hasil yang diperoleh pada 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); } 

Bangun dan jalankan:


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

Hasil ini memberi tahu kita bahwa semuanya baik-baik saja dengan kita:


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

Contoh 3


Jalankan uji CUDA-matrixMulCUBLAS standar lainnya (multiplikasi matriks).


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

Hasil

[Matrix Multiply CUBLAS] - Mulai ...
Perangkat GPU 0: "GeForce GTX 660" dengan kemampuan komputasi 3.0


MatrixA (640,480), MatrixB (480,320), MatrixC (640,320)
Menghitung hasil menggunakan CUBLAS ... selesai.
Kinerja = 436,24 GFlop / s, Waktu = 0,451 msec, Ukuran = 196608000 Ops
Menghitung hasil menggunakan host CPU ... selesai.
Membandingkan CUBLAS Matrix Multiply dengan hasil CPU: PASS


CATATAN: Sampel CUDA tidak dimaksudkan untuk pengukuran kinerja. Hasil dapat bervariasi ketika Peningkatan GPU diaktifkan.


Menarik bagi kami:


Kinerja = 436,24 GFlop / s,
Membandingkan CUBLAS Matrix Multiply dengan hasil CPU: PASS

Keamanan


Saya tidak menemukan menyebutkan metode otorisasi dalam dokumentasi untuk rCUDA. Saya pikir saat ini hal paling sederhana yang dapat dilakukan adalah membuka akses ke port yang diinginkan (8308) hanya dari alamat tertentu.


Menggunakan iptables, akan terlihat seperti ini:


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

Untuk selebihnya, saya meninggalkan masalah keamanan di luar cakupan posting ini.


Sumber dan tautan

[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 dan S. Schultz, “GPU Lokal dan Remote Berkinerja Mirip dengan EDR 100G InfiniBand,” dalam proses Konferensi Middleware Internasional, Vancouver, BC, Kanada, Desember 2015.
[4] C. Reaño dan F. Silla, “Perbandingan Kinerja Kerangka Virtualisasi GPU Remote CUDA”, dalam proses Konferensi Internasional tentang Komputasi Cluster, Chicago, IL, AS, September 2015.

Source: https://habr.com/ru/post/id416127/


All Articles