CUDA Programming Applications

کاربردهای برنامه نویسی کودا

CUDA Programming Applications

کاربردهای برنامه نویسی کودا

پردازنده برداری

یک پردازنده برداری ، یا آرایه پرداز ،رایانه خاصی است با چند پردازنده برای پردازش موازی تعداد زیادی از آرایه ها

آرایه پرداز در واقع یک واحد پردازش مرکزی (CPU) می باشد که یک مجموعه از دستورالعملهایی را اجرا می کند که روی آرایه تک بعدی از داده که همان بردار است ، عمل می کند. این درست در مقابل پردازنده های عددی قرار می گیرد که دستورالعملهای آن ، صرفا روی یک تکه داده عمل می کند ،اکثر پردازنده ها ،پردازنده های عددی می باشند. پردازنده های برداری در دهه 1970 مورد بحث واقع شدند و پایه و اساس اکثر ابررایانه های دهه 1980 تا 1990 را تشکیل دادند پیشرفتهای پردازنده های عددی ،مخصوصا ریزپردازنده ها ،باعث کاهش بکار گیری پردازنده های برداری سنتی در ابررایانه ها و همچنین کاهش استفاده از تکنیکهای پردازش برداری در پردازنده ها در اوایل دهه 1990 شد.امروزه اکثر پردازنده ها از یک معماری استفاده می کنند که در آن دستورالعملهایی برای پردازش برداری روی چندین تکه داده ، را بطور برجسته نشان می دهد.این دستورالعملها را SMID (تک دستور،چند داده)می شناسند.از مثالهای مشهود اینگونه دستورالعملها می توان به AltiVec,MMX,SSE اشاره نمود.نمونه هایی از استفاده از تکنیک پردازش برداری را می توان در کنسولهای بازی و شتاب دهنده های گرافیکی نیز پیدا کرد.در سال 2000 شرکتهای آی بی ام و سونی و توشیبا با همکاری هم موفق به تولید یک پردازنده سلولی (نوعی ریزپردازنده)شدند ،که این پردازنده سلولی شامل یک پردازنده عددی و هشت پردازنده برداری بود که در پلی استیشن 3 مورد استفاده قرار گرفت.

در برخی از طراحی های پردازنده ها ،دستورالعملهای چندگانه روی چندین تکه داده عمل می کنند که آنها را به عنوان MMID (چند دستوره،چند داده)می شناسیم.اینگونه طراحی ها مختص کاربردهای ویژه هستند و بطور عادی برای استفاده های متداول کاربرد ندارند

 

تاریخچه

پردازش برداری اولین بار در اوایل دهه ۱۹۶۰ توسط وستینگهاوس، در پروژه Solomon مورد توجه قرار گرفت و توسعه یافت. هدف این پروژه افزایش چشمگیر سرعت محاسبات ریاضی، با استفاده همزمان از تعداد زیادی عملیات ساده ریاضی، زیر نظر یک پردازنده بود. به این صورت که پردازنده در هر کلاک یک سیگنال مشترکی را برای تمامی واحدهای محاسبه و منطق (ALU)ها می‌فرستاد. در حالی که هر یک از این واحدهای محاسبه ورودی‌های مجزایی داشتند، یک کار مشترک روی آن ورودی‌ها انجام می‌دادند. این روش به ماشین‌های Solomon این قابلیت را می‌داد که یک الگوریتم را روی انبوهی از داده‌ها (که از آرایه‌ها تغذیه می‌شدند) اجرا نماید.
در سال ۱۹۶۲، وستینگهاوس پروژه را لغو کرد ولی دوباره در دانشگاه ایلینوی تحت عنوان ILLIAC IV از سر گرفته شد. در طراحی ابتدایی آن، این ماشین به ۲۵۶ واحد محاسبه و منطق مجهز بود، درحالی که وقتی در سال ۱۹۷۲ عرضه شد تنها ۶۴ واحد محاسبه و منطق داشت و فقط می‌توانست ۱۰۰ تا ۱۵۰ میلیون فلاپس کار کند. برای کار با اطلاعات فشرده و حجیم، مانند دینامیک محاسباتی سیالات، همین ماشین معیوب (به دلیل اینکه نتوانستند تعداد ۲۵۶ واحد محاسبه و منطق ایجاد کنند) سریعترین ماشین دنیا بود.
تکنیک پردازش برداری تقریبا در تمام طراحی‌های پردازنده‌های مدرن وجود دارند، اگرچه معمولا با نام SIMD می‌شناسند. در اینگونه پردازنده‌ها، واحد پردازش برداری در کنار واحد پردازنده عددی کار می‌کند و برنامه‌هایی با آن بخش کار می‌کنند که واقعا می‌دانند که این واحد حضور دارد.

 

 

جزئیات

بیشتر پردازنده‌ها دستورالعملی دارند که بیان می‌کند «A را با B جمع کن و درون C بریز». مقادیر A، B و C (حداقل در تئوری) به ندرت می‌تواند داخل دستورالعمل باشند (چه به صورت صریح، چه رجیستر). در واقع داده به ندرت به طور خام فرستاده می‌شود، بلکه به صورت اشاره‌ای به آدرس حافظه که داده داخل آن است، می‌باشد. رمزگشایی[۲] این نشانی و دریافت داده از حافظه[۳]، خود زمان بر است. با افزایش سرعت پردازنده‌ها، این تاخیر حافظه تبدیل به مانعی بزرگ برای عملکرد سریع پردازنده محسوب می‌شود.

خط لوله

برای کاهش زمان تاخیر، اغلب پردازنده‌های امروزی از فن خط لوله[۴] استفاده می‌کنند. در این تکنیک دستورالعمل‌ها از چندین بخش عبور می‌کند تا نوبتش برای اجرا فرارسد. اولین بخش، آدرس را خوانده و کدگشایی می‌کند، بخش بعد مقادیر آدرسها را از حافظه می‌گیرد و بعدی کار محاسبه و اجرا را انجام می‌دهد. در تکنیک خط لوله، رمز کار در این است که شروع کدگشایی دستورالعمل بعدی، باید حتی قبل از خروج دستورالعمل قبلی از پردازنده صورت گیرد؛ در نتیجه واحد کدگشایی آدرس همواره مشغول به کار می‌باشد. هر دستورالعمل برای اجرای کامل به همان زمان قبلی (بدون خط لوله) نیاز دارد. زمانی که آن را تاخیر می‌نامیم. ولی پردازنده با خط لوله می‌تواند دسته‌ای از دستورالعملها را خیلی سریعتر انجام دهد.

پردازنده برداری

پردازنده برداری یک قدم فراتر بر می‌دارد و بجای ایجاد خط لوله برای دستورالعمل‌ها، داده‌ها را نیز خط لوله می‌کند. دستورالعملهایی هستند که بجای اینکه بگویند «A را با B جمع کن»، می‌گوید «تمامی اعداد از اینجا تا آنجا را با تمامی اعداد از اینجا تا آنجا جمع کن». و بجای اینکه دستورالعمل‌ها را پشت سر هم رمزگشایی و داده‌های مربوط به آنها را از حافظه دریافت کند، یک دستورالعمل را از حافظه می‌خواند و با فرض اینکه می‌داند که آدرس بعدی یکی بیشتر از آدرس فعلی است، دستورالعمل بعدی را رمزگشایی می‌کند. این عمل صرفه جویی چشمگیری در زمان رمزگشایی می‌کند. برای نشان دادن اینکه چه تفاوتی می‌کند، فرض کنید می‌خواهیم دو آرایه ۱۰ تایی از اعداد را با هم جمع کنیم. در حالت عادی نیاز به یک حلقه داریم که هر بار یک زوج از این دو آرایه را انتخاب می‌کند و سپس آنها را با هم جمع می‌کند

execute this loop 10 times
   read the next instruction and decode it
   fetch this number
   fetch that number
   add them
   put the result here
 end loop


در حالی که در پردازش برداری اینگونه خواهد بود:

 read instruction and decode it
 fetch these 10 numbers
 fetch those 10 numbers
 add them
 put the results here


در پردازش برداری، اول اینکه فقط دو انتقال آدرس از پردازنده به حافظه داریم، همچنین در این حالت بجای اینکه ۱۰ مرتبه یک دستورالعمل را کدگشایی کند تنها یک بار این کار را انجام می‌دهد. همچنین کد مورد استفاده در پردازش برداری کوتاه‌تر است که این خود سبب کاهش حافظه مورد نیاز برای دستورالعمل‌های آن می‌باشد.


بررسی وابستگی بین این اعداد لازم نیست چون دستورالعمل برداری، چندین عمل غیر وابسته را معین می‌کند. این خود منطق کنترل را ساده می‌کند. مسئله زمانی جالب تر می‌شود که بتوان چند عمل را روی چند داده انجام داد. کد زیر را در نظر بگیرید که در آن می‌خواهیم دو گروه عدد را با هم جمع کنیم و سپس با گروه سوم ضرب کنیم. در این کد عمل دریافت دستورالعمل فقط یک بار انجام می‌شود (بر خلاف حالت عادی که ۲*۱۰=۲۰) و این دو عمل فقط در یک دستورالعمل انجام می‌پذیرد:

 

 read instruction and decode it
 fetch these 10 numbers
 fetch those 10 numbers
 fetch another 10 numbers
 add and multiply them
 put the results here


عملیات ریاضی بالا خیلی سریعتر انجام خواهند شد چرا که دیگر تاخیر در دریافت و کدگشایی دستورالعمل بعدی را نداریم (فقط یک دستورالعمل داریم).

جنبه منفی

باید توجه داشت که تمام مسائل را نمی‌توان با استفاده از این روش بهبود داد. پیاده سازی این دستورالعمل‌ها در پردازنده خود پیچیدگی زیادی را بر هسته پردازنده تحمیل می‌کند. این پیچیدگی‌ها معمولا سبب می‌شوند که دستورالعمل‌های دیگر دیرتر اجرا شوند. به عنوان مثال زمانی که بخواهیم فقط دو عدد تنها را با هم جمع کنیم. همچنین دستورالعمل‌های پیچیده سبب کندی قسمت رمزگشایی و پیچیدگی بیشتر آن خواهد شد، که این خود باعث کندی اجرای دستورات عادی می‌شود.

جنبه مثبت و کاربرد

در حقیقت، پردازش برداری برای انجام عملیات روی انبوهی از داده‌ها بهترین کارایی را دارند. برای همین است که این پردازنده‌ها اصولا در ابر رایانه‌ها استفاده می‌شوند. این ابر رایانه‌ها عموما برای پیشبینی وضعیت هوا و آزمایشگاه‌های فیزیک استفاده می‌شوند که انبوهی از داده را به چالش می‌گیرد.

 

مثال دنیای واقعی: دستورالعملهای برداری در معماری x86

کد زیر مثال حقیقی معماری x86 با دستورالعمل‌های برداری می‌باشد. در اینجا از دو آرایه اعداد اعشاری استفاده می‌کند.

//SSE simd function for vectorized multiplication of 2 arrays with single-precision floatingpoint numbers
//1st param pointer on source/destination array, 2nd param 2. source array, 3rd param number of floats per array
 void mul_asm(float* out, float* in, unsigned int leng)
 {    unsigned int count, rest;
 
      //compute if array is big enough for vector operation
      rest  = (leng*4)%16;
      count = (leng*4)-rest;
 
     // vectorized part; 4 floats per loop iteration
      if (count>0){      
      __asm __volatile__  (".intel_syntax noprefix\n\t"
      "loop:                 \n\t"
      "movups xmm0, [ebx+ecx] ;loads 4 floats in first register (xmm0)\n\t"
      "movups xmm1, [eax+ecx] ;loads 4 floats in second register (xmm1)\n\t"
      "mulps xmm0,xmm1       ;multiplies both vector registers\n\t"
      "movups [eax+ecx],xmm0 ;write back the result to memory\n\t"
      "sub ecx,16            ;increase address pointer by 4 floats\n\t"
      "jnz loop              \n\t"
      ".att_syntax prefix    \n\t"
        : : "a" (out), "b" (in), "c"(count), "d"(rest): "xmm0","xmm1");
      }
      
      // scalar part; 1 float per loop iteration
      if (rest!=0)
      {
       __asm __volatile__  (".intel_syntax noprefix\n\t"
      "add eax,ecx           \n\t"
      "add ebx,ecx           \n\t"
      
      "rest:                 \n\t"
      "movss xmm0, [ebx+edx]  ;load 1 float in first register (xmm0)\n\t"
      "movss xmm1, [eax+edx]  ;load 1 float in second register (xmm1)\n\t"
      "mulss xmm0,xmm1       ;multiplies both scalar parts of registers\n\t"
      "movss [eax+edx],xmm0  ;write back the result\n\t"
      "sub edx,4             \n\t"
      "jnz rest              \n\t"
      ".att_syntax prefix    \n\t"
        : : "a" (out), "b" (in), "c"(count), "d"(rest): "xmm0","xmm1");
      }
      return;
 }

 

 

اندازه حافظه گرافیکی

اندازه حافظه گرافیکی

مقدار حافظه گرافیکی .به طور مستقیم . به اندازه صفحه نمایش (که بر حسب اینچ است ) رزولشون تصویر ( که برحسب پیکسل است) و تنظیمات اجرای تصاویر مثل Anti Aliasing بستگی دارد.

پس یک صفحه نمایشی 17 اینچی با رزولوشنی برابر با 1280*1024 نیازی به داشتن کارت گرافیکی با حداکثر میزان حافظه گرافیکی نخواهد بود ویک کارت گرافیکی یک گیگابایتی کاملا مناسب است . اگر حافظه گرافیکی از ان میزانی که باید . کمتر باشد .پردازنده گرافیکی برای اینکه تصاویر پردازش شده را به این واحد ارجاع دهد تا برای صفحه نمایش صادر شود . به اجبار باید منتظر خالی شدن حافظه بماند تا داده های جدید را در ان ادرس دهی نماید و این دقیقا همان زمانی است که افت فریم صورت میگیرد . اگر رزولوشن صفحه نمایش یک کامپیوتر 768*1024پیکسل باشد و چون رنگی است به صورت سه کانال  RGB   نمایش داده میشود پس هر پیکسل ان داری 8*8*8 بیت است یعنی 24بیت . پس در صفحه نمایش این کامپیوتر باید تعداد 768*1024*24 بیت در هر ثانیه بروز رسانی شود که این کار توسط کارت گرافیک صورت میگیرد .

واژه هایی مانند Shared Memoryو Up ToوDedicated Memory به چه چیز اشاره دارد؟

آیا حافظه اصلی یا همان RAM   با کارت گرافیک ، به اشتراک گذاشته می شود یا خیر ؟ اگر کارت گرافیکی دارای حافظه گرافیکی یا همان  VRAM به آن اندازه که گفته شده نباشد . از واژه    Shared Memory        برای مشخصات ان استفاده می کنیم . فرض کنید که کارت گرافیکی تهیه می کنید و برروی ان نوشته است  Up To 2GB   و یا   2GB Shared   این به این معنی است که این کارت به طور خاص داری حافظه گرافیکی نمی باشد وبرای پردازش داده ها نیازمند به استفاده از حافظه اصلی سیستم است .و اعداد جلوی انها به معنی این است که چیپ های مورد استفاده در انها نهایتا امکان ادرس دهی 2 گیگابایت حافظه رادارند . این موضوع باعث میشود تا درمواقعی که سیستم نیازمند حافظه اصلی است (به دلیل پردازش سنگین ) دچارافت کارایی شود  Dedicated Memory   همان حافظه گرافیکی است . کارت های گرافیکی ای که داری   DM   باشند دیگر نیازی به بهره گیری از حافظه اصلی سیستم را ندارد و حافظه درون ساختی را دارا هستند که از حافظه اصلی سیستم که اشتراک شده نیز هست . بسیار سریعتر عمل کرده و بازدهی بالاتری دارد . پهنای باندی که این نوع از حافظه ها در اختیار کارت گرافیک قرار می دهد نیز کاملا واقعی است .یکی از معمولی ترین حافظه هایی که امروزه در کارت های گرافیکی مورد استفاده قرا می گیرد  با نام   GDDR5   شناخته می شود که برای انتقال داده ها از چهار مسیر بهره می برد.


شکل 3: Geforce GTX 960 G1 GAMING با معماری پیشرفته Maxwell

 
ادامه مطلب ...

معرفی پردازنده های گرافیکی:

معرفی پردازنده های گرافیکی:

پردازنده های گرافیکی ، یک مدار الکترونیکی خاص می باشند که دارای پردازنده های چند هسته ای بسیار موازی هستند GPU ها به کلاس کامپیوترهای موازی SIMD تعلق داشته و توان پردازشی بسیار بالایی نسبت به CPU ها ارائه می دهند ،این موضوع موجب گسترش کاربردهای این پردازنده ها در حوزه هایی فراتر از بازی های کامپیوتری و نگاشت پس زمینه شده است ، کاربردها و قابلیتهایی که نه تنها برای شتاب دادن به فرآیند ایجاد گرافیک هایی با کارآیی بالا موثرند ، بلکه در انجام کارهایی بسیار سنگین نیز خوب عمل کرده و از سرعت و دقت بالایی برخوردارند. و  GPU های مدرن با معماری موازی خود پردازنده های بسیار سریعی بوده و با قیمت و توان مصرفی کمتری عرضه می شوند.استفاده از GPU ها جهت پیاده سازی الگوریتمها و برنامه های بینایی ماشین و پردازش تصویر که بار پردازشی زیادی را جهت رسیدن به Frame Rate  بیشتر طلب می کنند ، یک راهکار اقتصادی و کارآمد به شمار می رود،موازی سازی آینده محاسبات است و معماری GPU ها برای موازی سازی بسیار مناسب است .این در حالی است که می توان گفت GPU پردازشگری است که به دلیل استفاده از معماری موازی و استفاده از تعداد هسته های زیاد ،امکان پردازش کاراتری را نسبت به CPU فراهم می کند در عین حالی که هزینه پایین تری دارد.

 

حافظه در کارتهای گرافیکی

زمانی که شما بر روی آیکن بازی کلیک می نمایید ، در ابتدا اطلاعات آن از هارد دیسک خوانده شده و بر روی RAM بارگذاری و به CPU ارسال می شود ،CPU با دانستن این که این پردازش مربوط به کارت گرافیک هست دستور می دهد تا پردازش های مذکور راهی کارت گرافیک شوند در این بین GPU اقدام به پردازش داده های تازه رسیده می کنند اما این داده های خام بایستی در محلی ذخیره شوند تا با داده های خام دیگر ادغام و به خروجی ارسال شوند ، این مکانی که پردازنده برای بارگذاری داده ها انتخاب مب کند همان حافظه کارت گرافیکی است که دارای مشخصات خاص خود هست .این مشخصات شامل مقدار فیزیکی حافظه ،فرکانس حافظه ،رابط حافظه و نسل حافظه است.GPU نیز همانند CPU به علت انجام عملیات پردازشی ،گرمای زیادی تولید می کند به همین دلیل روی آن یک Fan تعبیه شده است و در نمونه های پیشرفته ان همراه با Head Sink (لوله های روغن در گردش ) جهت خنک کردن GPU است


شکل 2:کارت گرافیک همراه با Head Sink

مقدمه ای بر CPU و GPU

مقدمه ای بر CPU و GPU

یکی  از عوامل اصلی کارآیی CPU ها ، فرکانس رو به افزایش آنها می باشد با دو برابر شدن فرکانس ،کارآیی نیز دو برابر می شود گرایش رو به رشد فرکانس به صورت نمایی همیشه وجود دارد .به هر حال در دهه 2000 این افزایش به دلیل محدودیتهای CPU متوقف شد ،از آنجا که مصرف توان یک CPU به مکعب فرکانس بستگی دارد چگالی توان ،درحال نزدیک شدن به توان مرکز راکتور هسته ای بود.عدم توانایی در سرد کردن این تراشه ها بطور موثر ،گرایش به رشد نمایی فرکانس را درست قبل از رسیدن به 4 گیگا هرتز متوقف ساخت.کارت گرافیک وظیفه پردازش تصویری را در یک سیستم کامپیوتری بر عهده دارد که به اختصار به آن GPU(Graphic Processing Unit) گفته می شود.چیپ پردازشگر کارت که می توان آن را با پردازنده کامپیوتر مقایسه کرد ،مغز متفکر کامپیوتر به شمار می رود .هسته گرافیکی داراری فناوری ساخت خاص خود بوده و متشکل از چندین میلیون تا چند میلیارد ترانزیستور است.مشخصات این هسته پردازشی معمولا شامل کلاک هسته یا همان فرکانس کاری در حالت عادی و توربو ،مدار تامین کننده توان مورد نیاز کارت گرافیکی ،پورتهای تامین توان خازن ها و چیپهای حافظه مورد استفاده در این قطعه به شمار می رود.همزمان با برخورد کردن CPU ها با سقف کارایی سریال ،کارآیی GPU به علت موازی بودن بطور گسترده و نمایی شروع به رشد نمود .از آنجایی که محاسبه رنگ یک پیکسل در صفحه نمایش ،میتواند مستقل از همه پیکسلهای دیگر انجام شود ،موازی سازی یک روش طبیعی در افزایش کارآیی GPU ها است،لذا به نظر می رشد بهترین راه افزایش کارآرایی در محاسبات با حجم بالا به کارگیری کاراتر GPU است که در سال 2006 شرکتnvidia اولین GPU همه منظوره با نام G80  تولید نمود.(شکل 1)

 


 


شکل 1 : اولین مدل کارت گرافیک G80


کلید موفقیت در محاسبات GPU تا حد زیادی به کارآیی کلی ان در مقایسه با CPU بستگی دارد ،امروزه با توجه به حداکثر پهنای بانددر تئوری و کارآیی گیگا فلاپها ، یک فاصله تقریبا 7 برابری بین این دو وجود دارد ، ریشه این فاصله در محدودیتهای فیزیکی هر  هسته و تفاوت معماری های این دو پردازنده است . CPU در واقع یک پردازنده سری بوده و برای اجرای سری عملیاتهای ترتیبی طراحی شده است در صورتیکه پردازنده های گرافیکی GPU پردازنده های چند هسته ای موازی هستند و در گروه کامپیوترهای موازی قرار دارند.، در پردازنده های گرافیکی یک سری عملیات یکسان بطور همزمان روی چند داده اجرا می شود.