ConstantExpectedAttribute: گشودن قفل بهینگی با آگاهسازی کامپایلر
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۸/۲۷ ۱۱:۰۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
ConstantExpectedAttribute را ارائه داد. این ابزار، پلی است میان "قصد" (Intent) ما به عنوان توسعهدهنده و "هوش" کامپایلر، که منجر به بهینهسازیهای قابل توجه در سطح کامپایلر و بهبود تجربهی توسعه (DX) میشود.LogLevel) و یک پیام دریافت میکند.public class Logger
{
public void Log(LogLevel level, string message)
{
if (level == LogLevel.Debug)
{
WriteDebug(message);
}
else if (level == LogLevel.Info)
{
WriteInfo(message);
}
else if (level == LogLevel.Error)
{
WriteError(message);
}
// و غیره...
}
// ... متدهای کمکی
}level از کجا آمده است. آیا یک مقدار ثابت مانند LogLevel.Info است که مستقیماً در کد نوشته شده؟ یا نتیجهی فراخوانی یک متد دیگر است؟ یا شاید از یک فایل کانفیگ یا ورودی کاربر در زمان اجرا (Runtime) خوانده شده است؟if-else را در زمان اجرا بررسی کند. این یعنی:if و else باید به زبان ماشین ترجمه و در فایل اجرایی گنجانده شوند.Log، پردازنده (CPU) باید این مقایسهها را انجام دهد.LogLevel.Info) فراخوانی میکنیم و خودش دست به بهینهسازی بزند. گاهی اوقات، با کمک تکنیکهایی مثل (PGO - Profile-Guided Optimization)، این اتفاق میافتاد.[ConstantExpected]و همکاری با کامپایلرConstantExpectedAttribute وارد میدان میشود. این صفت که در فضای نام System.Diagnostics.CodeAnalysis قرار دارد، دقیقاً همان حلقهی گمشدهی ارتباطی بین ما و کامپایلر است.public class Logger
{
public void Log(
[ConstantExpected] LogLevel level, // <-- جادو اینجاست
string message)
{
if (level == LogLevel.Debug)
{
WriteDebug(message);
}
else if (level == LogLevel.Info)
{
WriteInfo(message);
}
else if (level == LogLevel.Error)
{
WriteError(message);
}
}
}
// ... و استفاده از آن
logger.Log(LogLevel.Info, "عملیات شروع شد");[ConstantExpected]، ما دو کار بسیار مهم انجام میدهیم:logger.Log(LogLevel.Info, ...) میرسد، به لطف صفت ما، با اطمینان میداند که levelهمیشهLogLevel.Info است. این آگاهی، به او اجازهی بهینهسازیهای تهاجمی زیر را میدهد:level == LogLevel.Debug و level == LogLevel.Errorهمیشه غلط (false) هستند. در نتیجه، کل آن دو شاخه if را به سادگی از کد نهایی حذف میکند.level == LogLevel.Info را (که همیشه true است) نیز حذف کرده و مستقیماً متد WriteInfo(message) را جایگزین کل بلوک if میکند.WriteInfo خواهد بود. این یعنی کد کمتر، سریعتر و بهینهتر برای کش CPU.// هشدار IDE: این پارامتر انتظار یک مقدار ثابت را دارد var dynamicLevel = GetLogLevelFromConfig(); logger.Log(dynamicLevel, "این کد هشدار دریافت میکند");
MinوMaxpublic void SetThreadPoolSize(
[ConstantExpected(Min = 1, Max = 64)] int threadCount)
{
// کامپایلر حالا میداند که threadCount بین 1 تا 64 است
// و میتواند بهینهسازیهایی مثل "حذف بررسی کرانها" (Bounds Check Elimination)
// را روی آرایهها انجام دهد.
}LogLevel)، و برای فلگهای فعالسازی فیچرها.ConstantExpectedAttribute این است که با هر نسخه جدید NET.، قویتر میشود. این صفت در NET 7. معرفی شد. در NET 8. و NET 9.، زیرساختهای کامپایلر JIT و PGO به طور مداوم بهبود یافتند، به این معنی که توانایی آنها در استفاده از این "اشارهها" (Hints) بیشتر شد.ConstantExpected مینویسیم، فردا به طور خودکار سریعتر اجرا خواهد شد، بدون اینکه نیازی به تغییر در سورس کد ما باشد.ConstantExpectedAttribute یک نمونهی درخشان از فلسفهی مدرن NET. است: ایجاد ابزارهایی برای "همکاری" فعال بین توسعهدهنده و کامپایلر. این صفت یک تغییر غیرمخرب (Non-Breaking) و بدون هزینه در زمان اجرا (Zero-Cost) است. این به ما اجازه میدهد تا "قصد" خود را به شکلی واضح به کامپایلر اعلام کنیم و به او اجازه دهیم کارهایی را انجام دهد که قبلاً فقط آرزویش را داشتیم. در نهایت، بهترین بهینهسازیها آنهایی هستند که با الگوهای طبیعی کدنویسی همسو باشند. ConstantExpectedAttribute دقیقاً همین کار را میکند؛ بهینهسازی عملکرد را از یک کار پیچیده و تخصصی، به یک ابزار دمدستی و شفاف برای همهی توسعهدهندگان NET. تبدیل میکند.