CUDA و GPU عن بعد

CUDA جيدة للجميع ، طالما أن هناك بطاقة فيديو من Nvidia في متناول اليد. ولكن ماذا تفعل عندما لا توجد بطاقة رسومات Nvidia على الكمبيوتر المحمول المفضل لديك؟ أو هل تحتاج إلى إجراء تطوير في جهاز افتراضي؟


سأحاول أن أعتبر في هذا المقال حلًا مثل إطار rCUDA (Remote CUDA) ، والذي سيساعد عندما يكون هناك بطاقة فيديو Nvidia ، ولكنه غير مثبت في الجهاز الذي من المفترض أن يتم تشغيل تطبيقات CUDA عليه. لأولئك المهتمين ، مرحبا بكم في القطط.


TLDR

rCUDA (Remote CUDA) - إطار عمل يطبق واجهة برمجة تطبيقات CUDA ، مما يتيح لك استخدام بطاقة فيديو عن بعد. إنه في إصدار تجريبي عام ، متاح فقط في Linux. الهدف الرئيسي من rCUDA هو التوافق التام مع CUDA API ، ولا تحتاج إلى تعديل التعليمات البرمجية بأي شكل من الأشكال ، فقط قم بتعيين متغيرات البيئة الخاصة.


ما هو rCUDA


rCUDA (Remote CUDA) هو إطار عمل يطبق واجهة برمجة تطبيقات CUDA ، مما يسمح لك باستخدام بطاقة فيديو موجودة على الجهاز البعيد لحوسبة CUDA دون إجراء أي تغييرات على التعليمات البرمجية الخاصة بك. تم تطويره في جامعة البوليتكنيك في فالنسيا ( فريق RCuda ).


القيود


أنظمة غنو / لينكس فقط هي المدعومة حاليًا ، ومع ذلك ، يعد المطورون بدعم Windows في المستقبل. الإصدار الحالي من rCUDA ، 18.03beta ، متوافق مع CUDA 5-8 ، أي أن CUDA 9 غير مدعوم. أعلن المطورون عن التوافق التام مع CUDA API ، باستثناء الرسومات.


حالات الاستخدام الممكنة


  1. تشغيل تطبيقات CUDA في جهاز ظاهري عند إعادة توجيه بطاقة الفيديو أمر غير مريح أو مستحيل ، على سبيل المثال ، عندما يشغل مضيف بطاقة الفيديو ، أو عندما يكون هناك أكثر من جهاز ظاهري واحد.
  2. كمبيوتر محمول بدون بطاقة رسومات منفصلة.
  3. الرغبة في استخدام بطاقات فيديو متعددة (التكتل). نظريًا ، يمكنك استخدام جميع بطاقات الفيديو المتوفرة في الفريق ، بما في ذلك بشكل مشترك.

تعليمات موجزة


تكوين الاختبار


تم إجراء الاختبار على التكوين التالي:


الخادم:
أوبونتو 16.04 ، GeForce GTX 660


الزبون:
جهاز افتراضي مع Ubuntu 16.04 على كمبيوتر محمول بدون بطاقة رسومات منفصلة.


الحصول على rCUDA


أصعب مرحلة. للأسف ، في الوقت الحالي ، الطريقة الوحيدة للحصول على نسختك من هذا الإطار هي ملء نموذج الطلب المناسب على الموقع الرسمي. ومع ذلك ، يعد المطورون بالاستجابة في غضون يوم أو يومين. في حالتي ، أرسلوا لي توزيعًا في نفس اليوم.


تثبيت CUDA


تحتاج أولاً إلى تثبيت مجموعة أدوات CUDA على الخادم والعميل (حتى إذا لم يكن لدى العميل بطاقة فيديو 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 في المستقبل. من جانب العميل ، نحتاج إلى "استبدال" مكتبات CUDA القياسية بمكتبات rCUDA ، والتي نضيف لها المسارات المناسبة لمتغير البيئة 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


دعنا نبدأ بمثال بسيط لجهاز الاستعلام الذي يعرض ببساطة إعدادات CUDA لجهاز متوافق ، أي في حالتنا ، جهاز التحكم عن بعد GTX660.


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

هام! بدون EXTRA_NVCCFLAGS = - cudart = شارك لن تعمل المعجزة
استبدل <YYY> بالمسار الذي حددته لعينات CUDA عند تثبيت CUDA.


تشغيل المثال المجمع:


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

أهم شيء يجب أن نراه:


Device0 = 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 ، ومقارنة الإجابة بالنتيجة التي تم الحصول عليها على وحدة المعالجة المركزية.


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
وحدة المعالجة المركزية - 2.57236e + 13

مثال 3


قم بإجراء اختبار CUDA-matrixMulCUBLAS قياسي آخر (ضرب المصفوفة).


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

النتيجة

[Matrix Multiply CUBLAS] - البدء ...
جهاز GPU 0: "GeForce GTX 660" مع إمكانية الحساب 3.0


MatrixA (640،480)، MatrixB (480،320)، MatrixC (640،320)
نتيجة الحوسبة باستخدام CUBLAS ... تم.
الأداء = 436.24 GFlop / s ، الوقت = 0.451 مللي ثانية ، الحجم = 196608000 Ops
نتيجة الحوسبة باستخدام وحدة المعالجة المركزية المضيفة ... تم.
مقارنة CUBLAS Matrix Multiply مع نتائج وحدة المعالجة المركزية: PASS


ملاحظة: عينات CUDA غير مخصصة لقياسات الأداء. قد تختلف النتائج عند تمكين GPU Boost.


للاهتمام بنا:


الأداء = 436.24 GFlop / s ،
مقارنة CUBLAS Matrix Multiply مع نتائج وحدة المعالجة المركزية: 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 and S. Schultz، "Local and Remote GPUs أداء مماثل مع EDR 100G InfiniBand" ، في وقائع مؤتمر الوسيطة الدولي ، فانكوفر ، كولومبيا البريطانية ، كندا ، ديسمبر 2015.
[4] C. Reaño and F. Silla ، "مقارنة أداء إطارات CUDA Remote GPU Virtualization Framework" ، في وقائع المؤتمر الدولي حول الحوسبة العنقودية ، شيكاغو ، إلينوي ، الولايات المتحدة الأمريكية ، سبتمبر 2015.

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


All Articles