انواع بهینه سازی در کامیایلرها - نسخهی قابل چاپ |
انواع بهینه سازی در کامیایلرها - admin - 08 خرداد ۱۳۸۹ ۰۴:۵۴ ب.ظ
کامپایلرهای جدید قادر به انجام تغییرات زیادی بر روی کد ورودی به منظور افزایش کارایی آن میباشند. برای یک برنامه نویس سودمند خواهد بود که بداند یک کامپایلر چه بهینه سازی هایی را میتواند انجام دهد و کدام را نمیتواند. در این بخش به معرفی تعدادی از بهینه سازی هایی که یک کامپایلر میتواند انجام دهد میپردازیم. ۱- Function inlining کامپایلر میتواند فراخوانی یک تابع را با بدنه آن جایگزین نماید. به مثال زیر دقت نمایید: کد: // Example 1a کامپایلر میتواند دستور فراخوانی تابع square را با بدنه آن جایگزین نماید. به صورت زیر: کد: // Example 1b مزایای این بهینه سازی عبارتند از: ۱- بالاسری ناشی از فراخوانی و بازگشت و انتقال پارامتر به تابع حذف میشود. ۲- درصد محلی بودن ارجاعات در کد بیشتر و درنتیجه ضریب برخورد در حافظه نهان بیشتر میشود. ۳- اگر فقط یک فراخوانی روی تابع inline شده وجود داشته باشد کد کوچکتر میشود. ۴- inline نمودن یک تابع فرصتی را برای اعمال سایر بهینه سازیها روی کد بوجود می آورد که ذیلا توضیح داده خواهد شد. عیب inline آنمودن یک تابع این است که اگر درصد فراخوانی تابع در کد زیاد بوده باشد و به خصوص این که تابع بزرگ باشد باعث افزایش طول کد برنامه میشود. به هر حال اکر تابع کوچک باشد و اگر تعداد فراخوانی های آن کم باشد کامپایلر آن تابع را inline میکند. ۲- constant folding and constant propagation در بهینه سازی constant folding یک عبارت یا زیرعبارت که فقط شامل ثوابت باشد با نتیجه آن عبارت جایگزین میشود. به مثال زیر دقت نمایید: کد: // Example 2a کد: // Example 2b در بهینه سازی constant propagation یک ثابت در صورت امکان در بین یک سری از عبارات منتشر میشود. به مثال زیر توجه نمایید: کد: // Example 3a کامپایلر این کد را با کد زیر جایگزین مینماید: کد: // Example 3b کد: // Example 4 تابع sin در یک کتابخانه جدا تعریف شده و نمیتوان از کامپایلر انتظار داشت که بتواند این تابع را inline نماید و آن را در زمان کامپایل محاسبه نماید. ۳- Pointer elimination اگر در زمان کامپایل معلوم باشد که یک اشاره گر به چه خانه ای از حافظه اشاره میکند میتوان آن اشاره گر را حذف نمود. به مثال زیر دقت نمایید: کد: // Example 5a کد: // Example 5b اگر در یک عبارت چندین بار یک زیر عبارت تکرار شده باشد آنگاه کامپایلر فقط یکبار آن زیرعبارت را محاسبه مینماید. به مثال زیر دقت نمایید: کد: // Example 6a کامپایلر این کد را با کد زیر جایگزین مینماید: کد: // Example 6b در این بهینه سازی کامپایلر سعی میکند که متغیرهایی را که بیشتر از همه مورد استفاده قرار میگیرند در ثبات های cpu ذخیره کند. از جمله این متغیرها میتوان به شمارنده های حلقه ها، اشاره گرها، پارامترهای توابع و … اشاره کرد. دقت کنید که اگر در کد یک برنامه با آدرس یک متغیر کار میکنیم آن متغیر نمیتواند در یک ثبات قرار گیرد چراکه ثبات آدرس ندارد. ۶- Join identical branches در این بهینه سازی قسمت های مشترک کد مربوط به دو پرش if-then-else به منظور صرفه جویی در کد یکی میشوند. به مثال زیر دقت نمایید: کد: // Example 8a کامپایلر این کد را با کد زیر جایگزین مینماید: کد: // Example 8b با کپی کردن کدی که به آن پرش میشود میتوان از انجام پرش جلوگیری کرد. به مثال زیر دقت نمایید: کد: // Example 9a کد: // Example 9b همچنین اگر درست یا غلط بودن نتیجه یک عبارت شرطی مربوط به یک دستور شرطی در زمان کامپایل مشخص باشد میتوان پرش را حذف نمود. به مثال زیر دقت نمایید: // Example 10a کد: if (true) { کامپایلر این کد را با کد زیر جایگزین مینماید: کد: // Example 10b همچنین اگر دو دستور شرطی با عبارات شرطی یکسان به طور متوالی آمده باشند و برای کامپایلر واضح باشد که نتیجه این دو عبارت شرطی یکسان است در این صورت دستور شرطی دوم میتواند حذف شود و دستورات موجود در بدنه آن به بدنه دستور شرطی اول اضافه شود. به مثال زیر دقت نمایید: کد: // Example 11a کامپایلر این کد را با کد زیر جایگزین مینماید: کد: // Example 11b ۸- Loop unrolling بعضی از کامپایلرها حلقه را باز میکنند. معمولا این کار زمانی انجام میشود که بدنه حلقه خیلی کوچک باشد و باز کردن آن راه را برای انجام بهینه سازی های بیشتر باز کند. از طرف دیگر حلقه هایی نیز که تعداد تکرار آنها خیلی کوچک است به منظور حذف نمودن بالاسری حلقه سازی باز میشوند. به مثال زیر دقت نمایید: کد: // Example 12a کد: // Example 12b در این بهینه سازی، کامپایلر عبارت یا دستوری را که بود یا نبود آن در حلقه، هیچ فرقی نداشته باشد از حلقه خارج میکند. به مثال زیر دقت نمایید: کد: // Example 13a کد: // Example 13b |