A 33.1. Inference-szerződés és modellcsomag inference-szerződés és a 33.4. Üzemeltetés, drift és késő címke üzemeltetési monitoring után eljutottunk ahhoz a kérdéshez, amelyet a kiszolgálás üzemeltetője tesz fel: mekkora terhelést bír a kiválasztott modell, és milyen válaszidőt tapasztal a felhasználó? A „gyorsabb modell” vagy „nagyobb batch” állítás csak akkor tudományos állítás, ha megmondjuk, mit mérünk, milyen egységben, milyen batchméret mellett, és hogyan zárjuk ki a mérés első, nem reprezentatív hívásait. Ez az egység a mérési fogalmakat és egy kis CPU-s laborprotokollt ad.
Előismeret: a 7.4. Numerikus stabilitás és jelentésmegőrző implementáció numerikus stabilitásból azt használjuk, hogy a mérési kódnak dtype- és shape-ellenőrzéssel kell zárulnia; a 19.4. ConvNeXt és a recept/architektúra elválasztása recept/architektúra elválasztásból azt, hogy a modellváltozatok összehasonlításakor a receptet rögzíteni kell; a 23.4. Hibaszeletek és alkalmazási határ hibaszeletekből azt, hogy a sebességmérés nem helyettesíti a minőségi metrikát; a 24.4. VI. ellenőrző pont: megbízható CNN-training profilerfogalmaiból a wall time, warmup és memória alapjait; a 33.4. Üzemeltetés, drift és késő címke monitoringból pedig azt, hogy a latency megfigyelhető címke nélkül, míg a pontosság hiteles ground truth-ot igényel.
Induló kérdés: ha egy modell 400 kép/s throughputot ad, akkor egy felhasználói kérés válaszideje 2,5 ms? Becslés: nem. Ha a 400 kép/s érték 8-as batchből származik, és a 8 kép feldolgozása 20 ms-ig tart, akkor a kérésnek a teljes 20 ms-ot kell várnia, nem annak nyolcadát. A batch throughput és az egyedi kérés latency két külön mennyiség; ezt formalizáljuk a következő szakaszban.
Definíció (kiszolgálási batch). A kiszolgálási batch darab egyedből álló, együtt feldolgozott csomag. A bemenet , a kimenet logit. Ez ugyanaz a , mint a JELOLESEK.md dimenziója, de itt a jelentés fizikai: a kiszolgáló egyszerre képet dolgoz fel. A méretű batch egyetlen forward hívást jelent; egy kérés válasza a teljes batch feldolgozása után érkezik meg.
Definíció (batch latency és latency distribution). Legyen egy méretű batch forward hívás kezdetétől a visszatéréséig eltelt fal-idő (wall time) másodpercben. Egy mérési sorozatban valószínűségi változó; a latency distribution az ismételt mérésekből becsült eloszlás. Jellemzésére az átlag , a medián, a 90. percentilis és a 99. percentilis használatos. A jelentése: a mért ismétlések 90%-ában a latency legfeljebb . A percentilis empirikus becslés, nem a modell paramétere.
Definíció (throughput). A méretű batch steady-state átlagos feldolgozási rátája:
ahol az átlagos batch latency. A batch-szintű throughput , mértékegysége batch/s. A throughput összevont ráta; egyetlen kérés időtartamáról önmagában nem mond semmit.
Definíció (warmup). A warmup a mérési statisztikába be nem számító, a tényleges mérés előtt végrehajtott hívás. Célja, hogy a könyvtár inicializálása, a memóriaallokátor feltöltődése, a belső thread pool elindulása és az első fordítás/optimalizálás ne torzítsa a steady-state mérést. A warmup hívásoknak ugyanazt a bemeneti eloszlást, dtype-ot és device-ot kell használniuk, mint a méréseknek.
Definíció (CPU threads). A CPU-szálak a folyamaton
belüli párhuzamos végrehajtási egységek; PyTorch-ban a
torch.get_num_threads() és
torch.set_num_threads() hívásokkal kérdezhetők le és
állíthatók. A szálak száma nem throughput-garancia: a tényleges
párhuzamosságot az operátor típusa, a batchméret, a
memória-sávszélesség, az operációs rendszer ütemezője és az esetleges
NUMA-elrendezés együtt határozza meg.
Definíció (memory). Három különböző memóriafogalmat különböztetünk meg. (a) A modell paramétereinek és puffereinek bájtjai: ; ez -től független. (b) A forward aktivációk mérete: a közbülső tenzoralakok bájtjai, amelyek -vel arányosan nőnek. (c) A folyamat rezidens memóriája (RSS, resident set size) és annak csúcsa, amely a mérés alatt ténylegesen lefoglalt fizikai memóriát mutatja. A labor a (c) csúcsot rögzíti, a (a) értéket pedig analitikusan kiszámítja.
Állítás (throughput–latency konzisztencia). Legyen rögzített batchméret. Tegyük fel, hogy egy mérési protokoll ismétlést végez, minden ismétlésben a méretű batch feldolgozási latency-jét méri, és a batch-ek között nincs sorban állás vagy átfedő végrehajtás. Jelölje az -edik ismétlés latency-jét másodpercben, és legyen . Ekkor
minden -re, ahol az -edik batch elemszintű pillanatnyi rátája, és
Így a teljes mérésre: ha a teljes hasznos idő és a feldolgozott elemszám , akkor a teljes ráta
Bizonyítás. A pillanatnyi definíció: az -edik batch elemszáma osztva a batch feldolgozási idejével, azaz . Átrendezve . A protokoll szerint a batch-ek szekvenciálisan futnak, tehát a teljes feldolgozott elemszám , a teljes idő pedig . A teljes ráta definíció szerint a feldolgozott elemszám és a teljes idő hányadosa:
ami éppen . A batch-szintű ráta hasonlóan .
Fontos nemlinearitási megjegyzés. Az azonosság nem állítja, hogy vagy hogy független -től. A CPU-n végrehajtott mátrixműveletek párhuzamosítása és a memóriaelérés miatt tipikusan , így is lehet. Az azonosság pusztán a mérési definíciók konzisztenciája: ugyanazon mérésen belül a latency és a throughput reciprok dimenzióban vannak, de a batch élének válaszideje továbbra is , nem pedig .
Legyen a mérési protokoll rögzített: float32, CPU, 4 szál, warmup 10, ismétlés 50. Tegyük fel a következő számított példát — ez nem elvégzett mérés, hanem a képletek szemléltetésére szolgáló bemenet:
| [ms] | [kép/s] | [batch/s] | [ms/kép] | |
|---|---|---|---|---|
| 1 | 8,0 | 125 | 125 | 8,0 |
| 8 | 20,0 | 400 | 50 | 2,5 |
A táblázat mutatja a különbséget: -nál a felhasználói batch latency 20 ms, miközben a throughput 400 kép/s. A 400 kép/s helytelen inverze 2,5 ms lenne; a helyes per-kép átlag 2,5 ms, de ez statisztikai átlag, nem egy kérés válaszideje. Egy olyan kérés, amelyiknek a válasza a 8 képből álló batch feldolgozása után készül el, 20 ms-ot vár.
A következő minimális kód a fenti azonosságot ellenőrzi anélkül, hogy mérési eredményt állítana elő:
# munkakönyvtár: lab/n133_measure
B = 8
T_bar_s = 0.020 # számított példa, nem mért érték
Q = B / T_bar_s
R = 1.0 / T_bar_s
# kontroll: a teljes idő az összegből is kijön
n_rep = 50
total_time = n_rep * T_bar_s
assert abs(total_time - n_rep * T_bar_s) < 1e-12
assert abs(B * n_rep / total_time - Q) < 1e-12
print(f"Q={Q:.3f} elem/s, R={R:.3f} batch/s, batch_latency={T_bar_s*1e3:.3f} ms")A kód kimenete a számított példában:
Q=400.000 elem/s, R=50.000 batch/s, batch_latency=20.000 ms.
Az állítás a Q és a B*n_rep/total_time
egyenlőségét ellenőrzi.
A labor célja, hogy a hallgató saját gépén, rögzített protokollal mérje meg egy kis CNN latency-jét, throughputját és csúcs RSS-ét. A könyv kézirata nem tartalmaz előre elvégzett mérési eredményt; az alábbi kód a futtatási protokoll. A hallgató az eredményt maga állítja elő, és a gépadatokat is rögzíti.
Bemenet. Szintetikus input, nem a GlyphLab képeiből,
hogy az adatbetöltési overhead ne keveredjen a forward mérésbe:
,
csonkítatlan, torch.randn-nel. A modell egy kis CNN: két
konvolúciós blokk, max-pool, lineáris fej. A paraméterszám a labor első
ellenőrző sora.
# munkakönyvtár: lab/n133_measure
# Fájl: measure_latency.py
import json
import platform
import time
import resource
import torch
def main():
# --- rögzített környezet ---
torch.manual_seed(20261020)
device = torch.device("cpu")
dtype = torch.float32
threads = torch.get_num_threads()
torch.set_num_threads(threads) # explicit rögzítés a jegyzőkönyvhöz
batch_sizes = [1, 2, 4, 8, 16]
n_warm = 10
n_rep = 50
# --- kis CNN, eval módban, paraméterezés a n133 laborhoz ---
model = torch.nn.Sequential(
torch.nn.Conv2d(1, 8, kernel_size=3, padding=1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2), # 12x12
torch.nn.Conv2d(8, 16, kernel_size=3, padding=1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2), # 6x6
torch.nn.Flatten(),
torch.nn.Linear(16 * 6 * 6, 3),
).to(device=device, dtype=dtype)
model.eval()
n_params = sum(p.numel() for p in model.parameters())
param_bytes = sum(p.numel() * p.element_size() for p in model.parameters())
results = {
"machine": platform.platform(),
"python": platform.python_version(),
"torch": torch.__version__,
"device": str(device),
"dtype": str(dtype),
"threads": threads,
"seed": 20261020,
"warmup": n_warm,
"repeats": n_rep,
"n_params": n_params,
"param_bytes": param_bytes,
"batch_results": [],
}
for B in batch_sizes:
x = torch.randn(B, 1, 24, 24, dtype=dtype, device=device)
# shape- és dtype-kontroll a mérés előtt
assert x.shape == (B, 1, 24, 24)
assert x.dtype == dtype
assert x.device == device
# warmup: n_warm azonos alakú hívás, eredmény nincs a statisztikában
with torch.no_grad():
for _ in range(n_warm):
_ = model(x)
latencies_s = []
with torch.no_grad():
for _ in range(n_rep):
t0 = time.perf_counter()
z = model(x)
t1 = time.perf_counter()
latencies_s.append(t1 - t0)
# a kimenet shape-ellenőrzése: B x K = B x 3
assert z.shape == (B, 3)
assert torch.isfinite(z).all()
lat_s = torch.tensor(latencies_s, dtype=torch.float64)
mean_s = float(lat_s.mean())
median_s = float(lat_s.median())
p90_s = float(lat_s.quantile(0.90))
p99_s = float(lat_s.quantile(0.99))
q_elem_per_s = B / mean_s if mean_s > 0 else float("nan")
r_batch_per_s = 1.0 / mean_s if mean_s > 0 else float("nan")
results["batch_results"].append({
"B": B,
"mean_latency_s": mean_s,
"median_latency_s": median_s,
"p90_latency_s": p90_s,
"p99_latency_s": p99_s,
"min_latency_s": float(lat_s.min()),
"max_latency_s": float(lat_s.max()),
"throughput_elem_per_s": q_elem_per_s,
"throughput_batch_per_s": r_batch_per_s,
"mean_per_item_s": mean_s / B,
})
# csúcs RSS: Linux/macOS resource ru_maxrss; egység Linuxon KiB
peak_rss_kib = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
results["peak_rss_kib"] = peak_rss_kib
results["peak_rss_mib"] = peak_rss_kib / 1024.0
with open("latency_results.json", "w", encoding="utf-8") as f:
json.dump(results, f, indent=2, ensure_ascii=False)
print(json.dumps(results, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()Futtatás és ellenőrzés. A parancs a
lab/n133_measure munkakönyvtárból:
python measure_latency.pyA kód először kiírja a paraméterszámot: a fenti modellben
(első conv)
(második conv)
(lineáris)
paraméter, tehát n_params == 2979. A JSON-sémában minden
batch_results bejegyzésnek tartalmaznia kell a
,
a mean/median/p90/p99 latency, a throughput és a per-item átlag mezőket.
A hallgató által feltöltött táblázat sablonja:
| [ms] | [ms] | [elem/s] | [batch/s] | csúcs RSS [MiB] | |
|---|---|---|---|---|---|
| 1 | — | — | — | — | — |
| 2 | — | — | — | — | — |
| 4 | — | — | — | — | — |
| 8 | — | — | — | — | — |
| 16 | — | — | — | — | — |
A kötőjelek a hallgató által mérendő értékek helyét jelölik; a
könyvben nem szerepel előre kitöltött, elvégzett futást
állító táblázat. Értelmezési követelmény: a hallgatónak kommentárban meg
kell indokolnia, hogy
hogyan változik
-vel,
és hogy a beküldött számok konzisztensek-e a
azonossággal. A peak_rss_mib értéke a teljes processzre
vonatkozik, nemcsak a modellre; a paraméterbájtok mindössze néhány tíz
KiB nagyságrendűek.
A D16 (torch.utils.benchmark.Timer) és D17
(torch.profiler) hivatalos eszközök alternatív,
részletesebb mérésre; státuszukat a forráslista rögzíti. A fenti saját
protokoll time.perf_counter-t használ, így a labor nem függ
a két API dokumentációs részletétől.
F1 (fogalmi, nehézség 1). Magyarázza el saját szavaival, mi a különbség a batch latency és az elemenkénti átlagos latency között. Adjon egy konkrét számpéldát, amelyben az előbbi 20 ms, az utóbbi 2,5 ms, és nevezze meg, melyiket tapasztalja egyetlen kérés. Átadandó: legfeljebb 5 mondatos magyarázat és a számpélda.
F2 (levezetési, nehézség 2). Egy mérésben ms és ms ugyanarra a modellre, ugyanazon protokollal. Számítsa ki , , , , valamint a per-item átlagokat. Bizonyítsa be a azonosságot, és indokolja, miért nem mond ellent az azonosságnak az a megfigyelés, hogy . Átadandó: levezetés lépésenként, egységekkel.
F3 (implementációs, nehézség 2). Futtassa a fenti
measure_latency.py protokollt, és ellenőrizze: (a) a JSON
n_params mezeje 2979; (b) minden bejegyzésben
abs(throughput_elem_per_s - B/mean_latency_s) / max(1, throughput_elem_per_s) < 1e-6;
(c) a shape-assert nem szakadt meg. Rögzítse a gép- és verzióadatokat.
Átadandó: a kitöltött táblázat, a JSON fájl és a három ellenőrzés
eredménye.
F4 (hibakeresési, nehézség 2). Tekintse az alábbi módosított mérési ciklust:
lat = []
with torch.no_grad():
for i in range(n_rep):
t0 = time.perf_counter()
z = model(x)
t1 = time.perf_counter()
lat.append(t1 - t0)
mean_latency = sum(lat) / len(lat)A kód a warmupot elhagyta, és az első hívás latency-jét is belevette az átlagba. Magyarázza meg, miért torzít ez általában felfelé, milyen következménye van a throughput-számításra, és javítsa a kódot a ismétlés beillesztésével. Átadandó: hibamagyarázat, javított kód, és egy ellenőrzési javaslat, amely az első és a későbbi hívások latency-jét külön kiírja.
F5 (kísérleti/transzfer, nehézség 3). Tervezzen
kísérletet a torch.set_num_threads hatására a fenti
mini-CNN-en. Rögzítse: kutatási kérdés, baseline (például 1 szál),
változó faktor (1, 2, 4, 8 szál), batchméret, ismétlésszám, metrika
(,
),
és az érvényességi korlátokat (hyperthreading, NUMA, más gép, GPU
hiánya). Átadandó: egy protokollterv és annak indoklása; előre megadott
győztes nem szükséges.
F1 megoldás. A batch latency a teljes elemű csomag feldolgozási ideje; a kérés, amelynek válasza a batch feldolgozása után készül el, -t vár. A per-item átlag egy statisztikai átlag: az összes elemre jutó idő, mintha az idő egyenletesen oszlana el; ez nem egyetlen elem válaszideje. Példa: , ms, akkor a per-item átlag ms. Egy felhasználói kérés, amely a 8 képet egy batchben várja, 20 ms-ot tapasztal. A 2,5 ms csak akkor lenne a kérés latency-je, ha minden egyes elem külön, azonnal választ kapna; a batch végrehajtás ezt nem garantálja.
F2 megoldás. Adott: s, s. A definíció szerint
Ha elem egységként, akkor elem/s, elem/s, batch/s, batch/s. Per-item átlagok: ms/elem, ms/elem.
Az azonosság bizonyítása: mivel , mindkét oldalt -vel szorozva és -vel osztva — , mert — kapjuk:
A megfigyelés nem mond ellent: ez a párhuzamosítás és a fix overhead miatt lehetséges, és nem szerepel az azonosság feltételei között. Az azonosság minden egyes -re külön-külön teljesül; a különböző -k közötti kapcsolatot nem írja elő.
F3 megoldás. A futtatás után a
latency_results.json mezőinek ellenőrzése:
import json, math
with open("latency_results.json", encoding="utf-8") as f:
d = json.load(f)
assert d["n_params"] == 2979
for row in d["batch_results"]:
B = row["B"]
mean = row["mean_latency_s"]
q = row["throughput_elem_per_s"]
assert math.isclose(q, B / mean, rel_tol=1e-6, abs_tol=1e-12), row
assert row["throughput_batch_per_s"] == 1.0 / mean
assert mean > 0A shape-assert a kódban a mérés előtt és után fut; ha a kód hiba
nélkül lefutott, az assert nem szakadt meg. A seed 20261020, a dtype
float32, a device CPU. A konkrét mért számok a hallgató gépétől függnek;
minden közölt számhoz tartoznia kell a machine,
python, torch, threads
mezőknek.
F4 megoldás. Az első hívás magában foglalja a modulbetöltés utáni első kernel-végrehajtást, az allocator feltöltődését, a thread pool inicializálását és esetleg a belső optimalizációt. Ezért az első latency jellemzően jóval nagyobb, mint a steady-state hívásoké; az átlagba vétele felfelé torzítja -t. Mivel , a torzított nagy alulbecsüli a steady-state throughputot. A javítás:
with torch.no_grad():
for _ in range(n_warm):
_ = model(x)
lat = []
with torch.no_grad():
for _ in range(n_rep):
t0 = time.perf_counter()
z = model(x)
t1 = time.perf_counter()
lat.append(t1 - t0)Ellenőrzési javaslat: futtassunk ismétlést warmup nélkül, és írjuk ki az első, a második és a többi hívást külön; ha az első medián, a warmup elhagyása kimutatható torzítást okoz.
F5 megoldás. Minta-protokoll. Kérdés: csökkenti-e a
szálszám növelése a
batch átlagos latency-jét a mini-CNN-en? Baseline: 1 szál, ugyanaz a
modell, ugyanaz a batchméret. Változó faktor:
torch.set_num_threads(t) a
értékekre, mindegyik beállítás előtt új mérési ciklus warmuppal.
Megfigyelési egység: egy forward hívás latency-je; ismétlésszám: 50,
warmup: 10. Metrika:
,
és
.
Előfeltétel: a batchméret rögzített, mert a szálhatás batchfüggő; a
processz affinity és a gépi terhelés rögzítendő. Érvényességi korlát: az
eredmény csak az adott CPU-ra, operátorkészletre és batchméretre
vonatkozik; hyperthreading esetén a 4 fizikai mag 8 logikai szálat
adhat, de a gyorsulás nem lineáris; NUMA-rendszeren más elrendezés más
eredményt adhat; GPU-ra nem vihető át.
Tipikus tévedés. A „batch throughput nem egyetlen felhasználói kérés késleltetése” hiba az inverz mértékegység összekeverése: kép/s értékből ms válaszidőt számolni hamis. Ellenpélda a 4. szakasz táblázata: , ms mellett kép/s, de a kérés latency-je 20 ms. A 2,5 ms a per-item átlag, nem az egyedi válaszidő. A hiba következménye: a szolgáltatási szint (SLO) alultervezése, mert a tényleges válaszidő a batch teljes feldolgozási ideje.
Önellenőrzés. (a) Mértékegység: elemszám osztva idővel, idő; szorzatuk , tehát a dimenzióban helyes. (b) Alakellenőrzés: a laborban a bemenet , a kimenet , és a batchméret ciklus minden értékére külön-külön érvényes. (c) A mérési ismétléseknek ugyanazt a seedelt inputot kell használniuk egy batchméreten belül, hogy a gyorsítótárazási és content-függő hatások ne változzanak; a seed rögzítése a protokoll része. (d) A percentilisek becslése legalább 50 ismétlésből stabilabb; a min/max önmagában nem jellemzi az eloszlást.
Visszahívás. A 33.1.
Inference-szerződés és modellcsomag inference-szerződésből: a mért
objektum nem csupán egy súlyfájl, hanem az architektúra, a
preprocessing, a dtype, a device és az eval-állapot együttese. A
laborban ezért a modellt eval() módba tesszük, a dtype-ot
és device-ot explicit rögzítjük. A 30.1. Pretext és
downstream feladat pretext/downstream megkülönböztetéséből: a mérés
a kiválasztott downstream modellre vonatkozik, nem az előtanítási célra.
A 22.1. Adateredet, címke és csoportos split
adateredetből: a mérési input szintetikus, mert a cél a számítási
teljesítmény elkülönítése az adatbetöltéstől; a GlyphLab képeit a
tanítási/tesztelési folyamatban használjuk, nem a sebességmérés
bemeneteként.
A kiszolgálási batch
elemű csomag, latency-je
,
a mért eloszlásból átlagot és percentiliseket számolunk. A throughput
elem/s, a batch-szintű ráta
;
a levezetett
azonosság a mérési definíciók konzisztenciája, nem állít linearitást
-ben.
A warmup kizárja az első hívások állandó többletköltségét, a CPU-szálak
számát rögzíteni kell, a memória pedig paraméter-, aktiváció- és
processz-RSS szinten különbözik. A labor saját
time.perf_counter protokollt ad; a hallgató a gépadatokkal
együtt közli a mért táblázatot. A batch throughput önmagában nem egyedi
kérésválaszidő.
Következő alkalmazás. A következő, 34.2. Kvantálás és hibatovábbterjedés egységben a kvantálást vizsgáljuk; az ottani gyorsulási állításokat ugyanezen a latency/throughput mérési kereten kell majd a numerikus eltéréssel együtt értékelni.