Denne artikel er baseret pÃĨ fund fra en kernel-niveau GPU-sporingsundersÃļgelse udfÃļrt pÃĨ et rigtigt PyTorch-problem (#154318) ved hjÃĶlp af eBPF uprobes. Spordatabaser er offentliggjort i Ingero open-source-repositoriet til uafhÃĶngig verificering.
TL;DR
PyTorchâs DataLoader kan vÃĶre 50-124 gange langsommere end direkte tensor-indexering for in-memory GPU-arbejdslaster. Vi genskabte et rigtigt PyTorch-problem pÃĨ en RTX 4090 og spored hver CUDA-API-opkald og Linux-kernel-hÃĶndelse for at finde rodÃĨrsagen. GPUâen var ikke langsom â den sultede. DataLoader-arbejdere genererede 200.000 CPU-kontekstskift og 300.000 sideallokeringer i 40 sekunder, hvilket fik GPUâen til at vente i gennemsnit 301 ms pr. dataoverfÃļrsel, der burde tage mikrosekunder.
Problemet
En PyTorch-bruger rapporterede, at DataLoader var 7-22 gange langsommere end direkte tensor-indexering for en simpel MLP-inferensarbejdslast. Selv med num_workers=12, pin_memory=True og prefetch_factor=12, forblev gapet massivt. GPU-udnyttelse lÃĨ pÃĨ 10-20%.
Vi genskabte det. Gapet var endda vÃĶrre pÃĨ vores hardware:
| Metode | Tid | vs Direkte |
|---|---|---|
| Direkte tensor-indexering | 0,39 s | 1x |
| DataLoader (shuffle=True) | 48,49 s | 124 gange langsommere |
| DataLoader (optimeret, 4 arbejdere, pin_memory) | 43,29 s | 111 gange langsommere |
Arbejdslasten er trivial: 7M eksempler, 100 funktioner, 2-lagdelt MLP, batch-stÃļrrelse 1M. Modellen behandler en batch pÃĨ fÃĨ millisekunder. SÃĨ hvor gÃĨr tiden hen?
Hvad nvidia-smi viser
Ingenting nyttigt. GPU-udnyttelse flimrer mellem 0% og 30%. Hukommelsesbrug er stabil. Temperatur er fin. GPUâen er tydelig underudnyttet, men nvidia-smi kan ikke fortÃĶlle, hvorfor.
Hvad torch.profiler viser
Rapporteren prÃļvede PyTorchâs indbyggede profiler og âfik ingen meningsfulde sporfiler.â Dette er en almindelig frustration â applikationsniveau-profilere kan vise, hvilke CUDA-kerner der kÃļrer, men de kan ikke se host-siden scheduling, hukommelse og proces-livscyklus-hÃĶndelser, der bestemmer, om data ankommer til GPUâen pÃĨ tid.
Hvad kernel-niveau-sporing viser
Vi kÃļrte benchmarket, mens vi spored bÃĨde CUDA-API-opkald (via eBPF uprobes pÃĨ libcudart.so) og Linux-kernel-hÃĶndelser (scheduler-kontekstskift, hukommelsesside-allokeringer, proces-fork) samtidigt. Resultaterne fortÃĶller den komplette historie.
Full video-gennemgang: https://asciinema.org/a/RGwhPeXAPJdhXqxp
I videoen forbinder vi en ÃĨben-vÃĶgt LLM (MiniMax-M2.7) til spordatabasen via MCP (Model Context Protocol):
ollmcp -m minimax-m2.7:cloud -j /tmp/ingero-mcp-dataloader.jsonJSON-konfigurationen fortÃĶller MCP-klienten, hvor den kan finde Ingero-serveren og hvilken spordatabase, der skal indlÃĶses:
{
"mcpServers": {
"ingero": {
"command": "./bin/ingero",
"args": ["mcp", "--db", "investigations/pytorch-dataloader-starvation.db"]
}
}
}
Dette giver LLM direkte adgang til sporfilerne gennem 7 vÃĶrktÃļjer: get_trace_stats, get_causal_chains, get_per_process_breakdown og andre. AIâen kan forespÃļrge databasen, korrelerer CUDA-hÃĶndelser med kernel-scheduling-data og producerer en plain-language-diagnose uden nogen manuel analyse.
4 HIGH-severity ÃĨrsagskÃĶder
à rsagskÃĶde-motoren detekterede 4 high-severity-mÃļnstre, alle med samme rodÃĨrsag:
[HIGH] cudaStreamSync p99=42ms (1,638x p50=25us) - CPU 100% + 1,880 sched_switch-hÃĶndelser Tidslinje: [SYSTEM] CPU 100% [HOST ] 1,880 kontekstskift (21s off-CPU) [CUDA ] p99=42ms (1,638x p50=25us) RodÃĨrsag: DataLoader-arbejdere kÃĶmper om CPU, massiv sideallokeringstryk
[HIGH] cudaLaunchKernel p99=24.67ms (349x p50=70us) - CPU 100% Rod: 34 sched_switch-hÃĶndelser [HIGH] cuMemAlloc p99=627us (4.0x p50) - CPU 100% [HIGH] cuLaunchKernel p99=106us (4.0x p50) - CPU 100%
cudaStreamSync p99 er 1.638 gange p50. Det er ikke GPU-langsomhed â det er GPUâen, der venter pÃĨ data, der aldrig ankommer pÃĨ tid.
Figur 1: AI-genereret analyse efter kÃļrsel af /investigate. Modellen brugte Ingeroâs 7 MCP-vÃĶrktÃļjer til at forespÃļrge spordatabasen og producerede en plain-language-forklaring med handlelige anbefalinger, der er direkte afledt fra sporfilerne.

Den pr. proces-breakdown
Dette er, hvor det bliver klart. Hovedprocessen og dens 4 DataLoader-arbejdere er synlige som separate enheder:
Hovedprocessen:
- cudaMemcpyAsync (host-to-device-overfÃļrsel): gennemsnit 301ms, maks 2,9 sekunder - cudaStreamSync: p99 = 42ms (normalt 25us) - 1,567 kontekstskift, gennemsnit 16ms off-CPU, vÃĶrst stall 5 sekunder - 799,018 sideallokeringer
DataLoader-arbejder 1: 52,863 kontekstskift, 89,338 sideallokeringer, vÃĶrst stall 5s DataLoader-arbejder 2: 50,638 kontekstskift, 83,509 sideallokeringer, vÃĶrst stall 5s DataLoader-arbejder 3: 49,361 kontekstskift, 70,035 sideallokeringer, vÃĶrst stall 5s DataLoader-arbejder 4: 38,862 kontekstskift, 56,354 sideallokeringer, vÃĶrst stall 5s
I alt over arbejdere: ~191.000 kontekstskift og ~299.000 sideallokeringer i 40 sekunder.
Hvad dette betyder
DataLoader-arbejderne gÃļr tre dyre ting, som direkte indexering undgÃĨr helt:
- Shuffling og indexering: DataLoader med shuffle=True genererer en tilfÃĶldig permutation af indeks, derefter vÃĶlger hver arbejder sin del. Dette krÃĶver tilfÃĶldig hukommelsesadgang pÃĨ tvÃĶrs af den fulde 7M-eksemplar-tensor â frygteligt for cache-lokalitet og udlÃļser sidefejl.
- Collation og kopiering: Hver arbejder samler spredte eksempler i en kontinuert batch-tensor. Dette betyder at allokerer ny hukommelse (sideallokeringer), kopiere data fra tilfÃĶldige placeringer (cache-miss), og serialisere resultatet tilbage til hovedprocessen via delt hukommelse eller en kÃļ.
- Konkurrerende om CPU: Fire arbejdere + hovedprocessen pÃĨ en 4-vCPU-maskine betyder konstant preempt. Hver arbejder bliver afbrudt 50.000 gange. Den vÃĶrste stall er 5 sekunder â under hvilken GPUâen intet har at proces.
Med direkte indexering: X[i:i+batch_size] er en zero-copy-visning af en kontinuert tensor, der allerede er i hukommelse. .to(device) udlÃļser en enkelt DMA-overfÃļrsel fra en enkelt kontinuert region. Ingen arbejdere, ingen shuffling, ingen collation, ingen cross-process-kopier, ingen kontekstskift. GPUâen fÃĨr data pÃĨ mikrosekunder, ikke hundredvis af millisekunder.
LÃļsningen
Til in-memory GPU-arbejdslaster, hvor hele datasettet passer i RAM:
- Brug ikke DataLoader. Direkte indexering med en forhÃĨndsshufflet indeksarray er simpelt og 100 gange hurtigere:
indices = torch.randperm(num_samples) for i in range(0, num_samples, batch_size): batch = X[indices[i:i+batch_size]].to(device) output = model(batch)
- Hvis du mÃĨ bruge DataLoader, match num_workers til dine faktiske CPU-kerner minus 1. PÃĨ en 4-kernet maskine, num_workers=2 reducerer konkurrence. TilfÃļj persistent_workers=True for at undgÃĨ fork-overhead.
- Til stÃļrre-end-hukommelse-datasÃĶt hvor DataLoader er nÃļdvendig, skifter den virkelige flaskehals til disk-I/O. Brug prefetch_factor=2 (ikke hÃļjere â mere prefetching betyder mere hukommelsespres) og sikr, at din lagring kan fÃļlge med.
Det stÃļrre billede
Denne undersÃļgelse illustrerer en mÃļnster, vi ser konstant i GPU-arbejdslaster: GPUâen er hurtig, hosten er flaskehalsen, og GPU-mÃĨlinger kan ikke se det. nvidia-smi rapporterede lav udnyttelse, men kunne ikke forklare, hvorfor. torch.profiler fanget CUDA-kerner, men missede de 200.000 kontekstskift, der skete i brugerens rum.
Den eneste mÃĨde at se det fulde billede var at spore bÃĨde sider samtidigt â CUDA-API-opkald pÃĨ biblioteks-niveau og Linux-kernel-scheduling-hÃĶndelser â og korrelerer dem efter tid og proces-ID. Ã rsagskÃĶden âCPU 100% -> 1,880 sched_switch -> cudaMemcpyAsync 301ms -> cudaStreamSync 42msâ fortÃĶller den komplette historie pÃĨ en linje. Uden cross-stack-sporing ville dette vÃĶre blevet en mysterium â som det var for den oprindelige rapport, der tilbragte uger med at fejlfinde det.
Figur 2: NÃĨr man beder om âhvad er det centrale problem?â, identificerer modellen CPU-over-subscription, der forÃĨrsager host-side-scheduling-forsinkelser. cudaLaunchKernel gik fra 73us til 25,8ms (356 gange langsommere), fordi CPUâen ikke kunne planlÃĶgge lanceringen pÃĨ tid.

PrÃļv det selv
Reproducer benchmarket:
import torch, time from torch.utils.data import DataLoaderX = torch.randn(7_000_000, 100) model = torch.nn.Sequential( torch.nn.Linear(100, 512), torch.nn.ReLU(), torch.nn.Linear(512, 512), torch.nn.ReLU(), torch.nn.Linear(512, 10) ).cuda()
# Hurtig sti start = time.time() with torch.no_grad(): for i in range(0, len(X), 1_048_576): model(X[i:i+1_048_576].cuda()) torch.cuda.synchronize() print(f'Direkte: {time.time()-start:.3f}s')
# Langsom sti loader = DataLoader(X, batch_size=1_048_576, shuffle=True) start = time.time() with torch.no_grad(): for batch in loader: model(batch.cuda()) torch.cuda.synchronize() print(f'DataLoader: {time.time()-start:.3f}s')
Spor med Ingero for at se, hvad der sker under kÃļrsel:
git clone https://github.com/ingero-io/ingero.git cd ingero && make build sudo ./bin/ingero trace --duration 60s # i en terminal python3 benchmark.py # i en anden terminal ./bin/ingero explain --since 60s # efter benchmark er fÃĶrdig
Eller spring reproduction over og udforsk vores spordata direkte. UndersÃļgelsesdatabasen (764KB) er i repositoriet:
# Vis ÃĨrsagskÃĶder fra undersÃļgelsen ./bin/ingero explain --db investigations/pytorch-dataloader-starvation.db --since 5m# Pr. proces-breakdown (se DataLoader-arbejdere vs hovedprocessen) ./bin/ingero explain --db investigations/pytorch-dataloader-starvation.db --per-process --since 5m
# Forbind din AI-assistent til interaktiv undersÃļgelse ./bin/ingero mcp --db investigations/pytorch-dataloader-starvation.db
UndersÃļg med AI (anbefales). Den hurtigste mÃĨde at analysere sporfilerne er at forbinde enhver MCP-kompatibel AI direkte til databasen. Ingen manuel analyse nÃļdvendig.
Opret en konfigurationsfil:
cat > /tmp/ingero-mcp-dataloader.json << 'EOF'
{
"mcpServers": {
"ingero": {
"command": "./bin/ingero",
"args": ["mcp", "--db", "investigations/pytorch-dataloader-starvation.db"]
}
}
}
EOF
SÃĨ forbind din model:
# Med Ollama + MiniMax (hvad vi brugte i videoen) ollmcp -m minimax-m2.7:cloud -j /tmp/ingero-mcp-dataloader.json# Med Claude Code claude --mcp-config /tmp/ingero-mcp-dataloader.json
# Med enhver MCP-kompatibel klient # TilfÃļj konfigurationen ovenfor til din AI's MCP-indstillinger
Skriv /investigate for at udlÃļse den guidede analyse eller still et spÃļrgsmÃĨl: âHvad forÃĨrsagede GPU-sulten?â AIâen har adgang til 7 vÃĶrktÃļjer, der forespÃļrger spordatabasen direkte.
GitHub: github.com/ingero-io/ingero
Original issue: pytorch/pytorch#154318
Video-gennemgang: https://asciinema.org/a/RGwhPeXAPJdhXqxp
UndersÃļgelse udfÃļrt pÃĨ TensorDock RTX 4090 (24GB), Ubuntu 22.04, PyTorch 2.10.0+cu128.













