Denne artikkelen er basert pÃĨ funn fra en kernel-nivÃĨ GPU-sporing undersÃļkelse utfÃļrt pÃĨ en ekte PyTorch-problem (#154318) ved hjelp av eBPF uprobes. Spordatabaser er publisert i Ingero ÃĨpen kilde-repositorium for uavhengig verifisering.
TL;DR
PyTorchs DataLoader kan vÃĶre 50-124x langsommere enn direkte tensor-indeksning for arbeidsbelastninger i minnet pÃĨ GPU. Vi gjentok et ekte PyTorch-problem pÃĨ en RTX 4090 og sporet hver CUDA API-kall og Linux-kernel-hendelse for ÃĨ finne ÃĨrsaken. GPU-en var ikke langsom â den sultet. DataLoader-arbeidere genererte 200 000 CPU-kontekstskifter og 300 000 sideallokeringer pÃĨ 40 sekunder, og etterlot GPU-en ventende i gjennomsnitt 301 ms per datatransfer som skulle ta mikrosekunder.
Problemet
En PyTorch-bruker rapporterte at DataLoader var 7-22x langsommere enn direkte tensor-indeksning for en enkel MLP-inferensarbeidsbelastning. Selv med num_workers=12, pin_memory=True og prefetch_factor=12, forble gapet enormt. GPU-utnyttelse satt pÃĨ 10-20%.
Vi gjentok det. Gapet var enda verre pÃĨ vÃĨr maskinvare:
| Metode | Tid | vs Direkte |
|---|---|---|
| Direkte tensor-indeksning | 0,39 s | 1x |
| DataLoader (shuffle=True) | 48,49 s | 124x langsommere |
| DataLoader (optimalisert, 4 arbeidere, pin_memory) | 43,29 s | 111x langsommere |
Arbeidsbelastningen er trivial: 7M eksempler, 100 funksjoner, 2-lags MLP, batch-stÃļrrelse 1M. Modellen prosesserer en batch pÃĨ millisekunder. SÃĨ hvor gÃĨr tiden?
Hva nvidia-smi viser
Ingenting nyttig. GPU-utnyttelse blinker mellom 0% og 30%. Minnebruk er stabilt. Temperatur er fin. GPU-en er tydelig underutnyttet, men nvidia-smi kan ikke fortelle hvorfor.
Hva torch.profiler viser
Rapporten prÃļvde PyTorchs innebygde profiler og âfikk ingen meningsfulle spor-data.â Dette er en vanlig frustrasjon â applikasjonsnivÃĨ-profiler kan vise deg hvilke CUDA-kjerner som kjÃļrer, men de kan ikke se host-siden scheduling, minne og prosess-livslÃļpshendelser som bestemmer om data ankommer pÃĨ GPU pÃĨ tide.
Hva kernel-nivÃĨ-sporing viser
Vi kjÃļrte benchmarket mens vi sporet bÃĨde CUDA API-kall (via eBPF uprobes pÃĨ libcudart.so) og Linux-kernel-hendelser (scheduler-kontekstskifter, minneside-allokeringer, prosess-fork) samtidig. Resultatene forteller den komplette historien.
Full video-gjennomgang: https://asciinema.org/a/RGwhPeXAPJdhXqxp
I videoen koblet vi en ÃĨpen-vekt LLM (MiniMax-M2.7) til spordatabasen via MCP (Model Context Protocol):
ollmcp -m minimax-m2.7:cloud -j /tmp/ingero-mcp-dataloader.jsonJSON-konfigurasjonen forteller MCP-klienten hvor den skal finne Ingero-serveren og hvilken spordatabase som skal lastes:
{
"mcpServers": {
"ingero": {
"command": "./bin/ingero",
"args": ["mcp", "--db", "investigations/pytorch-dataloader-starvation.db"]
}
}
}
Dette gir LLM direkte tilgang til spor-data gjennom 7 verktÃļy: get_trace_stats, get_causal_chains, get_per_process_breakdown og andre. AI-en kan spÃļrre databasen, korrelere CUDA-hendelser med kernel-scheduling-data og produsere en ren-sprÃĨklig diagnose uten noen manuell analyse.
4 HIGH-severity ÃĨrsakskjeder
à rsakskjede-motoren detekterte 4 high-severity-mÃļnster, alle med samme rotÃĨrsak:
[HIGH] cudaStreamSync p99=42ms (1,638x p50=25us) - CPU 100% + 1,880 sched_switch events Tidslinje: [SYSTEM] CPU 100% [HOST ] 1,880 kontekstskifter (21s off-CPU) [CUDA ] p99=42ms (1,638x p50=25us) RotÃĨrsak: DataLoader-arbeidere kjemper om CPU, massiv sideallokeringstrykk
[HIGH] cudaLaunchKernel p99=24.67ms (349x p50=70us) - CPU 100% Rot: 34 sched_switch events [HIGH] cuMemAlloc p99=627us (4.0x p50) - CPU 100% [HIGH] cuLaunchKernel p99=106us (4.0x p50) - CPU 100%
cudaStreamSync p99 er 1,638 ganger p50. Det er ikke GPU-lengsel â det er GPU-en som venter pÃĨ data som aldri ankommer pÃĨ tide.
Figur 1: AI-generert analyse etter ÃĨ ha kjÃļrt /investigate. Modellen brukte Ingeros 7 MCP-verktÃļy til ÃĨ spÃļrre spordatabasen og produserte en ren-sprÃĨklig forklaring med handlebarske anbefalinger direkte fra spor-data.

Prosessen per-prosess
Dette er hvor det blir tydelig. Hovedprosessen og dens 4 DataLoader-arbeidere er synlige som separate enheter:
Hovedprosess:
- cudaMemcpyAsync (host-til-enhet-overfÃļring): gjennomsnitt 301ms, maks 2,9 sekunder - cudaStreamSync: p99 = 42ms (normalt 25us) - 1,567 kontekstskifter, gjennomsnitt 16ms off-CPU, verst stall 5 sekunder - 799,018 sideallokeringer
DataLoader-arbeider 1: 52,863 kontekstskifter, 89,338 sideallokeringer, verst stall 5s DataLoader-arbeider 2: 50,638 kontekstskifter, 83,509 sideallokeringer, verst stall 5s DataLoader-arbeider 3: 49,361 kontekstskifter, 70,035 sideallokeringer, verst stall 5s DataLoader-arbeider 4: 38,862 kontekstskifter, 56,354 sideallokeringer, verst stall 5s
Totalt over arbeidere: ~191 000 kontekstskifter og ~299 000 sideallokeringer pÃĨ 40 sekunder.
Hva dette betyr
DataLoader-arbeiderne gjÃļr tre dyre ting som direkte indeksning unngÃĨr helt:
- Shuffling og indeksning: DataLoader med shuffle=True genererer en tilfeldig permutasjon av indekser, sÃĨ hver arbeider velger sin del. Dette krever tilfeldig minneaksess over hele 7M-eksemplaret â dÃĨrlig for cache-lokalitet og utlÃļser sidefeil.
- Kollasjon og kopiering: Hver arbeider samler spredte eksempler i en sammenhengende batch-tensor. Dette innebÃĶrer ÃĨ allokere ny minne (sideallokeringer), kopiere data fra tilfeldige lokasjoner (cache-miss), og serialisere resultatet tilbake til hovedprosessen via delt minne eller en kÃļ.
- Konkurrerende om CPU: Fire arbeidere + hovedprosessen pÃĨ en 4-vCPU-maskin betyr konstant forhÃĨndtering. Hver arbeider blir descheduled 50 000 ganger. Verst-case-stall er 5 sekunder â under hvilken GPU-en har ingenting ÃĨ prosessere.
Med direkte indeksning: X[i:i+batch_size] er en null-kopi-utsikt over en sammenhengende tensor allerede i minnet. .to(device) utlÃļser en enkelt DMA-overfÃļring fra en enkelt sammenhengende region. Ingen arbeidere, ingen shuffling, ingen kollasjon, ingen cross-prosess-kopier, ingen kontekstskifter. GPU-en fÃĨr data pÃĨ mikrosekunder, ikke hundredvis av millisekunder.
LÃļsningen
For arbeidsbelastninger i minnet pÃĨ GPU hvor hele datasettet passer i RAM:
- Bruk ikke DataLoader. Direkte indeksning med en forhÃĨnds-shufflet indeks-array er enklere og 100x raskere:
indekser = torch.randperm(num_samples) for i in range(0, num_samples, batch_size): batch = X[indekser[i:i+batch_size]].to(device) output = model(batch)
- Hvis du mÃĨ bruke DataLoader, match num_workers med dine faktiske CPU-kjerner minus 1. PÃĨ en 4-kjerne-maskin, num_workers=2 reduserer konflikten. Legg til persistent_workers=True for ÃĨ unngÃĨ fork-overhead.
- For stÃļrre enn minne-datasett hvor DataLoader er nÃļdvendig, flytter den virkelige flaskehalen til disk-I/O. Bruk prefetch_factor=2 (ikke hÃļyere â mer prefetching betyr mer minne-trykk) og sikre at ditt lagringsutstyr kan holde pace.
Det stÃļrre bildet
Denne undersÃļkelsen illustrerer en mÃļnster vi ser konstant i GPU-arbeidsbelastninger: GPU-en er rask, hosten er flaskehalen, og GPU-mÃĨlinger kan ikke se det. nvidia-smi rapporterte lav utnyttelse, men kunne ikke forklare hvorfor. torch.profiler fanget CUDA-kjerner, men gikk glipp av de 200 000 kontekstskiftene som skjedde i brukerrommet.
Den eneste mÃĨten ÃĨ se det fullstendige bildet var ÃĨ spore bÃĨde sider samtidig â CUDA API-kall pÃĨ biblioteknivÃĨ og Linux-kernel-scheduling-hendelser â og korrelere dem med tid og prosess-ID. Ã rsakskjeden âCPU 100% -> 1,880 sched_switch -> cudaMemcpyAsync 301ms -> cudaStreamSync 42msâ forteller den komplette historien pÃĨ ÃĐn linje. Uten cross-stack-sporing ville dette ha forblet et mysterium â som det var for den opprinnelige rapportÃļren som brukte uker pÃĨ ÃĨ feilsÃļke det.
Figur 2: NÃĨr du blir bedt om âhva er det grunnleggende problemet?â, identifiserer modellen CPU-overskripping som ÃĨrsak til host-siden-scheduling-forsinkelser. cudaLaunchKernel gikk fra 73us til 25,8ms (356x langsommere) fordi CPU-en ikke kunne planlegge lanseringen i tide.

PrÃļv det selv
Reproduser 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()
# Rask vei 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 vei 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 ÃĨ se hva som skjer under panseret:
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 annen terminal ./bin/ingero explain --since 60s # etter benchmark er fullfÃļrt
Eller hopp over reproduksjonen og utforsk vÃĨre spor-data direkte. UndersÃļkelsesdatabasen (764KB) er i repoet:
# Vis ÃĨrsakskjeder fra undersÃļkelsen ./bin/ingero explain --db investigations/pytorch-dataloader-starvation.db --since 5m# Per-prosess-breakdown (se DataLoader-arbeidere vs hovedprosess) ./bin/ingero explain --db investigations/pytorch-dataloader-starvation.db --per-process --since 5m
# Kobler din AI-assistent for interaktiv undersÃļkelse ./bin/ingero mcp --db investigations/pytorch-dataloader-starvation.db
UndersÃļk med AI (anbefalt). Den raskeste mÃĨten ÃĨ analysere sporet er ÃĨ koble en MCP-kompatibel AI direkte til databasen. Ingen manuell analyse nÃļdvendig.
Lag en konfigurasjonsfil:
cat > /tmp/ingero-mcp-dataloader.json << 'EOF'
{
"mcpServers": {
"ingero": {
"command": "./bin/ingero",
"args": ["mcp", "--db", "investigations/pytorch-dataloader-starvation.db"]
}
}
}
EOF
SÃĨ kobler du modellen din:
# Med Ollama + MiniMax (hva vi brukte 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 en hvilken som helst MCP-kompatibel klient # Legg til konfigurasjonen over i MCP-innstillingene dine
Skriv /investigate for ÃĨ utlÃļse guidet analyse, eller spÃļr noen spÃļrsmÃĨl: âHva forÃĨrsaket GPU-sulten?â AI-en har tilgang til 7 verktÃļy som spÃļr databasen direkte.
GitHub: github.com/ingero-io/ingero
Opprinnelig problem: pytorch/pytorch#154318
Video-gjennomgang: https://asciinema.org/a/RGwhPeXAPJdhXqxp
UndersÃļkelse utfÃļrt pÃĨ TensorDock RTX 4090 (24GB), Ubuntu 22.04, PyTorch 2.10.0+cu128.













