بهینه‌سازی کامپایلر کیست؟ توضیح بده.
بهینه‌سازی یکی از مراحل اصلی در طراحی کامپایلر است که معمولاً پس از تولید کد میانی آغاز می‌شود و هدف آن بهبود کارایی برنامه بدون تغییر در رفتار ظاهری آن است. این فرآیند می‌تواند شامل کاهش حجم کد، کاهش تعداد محاسبات، حذف کدهای مرده، بهینه‌سازی تخصیص ثبات‌ها، بازآرایی حلقه‌ها، و افزایش locality حافظه باشد. با این حال، بهینه‌سازی ممکن است فرآیند اشکال‌زدایی را دشوارتر کند، زیرا ترتیب اجرای دستورها، ساختار کد یا موقعیت متغیرها ممکن است تغییر یابد. از این‌رو بسیاری از کامپایلرها اجازه نمی‌دهند بهینه‌سازی و اشکال‌زدایی به‌طور هم‌زمان فعال باشند. علاوه بر این، در صورتی که کد قرار است روی پردازنده‌هایی با ویژگی‌های متفاوت اجرا شود (مانند سرعت حافظه یا ساختار pipeline متفاوت)، اعمال بهینه‌سازی‌های خاص یک معماری ممکن است نادرست یا غیربهینه باشد. تکنیک‌های بهینه‌سازی ممکن است وابسته به زبان برنامه‌نویسی، وابسته به معماری مقصد یا مستقل از هر دو باشند؛ با این حال، بیشتر روش‌ها به صورت مستقل از زبان پیاده‌سازی می‌شوند و ویژگی‌های معماری مقصد را می‌توان با پارامترهایی مشخص کرد. هدف نهایی این تکنیک‌ها، تولید کدی است که حجم کمتر، سرعت اجرای بیشتر، و مصرف منابع پایین‌تری داشته باشد و در عین حال قابلیت اجرا بر روی معماری‌های متنوع را حفظ کند.بهینه‌سازی حلقه‌ها.حلقه‌ها بهترین کاندیداها برای بهینه‌سازی هستند چراکه بیشتر وقت برنامه در حلقه‌هاصرف می‌شود.تحلیل متغیرهای استقرایی: در هر حلقه اگر متغیری باشد که به صورت حاصل عملیات ریاضیبیان شده باشد، شاید بتوان آن را هربار بدون محاسبه (index variable) بر روی متغیر اندیسدوباره، با یک تغییر کوچک بروز کرد. این کار همچنین می‌تواند متغیر اندیس را به سمت بی‌مصرف شدن و در نهیت حذف پیش ببرد.مساوی داشته باشند و با داده (iteration) تلفیق حلقه‌ها: اگر دو حلقه مجاور تعداد تکرارهای یکدیگر کار نکنند، می‌توان آن‌ها را تلفیق کرده و به یک حلقه واحد تبدیل کرد تا بدینگونهجلوگیری شود. (توجه داشته باشید که برای loop overhead از پرداخت هزینه اضافیبررسی تساوی تعداد تکرار دو حلقه، نیازی نداریم تعداد دقیق تکرار را بدانیم). هر چندگاهی عکس این عمل نیز (تبدیل یک حلقه به چند حلقه جدا از هم) می‌تواند باعث بهینهشدن کد شود که این مورد بیشتر در مورد پردازنده‌های چند هسته‌ای بکار می‌روداست while بهتر از حلقه‌های do/while واژگون کردن حلقه: اغلب استفاده از حلقه‌هایچرا که چک کردن شرط حلقه در آخر حلقه باعث استفاده کمتر از پرش می‌شود. (به یادرا می‌گیرند. pipelining داشته باشید در اغلب پردازنده‌ها دستورها پرش جلوی استفاده ازجابجایی کد مستقل از حلقه: اگر مقدار یک عبارتی که باید محاسبه شود در تمام تکرارهایحلقه ثابت باشد، با جابجا کردن کد مربوط به محاسبه حاصل عبارت به بیرون حلقه میتوانیم سرعت برنامه را افزایش دهیم.بازکردن حلقه: اگر تعداد تکرار یک حلقه در زمان کامپایل مشخص باشد، می‌توان کد بدنهحلقه را به آن تعداد کپی کرد تا از چک کردن شرط حلقه و همچنین دستورها پرش جلوگیریمی‌شود و ممکن cache شود. هرچند این کار باعث افزایش طول کد و استفاده کمتر ازاست حتی برنامه را کندتر هم بکند. در واقع این تکنیک چیزی نیست که بتوان همیشه رویآن حساب کرد.اگر در داخل بدنه یک حلقه، انشعابی داشته باشیم که شرط آن: Loop unswitchingمستقل از حلقه است، می‌توانیم آن را به بیرون حلقه آورده و حلقه را در هر یک از شاخههای انشعاب کپی کنیم. این کار، اگر چه باعث افزایش حجم کد می‌شود، اما به پردازندهدرون حلقه‌ها را دارند کمک می‌کند (parallelization)های جدید که قابلیت هم‌زمان سازیکد را سریعتر اجرا کنند.اگر دستورها درون یک حلقه مجبور باشند پشت سر هم اجرا شوند (نتوان آنها: Pipeliningکرد) ولی اجرای آن‌ها در تکرارهای مختلف حلقه مستقل از هم باشد، می‌توان pipeline راpipeline کد چند بار اجرای آن (برای تکرارهای بعد) را در بدنه حلقه گذاشت تا با این کار بهکردن سخت‌افزاری کمک کنیم.بهینه‌سازی‌های مبتنی بر جریان داده.حذف زیرعبارت مشترک: اگر یک عبارت ریاضی چند بار در کد تکرار شده باشد می‌توانحاصل آن را فقط یکبار حساب کرده، در یک ثبات گذاشت و از آن استفده کرد.محاسبه مقادیر ثابت: می‌توان حاصل عبارتهایی که مقدار آن‌ها در زمان کامپایل مشخصاست را در زمان کامپایل محاسبه و جایگذاری کرد تا نیازی به محاسبه آن‌ها در زمان اجرانباشد.اگر دو متغیر به یک مکان از حافظه اشاره کنند،: (alias analysis) تحلیل نام‌های استعاریمی‌توان گفت دومی یک نام استعاری برای اولی است. اگر بدانیم یک متغیر هیچ ناماستعاری دیگری ندارد می‌توانیم برخی بهینه‌سازیهایی را انجام دهیم مثل اگر مقدار آن درزمان کامپایل مشخص بود، مقدار آن را جایگزین نام آن کنیم. در زبانهای برنامه‌نویسی دارایتقریباً هیچ بهینه‌سازی در این زمینه نمی‌توأم انجام داد چرا که هر (C اشاره گر (مثل زباناشاره‌گری ممکن است به یک متغیر دلخواه اشاره کند و یک نام استعاری برای آن باشد.بهینه‌سازی‌های مربوط به تولید کد.تخصیص مناسب ثباتها: برای تخصیص ثباتها به متغیرها معمول از رنگ‌آمیزی گراف تداخلاستفاده می‌شود. (تعداد رنگ‌ها به تعداد ثباتها). اگر نتوان گراف را با تعداد رنگ ذکر شده رنگکرد، یک یا چند متغیر باید در حافظه ذخیره شوند که به منظور بهینه‌سازی، این متغیرها بایدمتغیرهای کم استفده تر باشند.بسیاری از عملیاتها را می CISC انتخاب صحیح دستورالعملها: در پردازنده‌های با معماریتوان به چند طریق کد کرد. این که برای هر کاری از چه سلسله دستورالعملهایی استفادهشود در دست کامپایلر است تا با انتخاب صحیح دستورالعملهل، کد را بهینه کند.برای سرعت بخشیدن به pipelining ترتیب دهی بهنه دستورها: بسیاری از پردازنده‌ها ازبرنامه استفاده می‌کنند. اما گاهی اوقات ترتیب قرار گرفتن دستورالعمل‌ها در حافظه مانع ازمی‌شود. به عنوان مثال اگر یک دستور برای اجرا شدن به حاصل دستور قبل از pipeliningخود نیاز داشته باشد، می‌گوییم دستور دوم به اولی وابسته است و باعث جلوگیری ازمی‌شود. کامپایلرها برای جابجایی ترتیب دستورها بصورتی‌که معنی کد عوض pipeliningرا هم بتوان استفاده کرد از گراف وابستگی استفاده می‌کنند که pipelining نشود و حداکثراست. هر ترتیب توپولوژیکال رئوس گراف، معرف یک ترتیب (DAG) یک گراف جهتدار بی دورصحیح برای اجرای دستورها است.عبارت است از محاسبه دوباره مقدار متغیرها به جای بارگذاری آن‌ها از: Rematerializationحافظه، هنگامیکه محاسبه آن‌ها زمان کمتری می‌گیرد. اگرچه این عمل ممکن است در نگاهاول بی‌معنی به نظر برسد، ولی با توجه پیشرفت سریعتر سرعت پردازنده‌ها نسبت بهدسترسی به حافظه، این‌گونه بهینه‌سازی‌ها به مرور اهمیت بیشتری پیدا کرده‌اند.دیگر بهینه‌سازی‌ها.اگر فراخوانی بازگشتی یک تابع در انتهای آن صورت: (Tail Recursion) حذف بازگشتی از تهگرفته باشد، می‌توان آن به یک تابع غیربازگشتی تبدیل کرد تا ار هزینه فراخوانی چندبارهتابع جلوگیری شود.خذف بررسی کرانه‌ها: برای زبان‌های برنامه‌نویسی که قبل از دسترسی به یک آرایه ازطریق اندیس، مقدار اندیس را بررسی می‌کنند که در محدوده آرایه باشد، حذف اینبررسی در مواقعی که مقدار اندیس در زمان کامپایل معلوم است می‌تواند کمک بزرگی بهسرعت اجرای برنامه بکند.حذف کد مرده: کامپایلر می‌تواند با تشخیص اینکه حاصل یک قطعه کد در هیچ جای دیگریاستفاده نشده‌است، آن را حذف کند. این عملیات باید یکی از آخرین عملیاتهای بهینه‌سازی باشد چرا که بسیاری از بهینه‌سازی‌های دیگر حجم کد مرده را بیشتر می‌کنند.کردن توابع: این کار می‌تواند از هزینه فراخوانی توابع جلوگیری کند و بخصوص در Inlineکاربرد دارد. هر چند، به علت (functional) بهینه‌سازی زبان‌های شئ گرا و زبان‌های روال گراها که مقدار حافظه محدودی دارند با embedded system افزایش زیاد حجم کد، باید در موردهوشیاری بکار گرفته شودمنابع.https://www.ibm.com/docs/en/aix/7.1?topic=tuning-compiler-optimization-techniqueshttps://en.wikipedia.org/wiki/Optimizing_compilerhttps://developer.arm.com/documentation/100748/0613/writing-optimized-code/optimizing-for-code-size-or-performancehttps://www.geeksforgeeks.org/code-optimization-in-compiler-design/https://www.sciencedirect.com/topics/computer-science/compiler-optimizationhttps://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.htmlدانشنامه آزاد ویکی‌پدیای انگلیسی