Nvidia برنامه نویسی GPU بومی را در Rust اعلام کرد
آدرس مقاله: https://developer.nvidia.com/blog/introducing-cuda-rust-two-tracks-for-writing-gpu-kernels/ آدرس نظرات: https://news.ycombinator.com/item?id=49724881 امتیاز: 323 # نظرات: 127
در سپتامبر 2026، NVIDIA اعلام کرد که به برنامه نویسی GPU بومی در Rust متمایل است. CUDA C++ و CUDA Python بالغ هستند، زنجیره ابزارهای درجه سازمانی هستند و NVIDIA در حال رشد و بلوغ CUDA Rust تا سال 2027 و بعد از آن خواهد بود.
لایه سیستمی هوش مصنوعی شامل موتورهای استنتاج، سرویس دهی به زیرساخت ها، درایورها و زمان اجرای عامل می شود و با تغییر مدل ها و تکنیک ها، دائما تغییر می کند. بیشتر و بیشتر از آن در Rust نوشته میشود، که در زمان کامپایل کلاسهای کاملی از اشکالات را میگیرد بدون اینکه از کارایی کاسته شود.
NVIDIA بخشی از این تغییر به همین دلیل است. درایور نوا لینوکس به زبان Rust نوشته شده است. NVIDIA Dynamo بر روی هسته Rust ساخته شده است. NVTX دارای اتصالات Rust است.
هسته GPU استثناست. شما می توانید هسته ها را از Rust راه اندازی کنید، اما خود هسته اغلب باید به زبان دیگری نوشته شود.
NVIDIA CUDA Rust این شکاف را برطرف می کند. هستههای GPU را میتوان در Rust نوشت، بهطور بومی در PTX کامپایل کرد، بهجای اینکه یک پوشش در اطراف کد از جای دیگری باشد.
دو آهنگ برای استفاده از Rust وجود دارد که با دو آهنگ خود CUDA مطابقت دارد. SIMT مدلی است که قبلا در CUDA C++ یا numba-cuda نوشتهاید. شما مشخص می کنید که یک رشته چه کاری انجام می دهد و هزاران مورد از آنها را راه اندازی می کنید. Tile یک مدل برنامه نویسی جدیدتر است که در C++ و Python نیز موجود است. همه این فرانتاندها به شما اجازه میدهند بگویید که یک کاشی داده چه کاری انجام میدهد، و کامپایلر Tile IR بقیه را انجام میدهد.
وقتی یکی را برای ساختن انتخاب میکنید، ابتدا به کاشی برسید. کامپایلر تصمیم میگیرد که چگونه کاشیها به هر معماری نگاشت شوند، بنابراین منبع شما گزینههای خاص معماری را رمزگذاری نمیکند، و زمانی که به آن کنترل نیاز دارید یا میخواهید خودتان حافظه و رشتهها را مدیریت کنید، به SIMT رها میشوید.
اینکه به چه زبانی دست می یابید سوالی جدا از کدام مدل است. از نوردهی CUDA استفاده کنید که به بهترین وجه با پشته ای که در حال حاضر دارید مطابقت دارد. دو پروژه زیر برای زمانی است که آن پشته Rust باشد. ما قصد داریم از interop بین زبانی پشتیبانی کنیم، بنابراین انتخاب شما را از دیگران قفل نمی کند.
در زیر همان هسته در هر مسیر وجود دارد که به صورت عنصری بیش از 1024 شناور را انجام می دهد. هر دو برنامه کامل هستند، هر دو اجرا می شوند و هر دو یک خط چاپ می کنند، بنابراین می توانید آنها را در کنار هم بخوانید و ببینید چه تغییری می کند.
cuda-oxide یک باطن کدژن rustc سفارشی است. کامپایلسازی را رهگیری میکند، توابع #[هسته] را از طریق Rust MIR، چارچوب Pliron IR جامعه، و LLVM IR به PTX هدایت میکند، و هر چیز دیگری را به باطن استاندارد تحویل میدهد. لهجههای GPU در بالای Pliron متعلق به ما هستند. لهجهها و هر تبدیل در Rust باقی میمانند تا زمانی که باطن استاندارد LLVM تمام شود.
شما به لینوکس، یک GPU با قابلیت محاسبات 8.0 یا بالاتر، یک جعبه ابزار CUDA (12.x یا جدیدتر)، صدا زدن با هدرهای libclang و زنجیره ابزار شبانه پین شده نیاز دارید. پزشک اکسید محموله همه آن را بررسی می کند، از جمله سیستم اختیاری LLVM. cargo-oxide را نصب کنید، فرمان فرعی Cargo که بیلد را هدایت می کند:
سپس یک پروژه را داربست بزنید و آن را اجرا کنید. این الگو یک برنامه اضافه برداری کامل است:
اولین اجرای اکسید محموله پشتیبان کدژن را ایجاد می کند، بنابراین انتظار داشته باشید که مدتی طول بکشد. بعدا از حافظه پنهان استفاده مجدد می کند.
PASSED را چاپ می کند: همه 1024 عنصر صحیح هستند. این کل برنامه ای است که این کار را انجام داده است، دقیقا همان چیزی که cargo oxide new نوشته است، با نظرات اضافه شده در اینجا:
کد هاست و دستگاه در یک فایل زنده هستند، با یک دستور ساخته می شوند و نیازی به جعبه کرنل جداگانه ندارند.
ابتدا امضای هسته را بخوانید، زیرا کل استدلال ایمنی را در خود دارد. a و b برش های مشترک معمولی هستند که توسط هر رشته قابل خواندن هستند. c یک DisjointSlice<f32> است، نوعی که به هر رشته دسترسی انحصاری به عنصر خودش می دهد و نه چیز دیگری. وجود دارد زیرا &mut [f32] شکل اشتباهی برای کار است. هر رشته ای به همان &mut نیاز دارد که Rust به درستی آن را رد می کند. DisjointSlice آن یک قرض قابل تغییر را به قطعات هر رشته تقسیم می کند.
thread::index_1d() یک نوع شاخص را برمی گرداند، نه یک عدد صحیح خالی، و c.get_mut(idx) فقط آن نوع را می پذیرد. شما یک Option را برمیگردانید، بنابراین مورد خارج از محدوده، شاخهای است که شما مدیریت میکنید، نه یک خطای حافظه که بعدا پیدا میکنید.
راه اندازی به جای قابل اعتماد بررسی می شود. #[launch_contract] اعلام می کند که این هسته در یک بعد با بلوک های 256 رشته ای ایندکس می شود. përgatit_vecadd LaunchConfig1D شما را در برابر آن اعلان و محدودیتهای دستگاه زنده تأیید میکند و مدرکی را ارائه میکند که روش امن vecadd به آن نیاز دارد. هستههای بدون قرارداد فقط روشهای راهاندازی ناامن خام را نشان میدهند، زیرا LaunchConfig خالی چیزی در مورد هستهای که راهاندازی میکند نمیگوید.
cutile-rs یک سطح بالاتر کار می کند. شما محاسبات را بر روی کاشی ها به جای اسکالر انجام می دهید. هر بلوک کاشی، بدنه هسته را یکبار به عنوان یک رشته منطقی روی یک تانسور فرعی داده اجرا میکند و کامپایلر تصمیم میگیرد که چند رشته GPU واقعی آن را پشتیبان کند. ماکرو #[cutile::module] AST هسته را در باینری میزبان تعبیه میکند و JIT آن را از طریق CUDA Tile IR (IR کامپایلر سطح کاشی NVIDIA) در زمانی که برای اولین بار به هسته نیاز است، کامپایل میکند.
الزامات سبک تر از مسیر SIMT است. شما به یک GPU با قابلیت محاسبه 8.0 یا بالاتر، CUDA 13.3، Rust 1.89 یا جدیدتر و لینوکس نیاز دارید، اما بدون زنجیره ابزار شبانه و بدون LLVM خودتان.
cutile منتشر شده است، بنابراین چیزی برای شبیه سازی وجود ندارد:
در اینجا همان اضافه عنصری است که برای کاشی ها نوشته شده است. آن را در src/main.rs و cargo run قرار دهید:
مسیر Tile در Stable Rust به همان پاسخ می رسد و امضای آن همان استدلال ایمنی را ایجاد می کند. این بار DisjointSlice وجود ندارد. پارتیشن بندی روی میزبان فقط برای تانسورهای قابل تغییر لازم است و به هر بلوک کاشی یک تانسور فرعی قابل نوشتن می دهد که هیچ بلوک کاشی دیگری نمی تواند روی آن همپوشانی داشته باشد. این انحصار چیزی است که &mut از قبل تضمین می کند.
-1 در اشکال ورودی به جای اندازه، یک نگهبان است. این بعد هنگام راه اندازی از روی تانسور خوانده می شود، بنابراین شکل می تواند بدون کامپایل مجدد تغییر کند.
خط جالب در هاست .partition([128]) است و سه کار را همزمان انجام می دهد. این انحصار را واقعی می کند. هر کاشی دارای تکه 128 عنصری خود است و هیچ کاشی دیگری نمی تواند آن را لمس کند. هندسه پرتاب را اصلاح می کند، زیرا 1024 تقسیم بر 128 شبکه ای از 8 کاشی است.
شبکه به جای اینکه جداگانه محاسبه شود و با نمایه سازی هسته بررسی شود، از پارتیشن پیروی می کند. همچنین B را تامین میکند که هرگز در سایت تماس نوشته نمیشود زیرا راهانداز عرض کاشی را از پارتیشن میخواند. به همین دلیل است که یک خروجی &mut قبل از اینکه به طور کلی ارسال شود باید پارتیشن بندی شود.
سپس ببینید پرتاب چه چیزی را برمی گرداند. افزودنی که در هاست فراخوانی میکنید یک راهانداز ماکرو تولید شده است، نه عملکرد دستگاه بالا. مالکیت هر سه تانسور را در اختیار می گیرد و پس از اتمام GPU آنها را به صورت یک تاپل پس می دهد. این همان چیزی است که .first() برای آن است و خروجی را از آن باز می گرداند.
هیچ چیز تا .sync_on(&stream) اجرا نمی شود. همه چیز قبل از آن یک توصیف تنبل است، به جای ارسال، ضبط شده است. این شامل یکها، صفرها، فراخوانی هسته و حتی کپی برگشت به میزبان است. کل برنامه یک زنجیره با یک نقطه همگام سازی واحد است.
هر دو هسته در مورد حافظه ادعای یکسانی دارند. ورودی های آنها به اشتراک گذاشته می شود و خروجی آنها به تنهایی متعلق به یک نویسنده است. آنها فقط در سطحی که آن را می سازند و اینکه آیا اصلا برای ساخت آن به یک نوع هدفمند نیاز است یا خیر متفاوت هستند.
این مهم است زیرا هزاران رشته بدون ترتیب تضمین شده به بافرهای مشابه می رسند. وقتی دو نفر از آنها به یک آدرس برخورد می کنند و یکی در حال نوشتن است، سفارش دهنده نتیجه را تعیین می کند. این اشکالات به ندرت در صورت تقاضا تکثیر می شوند و قبل از شکست در تولید، آزمایشات را پشت سر می گذارند.
عبور بافر خروجی هسته SIMT به عنوان یکی از ورودی های خود، کامپایل نمی شود، چه آن هسته واقعا مسابقه دهد یا نه:
خطا[E0502]: نمی توان «c_dev» را به عنوان قابل تغییر قرض گرفت زیرا به عنوان غیرقابل تغییر نیز قرض گرفته می شود
همان نام مستعار در سمت Tile نیز کامپایل نمی شود:
هر دو مثال در زمان کامپایل، اشتباه کلاسیک الایاسینگ را میگیرند و در مکانهای مختلف خط میکشند. cuda-oxide هر فراخوان راه اندازی را بررسی می کند. مالکیت cutile-rs از تانسورها در سراسر مرز پرتاب پیروی می کند، که قوی تر از این دو ادعا است.
Tile هیچ حافظه مشترک یا ایندکس رشته ای به شما نمی دهد تا اشتباه کنید، زیرا کامپایلر هر دو را دارد. بلوک کاشی یک رشته منطقی واحد است، بنابراین هیچ رشتهای برای مسابقه وجود ندارد. این همان چیزی است که آن را با ساخت و ساز ایمن می کند، و همچنین چیزی است که شما با آن مبادله می کنید. SIMT این کنترل را حفظ می کند، و امروزه حافظه مشترک در آنجا نیاز به امنیت ندارد. حافظه مشترک پایه هسته های سریع SIMT است، بنابراین ایمن کردن آن مسیر یک کار فعال است.
هر دو پروژه در مراحل اولیه هستند و هیچ کدام آماده تولید نیستند. cuda-اکسید آلفای اولیه است. cutile-rs در ادامه در crates.io منتشر شده و قبلا خارج از NVIDIA در موتور استنتاج HuggingFace's Grout و در mistral.rs استفاده شده است. پوشش ناقص است و API ها جابه جا می شوند. جایی که لبه های ناهموار پیدا می کنید، ما می خواهیم در مورد آنها بشنویم.
محموله ها و جعبه ها این انتظار را ایجاد می کنند که شروع کار آسان است. برنامه نویسی GPU از لحاظ تاریخی برعکس بوده است و بستن این فاصله بخشی از کار است. مسیر SIMT هنوز به یک زنجیره ابزار شبانه سنجاق شده نیاز دارد، که دقیقا همان چیزی است که ما دوست نداریم از شما درخواست نکنیم.
Rust در GPU ها چیز جدیدی نیست. کارهای خوبی در این فضا وجود دارد که پیش از فضای ماست و در کنار آن ادامه دارد. ضمیمه اکوسیستم در کتاب cuda-oxide نقشههایی را نشان میدهد که ما نسبت به Rust-GPU، rust-cuda، CubeCL، و بقیه آنها مینشینیم، و در زمان بلوغ هر دو پروژه، با حفظکنندههای rust-cuda کار کردهایم.
چیزی که جدید است مهندسی است که ما در پشت آن قرار می دهیم و درک روشنی از اینکه به کجا می رود.
با آنچه در اینجا هست سرهم کنید و بیایید با ما روی آن کار کنید. زود است، باز است و آنچه اکنون می سازید، آینده را شکل خواهد داد.
NVIDIA هیجان زده است که با جامعه Rust گرایش پیدا می کند زیرا ما برنامه نویسی بومی Rust GPU را ارتقا می دهیم. پروژههایی مانند rust-cuda، rust-gpu، و cudarc پیشگام ازدواج پردازندههای گرافیکی و Rust بودند، و افراد پشت سر آنها، از جمله تیم VectorWare، همچنان که با جامعه Rust میسازیم، نحوه تفکر ما در مورد کار خود را شکل میدهند.
متن اصلی (انگلیسی)
Nvidia announces native GPU programming in Rust
Article URL: https://developer.nvidia.com/blog/introducing-cuda-rust-two-tracks-for-writing-gpu-kernels/ Comments URL: https://news.ycombinator.com/item?id=49724881 Points: 323 # Comments: 127