عنوان:

‫مهاجرت از Mono به CoreCLR در دات‌نت 11


نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۶/۱۷ ۰۸:۴۰
آدرس: www.dntips.ir
چکیده: در .NET 11، تحولی بنیادین در معماری اجرایی چارچوب .NET MAUI رقم خورده است: موتور اجرایی CoreCLR برای سیستم‌عامل‌های Android، iOS، tvOS و Mac Catalyst جایگزین موتور تاریخی Mono شد. اگرچه مایکروسافت این ارتقا را در لایه کد منبع، سازگار (Source-compatible) معرفی کرده است، تغییر زمان اجرای زیرین (Runtime) بر چرخه‌های مدیریت حافظه (Garbage Collection)، بهینه‌سازی‌های کامپایل درجا (Tiered JIT / Dynamic PGO)، ابزارهای عیب‌یابی (Diagnostics) و حجم نهایی خروجی‌های اجرایی اثرگذار است. این مقاله با رویکردی تحلیلی به بررسی تفاوت‌های ساختاری دو محیط زمان اجرا پرداخته، الگوهای واقعی سنجش کارایی روی دستگاه‌های همراه را تحلیل می‌کند و یک راهنمای کاربردی برای ارزیابی و مهاجرت ایمن اپلیکیشن‌ها به .NET 11 ارائه می‌دهد.

۱. مقدمه
توسعه برنامه‌های تلفن همراه در اکوسیستم دات‌نت از زمان Xamarin و ادوار ابتدایی .NET MAUI بر بستر موتور اجرای Mono پی‌ریزی شده بود. مونو به دلیل سازگاری بالا با محیط‌های ناهمگن و سخت‌افزارهای کم‌مصرف توانست پایه مستحکمی بسازد، اما تفاوت زیرساختی آن با CoreCLR (موتور مورد استفاده در ASP.NET Core، دسکتاپ و سرورها) شکاف‌هایی در یکپارچگی ابزارهای پروفایلینگ، سیستم GC و رفتارهای بهینه‌سازی در سطح کد میانی (IL) ایجاد می‌کرد.
با شروع انتشار پیش‌نمایش‌های .NET 11 و حذف قطعی سوئیچ انتخاب رانتایم Mono، تمام بار پردازش در تارگت‌های همراه به موتور پیش‌فرض دات‌نت، یعنی CoreCLR، منتقل شده است. بنابراین ارتقا به .NET 11 تنها به‌روزرسانی کتابخانه‌های پلتفرم نیست؛ بلکه جابه‌جایی کامل شالوده اجرایی اپلیکیشن است. این تغییر پرسش‌هایی اساسی را برای تیم‌های مهندسی به همراه دارد:
  • آیا CoreCLR روی پلتفرم‌های همراه ذاتاً سریع‌تر از Mono عمل می‌کند؟
  • تفاوت‌های بنیادین در رفتار Garbage Collector و کامپایل JIT چه آثاری بر نرخ فریم و زمان بارگذاری (Startup) دارند؟
  • چه ریسک‌هایی در حوزه بایندینگ‌های نیتیو و سازگاری بسته‌های NuGet وجود دارد؟

۲. مقایسه معماری: CoreCLR در برابر Mono
موتور CoreCLR و استراتژی NativeAOT نباید یکی پنداشته شوند: CoreCLR کدهای IL را از طریق JIT کامپایل و مدیریت می‌کند، در حالی که NativeAOT پیش از اجرا، کلاینت را به باینری مستقل و ماشین‌محور کامپایل می‌نماید و نیازمند قواعد سخت‌گیرانه‌تری در قبال Reflection و Trimming است.

جدول زیر ساختار فنی این دو محیط را در .NET MAUI مقایسه می‌کند:

مؤلفهموتور Monoموتور CoreCLR (.NET 11)
استراتژی کامپایلوابسته به پلتفرم: ترکیب مفسر (Interpreter)، JIT و Full AOT در iOSرویکرد چندسطحی (Tiered Compilation) با سطوح Tier 0 تا Tier 1
بهینه‌سازی پویامحدود و وابسته به پروفایل‌های ایستا در زمان بیلدبهره‌گیری از Dynamic PGO متناسب با رفتار متدهای داغ در زمان اجرا
مدیریت حافظه (GC)کالکتور SGen با پیکربندی تک‌منظوره برای پلتفرمGenerational GC دات‌نت بهینه‌سازی‌شده برای معماری‌های مدرن Arm64
پیش‌کامپایل هنگام انتشارپایپ‌لاین Mono AOT با ابعاد باینری نسبتاً متراکمپشتیبانی از ReadyToRun (R2R) جهت کاهش هزینه JIT در بارگذاری
اکوسیستم عیب‌یابیابزارهای سفارشی مونو و پایش غیریکپارچه لاگ‌هاپشتیبانی از CLI استاندارد شامل dotnet-trace و dotnet-counters
۳. بررسی عمیق تفاوت‌های زیرساختی

راهبرد کامپایل درجا (JIT) و اثر Tiered Compilation
در مدل چندسطحی CoreCLR:
  • متدها در ابتدای اجرا به‌صورت سریع و بدون بهینه‌سازی سنگین به زبان ماشین تبدیل می‌شوند (Tier 0)، تا زمان فراخوانی کدهای آغازین و راه‌اندازی اپ کاهش یابد.
  • پس از رسیدن تعداد دفعات اجرای متد به حد نصاب (Hot Paths)، سیستم کامپایل درجا آن متد را با بهینه‌سازی‌های عمیق بازکامپایل می‌کند (Tier 1).

در مقابل، رویکرد پیشین Mono بر پایه مدل‌های ایستا و رفتارهای پلتفرمی بود (مثلاً استفاده اجباری از AOT بر بستر iOS به دلیل قوانین App Store مبنی بر ممنوعیت کامپایل داینامیک در حافظه). در CoreCLR برای پلتفرم‌های همراه، تکنیک ReadyToRun کدهای ماشین پیش‌تولیدشده را تزریق می‌کند تا بار تبدیل IL در ثانیه‌های نخست را به حداقل برساند.

مدیریت حافظه و توقف‌های رندر (UI Glitches)
کالکتور SGen در Mono برای سخت‌افزارهای با رم پایین طراحی شده بود، اما در مدیریت سناریوهای تخصیص مکرر اشیاء موقت در چرخه‌های رندر XAML ممکن بود زمان توقف (Pause Time) غیرقابل‌پیش‌بینی ثبت کند.
موتور CoreCLR با سیستم تخصیص شی‌ء نسلی (Generations 0, 1, 2) و بهینه‌سازی‌های اختصاصی پردازنده‌های Arm64، توان عملیاتی بالاتری دارد. البته این رفتار در برخی سناریوهای اندروید ممکن است به دلیل استراتژی‌های حفظ حافظه مقیم (Resident Memory)، مصرف اولیه حافظه بالاتری نسبت به SGen نشان دهد.

یکپارچگی ابزارهای عیب‌یابی (Diagnostics)
بزرگ‌ترین مزیت فنی CoreCLR برای تیم‌های مهندسی، استانداردسازی ابزارهای لاگینگ و پایش است. توسعه‌دهنده با دستورات یکپارچه می‌تواند عملکرد تردها و مصرف حافظه را در سناریوهای تبلت و موبایل دنبال کند:
# پایش بلادرنگ نرخ تخصیص حافظه و تردها در اپلیکیشن در حال اجرا
dotnet-counters monitor -p <Process-ID> --counters System.Runtime

# ثبت گزارش پایش عملکرد جهت تحلیل گلوگاه‌های پردازشی در PerfView یا Speedscope
dotnet-trace collect -p <Process-ID> --providers Microsoft-Windows-DotNETRuntime

۴. نتایج تجربی کارایی روی سخت‌افزارهای واقعی
نتایج بنچ‌مارک‌های تیم مهندسی دات‌نت و بررسی‌های مستقل جامعه توسعه‌دهندگان الگوهای متفاوتی را در پلتفرم‌های مقصد نشان می‌دهد:

پلتفرم‌های مبتنی بر اپل (iOS / Mac Catalyst)
در پلتفرم‌های شرکت اپل، جایگزینی مونو با CoreCLR و بهره‌گیری از مدل‌های مدرن کدزنی اثرات مثبت قطعی ثبت کرده است:
  • بهبود محسوس در زمان راه‌اندازی اولیه بدون داده‌های کَش (Cold Startup).
  • سرعت اجرای بالاتر در راه‌اندازی مجدد با داده‌های کش‌شده (Warm Startup).
  • توان پردازشی ارتقایافته در بارهای محاسباتی سنگین به دلیل عملکرد مطلوب Dynamic PGO.

پلتفرم اندروید (Android)
برخلاف iOS، داده‌های عملکردی در سیستم‌عامل اندروید تفاوت‌های قابل توجهی بسته به وسعت پروژه دارند:
  • در اپلیکیشن‌های کوچک، تفاوت زمان راه‌اندازی CoreCLR و مونو کمتر از ۱۰ درصد برآورد شده است.
  • در پروژه‌های پیچیده تجاری و انترپرایز با تعداد صفحات بالا و حجم زیادی از بسته‌های ثالث (طبق گزارش‌های ردگیری‌شده در مخزن دات‌نت از جمله Issue #10588 و Issue #10914)، برخی تیم‌ها افزایش در حجم APK/AAB و تاخیر در بارگذاری اولیه را تجربه کرده‌اند.
  • بنابراین کارایی در اندروید شدیداً وابسته به نحوه تنظیم Trimming و ساختار رندر کدهای واسط کاربری است.

۵. نقشه راه انتشار و پالایش فایل پروژه
در مسیر توسعه .NET 11، تغییرات طی گام‌های زیر تثبیت شدند:
  • .NET 10: آزمایش اختیاری CoreCLR برای اندروید در قالب یک ویژگی Experimental.
  • .NET 11 Preview 4: تبدیل CoreCLR به رانتایم پیش‌فرض برنامه‌های موبایل، همراه با امکان بازگشت موقت به Mono.
  • .NET 11 Preview 6 به بعد: حذف کامل زیرساخت مونو و تبدیل CoreCLR به تنها محیط اجرایی پلتفرم‌های هدف.

برای جلوگیری از هشدارهای کامپایلر یا رفتارهای ناخواسته در فایل csproj.، تنظیمات منسوخ باید حذف شده و چارچوب‌های هدف به‌روزرسانی شوند:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <!-- ارتقای تارگت‌ها به نسخه ۱۱ -->
    <TargetFrameworks>net11.0-android;net11.0-ios;net11.0-maccatalyst</TargetFrameworks>
    <OutputType>Exe</OutputType>
    <UseMaui>true</UseMaui>

    <!-- هشدار: تگ‌های زیر منسوخ شده‌اند و باید از فایل پروژه پاک شوند -->
    <!-- <UseMonoRuntime>true</UseMonoRuntime> -->
    
    <!-- فعال‌سازی بهینه‌سازی باینری هنگام انتشار نهایی -->
    <PublishTrimmed Condition="'$(Configuration)' == 'Release'">true</PublishTrimmed>
  </PropertyGroup>
</Project>

۶. راهنمای گام‌به‌گام ارزیابی، اعتبارسنجی و مهاجرت
مهاجرت بدون ریسک به .NET 11 نیازمند اجرای تست‌های کنترل‌شده است:
[۱. ثبت معیار پایه (.NET 10)] ──> [۲. اعمال پیکربندی .NET 11] ──> [۳. اعتبارسنجی وابستگی‌ها] ──> [۴. پروفایلینگ و سنجش نهایی]

گام ۱: ثبت داده‌های پایه (Baseline)
پیش از تغییر نسخه، عملکرد برنامه روی .NET 10 را از نظر مصرف رم، زمان راه‌اندازی سرد و گرم و حجم فایل باینری روی دستگاه فیزیکی ذخیره کنید.

گام ۲: کامپایل یکسان در محیط انتشار (Release Build)
مقایسه‌ها باید بدون دخالت فرآیندهای Debugger و روی خروجی‌های Release با شرایط Trimming کاملاً یکسان صورت گیرند:

dotnet publish -c Release -f net10.0-android
dotnet publish -c Release -f net11.0-android

گام ۳: راستی‌آزمایی بسته‌های ثالث و بایندینگ‌های محلی
کتابخانه‌هایی که از قابلیت‌های زیر استفاده می‌کنند نیازمند بازبینی دقیق هستند:
  • کدهای وابسته به Reflection سنگین و تولید پویای کد در زمان اجرا (Dynamic Code Generation).
  • بایندینگ‌های پلتفرم نیتیو (JNI در اندروید و P/Invoke در iOS). تفاوت‌های جزئی در رفتار مدیریت رشته‌ها (String Marshalling) میان Mono و CoreCLR می‌تواند خطاهای زمان اجرا ایجاد کند.

گام ۴: اعتبارسنجی چرخه عمر توسعه (Developer Experience)
پایداری قابلیت‌های XAML Hot Reload، ابزار dotnet watch و عملکرد متصل‌شدن دیباگر به شبیه‌سازها و دستگاه‌های فیزیکی را در محیط توسعه تیم بسنجید.

۷. پرسش‌های متداول (FAQ)

آیا CoreCLR همواره در برنامه‌های .NET MAUI نسبت به Mono سریع‌تر است؟
خیر. در حالی که در iOS و Mac Catalyst بهبودهای مشهود در نرخ فریم و استارتاپ دیده می‌شود، در اندروید عملکرد به معماری نرم‌افزار، حجم کتابخانه‌های واسط و الگوهای تخصیص آبجکت بستگی دارد. در برخی پروژه‌ها بدون اعمال بهینه‌سازی‌های Trimming و R2R ممکن است افزایش جزئی زمان استارتاپ رخ دهد.

آیا مهاجرت به CoreCLR نیازمند بازنویسی کدهای #C است؟
خیر. سطح BCL و APIهای استاندارد (مانند الگوهای همزمانی async/await و مکانیزم‌های Task) تغییری نکرده‌اند. بازنویسی تنها زمانی مورد نیاز است که کلاینت شما از رفتارهای اختصاصی و تست‌نشده مونو در مدیریت حافظه، رفلکشن‌های غیرایمن، یا بایندینگ‌های نیتیو غیراستاندارد استفاده کرده باشد.

۸. نتیجه‌گیری
مهاجرت از Mono به CoreCLR یک پیشرفت معماری برای .NET MAUI است که ثبات اکوسیستم دات‌نت را در تمامی ابزارهای سرور، کلاینت و موبایل یکپارچه می‌کند. با وجود مزایای انکارناپذیر در بخش ابزارهای عیب‌یابی و عملکرد پردازشی کدهای داغ، مهندسان نباید بر پایه فرضیات اقدام به عرضه نهایی کنند. ایجاد خط مبنا روی .NET 10، ارزیابی رفتار بسته‌های شخص ثالث و سنجش دقیق متریک‌های رم و سرعت اجرا بر روی دیوایس‌های واقعی، متضمن انتقال بدون ریسک و پایدار به .NET 11 خواهد بود.