programmatūra · 6 min · 17.08.2026

Rust kodoli uz GPU: no 32 % ātrāk līdz 46 % lēnāk nekā CUDA un HIP

Rust programmu, kuras GPU kodolos nav neviena unsafe bloka, tagad var kompilēt tieši uz AMD un NVIDIA grafiskajām kartēm. RAJAPerf testu kopā uz NVIDIA H100 šāds kods nostrādā no 11 % ātrāk līdz 46 % lēnāk nekā ar roku optimizēts CUDA. Uz AMD MI250X rezultāts svārstās no 32 % ātrāk līdz 43 % lēnāk nekā HIP.

Skaitļi nāk no 13. augustā publicētā darba “GPU Offload in Rust: Portable, Safe, and Fast”. To sarakstījuši Manuel S. Drehwald (Toronto universitāte un Lawrence Livermore nacionālā laboratorija), Marcelo Domínguez (Universidad Rey Juan Carlos), Kevin Sala un Johannes Doerfert (abi no Livermoras) un Alán Aspuru-Guzik (Toronto). Viss kods dzīvo pašā rustc un LLVM aizmugurē, tāpēc atsevišķa ražotāja valoda vai DSL nav vajadzīga. Rezultātus 17. augustā apskatīja arī Phoronix.

Divas cargo kārtas vienam izpildfailam

Kompilēšana notiek divos piegājienos. Pirmajā rustc saņem eksperimentālu karogu -Z offload=Device un no tā paša pirmkoda uzbūvē ierīces bināro failu AMD vai NVIDIA mērķim. Otrajā piegājienā ar -Z Offload=Host=/path/to/device.bin kompilators jaunās offload intrinsikas pārvērš par libomptarget izsaukumiem un iestrādā ierīces bināro failu resursdatora LLVM IR. Pēdējo soli veic clang-linker-wrapper, kas piesaista offload izpildlaika bibliotēkas.

Clang OpenMP rīkkopa to pašu iepakošanu uztic atsevišķai programmai clang-offload-packager. Rust gadījumā rustc izsauc Offload API tieši, tāpēc ķēdē ir par vienu izpildāmo failu mazāk. Tā kā LLVM Offload prot fat binaries, vienā programmā var iesaiņot gan AMD, gan NVIDIA ierīces kodu. Intel mērķis spirv64-intel-unknown vēl top.

Region aizvieto neapstrādātās norādes

Parasts vec_add kodols nesader ar Rust noteikumu, ka maināma atsauce vienmēr ir unikāla: visi tūkstoši pavedienu saņem vienu un to pašu &mut [f32]. Projekts rust-cuda katru maināmo argumentu vienkārši pārvērš par neapstrādātu norādi. Jaunajā darbā tā vietā ienāk tips Region, kuram piesaistīta PartitioningStrategy.

Stratēģija nosaka, kurus elementus konkrētais pavediens drīkst lasīt un pārrakstīt, tāpēc divi pavedieni nekad nesaņem pārklājošos atmiņas gabalus. Pati iezīme ir unsafe, jo nepareiza implementācija dod nedefinētu uzvedību. Konkrētās stratēģijas turpretī ir rakstītas tīrā Rust bez kompilatora iekšējām iespējām. Autori tās plāno izdot kā atsevišķu crate, nevis iebūvēt standarta bibliotēkā.

Datu atrašanās vietu apraksta trešā saskarne. Preload<'a, T> uzvedas kā nemaināms aizņēmums no resursdatora vērtības, PreloadMut<'a, T> kā maināms. Kamēr PreloadMut ir dzīvs, borrow checker neļauj resursdatorā nolasīt sākotnējo masīvu. Brīdī, kad tas tiek nomests, kompilators ģenerē pārsūtīšanu no ierīces atpakaļ uz resursdatoru. Divi secīgi kodolu izsaukumi šādā kārtībā vairs nevar klusi ievilkt starppārsūtīšanu.

Autori savu izvēli formulē īsi:

Šis dizains maina daļu ērtības pret paredzamu veiktspēju.

Kur Rust zaudē CUDA

Kodolu izpildes laiki uz H100 un MI250X lielākoties sakrīt ar RAJA variantiem. Divi izņēmumi ir FIR un LTIMES: pret CUDA Rust tur atpaliek par 44 % un 46 %. Tie paši divi testi pret HIP dod Rust lielāko pārsvaru, attiecīgi 15 % un 32 %. Abi kodoli sastāv no īsas cilpas ar pāris instrukcijām, tāpēc atšķirīgi kompilatoru lēmumi par cilpas atritināšanu rezultātu maina uz abām pusēm.

Atmiņas pārsūtīšanā skaitļi ir negaidīti. Uz H100 visos testos kopā RAJA veic 55 pārsūtījumus uz ierīci ar apjomu 468 MB, Rust 53 pārsūtījumus ar 423 MB. Atpakaļ abi veic 9 pārsūtījumus: RAJA 99 MB, Rust 69 MB. Neraugoties uz mazāku apjomu, Rust pārsūtīšanai patērē 46 ms pret RAJA 16 ms. Iemeslu autori meklē atmiņas veidos un asinhronajos pārsūtījumos.

Optimizācijas šeit izšķir gandrīz visu. Naivā A saskarnes implementācija, kas datus pārsūta pie katras kodola palaišanas, uz MI250X iet vairāk nekā 400 reižu lēnāk par optimizēto variantu. Optimizāciju sarakstā ir gan pārsūtījumu pārvēršana par asinhroniem, gan to izcelšana ārā no cilpas.

Fast-math vietā algebriskie peldošie skaitļi

C++ GPU kodā parasti ieslēdz fast-math, kas apvieno septiņus karogus. Rust to atļaut nevar, jo pieņēmumi nnan un ninf safe kodā rada nedefinētu uzvedību. Vietā ir eksperimentālās algebriskās peldošā komata operācijas. Uz NVIDIA RTX A2000 tās FIR paātrina divkārt, bet DEL_DOT_VEC_2D, VOL3D un MATVEC3D uzlabo par aptuveni 20 %. FIR gadījumā LLVM cilpu papildus vektorizē ar vektora platumu 4. Pārējos kodolos efekta nav. Uz MI250X algebriskie skaitļi neko nemainīja.

Reģistru patēriņš uz RTX 2070 trīspadsmit RAJAPerf kodolos Rust variantam ir vidēji 33, RAJA-CUDA variantam 28. Starpību autori skaidro ar robežu pārbaudēm: uz GPU indeksu rēķina no pavediena un bloka numura, tāpēc Rust katru piekļuvi pārbauda. Izpildes laika mērījumos šīs pārbaudes neparādījās, jo RAJAPerf testos lielākā daļa masīvu izmēru un bloku izmēru ir zināmi jau kompilēšanas laikā.

Kas jau ir nightly

Eksperimenti balstīti uz LLVM 23.1.0-rc1. Pati iezīme Rust kompilatorā ir pieejama aiz karoga #![feature(gpu_offload)]. Sekošanas biļetenā saskaitīti ap 28 jau saplūdinātu izmaiņu pieprasījumu: resursdatora koda ģenerēšana ar automātisku datu kustību, kodolu palaišana, ierīces koda ģenerēšana, atbalsts slices un ģenēriskiem tipiem, CI integrācija un Intel GPU mērķis. Atlikuši dokumentācijas labojumi, stila formatējums un stabilizācijas pieprasījums.

Avoti

komentārisaruna

Komentāri

Šim rakstam vēl nav komentāru. Esi pirmais, kurš dalās ar savu viedokli.

Pievieno komentāru

Tavs e-pasts netiks publicēts. Obligātie lauki atzīmēti.

vēl no programmatūrasaistītie