نیلوفر آرازخانی
نیلوفر آرازخانی
خواندن ۳ دقیقه·۴ سال پیش

بیشتر راجع به برنامه نویسی Cuda بدونیم

اگر قبل از خوندن این مطلب مطلب قبلی من با عنوان اولین قدم در محاسبات موازی رو بخونید براتون بهتره...

متغیرهای داخلی (internal variable) ها در Cuda

یکی از اولین قدم ها در نوشتن برنامه نویسی Cuda دانستن سه تا از متغیرهای داخلی هست که در خود cuda معرفی شده است. که دراین قسمت به معرفی مختصر ولی جامع درباره اش می پردازم. (تناقض شد که!)

اولین متغیر blockDim هست که تعداد threadها در هر block را تعریف میکند. متغیر بعدی threadId هست که برای هر thread در یک block مقدار منحصر به فردیه. مثلا اگر blockDim باشه ۲۵۶ اون وقت threadId میشه از ۰ تا ۲۵۵. متغیر سوم و آخری blockId هست که برای هر block مقدار منحصر به فردی هستش.

حالا برای اینکه یک مقدار با این تکه کد نویسی ها آشنا بشیم بهتره بریم و یک مثال ساده از Cuda ببینیم.

در این مثال میخواهیم دو بردار را با هم جمع کنیم.

__global__ void vectorAdd(float *a, float *b, float *c){ int i=blockId.x * blockDim.x + threadId.x; c[i]= a[i] + b[i]; } .... dim3 blocksPergrid(N/256,1,1); dim3 threadsPerBlock(256,1,1); vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(a,b,c);

در این مثال i به این صورت مقایسه میشود که مثلا اگر blockId برابر با صفر باشد اولین i برابر با 0 میشود و به همین صورت تک تک c[i]ها حساب میشوند. همان طور که در نوشته قبلی اشاره شده __global__ برای این استفاده میشود که نشان دهد این قسمت kernel هست و باید بر روی GPUران شود.

حالا بریم ببینیم چطور باید به کامپیوتر بگوییم که این متغیرهایی که به عنوان اشاره گر به صورت a,b,c به تابع vectorAdd فرستاده شده اند به Memory Spaceدر GPU اشاره میکنند نه CPU!

از تابع cudaMalloc برای اختصاص دادن حافظه استفاده میشه و برای خالی کردن این میزان از حافظه از cudaFree استفاده میشود.

پس در مثال بالا باید این خطوط برای تمام متغیرهای a,b,c اضافه بشوند که :

float *a; cudaMalloc(&a, N*sizeof(float));

به این معنی ست که آدرسی که پوینتر به آن اشاره میکند برابر با سایز N*sizeof(float) است و این مقدار در حافظه GPU گرفته شده است.

بعد از اینکه مقداری در حافظه GPU با استفاده از cudamalloc گرفته شد حالا باید بتوان از آن به حافظه CPU یا از حافظه CPU دیتا کپی کرد برای همین از تابع CudaMemcpy استفاده میشه که این تابع در زیر به صورت یک مثال توضیح داده شده است:

cudaMemcpy(array_device, array_host, N*sizeof(float), cudaMemcpyHostToDevice)

که در این مثال device یعنی GPU و Host یعنی CPU که هر یک جداگانه تعریف شده اند. اولین مقدار که device است مقصد و مبدا آن host است که یعنی data از CPU به GPU کپی میشوند.

synchronization

حالا که بحث کپی کردن از CPU پیش میاد همون طور که تا الان ممکنه حدس زده باشید مشکل synchronization یا همزمانی داریم! چون وقتی که تابع کرنل صدا زده میشه CPU میره ادامه کدها رو اجرا کنه و این ممکنه در کار اختلال ایجاد کنه و باعث ایجاد باگ بشه!

برای همین بعد از اینکه تابع کرنل صدا زده میشه از تابع زیر استفاده میشه که به CPU میگه وایستا تا کار GPU تموم بشه داداش :دی!

cudaThreadSynchronize( )

یک تابع دیگه هم هست به اسم syncthreads() که برای همزمان شدن بین thread ها استفاده میشه.

دست به کد شیم!

این قسمت رو بزاریم برای پست بعدی! :)

اگه تا اینجا خوندید مرسی:)

اگه تا اینجا خوندید و هیچی نفهمیدید برید اول این پست رو بخونید :)


موازیcudaبرنامه نویسیمهندسی نرم افزارکامپیوتر
شاید از این پست‌ها خوشتان بیاید