در هم کردن ثابت و ترویج ثابت از بهینهسازیهای مرتبط به هم کامپایلر هستند که توسط بسیاری از کامپایلرهای مدرن مورد استفاده قرار میگیرند.[۱] یک شکل پیشرفته از ترویج ثابت که با عنوان ترویج ثابت شرطی پراکنده شناخته میشود، میتواند با دقت بیشتری ثابتها را رواج دهد و همزمان کد مرده را حذف کند.
در هم کردن ثابت فرایند تشخیص و ارزیابی عبارات ثابت در زمان کامپایل، به جای محاسبه آنها در زمان اجرا است. ترمها در عبارات ثابت معمولاً مقادیر ساده هستند، مانند عدد صحیح 2
، اما ممکن است متغیرهایی باشند که مقادیر آنها در زمان کامپایل شناخته شده است. جمله زیر را در نظر بگیرید:
i = 320 * 200 * 32;
در واقع بیشتر کامپایلرها دو دستورالعمل ضرب و یک ذخیرهسازی برای این عبارت ایجاد نمیکنند. در عوض، آنها ساختارهایی مانند اینها را شناسایی کرده و مقادیر محاسبه شده را در زمان کامپایل جایگزین میکنند (در این مثال ۲٬۰۴۸٬۰۰۰).
در هم کردن ثابت میتواند از شناسههای حسابی استفاده کند. اگر x
عدد باشد، مقدار 0 * x
صفر است حتی اگر کامپایلر مقدار x
را نداند (توجه داشته باشید که این مورد برای ممیز شناورهای IEEE معتبر نیست زیرا x
میتواند بینهایت باشد یا عدد نباشد. با این حال، برخی از زبانها که از کارایی مانند GLSL پشتیبانی میکنند، این امر را برای ثابتها اجازه میدهند که بعضی مواقع میتواند باعث بروز اشکالاتی شود).
در هم کردن ثابت ممکن است برای مثالهای غیر عددی نیز استفاده شود. برای به هم چسباندن رشتهها و رشتههای ثابت نیز میتوان از درهم کردن ثابت استفاده کرد. کدهایی مانند "abc" + "def"
ممکن است با "abcdef"
جایگزین شود.
در پیادهسازی یک کامپایلر میانی باید دقت شود که رفتار عملیات حسابی بر روی معماری میزبان مطابق با معماری هدف باشد، زیرا در غیر این صورت فعال کردن قابلیت در هم کردن ثابت باعث تغییر رفتار برنامه میشود. این مورد در خصوص عملیات ممیز شناور از اهمیت ویژه ای برخوردار است، که پیادهسازی دقیق آن ممکن است بسیار متفاوت باشد.
ترویج ثابت فرایند جایگزینی مقادیر ثابتهای شناخته شده در عبارات در زمان کامپایل است. چنین ثابتهایی شامل مواردی است که در بالا تعریف شده است، به علاوهٔ توابع ذاتی که به مقادیر ثابت اعمال میشوند. شبه کد زیر را در نظر بگیرید:
int x = 14;
int y = 7 - x / 2;
return y * (28 / x + 2);
با ترویج x به دست میآید:
int x = 14;
int y = 7 - 14 / 2;
return y * (28 / 14 + 2);
با ادامهٔ ترویج کد زیر به دست میآید (که احتمالاً با حذف کد مرده x و y بهینه میشود)
int x = 14;
int y = 0;
return 0;
ترویج ثابت در کامپایلرها با استفاده از نتایج آنالیز رسیدن به تعریف پیادهسازی شده است. اگر همه رسیدن به تعریفهای متغیر، یک تخصیصی باشد که یک ثابت را به متغیر اختصاص میدهد، در آن صورت متغیر یک مقدار ثابت دارد و میتواند با آن ثابت جایگزین شود.
ترویج ثابت همچنین میتواند باعث شود که شاخههای مشروط به یک یا چند جمله بدون شرط ساده شوند، وقتی عبارت شرطی میتواند در زمان کامپایل به صحیح یا غلط ارزیابی شود تا تنها نتیجه ممکن را تعیین کند.
در هم کردن و ترویج ثابت معمولاً برای رسیدن به تعداد زیادی سادهسازی و کاهش، با استفاده پشت سر هم آنها بهطور حلقه ای تا زمانی که تغییر جدیدی رخ ندهد، به کار میروند. این شبه کد را در نظر بگیرید:
int a = 30;
int b = 9 - (a / 5);
int c;
c = b * 4;
if (c > 10) {
c = c - 10;
}
return c * (60 / a);
با یک بار استفاده از ترویج ثابت و به دنبال آن در هم کردن ثابت، به دست میآید:
int a = 30;
int b = 3;
int c;
c = b * 4;
if (c > 10) {
c = c - 10;
}
return c * 2;
تکرار هر دو مرحله برای دو بار، نتیجه میدهد:
int a = 30;
int b = 3;
int c;
c = 12;
if (true) {
c = 2;
}
return c * 2;
همانطور که a
و b
به ثابتها ساده شدهاند و مقادیر آنها در هر جا که استفاده شدهاند جایگزین شده، کامپایلر اکنون حذف کد مرده را برای دور ریختن آنها اعمال میکند و کد را کاهش میدهد:
int c;
c = 12;
if (true) {
c = 2;
}
return c * 2;
در کد بالا، به جای true
بسته به چارچوب کامپایلر، میتوانست ۱ یا هر ساختار بولین دیگری باشد. با ترویج ثابت سنتی، ما فقط به همین مقدار بهینهسازی خواهیم رسید. این نمیتواند ساختار برنامه را تغییر دهد. یک بهینهسازی مشابه دیگر وجود دارد، به نام ترویج ثابت شرطی پراکنده، که شاخه مناسب را بر اساس if-condition
انتخاب میکند.[۲] کامپایلر اکنون میتواند تشخیص دهد که دستور if
همیشه مقدار صحیح ارزیابی میشود ، c
میتواند از بین ببرد و کد را حتی بیشتر کاهش دهد:
return 4;
اگر این شبه کد بدنه یک تابع را تشکیل دهد، کامپایلر میتواند از دانشی که در مورد خروجی آن (یعنی عدد صحیح ثابت 4
) دارد، برای از بین بردن صدا زدنهای غیر ضروری تابع استفاده کند و در نتیجه باعت کارایی بیشتر شود.