programmatūra · 6 min · 21.08.2026

Rust 1.98 atļauj pārkārtot peldošā punkta rēķinus: summa no 595 līdz 144 mikrosekundēm

Rust 1.98 pievieno f32 un f64 tipiem piecas metodes, kas rēķina to pašu, ko operatori +, -, *, / un %, bet atļauj kompilatoram darbības pārkārtot: algebraic_add, algebraic_sub, algebraic_mul, algebraic_div un algebraic_rem. Laidiens iznāca 20. augustā.

Itamar Turner-Trauring pārmērīja, ko tas dod. Miljona f64 vērtību saskaitīšana ar parasto + aizņem 595,2 mikrosekundes. Tā pati cilpa ar algebraic_add pabeidz darbu 144,5 mikrosekundēs, 4,1 reizi ātrāk. Mērījumi veikti uz Intel i7-12700K, kompilējot ar -C target-cpu=x86-64-v3.

Kāpēc parastā saskaitīšana ir lēna

Peldošā punkta saskaitīšana nav asociatīva. Izteiksme (a + b) + c var dot citu rezultātu nekā a + (b + c), jo pēc katra soļa rezultātu noapaļo. Rust semantika prasa ievērot rakstīto secību, tāpēc LLVM summēšanas cilpu nedrīkst sadalīt četrās vai astoņās paralēlās SIMD joslās. Tā vietā kompilators ģenerē vienu garu atkarību ķēdi, kurā katrs saskaitīšanas rezultāts jāsagaida pirms nākamā soļa.

Veselos skaitļos šī problēma nepastāv. Tā pati cilpa ar i64 miljonu vērtību saskaita 151,9 mikrosekundēs, kas iznāk apmēram puse instrukcijas uz vērtību. Kompilators tur brīvi pārkārto darbības, jo veselo skaitļu saskaitīšana ir asociatīva.

Ar jaunajām metodēm izstrādātājs pats norāda vietas, kur pārkārtošana ir pieļaujama. Kompilators tad drīkst pārvērst a + b + c + d par (a + b) + (c + d) un rēķināt abus pārus vienlaikus.

Piecas metodes vietā viena kompilatora karoga

C un C++ tam pašam mērķim lieto -ffast-math. Karogs iedarbojas uz visu kompilācijas vienību un papildus atļauj kompilatoram pieņemt, ka NaN un bezgalība kodā neparādās. Rust izvēlējās šaurāku ceļu. Atļauja tiek dota vienā izsaukuma vietā, pārējais kods paliek pie IEEE 754 noteikumiem. Pieņēmumi par NaN nemainās. Metodes nekad neizraisa nedefinētu uzvedību.

Ierosinājums nāca no sūdzības, ka stabilā Rust uzrakstīts skalārais reizinājums strādā līdz 8 reizēm lēnāk par C++ versiju uz x86-64 procesoriem. Pieprasījumu Expose algebraic floating point intrinsics sagatavoja izstrādātājs ar segvārdu calder. Metodes pieejamas arī f16 un f128 tipiem, taču šie divi tipi paši vēl nav stabilizēti, tāpēc stabilajā kanālā tos izmantot nevar.

Tā pati atkarību ķēde bija apejama arī agrāk, tikai ar rokām. Viens paņēmiens ir cilpu attīstīt pašam un krāt summu četros vai astoņos atsevišķos mainīgajos, kurus beigās saliek kopā. Kompilators tādu kodu vektorizē, jo katra josla ir atsevišķs mainīgais. Kods pēc tam ir divreiz garāks un jāpielāgo katram datu izmēram.

Otrs paņēmiens ir rakstīt SIMD instrukcijas tieši caur core::arch, kas piesien kodu konkrētai procesora arhitektūrai. Trešais bija std::intrinsics::fadd_algebraic, tā pati funkcionalitāte nestabilā veidā, pieejama tikai nightly kompilatorā. Rust 1.98 no šī saraksta atkrīt trešais variants, pirmie divi paliek noderīgi tur, kur vajag precīzu kontroli pār instrukcijām.

Ko par to maksā

Dokumentācija brīdina par cenu tieši:

Vienas un tās pašas ievaddatu vērtības var dot atšķirīgus rezultātus pat vienas programmas izpildes laikā.

Kompilators konkrēto pārkārtojumu izvēlas pats. Izvēle var atšķirties starp diviem izsaukumiem viena binārfaila iekšienē, jo katrs izsaukums iekļaujas citā kontekstā. Praksē tas skar bitu precizitātes testus, kešatmiņas atslēgas un kontrolsummas, ko rēķina no peldošā punkta rezultāta. Kodā, kur svarīgs atkārtojamības garants, jaunās metodes neder.

Ieguvums arī nav vienāds visur. Otrs Turner-Trauring mērījums, kvadrātisko starpību summa, ar parastajiem operatoriem prasa 628,7 mikrosekundes un 4,5 instrukcijas uz vērtību. Algebraiskā versija to izdara 371,1 mikrosekundē ar vienu instrukciju uz vērtību, kas iznāk 1,7 reizes ātrāk. Ātrums pieaug tur, kur cilpa krāj vienu rezultātu no daudzām vērtībām: summas, skalārie reizinājumi, normas, dispersijas. Elementu pēc elementa reizināšanā bez krāšanas nav ko pārkārtot, tāpēc arī ieguvuma nav.

Skaitļu formatēšana bez itoa

Otrs laidiena jaunums attiecas uz veselu skaitļu pārvēršanu tekstā. Visiem primitīvajiem veselo skaitļu tipiem parādījusies metode format_into, kas ņem &mut NumBuffer<Self> un ieraksta ciparus tajā. Buferim ir fiksēts izmērs, tāpēc formatēšana notiek bez dinamiskās izsaukumu pārvirzes, kuru prasa Display mehānisms. Laidiena paziņojumā rakstīts, ka ātrums sasniedz itoa bibliotēkas līmeni. Pati NumBuffer struktūra atrodas core::fmt, tāpēc strādā arī bez standarta bibliotēkas.

Pārējie stabilizētie API

  • str::substr_range un [T]::subslice_range atgriež apakšvirknes pozīciju sākotnējā datu blokā
  • str::strip_circumfix un [T]::strip_circumfix noņem prefiksu un sufiksu vienā izsaukumā
  • NonZero<{integer}>::from_str_radix nolasa skaitli no teksta ar norādītu bāzi
  • String::from_utf16le, from_utf16be un abu zaudējošās versijas
  • atomu tipiem from_mut, get_mut_slice un from_mut_slice
  • std::range::legacy

Dokumentācija tagad arī apraksta ManuallyDrop un Box mijiedarbību ar stabilu garantiju. Vērtības pārvietošana no jau atbrīvota Box ir atļauta, agrāk šī vieta bija neskaidra. Atjauninājumu esošā instalācijā ievelk rustup update stable.

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