GPU پروگرامنگ C ++ کے ساتھ۔

Gpu Programming With C



اس گائیڈ میں ، ہم C ++ کے ساتھ GPU پروگرامنگ کی طاقت کو دریافت کریں گے۔ ڈویلپرز C ++ کے ساتھ ناقابل یقین کارکردگی کی توقع کر سکتے ہیں ، اور کم درجے کی زبان کے ساتھ GPU کی غیر معمولی طاقت تک رسائی حاصل کرنے سے فی الحال دستیاب کچھ تیز ترین حساب حاصل ہو سکتا ہے۔

تقاضے۔

اگرچہ لینکس کے جدید ورژن کو چلانے کی صلاحیت رکھنے والی کوئی بھی مشین C ++ کمپائلر کو سپورٹ کر سکتی ہے ، آپ کو اس مشق کے ساتھ ساتھ چلنے کے لیے NVIDIA پر مبنی GPU کی ضرورت ہوگی۔ اگر آپ کے پاس جی پی یو نہیں ہے تو ، آپ جی پی یو سے چلنے والی مثال کو ایمیزون ویب سروسز یا اپنی پسند کے دوسرے کلاؤڈ فراہم کنندہ میں گھما سکتے ہیں۔







اگر آپ فزیکل مشین کا انتخاب کرتے ہیں تو ، براہ کرم یقینی بنائیں کہ آپ کے پاس NVIDIA ملکیتی ڈرائیور نصب ہیں۔ آپ اس کے لیے ہدایات یہاں حاصل کر سکتے ہیں: https://linuxhint.com/install-nvidia-drivers-linux/



ڈرائیور کے علاوہ ، آپ کو CUDA ٹول کٹ کی ضرورت ہوگی۔ اس مثال میں ، ہم اوبنٹو 16.04 LTS استعمال کریں گے ، لیکن درج ذیل یو آر ایل پر زیادہ تر بڑی تقسیم کے لیے ڈاؤن لوڈ دستیاب ہیں: https://developer.nvidia.com/cuda-downloads



اوبنٹو کے لیے ، آپ .deb پر مبنی ڈاؤن لوڈ کا انتخاب کریں گے۔ ڈاؤن لوڈ شدہ فائل میں ڈیفالٹ طور پر .deb ایکسٹینشن نہیں ہوگی ، لہذا میں اس کا نام تبدیل کرنے کی سفارش کرتا ہوں تاکہ آخر میں .deb ہو۔ پھر ، آپ انسٹال کرسکتے ہیں:





سودو dpkg -میںpackage-name.deb

آپ کو ممکنہ طور پر GPG کلید انسٹال کرنے کے لیے کہا جائے گا ، اور اگر ایسا ہے تو ، ایسا کرنے کے لیے فراہم کردہ ہدایات پر عمل کریں۔

ایک بار جب آپ یہ کر لیتے ہیں تو ، اپنے ذخیروں کو اپ ڈیٹ کریں:



سودو اپ ڈیٹ حاصل کریں۔
سودو apt-get installمعجزات-اور

ایک بار کام کرنے کے بعد ، میں دوبارہ شروع کرنے کی سفارش کرتا ہوں تاکہ یہ یقینی بنایا جا سکے کہ ہر چیز مناسب طریقے سے بھری ہوئی ہے۔

جی پی یو ڈویلپمنٹ کے فوائد

سی پی یوز بہت سے مختلف آدانوں اور آؤٹ پٹ کو سنبھالتے ہیں اور نہ صرف پروگرام کی ضروریات کی وسیع اقسام سے نمٹنے کے لئے بلکہ مختلف ہارڈ ویئر کنفیگریشنز کو سنبھالنے کے لئے افعال کی ایک بڑی درجہ بندی رکھتے ہیں۔ وہ میموری ، کیچنگ ، ​​سسٹم بس ، سیگمنٹنگ ، اور آئی او فنکشنلٹی کو بھی سنبھالتے ہیں ، جس سے وہ تمام ٹریڈز کا جیک بن جاتے ہیں۔

GPUs اس کے برعکس ہیں - ان میں بہت سے انفرادی پروسیسر ہوتے ہیں جو بہت آسان ریاضی کے افعال پر مرکوز ہوتے ہیں۔ اس کی وجہ سے ، وہ کاموں کو CPUs سے کئی گنا تیز تر کرتے ہیں۔ اسکیلر افعال میں مہارت (ایک فنکشن جو ایک یا زیادہ ان پٹ لیتا ہے لیکن صرف ایک آؤٹ پٹ دیتا ہے) ، وہ انتہائی مہارت کی قیمت پر انتہائی کارکردگی حاصل کرتے ہیں۔

مثال کا کوڈ۔

مثال کے کوڈ میں ، ہم ایک ساتھ ویکٹر شامل کرتے ہیں۔ میں نے رفتار کے مقابلے کے لیے کوڈ کا CPU اور GPU ورژن شامل کیا ہے۔
gpu-example.cpp مندرجات ذیل میں:

#cuda_runtime.h شامل کریں
#شامل کریں
#شامل کریں
#شامل کریں
#شامل کریں
#شامل کریں

ٹائپڈفگھنٹے::کرونو::ہائی_ ریزولوشن_کلاک۔گھڑی؛

#آئی ٹی ای آر 65535 کی وضاحت کریں۔

// ویکٹر ایڈ فنکشن کا سی پی یو ورژن۔
باطلvector_add_cpu(int *کو ،int *ب ،int *ج ،intn) {
intمیں؛

// ویکٹر عناصر a اور b کو ویکٹر c میں شامل کریں۔
کے لیے (میں= ؛میں<n؛ ++۔میں) {
ج[میں] =کو[میں] +ب[میں]؛
}
}

// ویکٹر ایڈ فنکشن کا GPU ورژن۔
__ عالمی سطح پرباطلvector_add_gpu(int *gpu_a ،int *gpu_b ،int *gpu_c ،intn) {
intمیں=تھریڈ آئی ڈی ایکس۔ایکس؛
// لوپ کی ضرورت نہیں کیونکہ CUDA رن ٹائم۔
// یہ ITER بار تھریڈ کرے گا۔
gpu_c[میں] =gpu_a[میں] +gpu_b[میں]؛
}

intمرکزی() {

int *کو ،*ب ،*ج؛
int *gpu_a ،*gpu_b ،*gpu_c؛

کو= (int *)malloc(آئی ٹی ای آر* کا سائز(int))؛
ب= (int *)malloc(آئی ٹی ای آر* کا سائز(int))؛
ج= (int *)malloc(آئی ٹی ای آر* کا سائز(int))؛

// ہمیں GPU تک قابل رسائی متغیرات کی ضرورت ہے ،
// تو cudaMallocManaged یہ فراہم کرتا ہے۔
cudaMallocManaged(&gpu_a ، ITER۔* کا سائز(int))؛
cudaMallocManaged(&gpu_b ، ITER۔* کا سائز(int))؛
cudaMallocManaged(&gpu_c ، ITER۔* کا سائز(int))؛

کے لیے (intمیں= ؛میں<آئی ٹی ای آر؛ ++۔میں) {
کو[میں] =میں؛
ب[میں] =میں؛
ج[میں] =میں؛
}

// سی پی یو فنکشن کو کال کریں اور وقت دیں۔
آٹوcpu_start=گھڑی::ابھی()؛
vector_add_cpu(اے ، بی ، سی ، آئی ٹی ای آر۔)؛
آٹوcpu_end=گھڑی::ابھی()؛
گھنٹے::لاگت << 'vector_add_cpu:'
<<گھنٹے::کرونو::مدت_ کاسٹ<گھنٹے::کرونو::نانو سیکنڈ>(cpu_end-cpu_start).شمار()
<< 'نانو سیکنڈn'؛

// جی پی یو فنکشن کو کال کریں اور وقت دیں۔
// ٹرپل اینگل بریکٹ ایک CUDA رن ٹائم ایکسٹینشن ہے جو اجازت دیتا ہے۔
// ایک CUDA دانا کال کے پیرامیٹرز پاس کیے جائیں۔
// اس مثال میں ، ہم ITER تھریڈز کے ساتھ ایک تھریڈ بلاک پاس کر رہے ہیں۔
آٹوgpu_start=گھڑی::ابھی()؛
vector_add_gpu<<<، آئی ٹی ای آر۔>>> (gpu_a ، gpu_b ، gpu_c ، ITER۔)؛
cudaDeviceSynchronize()؛
آٹوgpu_end=گھڑی::ابھی()؛
گھنٹے::لاگت << 'vector_add_gpu:'
<<گھنٹے::کرونو::مدت_ کاسٹ<گھنٹے::کرونو::نانو سیکنڈ>(gpu_end-gpu_start).شمار()
<< 'نانو سیکنڈn'؛

// مفت GPU- فنکشن پر مبنی میموری الاٹمنٹ۔
cudaFree(کو)؛
cudaFree(ب)؛
cudaFree(ج)؛

// مفت CPU- فنکشن پر مبنی میموری الاٹمنٹ۔
مفت(کو)؛
مفت(ب)؛
مفت(ج)؛

واپسی ؛
}

میک فائل مندرجات ذیل میں:

INC= -میں/usr/مقامی/معجزات/شامل
این وی سی سی=/usr/مقامی/معجزات/ہوں/این وی سی سی
NVCC_OPT= -std = c ++۔گیارہ

تمام:
$(این وی سی سی)$(NVCC_OPT)gpu-example.cppیاجی پی یو مثال

صاف:
-رام جی پی یو مثال

مثال چلانے کے لیے ، اسے مرتب کریں:

بنانا

پھر پروگرام چلائیں:

./جی پی یو مثال

جیسا کہ آپ دیکھ سکتے ہیں ، CPU ورژن (vector_add_cpu) GPU ورژن (vector_add_gpu) کے مقابلے میں کافی آہستہ چلتا ہے۔

اگر نہیں تو ، آپ کو gpu-example.cu میں ITER ڈیفائن کو زیادہ تعداد میں ایڈجسٹ کرنے کی ضرورت پڑ سکتی ہے۔ اس کی وجہ GPU سیٹ اپ کا وقت کچھ چھوٹے CPU- گہری لوپس سے زیادہ ہے۔ میں نے اپنی مشین پر اچھی طرح کام کرنے کے لیے 65535 پایا ، لیکن آپ کا مائلیج مختلف ہو سکتا ہے۔ تاہم ، ایک بار جب آپ اس حد کو صاف کردیتے ہیں تو ، GPU ڈرامائی طور پر CPU سے تیز ہوتا ہے۔

نتیجہ

مجھے امید ہے کہ آپ نے C ++ کے ساتھ GPU پروگرامنگ میں ہمارے تعارف سے بہت کچھ سیکھا ہوگا۔ مندرجہ بالا مثال بہت زیادہ کام نہیں کرتی ہے ، لیکن ظاہر کردہ تصورات ایک فریم ورک مہیا کرتے ہیں جسے آپ اپنے خیالات کو شامل کرنے کے لئے اپنے GPU کی طاقت کو دور کرنے کے لئے استعمال کرسکتے ہیں۔