- فایل UShortSimdProcessor.cs
- در این فایل کلاسی با نام UShortSimdProcessor قرار دارد و همانطور که از نام کلاس پیداست، توابعی برای کار بر روی اعداد از نوع دادهای ushort یا همان اعداد صحیح کوچک بدون علامت نوشته شدهاند.
- در این کلاس 12 تابع برای انجام 6 عملیات زیر نوشته شدهاست که به ازای هر عملیات، دو تابع یکی در حالت معمولی و یکی در حالت SIMD نوشته شده است.
- جمع دو آرایه اول ورودی که مجموع در آرایه سوم ریخته میشود
- جمع دو آرایه بدون استفاده از آرایه کمکی که مجموع در آرایه اول ریخته میشود
- بدست آوردن کمترین و بیشترین مقدار در یک آرایه اعداد صحیح کوچک بدون علامت
- جمع عناصر آرایه ورودی و ذخیره مجموع آنها در یک متغیر کمکی
- جمع عناصر آرایه ورودی و ذخیره مجموع آنها در یک متغیر کمکی بدون بررسی سرریز (Overflow)
- محاسبه میانگین و بدست آوردن کمترین و بیشترین مقدار در یک آرایه اعداد صحیح کوچک بدون علامت
- در بالای هر تابع در این فایل توضیحات لازم دربارهی فعالیت آن تابع ذکر شده است.
حال در کلاس PerformanceTests برای انجام آزمایشات و مقایسه زمان اجرا، 10 تابع وجود دارند که 10 عملیات مختلف را بر روی 3 نوع دادهای، اجرا میکنند. 3 عملیات از کلاس IntSimdProcessor و یک عملیات از کلاس FloatSimdProcessor و 6 عملیات از کلاس UShortSimdProcessor را مورد آزمایش قرار دادهایم که در مجموع شامل 10 آزمایش در 10 تابع مختلف شده است.
public static void TestIntArrayAdditionFunctions(int testSetSize) {
WriteLine();
Write("Testing int array addition, generating test data...");
var intsOne = GetRandomIntArray(testSetSize); //تولید آرایه عددی به صورت تصادفی
var intsTwo = GetRandomIntArray(testSetSize);
WriteLine($" done, testing...");// پایان تولید آرایهها و شروع پردازش
var naiveTimesMs = new List<long>(); // تعریف لیستی برای ریختن زمان پاسخ دهی در حالت ساده و معمولی
var hwTimesMs = new List<long>(); // تعریف لیستی برای ریختن زمان پاسخ دهی در حالت SIMD و سخت افزاری
for (var i = 0; i < 3; i++) { // ایجاد حلقه برای تکرار محاسبات برای اندازه گیری زمان در حالت تکراری
stopwatch.Restart();//شروع ثبت زمان
var result = IntSimdProcessor.NaiveSumFunc(intsOne, intsTwo);//اجرای تابع جمع دو آرایه
var naiveTimeMs = stopwatch.ElapsedMilliseconds;//ثبت زمان
naiveTimesMs.Add(naiveTimeMs);//افزودن زمان ثبت شده به لیست زمانهای ساده و معمول
WriteLine($"Naive analysis took: {naiveTimeMs}ms (last value = {result.Last()}).");
stopwatch.Restart();//شروع ثبت زمان
result = IntSimdProcessor.HWAcceleratedSumFunc(intsOne, intsTwo);//اجرای تابع جمع دو آرایه در حالت سخت افزاری
var hwTimeMs = stopwatch.ElapsedMilliseconds;//ثبت زمان
hwTimesMs.Add(hwTimeMs);//افزودن زمان به لیست زمانهای سخت افزاری
WriteLine($"Hareware accelerated analysis took: {hwTimeMs}ms (last value = {result.Last()}).");
}//پایان حلقه و چاپ نتایج
WriteLine("Int array addition:");
WriteLine($"Naive method average time: {naiveTimesMs.Average():.##}");
WriteLine($"HW accelerated method average time: {hwTimesMs.Average():.##}");
WriteLine($"Hardware speedup: {naiveTimesMs.Average() / hwTimesMs.Average():P}%");
}
در بالا تکه کدی مربوط به تابع آزمایش اول از کلاس PerformanceTests قرار دارد و وظیفه دارد عملیات جمع دو آرایه را با استفاده از یک آرایه کمکی اعداد صحیح، هم در حالت معمولی و هم در حالت SIMD انجام دهد و زمان اجرای آنها را ثبت و نمایش دهد تا بتوانیم این زمان اجراها را با هم مقایسه کنیم.
ساختار و روند اجرای کلیه آزمایشها و توابع در کلاس PerformanceTests با یکدیگر یکسان است و از یک stopwatch یا همان کرنومتر برای محاسبه زمان اجرا استفاده شده است.
هر کدام از این توابع یک عملیات را
مورد بررسی قرار میدهند و هر عملیات را 3 مرتبه اجرا میکنند تا زمان تکرار اجرا
نیز مورد مقایسه قرار گیرد.
نام تابع ذکر شده نشان دهنده آزمایش بر
روی آرایه اعداد صحیح یا همان Integer میباشد که شامل یک پارامتر ورودی از نوع عدد صحیح میباشد. این پارامتر ورودی
نشان دهنده اندازه هر آرایهای میباشد که قرار است تولید شود.
TestIntArrayAdditionFunctions(int testSetSize)
در قدم اول این تابع، باید آرایهها را تولید کنیم که کد آن به صورت زیر است.
Write("Testing int array addition, generating test data...");
var intsOne = GetRandomIntArray(testSetSize);
var intsTwo = GetRandomIntArray(testSetSize);
WriteLine($" done, testing...");
ابتدا در خروجی چاپ میکنیم که در حال ایجاد دادههای مربوط به آزمایش هستیم و سپس با استفاده از تابع GetRandomIntArray آرایهای را ایجاد میکنیم و در متغیرهای مربوطه میریزیم. این تابع دارای یک پارامتر ورودی از نوع عدد صحیح است که آرایهای را به طول پارامتر ورودی تولید میکند. این تابع در فایل Utilities.cs قرار دارد.
در پایان تولید آرایهها، اتمام تولید و ایجاد آرایهها را با چاپ در خروجی اعلام میکنیم.
سپس با معرفی دو لیست زیر میتوانیم زمانهای اجرا را در آنها بریزیم و در پایان، تابع میانگین این زمانها را محاسبه و چاپ کنیم. لیست اول برای نگهداری زمانهای اجرای عملیات در حالت معمولی و لیست دوم برای نگهداری زمانهای اجرای عملیات در حالت SIMD میباشد.
var naiveTimesMs = new List<long>();
var hwTimesMs = new List<long>();
سپس با ایجاد حلقه ای از 0 تا 3 که در کل 3 مرتبه اجرا میشود عملیات را تکرار و زمان آن را ثبت میکنیم.
for (var i = 0; i < 3; i++)
درون حلقه یک عملیات را در دوحالت معمولی یا ساده و SIMD اجرا میکنیم. قبل از اجرای عملیات اول ابتدا stopwatch را ریست میکنیم. با این کار زمان صفر شده و شروع به اندازه گیری میکند. سپس عملیات مربوط به جمع دو آرایه را در حالت معمولی که در فایل IntSimdProcessor.cs قرار دارد، فراخوانی میکنیم. پس از اجرای این عملیات مقدار stopwatch را به میلی ثانیه در یک متغیر ذخیره میکنیم و این مقدار را به لیست زمانهای اجرای معمولی اضافه میکنیم. در نهایت نتیجه زمان اجرا را در خروجی چاپ میکنیم.
stopwatch.Restart();
var result = IntSimdProcessor.NaiveSumFunc(intsOne, intsTwo);
var naiveTimeMs = stopwatch.ElapsedMilliseconds;
naiveTimesMs.Add(naiveTimeMs);
WriteLine($"Naive analysis took: {naiveTimeMs}ms (last value = {result.Last()}).");
پس از اجرای عملیات در حالت ساده یا معمولی، حال نوبت همان عملیات در حالت SIMD میباشد. دوباره stopwatch را ریست میکنیم و عملیات در SIMD را اجرا کرده و بعد از آن مقدار stopwatch را درون متغیری میریزیم و آن را به لیست زمانهای اجرای عملیات در SIMD اضافه میکنیم و در نهایت نتیجه زمان اجرا را در خروجی چاپ میکنیم.
stopwatch.Restart();
result = IntSimdProcessor.HWAcceleratedSumFunc(intsOne, intsTwo);
var hwTimeMs = stopwatch.ElapsedMilliseconds;
hwTimesMs.Add(hwTimeMs);
WriteLine($"Hareware accelerated analysis took: {hwTimeMs}ms (last value = {result.Last()}).");
پس از اجرای حلقه، حال نوبت به نمایش نتیجه میانگین زمانها در خروجی است. ابتدا میانگین زمانهای اجرا در حالت ساده یا معمولی را که به میلی ثانیه است را در خروجی چاپ میکنیم. بعد از آن میانگین زمانهای اجرا در حالت SIMD را در خروجی چاپ میکنیم و در آخر سرعت زمان اجرا در حالت SIMD را نسبت به حالت معمولی به درصد چاپ میکنیم.
WriteLine($"Naive method average time: {naiveTimesMs.Average():.##}");
WriteLine($"HW accelerated method average time: {hwTimesMs.Average():.##}");
WriteLine($"Hardware speedup: {naiveTimesMs.Average() / hwTimesMs.Average():P}%");
در این مقاله تنها به توضیحی در مورد این آزمایش اکتفا میکنیم. لازم به ذکر است که دیگر آزمایشها نیز دقیقا ساختاری مشابه این آزمایش را دارند و تنها عملیات اجرا در آنها متفاوت است. در کلاس PerformanceTests توضیحات لازم مربوط به هر آزمایش و تابع داده شده است و میتوانید با مراجعه به کد برنامه آنها را مورد بررسی قرار دهید.
برای اجرای تمامی آزمایشها، کلیه توابع نوشته شده در کلاس PerformanceTests را در کلاس Program و در تابع Main که تابع شروع کننده برنامه میباشد، پس از بررسی وضعیت نوعهای دادهای قرار میدهیم.
تصویر مربوط به اجرای کامل برنامه را میتوانید مشاهده میکنید.
این جدول بر اساس یک بار اجرای برنامه در سیستم من ترسیم شده است و اجرای برنامه در سیستمهای مختلف خروجیهای متفاوتی را دارد. لازم به ذکر است که اندازه آرایهها بسیار بزرگ است و این نتایج با آرایههایی به طول بیش از هزاران هزار عنصر میباشد.
زمانها در جدول به میلی ثانیه میباشد.
ردیف | عملیات | دور اول | دور دوم | دور سوم | میانگین حالت ساده | میانگین حالت SIMD |
درحالت ساده | درحالت SIMD | درحالت ساده | درحالت SIMD | درحالت ساده | درحالت SIMD |
1 | جمع دو آرایه با استفاده از یک آرایه
کمکی در اعداد صحیح | 157 | 131 | 128 | 131 | 128 | 138 | 137.67 | 133.33 |
2 | جمع دو آرایه بدون استفاده از آرایه
کمکی در اعداد float | 122 | 133 | 99 | 99 | 99 | 93 | 106.67 | 108.33 |
3 | جمع دو آرایه بدون استفاده از آرایه
کمکی در اعداد صحیح | 83 | 73 | 86 | 88 | 78 | 81 | 82.33 | 80.67 |
4 | جمع دو آرایه اول ورودی - مجموع در
آرایه سوم ریخته میشود - در اعداد صحیح کوچک بدون علامت | 58 | 63 | 50 | 48 | 58 | 46 | 55.33 | 52.33 |
5 | جمع دو آرایه بدون استفاده از آرایه
کمکی در اعداد صحیح کوچک بدون علامت | 55 | 40 | 53 | 36 | 53 | 46 | 53.67 | 40.67 |
6 | بدست آوردن کمترین و بیشترین مقدار
در یک آرایه اعداد صحیح | 91 | 36 | 91 | 39 | 90.67 | 38 | 90.66 | 38 |
7 | بدست آوردن کمترین و بیشترین مقدار
در یک آرایه اعداد صحیح کوچک بدون علامت | 90 | 20 | 89 | 19 | 88 | 18 | 89 | 19 |
8 | جمع عناصر آرایه ورودی و ذخیره مجموع
آنها در یک متغیر کمکی | 33 | 309 | 32 | 263 | 31 | 291 | 32 | 287.67 |
9 | جمع عناصر آرایه ورودی و ذخیره مجموع
آنها در یک متغیر کمکی بدون بررسی سرریز | 30 | 13 | 29 | 13 | 30 | 12 | 29.67 | 12.67 |
10 | محاسبه میانگین و بدست آوردن کمترین
و بیشترین مقدار در آرایه اعداد صحیح کوچک بدون علامت | 89 | 50 | 90 | 51 | 90 | 49 | 89.57 | 50 |
سورس کامل برنامه را که شامل تغییراتی در توابع برای بهبود و اضافه شدن کامنت برای فهم بیشتر کدها میباشد، در زیر میتوانید دریافت کنید: