در دو مقالهی پیشین «نکات و ترفندهای بهینهسازی برنامه C برای میکروکنترلر AVR-قسمت اول» و «قسمت دوم» به معماری میکروکنترلرهای ۸بیتی AVR و کامپایلر GCC و نکات بهینهسازی حجم کدبرنامه C پرداختیم. درمقالهی پیشرو با نکات مربوطبه کاهش زمان اجرای برنامه (execution time) برای بهینه سازی برنامه C آشنا میشویم.
نکاتو ترفندهای کاهش زمان اجرای برنامه برای بهینهسازی برنامه C
1)انواع دادهها و اندازهی آنها
انتخاب نوع داده و اندازهی دادهی مناسب افزون بر کاهشحجم کدبرنامه، زماناجرا را نیز کاهشخواهدداد. برای میکروکنترلرهای ۸بیتی AVR، دسترسیبه مقدار ۸بیتی، کارآمدترین روش است. درمثالزیر میتوانید تفاوت زمان اجرا را با ۲ متغیر ۸بیتی و ۱۶بیتی مشاهدهکنید.
2) دستورات شرطی
یادآوری: عملگر کاهش (Decrement Operator) مقدار یک متغیر را یک واحد کاهش میدهد. عملگر کاهش دارای دو نوع است. در صورتی که عملگر کاهش، بعد از متغیر بیاید، حاصل عبارت برابر با مقدار اولیهی متغیر میشود و به آن عملگر پسا-کاهش (post-decrement) گفته میشود. ولی در صورتی که این عملگر قبل از متغیر، استفاده شود، حاصل عبارت برابر با مقدار تغییر کردهی متغیر است. به این عملگر، عملگر پیش-کاهش (Pre-Decrement) گفته میشود.
معمولا عملگرهای پیش-کاهش (pre-decrement) یا پسا-کاهش (post-decrement) در کدهای عادی هیچ تفاوتی ندارند و کد یکسانی را تولید حواهند کرد. برای عملگرهای پیش-افزایش (pre-increment) یا پسا-افزایش (post-increment) نیز مشابه است. بااینحال استفادهاز این نوع عملگر بهعنوان اندیس حلقهها و یا در دستوراتشرطی کد متفاوتی تولید خواهدکرد. همانطورکه در «قسمت اول-اندیس حلقهی تکرار» مشاهدهکردیم، بهکاربردن اندیس حلقه بهصورت کاهشی، حجم کد کمتری را تولید خواهدکرد. استفادهاز اندیسهای کاهشی در جملاتشرطی به افزایش سرعت اجرا نیز کمک خواهدکرد. ازسویدیگر در عملگر کاهشی، دونوع عملگر پیش-کاهش و پسا-کاهش نیز نتایج متفاوتی تولید میکند. درمثالزیر خواهیمدید نوشتن جملهی شرطی بااستفادهاز عملگر پیش-کاهش، زمان اجرای کد را کاهش میدهد. مقدار Cycle Counter زمان اجرای طولانیترین حلقه را نشان میدهد. «loop_cnt» در دومثال راست و چپ مقدار متفاوت دارد تا هر دو کد بهصورت مشابه اجرا شود. در این کد درهر اجرا، PORTC0 به تعداد ۹بار و PORTB0 یکبار toggle میشوند.
3) بازکردن حلقه (Unrolling loops)
دربرخی برنامهها برای افزایشسرعت اجرا از روش بازکردن حلقه استفاده میشود. این روش برای حلقههای کوتاه مناسب است. پساز اینکه یک حلقهی باز (unrol) میشود، شرط حلقه برای چککردن وجودندارد و در هرمرحله اجرای حلقه، شاخههای کمتری اجرا میشوند. درمثالزیر یک پورت، ۱۰ بار toggle میشود. همانطورکه میبینید با بازکردن حلقه، سرعت اجرای برنامه افزایش مییابد. ازطرفی حجم کد برنامه افزایش یافتهاست. این مثال توجهبه تعادل میان حجم کد و سرعت برنامه را نشان میدهد. اگر کامپایلر از سطح بهینهسازی O3- استفاده کند بهصورت خودکار حلقه را باز میکند.
4)جریان کنترل (Control flow): دستورات if-else و switch-case
دستورات if-else و switch-case بهطور گسترده در برنامهنویسی C کاربرد دارند. این دستورات یک روش مناسب برای کاهش زمان اجرای برنامه و بهینهسازی برنامه C به شمار میآیند. برای دستور if-else همیشه محتملترین شرط را در مکان اول بنویسید و شرطهای با احتمالکمتر را در مکانهای بعدی دستور قراردهید. بااینکار در زمان اجرای برنامه صرفهجویی میشود. به کارگیری دستور switch-case بهجای if-else باعث میشود که کامپایلر lookup table تشکیلدهد و به مکان با شرط برقرار، پرش کند. اگر استفادهاز دستور switch-case برای برنامهی موردنظر مشکل است میتوان از تعدادی if-else با زیر شاخههای کوچکتر استفادهکرد. این روش زمان اجرای برنامه را برای بدترین شرایط کاهش میدهد. درمثالزیر داده از ADC دریافت میشود و با USART ارسال میشود. دراین مثال زمان اجرا کاهشیافته اما حجم کد زیاد شدهاست. بنابراین باتوجهبه شرایط موردنیاز، میان سرعت و حجم کد باید تعادل برقرار کنیم.
منبع: سیسوگ