34.1 Mért késleltetés, throughput és memória

34.1.1 1. Cél, előismeret és induló kérdés

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.

34.1.2 2. Definíciók: batch, latency distribution, throughput, warmup, CPU threads, memory

Definíció (kiszolgálási batch). A kiszolgálási batch B∈ℕ+B\in\mathbb N_+ darab egyedből álló, együtt feldolgozott csomag. A bemenet X∈ℝB×Cin×H×WX\in\mathbb R^{B\times C_{in}\times H\times W}, a kimenet Z∈ℝB×KZ\in\mathbb R^{B\times K} logit. Ez ugyanaz a BB, mint a JELOLESEK.md NN dimenziója, de itt a jelentés fizikai: a kiszolgáló egyszerre BB képet dolgoz fel. A BB 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 TB∈ℝ+T_B\in\mathbb R_{+} egy BB 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 TBT_B 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 T‾B\bar T_B, a medián, a 90. percentilis p90p_{90} és a 99. percentilis p99p_{99} használatos. A p90p_{90} jelentése: a mért ismétlések 90%-ában a latency legfeljebb p90p_{90}. A percentilis empirikus becslés, nem a modell paramétere.

Definíció (throughput). A BB méretű batch steady-state átlagos feldolgozási rátája:

QB=BT‾B[elems],Q_B=\frac{B}{\bar T_B}\quad\left[\frac{\text{elem}}{\text{s}}\right],

ahol T‾B\bar T_B az átlagos batch latency. A batch-szintű throughput RB=1/T‾BR_B=1/\bar T_B, 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 nwarmn_{\mathrm{warm}} 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: ∑pnumel(p)⋅element_size(p)\sum_p \mathrm{numel}(p)\cdot \mathrm{element\_size}(p); ez BB-től független. (b) A forward aktivációk mérete: a közbülső tenzoralakok bájtjai, amelyek BB-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.

34.1.3 3. Levezetési kötelezettség: throughput és batch-latency kapcsolata

Állítás (throughput–latency konzisztencia). Legyen B∈ℕ+B\in\mathbb N_+ rögzített batchméret. Tegyük fel, hogy egy mérési protokoll nrepn_{\mathrm{rep}} ismétlést végez, minden ismétlésben a BB 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 TB(i)T_B^{(i)} az ii-edik ismétlés latency-jét másodpercben, és legyen T‾B=1nrep∑i=1nrepTB(i)>0\bar T_B=\frac1{n_{\mathrm{rep}}}\sum_{i=1}^{n_{\mathrm{rep}}}T_B^{(i)}>0. Ekkor

TB(i)=BQB(i),QB(i)=BTB(i)T_B^{(i)}=\frac{B}{Q_B^{(i)}},\qquad Q_B^{(i)}=\frac{B}{T_B^{(i)}}

minden ii-re, ahol QB(i)Q_B^{(i)} az ii-edik batch elemszintű pillanatnyi rátája, és

QB=BT‾B,RB=1T‾B.Q_B=\frac{B}{\bar T_B},\qquad R_B=\frac1{\bar T_B}.

Így a teljes mérésre: ha a teljes hasznos idő ∑iTB(i)\sum_i T_B^{(i)} és a feldolgozott elemszám B⋅nrepB\cdot n_{\mathrm{rep}}, akkor a teljes ráta

B⋅nrep∑iTB(i)=BT‾B=QB.\frac{B\cdot n_{\mathrm{rep}}}{\sum_i T_B^{(i)}}=\frac{B}{\bar T_B}=Q_B.

Bizonyítás. A pillanatnyi definíció: QB(i)Q_B^{(i)} az ii-edik batch elemszáma osztva a batch feldolgozási idejével, azaz B/TB(i)B/T_B^{(i)}. Átrendezve TB(i)=B/QB(i)T_B^{(i)}=B/Q_B^{(i)}. A protokoll szerint a batch-ek szekvenciálisan futnak, tehát a teljes feldolgozott elemszám B⋅nrepB\cdot n_{\mathrm{rep}}, a teljes idő pedig ∑iTB(i)\sum_i T_B^{(i)}. A teljes ráta definíció szerint a feldolgozott elemszám és a teljes idő hányadosa:

B⋅nrep∑i=1nrepTB(i)=B1nrep∑iTB(i)=BT‾B,\frac{B\cdot n_{\mathrm{rep}}}{\sum_{i=1}^{n_{\mathrm{rep}}}T_B^{(i)}}=\frac{B}{\frac1{n_{\mathrm{rep}}}\sum_i T_B^{(i)}}=\frac{B}{\bar T_B},

ami éppen QBQ_B. A batch-szintű ráta hasonlóan RB=1/T‾BR_B=1/\bar T_B. ▫\square

Fontos nemlinearitási megjegyzés. Az azonosság nem állítja, hogy TB=B⋅T1T_B=B\cdot T_1 vagy hogy QBQ_B független BB-től. A CPU-n végrehajtott mátrixműveletek párhuzamosítása és a memóriaelérés miatt tipikusan TB<B⋅T1T_B<B\cdot T_1, így QB>Q1Q_B>Q_1 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 TBT_B, nem pedig 1/QB1/Q_B.

34.1.4 4. Eredeti kézi kis példa, majd minimális ellenőrző kód

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:

BB T‾B\bar T_B [ms] QBQ_B [kép/s] RBR_B [batch/s] τ‾B=T‾B/B\bar\tau_B=\bar T_B/B [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: B=8B=8-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.

34.1.5 5. Konkrét labor: kis CPU-modell mérése több batchmérettel

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: X∈ℝB×1×24×24X\in\mathbb R^{B\times 1\times 24\times 24}, X∼𝒩(0,1)X\sim\mathcal N(0,1) 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.py

A kód először kiírja a paraméterszámot: a fenti modellben 8⋅9+88\cdot 9+8 (első conv) +16⋅8⋅9+16+16\cdot 8\cdot 9+16 (második conv) +16⋅36⋅3+3+16\cdot 36\cdot 3+3 (lineáris) =2979=2979 paraméter, tehát n_params == 2979. A JSON-sémában minden batch_results bejegyzésnek tartalmaznia kell a BB, 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:

BB T‾B\bar T_B [ms] p90p_{90} [ms] QBQ_B [elem/s] RBR_B [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 QBQ_B hogyan változik BB-vel, és hogy a beküldött számok konzisztensek-e a QB=B/T‾BQ_B=B/\bar T_B 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.

34.1.6 6. Feladatok

F1 (fogalmi, nehézség 1). Magyarázza el saját szavaival, mi a különbség a batch latency TBT_B és az elemenkénti átlagos latency T‾B/B\bar T_B/B 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 TB=12T_B=12 ms és T2B=18T_{2B}=18 ms ugyanarra a modellre, ugyanazon protokollal. Számítsa ki QBQ_B, Q2BQ_{2B}, RBR_B, R2BR_{2B}, valamint a per-item átlagokat. Bizonyítsa be a T2B=2B/Q2BT_{2B}=2B/Q_{2B} azonosságot, és indokolja, miért nem mond ellent az azonosságnak az a megfigyelés, hogy T2B<2TBT_{2B}<2T_B. Á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 nwarmn_{\mathrm{warm}} 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 (T‾B\bar T_B, QBQ_B), é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.

34.1.7 7. Megoldások és indoklás

F1 megoldás. A batch latency TBT_B a teljes BB elemű csomag feldolgozási ideje; a kérés, amelynek válasza a batch feldolgozása után készül el, TBT_B-t vár. A per-item átlag TB/BT_B/B egy statisztikai átlag: az összes elemre jutó idő, mintha az idő egyenletesen oszlana el; ez nem egyetlen elem válaszideje. Példa: B=8B=8, TB=20T_B=20 ms, akkor a per-item átlag 20/8=2,520/8=2{,}5 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: TB=0,012T_B=0{,}012 s, T2B=0,018T_{2B}=0{,}018 s. A definíció szerint

QB=BTB,Q2B=2BT2B,RB=1TB,R2B=1T2B.Q_B=\frac{B}{T_B},\qquad Q_{2B}=\frac{2B}{T_{2B}},\qquad R_B=\frac1{T_B},\qquad R_{2B}=\frac1{T_{2B}}.

Ha B=1B=1 elem egységként, akkor QB=1/0,012=83,33Q_B=1/0{,}012=83{,}33 elem/s, Q2B=2/0,018=111,11Q_{2B}=2/0{,}018=111{,}11 elem/s, RB=83,33R_B=83{,}33 batch/s, R2B=55,56R_{2B}=55{,}56 batch/s. Per-item átlagok: TB/B=12T_B/B=12 ms/elem, T2B/(2B)=9T_{2B}/(2B)=9 ms/elem.

Az azonosság bizonyítása: mivel Q2B=2B/T2BQ_{2B}=2B/T_{2B}, mindkét oldalt T2BT_{2B}-vel szorozva és Q2BQ_{2B}-vel osztva — Q2B>0Q_{2B}>0, mert T2B>0T_{2B}>0 — kapjuk:

T2B⋅Q2B=2B⟹T2B=2BQ2B.T_{2B}\cdot Q_{2B}=2B \implies T_{2B}=\frac{2B}{Q_{2B}}.

A T2B<2TBT_{2B}<2T_B 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 BB-re külön-külön teljesül; a különböző BB-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 > 0

A 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‾B\bar T_B-t. Mivel QB=B/T‾BQ_B=B/\bar T_B, a torzított nagy T‾B\bar T_B 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 nrep=10n_{\mathrm{rep}}=10 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ő >2×>2\times 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 B=8B=8 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 t∈{1,2,4,8}t\in\{1,2,4,8\} é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: T‾B\bar T_B, p90p_{90} és QBQ_B. 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.

34.1.8 8. Tipikus tévedés, önellenőrzés és korábbi fogalom visszahívása

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: QB=400Q_B=400 kép/s értékből 1/QB=2,51/Q_B=2{,}5 ms válaszidőt számolni hamis. Ellenpélda a 4. szakasz táblázata: B=8B=8, TB=20T_B=20 ms mellett QB=400Q_B=400 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: QBQ_B elemszám osztva idővel, TBT_B idő; szorzatuk BB, tehát a TB=B/QBT_B=B/Q_B dimenzióban helyes. (b) Alakellenőrzés: a laborban a bemenet (B,1,24,24)(B,1,24,24), a kimenet (B,3)(B,3), é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.

34.1.9 9. Rövid összefoglaló

A kiszolgálási batch BB elemű csomag, latency-je TBT_B, a mért eloszlásból átlagot és percentiliseket számolunk. A throughput QB=B/T‾BQ_B=B/\bar T_B elem/s, a batch-szintű ráta RB=1/T‾BR_B=1/\bar T_B; a levezetett TB=B/QBT_B=B/Q_B azonosság a mérési definíciók konzisztenciája, nem állít linearitást BB-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.

34.1.10 Felhasznált források