Cél és előfeltétel
Most egy futási időt megfigyelő programot készítesz, de a mérés eredményét tudatos korlátok között értelmezzük. Előfeltétel a költségmodell és az adatelrendezési lecke. A három feladat külön, kicsi edition 2024 program. Nem kell hosszú futás, külső függőség vagy a számítógépet terhelő adatmennyiség.
A funkcionális teszt azt kérdezi, helyes-e az eredmény. Az időmérés más kérdés: mennyi időt mutatott egy adott környezetben egy körülhatárolt végrehajtás? A kettőt ne cseréld fel. Ha egy számítás téves eredménnyel végez gyorsabban, nem lett jobb megoldás. Ha pedig egy helyes megoldás egyszer kevesebb időt mutatott, abból még nem következik ismételhető vagy más gépre átvihető gyorsulás.
Mérési határ és optimalizáló fordító
Az Instant két időpont közötti időtartam megfigyelésére való. Nem naptári dátum, és a kiolvasásának is van költsége. Nagyon rövid műveleteknél a felbontás és a mérés környezete jelentős lehet. Ezért nem írunk olyan assertot, hogy az eltelt idő pozitív vagy egy küszöb alatt van. Egy nulla időtartam sem bizonyítja, hogy a program semmit nem dolgozott.
A black_box a fordítónak adott, legjobb igyekezetű optimalizálási akadály. Hasznos lehet annak csökkentésére, hogy a fordító a kísérlet céljától eltérően konstansra egyszerűsítse a munkát. Nem teljes garancia a gépi kódra, és nem teremt automatikusan korrekt benchmarkot. A bemenet, a kimenet megfigyelése, az ismétlés, a buildbeállítás és a környezet továbbra is a te felelősséged.
1. feladat: figyelj meg egy helyes számítást
Mérj meg egy száz elem összegét adó függvényt. Az adat létrehozása legyen a mérési intervallumon kívül, a függvény bemenete és eredménye pedig haladjon át black_box híváson. Ellenőrizd a matematikai eredményt, az időtartamot csak írd ki. A példa szándékosan kicsi és gyorsan lefut.
Megoldás
use std::hint::black_box;
use std::time::Instant;
fn total(xs: &[u32]) -> u64 {
xs.iter().map(|x| u64::from(*x)).sum()
}
fn main() {
let input: Vec<u32> = (0..100).collect();
let start = Instant::now();
let result = black_box(total(black_box(&input)));
let observed = start.elapsed();
assert_eq!(result, 4950);
println!("egyetlen megfigyeles: {observed:?}");
}
A mostani mérési határ nem tartalmazza a Vec létrehozását. Ha a termékben a számításhoz minden alkalommal új vektor készülne, azt külön végponttól végpontig tartó mérésben is látni kellene. Egyetlen számolás eredményének ellenőrzése nem ellenőrzi az összes lehetséges bemenetet sem; itt a mérési keret működését tanuljuk, a következő feladatokban több mintával egészítjük ki.
2. feladat: több megfigyelés az eredmény megőrzésével
Készíts öt rövid mintát ugyanazzal a bemenettel. Minden körben ellenőrizd az eredményt, majd rendezd az időtartamokat. Ne követeld meg, hogy a második kör gyorsabb legyen az elsőnél. A minta célja annak láthatóvá tétele, hogy több megfigyelési adatunk van, nem statisztikai bizonyosság előállítása öt számból.
Megoldás
use std::hint::black_box;
use std::time::Instant;
fn main() {
let input: Vec<u64> = (0..100).collect();
let mut observed = Vec::new();
for _ in 0..5 {
let start = Instant::now();
let result: u64 = black_box(&input).iter().copied().sum();
let result = black_box(result);
observed.push(start.elapsed());
assert_eq!(result, 4950);
}
observed.sort_unstable();
assert_eq!(observed.len(), 5);
println!("ot rendezetten megfigyelt idotartam: {observed:?}");
}
A rendezés és a konzolos kiírás kívül van az egyes mérési intervallumokon. A körök azonban továbbra is ugyanabban a folyamatban, hasonló körülmények között futnak. A háttérfolyamatok, az energiamenedzsment és a korábbi adatérintés hatása nem tűnt el. Egy szélső érték egyszerű eldobása magyarázat nélkül ugyanolyan problémás lehet, mint egyetlen legjobb kör reklámozása.
3. feladat: előbb igazold a két változat egyenértékűségét
Készíts ciklusos és iterátoros párosösszegzőt néhány kis tesztadathoz. Az üres, csak páratlan és vegyes bemenetet is vizsgáld. Itt egyáltalán nem kell időmérő: az algoritmusváltozatok összehasonlítása előtt a helyességi alapot készítjük el. A tesztadatok összege szándékosan kényelmesen elfér u64-ben.
Megoldás
fn loop_sum(xs: &[u32]) -> u64 {
let mut total = 0_u64;
for x in xs {
if x % 2 == 0 { total += u64::from(*x); }
}
total
}
fn iterator_sum(xs: &[u32]) -> u64 {
xs.iter().filter(|x| **x % 2 == 0).map(|x| u64::from(*x)).sum()
}
fn main() {
for (input, expected) in [
(&[][..], 0_u64), (&[1, 3, 5][..], 0), (&[2, 3, 4][..], 6)
] {
assert_eq!(loop_sum(input), expected);
assert_eq!(iterator_sum(input), expected);
}
}
A két változat az itt felsorolt bemeneteken ugyanazt a szerződést teljesíti. Ez nem jelenti, hogy minden lehetséges felhasználásra teljes bizonyítás készült, és semmit nem mond a gépi kód azonosságáról. A következő mérési kísérletbe a tényleges alkalmazás bemeneti méreteit és eloszlását is be kell vinnünk. Az eltérő stílus önmagában nem teljesítménymutató.
Mit kell feljegyezni egy későbbi összehasonlításhoz?
Rögzítsd a fordító verzióját, a célarchitektúrát, az optimalizálási beállítást, a bemeneti adatok méretét és a mérési intervallum határát. A debug és optimalizált futás ne keveredjen egy táblázatban közös megjelölés nélkül. Ha két változatot mindig azonos sorrendben futtatsz, a sorrendhatás is összekeveredhet a vizsgált eltéréssel. A nyers megfigyeléseket őrizd meg az értelmezés mellett.
A kapott időértéket ne írd bele hordozható tesztfeltételbe. Az automatizált helyességi ellenőrzésnek akkor is működnie kell, ha a gép közben más munkát végez. Ha a rendszernek valódi válaszidő-követelménye van, annak külön mérési környezet, terhelési terv és elfogadási szabály szükséges; a tanulóprogram egyszeri időpontjai ezt nem helyettesítik.
A megfigyelés értékét a rögzített körülmények adják: ugyanazt a programot később ugyanazzal a bemenettel vizsgálhatod újra, és a változtatást ezekhez képest értelmezheted.
Elfogadási feltételek
- Minden mintában az összeg helyes; a kiírt időre nincs pozitivitási vagy gyorsasági assert.
- Az ismétlések száma öt, az előkészítés és kiírás helye egyértelmű a mérési határhoz képest.
- A ciklusos és iterátoros változat mindhárom rögzített tesztesetre helyes eredményt ad.
Összefoglaló és továbblépés
Most már külön tudod mondani, mit ellenőriztél és mit figyeltél meg. A naplóelemző miniprojektben először a helyességet és a tulajdonjogi határokat készítjük el, és csak utána lenne értelme teljesítményhipotézist vizsgálni.