موضوع مقاله: بهینه سازی درون تابع | مخاطب: عمومی | لحن: دانشنامه‌ای | ساختار: عنوان، مقدمه، 3 بخش، جمع‌بندی | مقاله: | # بهینه سازی درون تابع | مقدمه: بهینه سازی درون تابعی با بقیه بهینه سازهای کامپایلر متفاوت است به این دلیل که در این بهینه سازی تمام برنامه تحلیل می‌شود ولی بقیه بهینه سازها تنها به یکی از توابع یا حتی به یک بلوک از کد نگاه می‌کنند. | ## بخش 1: بهینه سازی درون تابع | بهینه سازی درون تابعی به دنبال کاهش محاسبات اضافی، استفاده‌های ناکارامد از حافظه و ساده سازی کدهای تکراری مانند حلقه هاست. اگر روالی وجود داشته باشد که درون حلقه ای صدا زده شده باشد، آنگاه تحلیل بهینه سازی درون تابع ممکن است برای ما مشخص کند که بهتر است آن روال به صورت توسعه برخط اجرا شود. به علاوه اینکه، بهینه سازی درون تابع ممکن است ترتیب روال‌ها را برای لایه ای بودن حافظه برای بهبود عملکرد حافظه و محلیت داده‌های حافظه عوض کند. همین‌طور بهینه سازی درون تابع ممکن است بهینه سازی‌های معمولی کامپایلر در سطح کل برنامه هم باشد. برای مثال می‌توان به حذف کدهای مرده (DCE) اشاره کرد که در در آن کدهایی که هرگز اجرا نمی‌شوند حذف می‌شوند. برای رسیدن به این هدف کامپایلر شاخه‌هایی از کد را که اجرا نمی‌شوند را آزمایش می‌کند و در صورت نیاز کدهای داخل آن شاخه را حذف می‌کند. بهینه سازی درون تابع همنیطور استفاده بهتر از ثوابت تعریف شده در کد را تضمین می‌کند.کامپایلرهای مدرن بهینه سازی درون تابع را به عنوان یک گزینه اختیاری در زمان کامپایل ارایه می‌دهند. پردازش و کار واقعی بهینه سازی درون تابع در هر قدم بین کد قابل خواندن برای انسان‌ها و تولید کد دودویی قابل اجرا برنامه اتفاق می‌افتد. مسئله اینجاست که این موضوع برای کامپایلر ممکن نیست که به صورت درست یک برنامه را تحلیل کند و مشخص کند این برنامه برای انجام چه کاری است و برنامه‌نویس چه قصدی از نوشتن این برنامه دارد. از طرف دیگر برنامه‌نویس‌ها با داشتن هدف از نوشتن برنامه شروع می‌کنند و برنامه ای را متناسب با آن می‌نویسند تا آن هدف را بدون پردازش اضافی برای پردازش محقق کنند. به دلایل متنوع شامل خوانایی، برنامه‌ها به‌طور پیوسته به تعدادی روال و مراحل شکسته می‌شوند که حالت‌های کلی تر را رسیدگی کنند. اگرچه، عام بودن هر روال ممکن است منجر به تلاش اضافه و بیهوده برای کاربردهای خاص شود. برای مثال تصور کنید که روالی وجود دارد که تابع F(x) را محاسبه می‌کند و برنامه در مکان‌های متفاوتی مقدار تابع F(6) را درخواست می‌دهد. محاسبه درخواست‌های دوم به بعد برای این مقدار در مکان‌های دیگر کاملاً غیر ضروریست. نتیجه می‌توانست با فرض اینکه تابع خالص باشد به جای آن ذخیره شود و در جاهای دیگر که تابع صدا زده شده بود به کار گرفته شود. برای مثال زمانی که تابع در اجرای خود ارجاعی به متغیرهایی به غیر از متغیر ورودی ۶ در مثال ما داشته باشد که در بین زمان صدا زدن تابع مقدار آن عوض شده باشد و اثرات حاشیه ای مثل چاپ کردن مقداری در لاگ، شمردن تعداد محاسبه‌ها، جمع کردن زمان گرفته شده از واحد کنترل مرکزی، آماده کردن جدول‌های داخلی برای ساده‌سازی، اجرای زیر دستورهایی برای متغیرهای مرتبط و …. از دست دادن این اثرهای حاشیه ای منجر به پذیرفتن مقدار خروجی برای دفعه‌های دیگر می‌شود. در حالت عمومی تر، جدا از بهینه سازی‌ها، دلیل دوم برای استفاده از روال‌ها این است که از ایجاد کدهای اضافی و تکراری که خروجی یکسانی دارند جلوگیری کند. بنابر این یک رویکر عمومی برای بهینه سازی عکس عمل بالا به این صورت خواهد بود که: تعدادی یا همهٔ فراخوانی‌های خاص با کد مربوطه و پارامترهای مناسب جایگزین می‌شوند. بهینه سازی کل برنامه(WPO) و بیهینه سازی زمان ارتباط(LTO) “بهینه سازی کامل برنامه"(WPO) بهینه سازی کامپایلر یک برنامه با استفاده از اطلاعات مربوط به همه (ماژول (برنامه نویسی)|ماژول‌ها) در برنامه است. | ## بخش 2: بهینه سازی درون تابع | به طور معمول، بهینه سازی‌ها بر اساس [واحد ترجمه (برنامه نویسی) | در هر ماژول ، "کامپایل"] انجام می‌شوند. «بهینه سازی زمان پیوند» '(LTO) نوعی بهینه سازی برنامه است که توسط یک کامپایلر به یک برنامه در زمان پیوند انجام می‌شود. بهینه سازی زمان لینک در زبان‌های برنامه‌نویسی مرتبط است که برنامه‌ها را به صورت فایل به فایل کامپایل می‌کنند، و سپس آن فایل‌ها را به هم پیوند می‌دهد (مانند C و :فورترن)، به جای اینکه همه را با هم پیوند دهد. هنگامی که همه فایل‌ها به‌طور جداگانه به فایل شی (آبجکت فایل) تفسیر شدند، به‌طور سنتی، کامپایلر فایل‌های شی را به یک فایل واحد، اجرایی پیوند می‌دهد. با این حال، در LTO همان‌طور که توسط مجموعه کامپایلرهای GNU (جی‌سی‌سی: GCC) یا ال‌ال‌وی‌ام پیاده سازی شده‌است، مفسر قادر است نمایش میانی (جی‌سی‌سی bytod کد یا بیت کد LLVM) خود را در دیسک قرار دهد به گونه ای که تمام واحدهای مختلف مفسر که برای ساختن یک فایل اجرایی واحد به کار می‌روند، می‌توانند به عنوان یک ماژول واحد هنگامی که در نهایت اتفاق می‌افتد بهینه سازی شوند. این دامنه بهینه سازی‌های درون تابع را در بر می‌گیرد تا کل برنامه را شامل شود (یا به عبارت دیگر، هر آنچه در زمان پیوند قابل مشاهده است). با بهینه سازی زمان پیوند، کامپایلر می‌تواند اشکال مختلف بهینه سازی درون تابع را برای کل برنامه اعمال کند، و این امکان را برای تجزیه و تحلیل عمیق‌تر، بهینه سازی بیشتر و در نهایت عملکرد بهتر برنامه فراهم می‌کند. در عمل، LTO همیشه کل برنامه را بهینه نمی‌کند - [کتابخانه (محاسبات) | توابع کتابخانه]، به ویژه پیوند پویا اشیاء مشترک، به‌طور عمد برای جلوگیری از تکرار بیش از حد و قابلیت تغییر در زمان نگه داشته می‌شوند. هنگام فعال کردن افزونه لینک دهنده ، استفاده از برنامه-Ամբողջ کلمه ای لازم نیست (به افزونه فیوز-پیوند دهنده مراجعه کنید). با توجه به نگرانی‌های مربوط به عملکرد، حتی از کل واحد همواره به‌طور مستقیم استفاده نمی‌شود - یک برنامه می‌تواند به سبک تقسیم و تسخیر LTO مانند WHOPR GCC تقسیم شود. و البته وقتی برنامه ای که ساخته می‌شود، خود یک کتابخانه است، بهینه‌سازی هر نمادی را که در دسترس خارجی (صادر شده) قرار دارد، نگه می‌دارد، بدون آنکه تلاش زیادی در از بین بردن آنها به عنوان بخشی از DCE داشته باشید. شکل بسیار محدود تری از WPO هنوز هم بدون LTO امکان‌پذیر نیست، همان‌طور که توسط سوئیچ C | کد | -بعد برنامه-} GCC توضیح داده شده‌است. این حالت باعث می‌شود که GCC فرض کند که ماژول در حال تدوین شامل نقطه ورود (معمولاً ) کل برنامه است، به گونه ای که هر تابع دیگری در آن از بیرون استفاده نمی‌شود و می‌توان با خیال راحت ازبهینه شود. اگر در عوض پارامترها استراتژی ارزشیابی # فراخوانی توسط مرجع، پس اقدام بر روی آنها در "Silly" در واقع بر اصل متغیرها تأثیر می‌گذارد. این کار معمولاً با انتقال آدرس ماشین پارامترها به رویه انجام می‌شود تا تغییرات رویه در قسمت اصلی ذخیره سازی انجام شود. از چپ به راست متغیر p1 رو نویسی می‌شود.}و در این حالت کپی کردن مقدار "p1” (که تغییر یافته‌است) در"b” بی‌معنی است، زیرا بلافاصله با مقدار "p2" بازنویسی می‌شود، که این مقدار اصلاح نشده‌است. در این رویه از مقدار اصلی "b" استفاده می‌شود، بنابراین جمله سوم می‌شودwrite(5); {−۶ نیست.}چنین اختلافاتی در رفتار احتمالاً باعث ایجاد پازل خواهد شد، و این سؤالات راجع به ترتیب کپی پارامترها تشدید می‌شود: آیا در هنگام خروج مانند ورود نیز چپ به راست می‌شود؟ احتمالاً این جزئیات در کتابچه راهنمای کامپایلر توضیح داده نشده‌است، و اگر وجود داشته باشد، احتمالاً به دلیل نامربوط بودن به وظیفه منتقل می‌شوند و تا زمانی که یک مشکل پیش می‌آید فراموش می‌شوند. اگر (به احتمال زیاد) مقادیر موقتی از طریق یک برنامه ذخیره سازی پشته ارائه می‌شود، احتمالاً روند کپی به عقب (copy-back) به ترتیب معکوس برای کپی به داخل (copy-in) خواهد بود، که در این مثال به این معنی است که"p1” در عوض آخرین مقدار برگشتی به "b" خواهد بود. فرایند گسترش یک روال درون خطی را نباید به عنوان نوعی جایگزینی متن در نظر گرفت (مانند گسترش کلان) زیرا خطاهای نحوی ممکن است هنگام تغییر پارامترها ایجاد شود و از فراخوانی‌های خاص از ثوابت به عنوان پارامتر استفاده کند. | ## بخش 3: بهینه سازی درون تابع | از آنجا که این مهم است که اطمینان داشته باشید که هرگونه ثابت که به عنوان پارامتر ارائه می‌شود، هیچ تغییری در ارزش خود نکند (ثابت می‌تواند در حافظه نگه داشته شود همان‌طور که متغیرها هستند) مبادا استفاده‌های بعدی از آن ثابت (ساخته شده از طریق مراجعه به محل حافظه آن) خراب باشد. روش معمول این است که کامپایلر برای کپی کردن مقدار ثابت در یک متغیر موقت کدی را تولید کند که آدرس آن به رویه‌ها منتقل می‌شود، تولید کند و اگر مقدار آن تغییر یابد، مهم نیست. به عبارت دیگر، یک برنامه آزمایشی کتبی با دقت نوشته شده می‌تواند در مورد اینکه پارامترها براساس مقدار یا مرجع منتقل می‌شوند، گزارش دهند، و در صورت استفاده، از چه نوع طرح کپی به داخل و کپی به خارجی استفاده می‌کنند. با این حال، تنوع بی پایان است: پارامترهای ساده ممکن است با فراخوانی با کپی منتقل شوند در حالی که ساختارهای بزرگ مانند آرایه ممکن است توسط مرجع منتقل شود. ثابتهای ساده مانند صفر ممکن است توسط کدهای ویژه ماشین (مانند پاک کردن یا LoadZ) ایجاد شود در حالی که ثابت‌ترین پیچیدگی‌ها ممکن است در حافظه ذخیره شده و با استفاده از هر تلاشی برای اصلاح آن منجر به خاتمه برنامه فوری و غیره شوند. به احتمال زیاد، این امری در بسیاری از رویه‌های دارای خاصیت کاهشی یا برنامه‌ریز اعلام شده‌است که ممکن است بهینه سازیهای کامپایلر را برای یافتن مزیت فراهم کند. هر پارامتر برای یک روال ممکن است فقط خواندنی باشد، روی آن نوشته شود، هم خوانده شود و هم نوشته شود، یا نادیده گرفته شود و باعث شود فرصتهایی مانند ثابت‌هایی که نیازی به محافظت از طریق متغیرهای موقتی ندارند، ایجاد شود، اما ممکن است آنچه در هر فراخوانی اتفاق می‌افتد به شبکه پیچیده‌ای از توجه‌ها بستگی داشته باشد. سایر رویه‌ها، به ویژه رویه‌های شبیه به تابع، رفتارهای خاصی خواهند داشت که در فراخوانی‌های خاص ممکن است از برخی کارها جلوگیری کند: به عنوان مثال، تابع گاما در صورت فراخوانی با یک پارامتر عدد صحیح، می‌تواند به یک محاسبه شامل فاکتورگیری عدد صحیح تبدیل شود. برخی از زبانهای رایانه اعلان‌های مربوط به استفاده از پارامترها را فعال می‌کنند (یا حتی به آن احتیاج دارند)، و ممکن است فرصتی را فراهم کنند تا اعلام کنند متغیرها مقادیر خود را برای برخی از مجموعه‌ها محدود می‌کنند (به عنوان مثال ،۶<x)تا خرد کردن برای فرایند بهینه سازی و همچنین فراهم کردن بررسی‌های ارزشمند در انسجام کد منبع برای شناسایی اشتباهات آسان شود. اما این هرگز کافی نیست - فقط به بعضی از متغیرها محدودیت‌های ساده ای داده می‌شود، در حالی که برخی دیگر به مشخصات پیچیده‌ای نیاز دارند: چگونه می‌توان مشخص کرد که متغیر "P" یک عدد اول باشد و اگر چنین باشد، یک عضو عدد اول هست یا نیست. پیچیدگی‌ها فوری هستند: با توجه به اینکه "M" یک ماه است، محدوده‌های معتبر برای "D" روزهای یک ماه چیست؟ مشخصات کامل و کنترل با جزییات منجر به بیان و تعریف مجدد توابع برنامه به شکل دیگر می‌شود و جدا از زمانی که کامپایلر در ترجمه آنها مصرف می‌کند، در معرض اشکالات و باگ خواهند بود. در مواردی که برنامه ای ورودی را نمی‌خواند (مانند مثال)، می‌توان تصور کرد که تحلیل‌های کامپایلر در حال انجام است به طوری که نتیجه بیشتر از یک سری خط‌های چاپ شده نخواهد بود، یا احتمالاً برخی از حلقه‌ها به‌طور مصلحتانه چنین مقادیری را تولید می‌کنند. آیا آنگاه برنامه ای را برای تولید عددهای اول و تبدیل به بهترین روش شناخته شده برای انجام این کار، یا در عوض مراجعه به کتابخانه ارائه می‌دهد؟ تاریخچه برای زبانهای رویه ای یا الگول، تجزیه و تحلیل بینابینی و بهینه سازی به نظر می‌رسد که اوایل دهه ۱۹۷۰ وارد عمل تجاری شده‌اند. بهینه سازی کامپایلر IBM PL / I از تحلیل‌های بین رویه ای به منظور درک عوارض جانبی فراخونی رویه و استثنائات (به اصطلاح PL / I به عنوان "در شرایط") استفاده کرد. تکنیک‌های تحلیل بین رویه ای و بهینه سازی موضوع تحقیقات دانشگاهی در دهه‌های ۱۹۸۰ و ۱۹۹۰ بود. در اوایل دهه ۱۹۹۰ با کامپایلرهایی از محدب ("کامپایلر برنامه" برای محدب C4 و از اردنت کامپایلر برای [Ardent Titan وارد دنیای کامپایلر تجاری شد و دوباره ظهور کردند. این کامپایلرها نشان داده‌اند که فناوری‌ها می‌توانند به اندازه کافی سریع ساخته شوند تا به عنوان یک کامپایلر تجاری قابل قبول باشند. متعاقباً تکنیکهای بین رویه ای در تعدادی از سیستمهای تجاری و غیرتجاری دیده می‌شوند. | ## جمع‌بندی | نشانه‌ها و پیاده سازی کامپایلرهای Intel C / C ++ اجازه بهینه سازی درون تابعی تمام برنامه را می‌دهند.