خیلی دوست داشتم یک روش ساده پیدا کنم که جدا از پیچیدگیها مرسوم فریمورکهای استفادهها از gpu ها بتوانم کدهای سادهام را به سریعترین حالت روی گرافیک اجرا بگیرم که خوب دیدم راهحلهای جالبی هست. یکی از این راهحلها که من خیلی حال کردم و خوب از استفاده کردم. ILGPU بود که برای فریمورک و ماشین داتنت طراحی و پیادهسازی شده.
چند تا نکته کوچک:
ما نمیتوانیم GPU را مانند یک CPU در پیادهسازی در نظر بگیریم، در واقع در این سه حوزه باید مقایسه کرد:
در واقع GPU یک CPU نیست. ( GPU یک مجتمع از هزار یا هزاران هسته با اشتراک چند ده یا صدها واحد کنترل و کش است ولی CPU یک یا حداکثر چند ده هسته هست با همان تعداد واحد کنترل و کش و داستان TPU که جدا و اختصاصی برای یادگیری ماشین هست با معماری ماتریسی و سیستولی)
یک پردازنده سنتی یکچرخه بسیار ساده دارد: واکشی، رمزگشایی و اجرا.
یک دستورالعمل را از حافظه میگیرد (واکشی)، نحوه اجرای دستورالعمل مذکور (رمزگشایی) را مشخص میکند و دستور را انجام میدهد (اجرا). سپس این چرخه برای تمام دستورالعملهای الگوریتم شما تکرار میشود. اجرای این جریان خطی از دستورالعملها برای اکثر برنامهها خوب است زیرا CPU ها بسیار سریع هستند و اکثر الگوریتمها بهصورت سریالی هستند.
وقتی الگوریتمی دارید که میتواند بهصورت موازی پردازش شود چه اتفاقی میافتد؟ یک CPU چندین هسته دارد که هرکدام واکشی، رمزگشایی و اجرای خود را انجام میدهند. میتوانید الگوریتم را در تمام هستههای CPU پخش کنید، اما در نهایت هر هسته همچنان یک جریان دستورالعملها را اجرا میکند، احتمالاً همان جریان دستورالعملها، اما با دادههای متفاوت. CPU ها ترفندی برای برنامههای موازی به نام SIMD دارند. اینها مجموعهای از دستورالعملها هستند که به شما اجازه میدهند تا یک دستور عملیات را روی چندین قطعه داده به طور همزمان انجام دهد. (بحث بیشتر مختص درس معماری کامپیوترهاست که بسیار مفصل است.) کتابخانه و فریمورکهای زیادی به ما کمک میکنند که اکثرشان تبدیل و دسترسی به کدهای c++ را پیشنهاد میکنند. من قبلاً برای کارهای گرافیکی از کتابخانه SHARPDL استفاده کرده بودم. وای الان منظور کارهای گرافیکی نیست و اجرای کد روی پردازنده گرافیکی به رام موضوعیت دارد.
لیست اینها عبارتاند از:
شما می توانید تست این برنامه را در گیت هاب مشاهده نمایید.
برنامهنویسی بسیار سادهای دارد. کافی است بعد از نوشتن روال، اجرا به تابع کرنل پاس بدهید و تمام.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | using ILGPU; using ILGPU.Runtime; using ILGPU.Runtime.Cuda; using System.Diagnostics; //گرفتن یک زمینه پیشفرض برای شروع کار using var context = Context.CreateDefault(); بهازای هر دستگاه موجود یکبار برنامه را اجرا میکنیم.// foreach (var device in context.Devices) { using var accelerator = device.CreateAccelerator(context); Console.WriteLine($"Performing operations on {accelerator}"); var sw = new Stopwatch(); sw.Restart(); MyKernel(accelerator); sw.Stop(); Console.WriteLine($"- Naive implementation: {sw.ElapsedMilliseconds}ms"); } Console.ReadLine(); روال اصلی برنامه که محاسبه عدد اول را دارد// static void MyCalcute(Index1D total, ArrayView<long> results, Input<int> TotalTag) { int Total = 0; for (int i = 1; i < 10000; i++) { bool isPrime = true; for (int j = 2; j < i - 1; j++) if ((i % j) == 0) { isPrime = false; break; } if (isPrime) { results[Total] = i; Total++; } } } پاسدادن روال به کرنل دستگاه// static void MyKernel(Accelerator accelerator) { using var buffer = accelerator.Allocate1D<long>(10000); var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<long>, Input<int>>(MyCalcute); kernel( (int)buffer.Length, buffer.View, 10000); var results = buffer.GetAsArray1D(); } |
البته این با CUDA متفاوت است؛ ولی نگران نباشید. کافی ایست این افزونه را درصورتیکه پردازنده گرافیکی شما پشتیبانی میکند دانلود کنید و سپس با تغییر کوچکی از این روش استفاده کنید.
1 2 3 | using var context = context.GetCudaDevices() بهجای Context.CreateDefault(); |
هدفم صرفاً شراکت شما در حال برده شده از این کار بود. طبعاً دانش و اطلاعات بیشتری باید در این زمینه داشت برای کارهای بزرگتر.
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.