چگونه زمان کامپایل کدهای راست (Rust) را نصف کنیم؟ ۳ ترفند کاربردی برای Cargo

کاهش چشمگیر زمان کامپایل در زبان راست. راهنمای کاربردی استفاده از Line Tables، ترد‌های موازی و موتور Cranelift برای افزایش سرعت توسعه.

چگونه زمان کامپایل کدهای راست (Rust) را نصف کنیم؟ ۳ ترفند کاربردی برای Cargo

چکیده نکات کلیدی (Quick Summary)

  • سبک‌سازی اطلاعات دیباگ: با تنظیم debug = "line-tables-only"، زمان بازکامپایل افزایشی حدود ۲۱٪ کاهش می‌یابد در حالی که شماره خطاهای پنیک حفظ می‌شود.
  • پردازش چندتردی فرانت‌اند: فلگ نایتلی -Zthreads=8 بررسی نوع و سیستم قرض‌گیری را به‌صورت موازی روی هسته‌های پردازنده توزیع می‌کند.
  • شتاب‌دهی با Cranelift: جایگزینی موتور LLVM با Cranelift در محیط توسعه، زمان تولید کدهای محلی را به‌شدت کوتاه می‌کند.
  • بدون افت کارایی در محیط پروداکشن: تمام بهینه‌سازی‌ها صرفاً پروفایل توسعه (dev) را هدف قرار می‌دهند و بیلد نهایی (release) همچنان از نهایت قدرت LLVM استفاده می‌کند.
  • سرعت تا ۴۷٪ بالاتر: تلفیق این سه راهکار باعث می‌شود زمان کامپایل کامل ۴۶.۹٪ و زمان بازکامپایل ۴۶٪ سریع‌تر شود.

احتمالاً این سناریو برای شما هم آشناست: غرق در کدنویسی هستید، تمرکزتان در اوج قرار دارد و با اشتیاق روی قابلیت جدید پروژه‌تان در راست (Rust) کار می‌کنید. کد را ذخیره می‌کنید و با خیالی آسوده دستور cargo build را می‌زنید.

اما یک دقیقه و نیم طول می‌کشد تا کامپایلر پاسخ دهد!

با خودتان می‌گویید: «اشکالی ندارد، بیلد اول بود و دفعه بعد سریع‌تر می‌شود». ولی وقتی فقط یک خط ساده را تغییر می‌دهید، باز هم ۳۰ ثانیه معطل می‌مانید. تمرکز ذهنی شما کاملاً از بین می‌رود و چرخه آزمون‌وخطا خسته‌کننده می‌شود.

آیا راهی برای نجات از این زمان‌های انتظار طولانی وجود دارد؟ بله! با درک مراحل داخلی کامپایلر و اعمال ۳ بهینه‌سازی کلیدی در تنظیمات کارگو (Cargo)، می‌توانید زمان کامپایل کدهای راست را تا مرز ۵۰ درصد کاهش دهید.

Featured Snippet Bait: برای کاهش زمان کامپایل در زبان راست، بهینه‌سازی سه مرحله اجرای cargo build ضروری است: اول سبک‌سازی اطلاعات خطایابی با debug = "line-tables-only"، دوم فعال‌سازی پردازش موازی نوع‌ها با -Zthreads=8 و سوم استفاده از موتور کدساز Cranelift در پروفایل توسعه. ترکیب این روش‌ها سرعت کامپایل پروژه‌ها را تا ۴۷٪ افزایش می‌دهد.


۱. در پشت‌صحنه cargo build چه می‌گذرد؟

قبل از دستکاری تنظیمات، بیایید ببینیم چرا کامپایلر راست (rustc) به زمان زیادی نیاز دارد. هر بار که دستور cargo build را اجرا می‌کنید، کد شما از ۳ مرحله پردازشی سنگین عبور می‌کند:

flowchart LR
    subgraph S1 ["مرحله اول: فرانت‌اند"]
        direction TB
        A["کد منبع"] --> B["تبدیل به HIR/MIR"]
        B --> C["بررسی نوع و قرض‌گیری"]
    end

    subgraph S2 ["مرحله دوم: تولید کد"]
        direction TB
        D["نمایش میانی MIR"] --> E["موتور LLVM یا Cranelift"]
        E --> F["فایل‌های شیء (.o / .rlib)"]
    end

    subgraph S3 ["مرحله سوم: پیوند"]
        direction TB
        G["فایل‌های شیء و کتابخانه‌ها"] --> H["لینکر سیستم"]
        H --> I["فایل اجرایی نهایی"]
    end

    S1 --> S2 --> S3

۱. مرحله اول؛ تحلیل اولیه و بررسی نوع‌ها (Frontend): در این بخش، کامپایلر کدهای منبع را می‌خواند، ماکروها را بسط می‌دهد و سیستم بررسی نوع و سیستم بررسی قرض‌گیری (Borrow Checker) را اجرا می‌کند. در این مرحله هنوز هیچ کد ماشینی تولید نشده است. ۲. مرحله دوم؛ تولید کد ماشین (Code Generation): در این گام، نمایش میانی کد (MIR) به دستورات واقعی پردازنده ترجمه شده و درون فایل‌های شیء (.o و .rlib) ذخیره می‌شود. به‌طور پیش‌فرض، وظیفه این کار بر عهده LLVM است. ۳. مرحله سوم؛ پیونددهی (Linking): لینکر (Linker) تمام فایل‌های شیء، کتابخانه‌های وابسته و کدهای ران‌تایم را به یکدیگر متصل کرده و فایل اجرایی نهایی را می‌سازد.

هر کدام از این مراحل گلوگاه‌های خاص خود را دارند. بیایید گام‌به‌گام آن‌ها را برطرف کنیم.


۲. بهینه‌سازی اول: سبک‌سازی اطلاعات دیباگ با line-tables-only

اولین ترفند بسیار ساده است و تنها با افزودن یک خط در فایل Cargo.toml پیاده‌سازی می‌شود.

به‌صورت پیش‌فرض در پروفایل توسعه (cargo build)، مقدار debug = true فعال است. این تنظیمات جدول‌های نمادین سنگینی مانند DWARF یا PDB تولید می‌کنند تا ابزارهای خطایاب (مانند GDB یا LLDB) بتوانند تمام مقادیر متغیرها را خط‌به‌خط نمایش دهند. اما این اطلاعات حجیم، دیسک و لینکر را در تغییرات سریع روزمره به‌شدت کند می‌کنند.

در جریان توسعه روزمره، اغلب اوقات ما به دیباگر پیشرفته نیازی نداریم؛ بلکه فقط می‌خواهیم بدانیم اگر برنامه با خطای «پنیک» (Panic) متوقف شد، خطا در کدام فایل و چه شماره خطی رخ داده است.

کافی است تنظیمات زیر را به فایل Cargo.toml پروژه اضافه کنید:

[profile.dev]
debug = "line-tables-only" # یا مقدار عددی 1

نتیجه این تغییر چیست؟

تنظیم line-tables-only اطلاعات سنگین موقعیت متغیرها را حذف می‌کند، اما شماره خط و نام فایل مربوط به خطاهای پنیک را دست‌نخورده باقی می‌گذارد:

  • کاهش زمان بیلد کامل (Clean Build): حدود ۹٪ سریع‌تر
  • کاهش زمان بازکامپایل (Rebuild): حدود ۲۱٪ سریع‌تر

یک تغییر تک‌خطی و ساده که سرعت چرخه آزمون شما را بلافاصله بالا می‌برد!


۳. بهینه‌سازی دوم: فعال‌سازی بررسی موازی با فلگ -Zthreads

شاید تعجب کنید اگر بدانید در بخش عمده‌ای از تاریخچه زبان راست، مرحله اول کامپایل (بررسی نوع‌ها و سیستم وام‌گیری) فقط روی یک هسته پردازنده اجرا می‌شد! فرقی نمی‌کرد پردازنده سیستم شما ۴ هسته دارد یا ۴۰ هسته؛ کامپایلر فقط یک هسته را درگیر می‌کرد و بقیه هسته‌ها بیکار می‌ماندند.

قابلیت فرانت‌اند چندتردی (Multi-threaded Frontend) این محدودیت را برطرف می‌کند. با این قابلیت، بررسی‌های اولیه کدهای راست روی چندین هسته تقسیم و پردازش می‌شوند.

برای فعال‌سازی این قابلیت، فایل .cargo/config.toml را در مسیر پروژه یا ریشه سیستم باز کنید و خطوط زیر را بنویسید:

# .cargo/config.toml
[build]
rustflags = ["-Z", "threads=8"]

[!TIP] مقدار threads=8 روی اغلب سیستم‌های مدرن، تعادلی ایده‌آل میان حداکثر سرعت پردازش و مصرف بهینه حافظه رم (RAM) برقرار می‌کند.

پیش‌نیاز و میزان تاثیرگذاری

از آنجا که این قابلیت هنوز در مرحله ارزیابی است، برای استفاده از آن باید از نسخه نایتلی (Nightly) کامپایلر استفاده کنید:

cargo +nightly build

وقتی بهینه‌سازی اول (line-tables-only) را با بهینه‌سازی دوم (-Zthreads=8) ترکیب می‌کنید، ارقام شگفت‌انگیزی ثبت می‌شود:

  • کاهش زمان بیلد کامل: ۳۳٪ سریع‌تر
  • کاهش زمان بازکامپایل: ۳۳٪ سریع‌تر

نکته جالب اینجاست که هم‌تیمی‌های شما هم از این ترفند سود خواهند برد؛ زیرا فعال کردن این فلگ در پایپ‌لاین‌های CI/CD، زمان اجرای تست‌ها را به شکل محسوسی پایین می‌آورد.


۴. بهینه‌سازی سوم: تعویض موتور کدساز LLVM با Cranelift

مرحله دوم کامپایل یعنی تولید کد ماشین (Codegen)، جایی است که نمایش میانی کدهای راست به بایت‌های قابل فهم پردازنده تبدیل می‌شود. موتور پیش‌فرض راست LLVM است؛ پروژه‌ای قدرتمند که وسواس زیادی برای تولید بهینه‌ترین کد ماشین ممکن به خرج می‌دهد.

اما وسواس بالای LLVM در بهینه‌سازی کد، باعث بالا رفتن چشمگیر زمان کامپایل می‌شود. در زمان کدنویسی روزمره، شما به کدهای فوق‌سریع برای پروداکشن نیاز ندارید، بلکه فقط می‌خواهید برنامه سریع‌تر اجرا و تست شود.

اینجاست که پروژه Cranelift وارد میدان می‌شود. موتور Cranelift در اصل برای اجرای سریع کدهای وب‌اسمبلی در پروژه Wasmtime طراحی شده و اولویت اصلی آن، سرعت خیره‌کننده در تولید کد ماشین است.

flowchart TD
    subgraph DevProfile ["محیط توسعه: cargo build"]
        A1["کدهای میانی راست"] --> B1["موتور کدساز Cranelift"]
        B1 --> C1["تولید فوق‌سریع کدهای محلی"]
    end

    subgraph ReleaseProfile ["محیط پروداکشن: cargo build --release"]
        A2["کدهای میانی راست"] --> B2["موتور کدساز LLVM"]
        B2 --> C2["کد نهایی فوق‌بهینه با بالاترین کارایی"]
    end

نحوه نصب و راه‌اندازی Cranelift

۱. ابتدا کامپوننت Cranelift را از طریق rustup روی نسخه نایتلی نصب کنید:

rustup component add rustc-codegen-cranelift-preview --toolchain nightly

۲. سپس آن را در فایل .cargo/config.toml فعال کنید:

# .cargo/config.toml
[unstable]
codegen-backend = true

[profile.dev]
codegen-backend = "cranelift"

توجه داشته باشید که ما Cranelift را صرفاً برای بخش [profile.dev] تنظیم کرده‌ایم. به این ترتیب، بیلدهای اصلی شما با فلگ --release همچنان با موتور پرقدرت LLVM کامپایل خواهند شد تا بهترین کارایی ممکن در سرورها حفظ شود.

[!WARNING] از آنجا که Cranelift موتوری تازه‌نفس است، ممکن است در برخی دستورات سطح پایین پردازنده یا اسمبلی مستقیم با محدودیت مواجه شود. پیشنهاد می‌کنیم این تغییر را در یک شاخه (Branch) جداگانه از گیت امتحان کنید و در صورت بروز خطا، موقتاً به LLVM بازگردید.


۵. مقایسه جامع: ترکیب هر ۳ بهینه‌سازی و ثبت رکورد جدید

بیایید ببینیم وقتی هر سه بهینه‌سازی را هم‌زمان در یک پروژه واقعی پیاده می‌کنیم، چه نتایجی حاصل می‌شود:

| وضعیت بهینه‌سازی | کاهش زمان بیلد اول (Clean) | کاهش زمان بیلد افزایشی (Rebuild) | شرایط و الزامات | | :--- | :--- | :--- | :--- | | حالت پیش‌فرض (cargo build) | ۰٪ (مبنای سنجش) | ۰٪ (مبنای سنجش) | کندترین حالت با اطلاعات دیباگ کامل | | + بهینه‌سازی ۱: دیباگ سبک (line-tables) | ~۹٪ سریع‌تر | ~۲۱٪ سریع‌تر | بدون نیاز به نسخه نایتلی | | + بهینه‌سازی ۲: پردازش موازی فرانت‌اند (-Zthreads=8) | ~۳۳٪ سریع‌تر | ~۳۳٪ سریع‌تر | نیازمند نسخه نایتلی | | + بهینه‌سازی ۳: موتور Cranelift (codegen-backend) | ~۴۶.۹٪ سریع‌تر | ~۴۶.۰٪ سریع‌تر | مخصوص پروفایل توسعه در نایتلی |

با ترکیب دیباگ سبک، بررسی چندتردی و موتور کدساز Cranelift، زمان کامپایل کدهای شما تقریباً نصف می‌شود. حالا به‌جای معطلی ۹۰ ثانیه‌ای، بیلد اولیه در حدود ۴۷ ثانیه تمام می‌شود و تغییرات کوچک به‌جای ۳۰ ثانیه، فقط ۱۶ ثانیه زمان می‌برند. به همین سادگی، غوطه‌وری ذهنی و سرعت توسعه شما حفظ خواهد شد!


نتیجه‌گیری: بازگرداندن سرعت و لذت به برنامه‌نویسی راست

صبر کردن برای پایان کامپایل، یکی از بزرگ‌ترین موانع بهره‌وری در میان توسعه‌دهندگان راست است. با به‌کارگیری line-tables-only در نسخه پایدار و فعال‌سازی -Zthreads و Cranelift در محیط توسعه نایتلی، می‌توانید ساعت‌ها در وقت هفتگی خود صرفه‌جویی کنید.

خلاصه مراحل اجرایی: ۱. تنظیم debug = "line-tables-only" در فایل Cargo.toml. ۲. افزودن تنظیمات threads = 8 و codegen-backend = "cranelift" به .cargo/config.toml. ۳. اجرای بیلدها با cargo +nightly build در زمان توسعه روزمره.

اگر علاقه‌مندید فراتر از آموزش‌های مقدماتی گام بردارید و نرم‌افزارهایی در مقیاس صنعتی با زبان راست تولید کنید، آشنایی با ترفندهای سطح عمیق کامپایلر و معماری سیستم نقشی حیاتی در مسیر حرفه‌ای شما خواهد داشت.

بزرگ‌ترین چالش شما در مدیریت زمان کامپایل پروژه‌های راست چیست؟ تجربیات و بنچمارک‌های خود را در بخش نظرات با ما در میان بگذارید!


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

:::details آیا این بهینه‌سازی‌ها سرعت اجرای نهایی برنامه را در سرور کم می‌کنند؟ خیر. تمام این تنظیمات منحصراً برای پروفایل توسعه (dev) اعمال می‌شوند. هنگام آماده‌سازی برنامه برای سرور با دستور cargo build --release، کارگو به‌صورت خودکار موتور قدرتمند LLVM را با بالاترین سطح بهینه‌سازی (opt-level = 3) به کار می‌گیرد. :::

:::details آیا می‌توان از تنظیم debug = "line-tables-only" در نسخه پایدار (Stable) راست استفاده کرد؟ بله! بهینه‌سازی اول کاملاً با نسخه پایدار کامپایلر راست در تمام ویرایش‌های ۲۰۱۸، ۲۰۲۱ و ۲۰۲۴ سازگار است و هیچ نیازی به نصب نسخه نایتلی ندارد. :::

:::details چرا موتور Cranelift به‌صورت پیش‌فرض در کارگو فعال نیست؟ موتور Cranelift فوق‌العاده سریع است، اما هنوز یک پروژه جوان به حساب می‌آید. در مقابل، LLVM دهه‌ها سابقه پشتیبانی از انواع معماری‌های سخت‌افزاری و موارد خاص را در کارنامه دارد. به همین دلیل Cranelift فعلاً به‌صورت یک ویژگی اختیاری و تجربی در دسترس است. :::