قبلاً در مقالهای تحت عنوان “چرا کدویژن نه!“ به بررسی شبه کامپایلر کدویژن و ناکارآمدی آن پرداختیم. مقاله مذکور بیشتر با محوریت قابلیتهای استاندارد یک کامپایلر بود که کدویژن آنها را نداشت و به عملکرد واقعی کدویژن پرداخته نشده بود. در این مقاله میخواهیم در رابطه با بهترین کامپایلر AVR صحبت کنیم.
با چند تن از دوستان در خصوص همین مسئله تبادلنظر داشتیم. دوستان معتقد بودند که کدویژن در کامپایل و بهینهسازی کد خروجی خیلی موفقتر از GCC عمل میکند و در استاندارد نبودن آن مهم نیست و بهینه بودن کد خروجی از درجه اهمیت بالاتری برخوردار است. هرچند به نظر بنده، استاندارد بودن از درجه اهمیت بالاتری برخوردار است؛ چراکه کد قابلحمل و توسعه میشود. اگر کد شما قابلحمل باشد، برای انتقال آن از AVR به ARM نیازی به بازنویسی و حذف بخشهای غیراستاندارد نیست.
از طرفی برتری داشتن یک شبه کامپایلر که توسط یک شرکت با تعداد افراد محدود ایجاده شده، از یک کامپایلر اپن سورس که جامعهای از برنامهنویسان آزاد برای توسعه آنوقت گذاشتهاند، قابلپذیرش نیست. ولی قرار بر این شد که قابلیت اپتیمایز کد و همچنین سرعت اجرای کد ایجادشده توسط این دو کامپایلر را بررسی کنیم تا بتوان راحتتر در خصوص آنها قضاوت کرد.
در ادامه به بررسی دو کامپایلر Code vision و GCC خواهیم پرداخت.
سختافزار
برای انجام تستها نیاز به یک سختافزار پایه است که کدهای ایجادشده توسط هر کامپایلر را بر روی آن پروگرام کرد و بتوان نتیجه آن را مشاهده کرد. برای همین منظور ما از برد آردوینو Nano بهعنوان سختافزار استفاده کردیم.
این برد از پردازنده Atmega328p استفاده میکند که توسط کریستال ۱۶ مگاهرتز کلاک آن تأمین میشود. برای اینکه بتوان نتیجه را ملموستر احساس کرد، فرکانس کاری میکرو را با تقسیم فرکانس ورودی بر ۱۶ به یک مگاسیکل کاهش دادیم. در تمام برنامهها برای اندازهگیری سرعت اجرای برنامه، به کمک یک لاجیک آنالایز، سرعت صفر و یک شدن پایه PORTB.4 را اندازهگیری میکنیم که درواقع ملاکی از سرعت اجرای برنامه است؛ هرچه این پایه با سرعت بیشتری صفر و یک شود یعنی سرعت اجرای برنامه بیشتر و عملکرد کامپایلر در کامپایل کد بهتر است.
بهترین کامپایلر AVR
ما برای اینکه متوجه بشویم بهترین کامپایلر AVR چیست، از کامپایلر کدویژن ورژن 3.12 استفاده میکند.
و از کامپایلر GCC ورژن 3.5 بهعنوان کامپایلر GCC استفاده کردهایم.
شرایط یکسان برای کامپایلرها
ایجاد شرایط یکسان برای اینکه به نتیجه مناسبی برای بهترین کامپایلر AVR برسیم با توجه به تفاوتهای کلیدی بین این دو کامپایلر مقداری سخت است اما ما سعی کردیم که دو کامپایلر را در بهترین حالت اپتیمایز کد قرار دهیم.
همانطور که در عکس فوق مشاهده میکنید، هر دو کامپایلر در حالت اپتیمایز حجم کانفیگ شدهاند. در این کانفیگ، کامپایلر به نحوی تنظیم میشود که کمترین حجم ممکن را ایجاد کند.
برنامه تست اول:
int main(void) { CLKPR=(1<<CLKPCE); CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (1<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0); DDRB |= 1<<4; while(1) { PORTB ^= 1<<4; } }
در این برنامه خروجی Toggle شده و از هیچ تأخیری هم استفاده نشده است.
CLKPR=(1<<CLKPCE); CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (1<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0);
دقت داشته باشید که دو خط اول برنامه برای تنظیم تقسیمکنندهی ورودی است که فرکانس 16 مگاهرتز را به 1 مگاهرتز کاهش دهد.
برنامه تست دوم:
int main(void) { float Var = 0; CLKPR=(1<<CLKPCE); CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (1<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0); DDRB |= 1<<4; while(1) { Var += 0.1f; PORTB = ((uint8_t)Var) << 4; } }
در این برنامه علاوه بر تغییر وضعیت خروجی، محاسبات سادهی اعداد اعشاری نیز صورت گرفته است.
برنامه تست سوم:
uint8_t foo(uint32_t x) { x = x - ((x >> 1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0F0F0F0F; x = x + (x >> 8); x = x + (x >> 16); return x & 0x0000003F; } int main(void) { uint32_t x = 0xFAFAFAFA; CLKPR=(1<<CLKPCE); CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (1<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0); DDRB |= 1<<4; while(1) { PORTC = foo(x++); PORTB ^= 1<<4; } }
در این برنامه علاوه بر تغییر وضعیت خروجی، یک سری عملیات ریاضی و منطقی بر روی متغیرهای 32 بیتی انجامشده به همراه فراخواندن یک متد.
برنامه تست چهارم:
uint8_t foo(uint64_t x) { x = x - ((x >> 1) & 0x5555555555555555); x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333); x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F; x = x + (x >> 8); x = x + (x >> 16); return x & 0x000000000000003F; } int main(void) { uint64_t x = 0xFAFAFAFAFAFAFAFA; CLKPR=(1<<CLKPCE); CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (1<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0); DDRB |= 1<<4; while(1) { PORTC = foo(x++); PORTB ^= 1<<4; } }
این برنامه همانند برنامه سوم است، با این تفاوت که عملیات ریاضی و منطقی بر روی متغیرهای 64 بیتی انجام شده است.
برنامه تست پنجم :
int main(void) { uint32_t aaa , bbb, ccc; CLKPR=(1<<CLKPCE); CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (1<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0); DDRB |= 1<<4; while(1) { aaa++; bbb++; ccc = aaa*(bbb^2); ccc = ccc/bbb; PORTB ^= 1<<4; } }
در این برنامه یک سری عملیات ریاضی بر روی متغیرها انجام شده و هم زمان خروجی تغییر وضعیت داده شده است.
نتایج:
حجم برنامه:
نمودار فوق نشاندهندهی حجم خروجی کامپایلر برای هر یک از برنامهها است. نمودار نارنجی مربوط به کامپایلر کدویژن و نمودار آبی مربوط به کامپابلر GCC است. دقت داشته باشید هر چه برنامه با حجم کمتری ایجاد شده باشد، بهینهتر است؛ چراکه شما قادر هستید برنامهی بیشتری را روی حافظهی مشخصی ذخیره کنید. پس نمودار کوچکتر یعنی عملکرد بهتر.
همانطور که از نتایج به دست آمده مشخص است، در این بخش، کامپایلر GCC به صورت 100 درصد عملکرد بهتری نسبت به شبه کامپایلر کدویژن دارد.
سرعت اجرا :
توضیح در خصوص جواب برنامه 5 و اختلاف فاحش آن :
کامپایلر GCC دارای هوشمندی زیادی در اپتیمایز کد است. این کامپایلر با تشخیص قسمتهای غیرضروری در برنامه و حذف آنها، هم سرعت اجرا بالا میبرد و هم در حافظهی برنامه صرفهجویی میکند. اگر دقت کنید در برنامه شماره 5 یک سری عملیات ریاضی روی سه متغیر aaa و bbb و ccc انجام شده است که نتیجه عملیات فوق هیچ جا مورداستفاده قرار نگرفته است؛ به همین دلیل کامپایلر این عملیات را غیرضروری فرض و تمام روال آن را از برنامه حذف کرده است. به همین دلیل سرعت و حجم برنامه شماره 5 و 1 یکی است. اما شبه کامپایلر کدویژن دارای این هوشمندی نیست و نمیتواند قسمتهای غیرضروری برنامه را تشخیص دهد و آنها را حذف کند.
بهترین کامپایلر AVR کدام است؟
همانطور که انتظار میرفت کامپایلر GCC در تمام موارد عملکردی بهمراتب بهتری از شبه کامپایلر کدویژن دارد. یکی از دلایل موفقیت صد درصدی کامپایلر GCC، متنباز بودن و توسعهی آن توسط برنامه نویسان مجرب و کارکشته سراسر جهان است. کامپایلر GCC در برنامههای زیادی مورد استفاده قرار گرفته است؛ ازجمله: AVR Studio یا Arduino و… . این نیز خود نشاندهندهی برتری این کامپایلر است.
توصیه میکنم اگر از کدویژن استفاده میکنید، آن را کنار بگذارید و برای حرفهایتر شدن، با GCC شروع به کدنویسی کنید.
منبع: سیسوگ
[…] […]