این بار محاسبات موازی را با جزییات بیشتری باهم نگاه میکنیم. محاسبات موازی برای سریع تر انجام شدن برنامه ها استفاده میشه.
حال فرض کنید میخواهیم یک برنامه را به صورت موازی اجرا کنیم. برای اجرای موازی برنامه نیاز هست که کامپیوتر ما دارای GPU باشد. GPU که مخفف Graphics Processing Units هست. GPU کار کرد سریع تری نسبت به CPU دارد ولی برای استفاده از آن نیاز است که CPU همراهی اش کند! یعنی GPUبه تنهایی کار را سریع نمیکند و در ارتباط با CPU است که میتواند کارها را سریع کند. همین طور که در تصویر زیر پیداست GPU بخش هایی از کد را اجرا میکند که اجرای آن سنگین تر است که به این قسمت از کد Kernels گفته میشه. CPU و GPU هر کدام Memory خودشان را دارند و با استفاده از Bus با یکدیگر در ارتباط هستند و Data ردو بدل میکنند.
خب حالا فرض کنید میخواهیم برنامه ای بنویسیم که به صورت موازی کار کند. در واقع باید این برنامه بتواند برای اجرا شدن از GPU استفاده کند و کدها بر روی ساختار موازی آن اجرا شوند. برای این کار نیاز است که از زبان برنامه نویسی Cuda استفاده شود. این زبان یک زبان برگرفته شده از c/c++ است که به NVIDIA GPU اجازه مدیریت حافظه و تعریف کرنل برای برنامه را میدهد. هر GPUشامل تعدادی Multiprocessor است که هر کدام از این Multiprocessor ها چند coreدارند و به همین دلیل میتوان بر روی این core ها برنامه را به صورت موازی اجرا کرد. تصویر پایین صورت دقیق تری از تصویر بالاست.
تا اینجا دیدیم ساختار GPU به چه صورته، حالا برای اینکه بتونیم در برنامه نویسی از این multiprocessor ها و core ها استفاده کنیم، نیاز هستش که بتونیم به صورت Abstract این موضوعات سخت افزاری را بر روی برنامه بیاریم. برای همین از Block به عنوان نمادی برای Multiprocessorها استفاده شده و از thread برای بیان coreها استفاده شده.
برای دیدن دو تا کد یکی در C++ و یکی در Cuda خودتون رو آماده کنید!
در برنامه نویسی C++ میخواهیم یک آرایه را با مقادیری پر کنیم:
for( i=0; i<N; i++){ result[i]=2*i; }
اگر بخواهیم به این صورت عمل کنیم، هر بار یک قسمت از دیتا تغیر میکند یعنی اجرای برنامه را اگر به صورت تصویر نمایش دهیم مانند شکل زیر است:
حال همین برنامه را باید به صورت موازی و با استفاده از multiprocessorدر Cuda اجرا کنیم! شاید همین الان حدس زده باشید که باید برنامه به صورت موازی و برای هر کدام از محاسبات یک thread در نظر بگیریم! و تصویر آن به صورت زیر است:
برای اجرای این برنامه بر روی cuda یک function باید تعریف کرد. قبل از اون بهتره با یک تایپ جدید در Cuda آشنا شد! به نام dim3.
dim3 MyVariable=(12, 13,14) //MyVariable.x = 12 , MyVairable.y=13, MyVariable.z=14
حالا که این تایپ جدید رو دیدیم بریم برای نوشتن این حلقه در Cuda.
__global__ void MyFunction(int *result) { int i=threadId.x; result[i]=2*i; } dim3 blocksPerGrid(1,1,1); //declaring number of blocks (SM in second Image) dim3 threadsPerBlock(N,1,1); //declare number of threads(Core in second Image) MyFunction<<<blocksPerGrid,threadsPerBlock>>>(result); //call the function in this way! using //<<<>>>
شاید اولش که این تکه کد رو میبینید این جوری بشید O_o ولی در واقع زیاد چیز عجیبی نیست! به عنوان آخرین مطلب این پست این تکه کد رو توضیح میدم:
خط اول __global__ داره میگه این کد باید روی GPU اجرا بشه! threadIdهم برای هر thread خود برنامه به صورت unique در نظر گرفته و از تایپ dim3 هستش. تعداد بلاک ها و تعداد thread ها هم خودمون تعریف میکنیم و در آخر با توجه به تعداد بلاک و threadهای تعریف شده برنامه اجرا میشه :)
اگه تا اینجا خوندید، مرسی :)