هنگامیکه دربارهی بهینهسازی برنامه C صحبت میکنیم، معمولاً به دوجنبه اشاره داریم: 1) حجم کد برنامه 2) سرعت اجرای برنامه.
کامپایلرهای امروزی C گزینههای متنوعی برای بهینهسازی برنامه C میکروکنترلر AVR در هردو جنبهی حجم و سرعت اجرای کد دارند. با اینحال یک برنامه با کدنویسی خوب این فرصت را برای کامپایلرها ایجاد میکند که کد را به بهترین حالت بهینه کنند. دربرخی مواقع بهینهکردن کد برنامه در یک جنبه، تأثیر منفی بر جنبهی دیگر دارد. بنابراین برنامهنویسان باتوجهبه نیازهای خاص خود باید بین این دو جنبهی بهینهسازی، تعادل برقرار کنند. دانستن نکات و ترفندهای بهینهسازی برنامه C میکروکنترلرهای هشتبیتی AVR به برنامهنویسان کمک میکند که برنامهای با کارایی بالا داشته باشند.
در این مقاله قصد داریم بههمین نکات بپردازیم. اما پیشاز بهینه کردن برنامههای سیستمهای تعبیهشدهی خود (embedded systems) نیاز است که درک درستیاز معماریو ساختار هستهی AVR داشته باشیم. همچنین باید بدانیم کامپایلر از چه ترفندهایی برای تولید بهینهی کد استفاده میکند. با ما همراه باشید.
معماری میکروکنترلر هشت بیتی AVR
یکیاز ویژگیهای مهم میکروکنترلر AVR، برخورداریاز معماری هاروارد (HARVARD) است. دراین معماری، داده و دستورالعمل در حافظهای جداگانه قرارگرفته و همچنین از مسیرهای سیگنال جداگانه استفاده میکنند. میکروکنترلرهای AVR دارای ۳۲ رجیستر کاری ۸بیتی هستند. این رجیسترها جزئیاز حافظهیداده بهشمار میآیند. گفتنیاست تعداد رجیسترهای کاری بر کارایی پردازنده تأثیر بهسزایی دارد؛ چراکه دستورات منطقیو محاسباتی با رجیسترهای کاری کار میکنند. ALU میتواند در سیکل کلاک به یک زوج از رجیسترهای کاری دسترسی پیدا٬کند و با دو عملوند که در این دو رجیستر هستند دستورالعمل را اجرا٬کرده و نتیجه را به رجیستر مقصد بازگرداند. زمان اجرای دستورالعملها تنها یک سیکل کلاک است و در یک ساختار Pipeline فازهای اجرای دستورالعمل و واکشی بهصورت موازی انجام میشود. حافظهی برنامه بهصورت ۱۶ بیتی سازمان یافتهاند و تمام دستورالعملهای AVR شانزده یا ۳۲ بیت عرض دارند. در میکروکنترلرهای AVR، معماری ریسک (RISC) پیشرفته در راستای کاهش حجم کدبرنامه و اجرای عملیات در یک سیکل کلاک و بهینهسازی برنامه C بهکار گرفتهشدهاست.
کامپایلر GCC
پیشاز این، درمقالهی «کامپایلر Codevisionavr در مقابل کامپایلر GCC و مقایسه تخصصی آنها» با تفاوتها و برتری کامپایلر GCC نسبتبه کدویژن آشنا شدیم. در این مقاله بر اساس کامپایلر GCC پیش میرویم با این حال همهی نکات و ترفندها برای کامپایلرهای دیگر قابلاجرا است. کامپایلر GCC سطوح مختلف بهینهسازی را فراهم میکند. کامپایلر میتواند بهینهسازی را یا برروی حجمکد و یا برروی سرعت اجرایکد انجامدهد که این عمل در پنج سطح مختلف Os , -O3 , -O2 , -O1 , -O0- انجام میگردد. سطح O0- بدون انجام بهینهسازی است. در کنار این سطوح، گزینههای دیگری نیز برای انتخاب شرایط بهینهسازی موردنظر خود وجود دارد. در این لینک همهی سطوح بهینهسازی بهصورت کامل آورده شده است.
نکات و ترفندهای کاهش حجم برنامه برای بهینهسازی برنامه C
1) انواع دادهها و اندازهی آنها
از کوچکترین نوع دادهی ممکن استفادهکنید. کد خود را از لحاظ انواع دادهی بهکار رفته ارزیابیکنید. برای خواندن یک مقدار ۸ بیتی به یک متغیر ۱ بایتی نیاز است نه متغیر ۲ بایتی. اندازهی انواع دادهها در فایل هدر stdint وجود دارد.
در نمونهی٬زیر مشاهدهمیکنیم که درمثال سمتچپ، نوع دادهی int دو بایتی و درمثال سمتراست نوع دادهی char یک بایتی تعریفشدهاست. همانطور که مشاهدهمیکنید از حافظه، ۲ بایت کمتر استفادهشدهاست:
2) متغیرهای عمومی (Global) و محلی (local)
دربیشتر موارد استفادهاز متغیرهای عمومی توصیه نمیشود. تاجاییکه ممکناست از متغیرهای محلی استفاده کنید. اگر یک متغیر تنها در یک تابع بهکار میرود، آنرا بهصورت محلی در خود تابع تعریف کنید. اگر یک متغیر عمومی تعریف شود، آدرس منحصربهفردی در حافظهی SRAM بهآن اختصاص دادهمیشود و مقدار آن در تمام مدت اجرای برنامه حفظ میشود. متغیرهای محلی برای همهی توابع قابلاستفاده نیستند و محلیاز حافظه یا رجیستر کاری بهآن اختصاص دادهمیشود و پساز خروجاز تابع این فضا آزاد میشود. در مثال سمتچپ متغیرعمومی و در مثال سمتراست متغیرمحلی بهکار رفتهاست. حجم اشغالی حافظهی برنامه و داده نشان میدهد که متغیر محلی فضای کمتری از حافظه را اشغال میکند.
3) اندیس حلقهی تکرار
همانطورکه میدانیم، سهنوع حلقهی تکرار while و for و do-while وجود دارد. اگر سطح بهینهسازی –Os فعال باشد، کامپایلر حلقهها را بهگونهای بهینه میکند که حجم کد یکسانی داشتهباشند. با اینحال ما میتوانیم با ترفندهایی، حجم کد را بیشتر کاهش دهیم. اگر از حلقهی do-while در برنامهی خود استفاده میکنیم، بستهبه اینکه از اندیس افزایشی یا کاهشی تعریف میشود، اندازهی کد متفاوتی ایجاد خواهد شد. ما بهطور معمول در برنامهها اندیس افزایشی را بهکار میبریم. بهاینمعنا که از مقدار صفر تا مقدار حداکثر افزایش مییابد. اما ازنظر بهینهسازی کد بهتر است که از مقدار حداکثر تا صفر کاهش یابد؛ بهاین علت که در حلقهی افزایشی، باید هربار که حلقهی تکرار اجرا میشود، مقدار اندیس با مقدار حداکثر حلقه که در بخش شرط حلقهی تعریفشده، مقایسهشود. در حلقهی کاهشی نیاز نیست که در هربار اجرای حلقه شرط حلقه چک شود زیرا مقدار اندیس اگر به صفر برسد، پرچم Z را در رجیستر وضعیت SREG یک میکند. درمثال، تفاوت حجم اشغالی حافظه را در دو حالت حلقهی کاهشی و افزایشی مشاهده میکنید.
4) تلفیق حلقههای تکرار
اگر حلقههای متفاوت، محدودهی تکرار یکسان داشتهباشند و از دادهی یکدیگر استفاده نکنند، میتوانیم آنها را تلفیق کرده و به یک حلقهی واحد تبدیل کنیم تا تعداد حلقهها را در برنامه کاهشدهیم. تلفیق حلقهها به کاهش حجم کد و افزایش سرعت اجرای برنامه منجر میشود.
در قسمتهای دیگر با نکات و ترفندهای بیشتری دربارهی کاهش حجم کد برنامه برای بهینهسازی برنامه C آشنا خواهیم شد. پس از آن به سراغ افزایش سرعت اجرای برنامه و کاهش زمان اجرا میرویم.
منبع: سیسوگ
[…] مقالهی «نکات و ترفندهای بهینهسازی برنامه C برای میکروکنترلر AV…» به معماری میکروکنترلرهای ۸بیتی AVR و کامپایلر GCC و نکات […]
[…] دو مقالهی پیشین «نکات و ترفندهای بهینهسازی برنامه C برای میکروکنترلر AV…» و «قسمت دوم» به معماری میکروکنترلرهای ۸بیتی AVR و […]