ResNet-50 max training batch: OpenNN vs PyTorch vs TensorFlow
This benchmark asks a capacity question, not a speed question: what is the largest fp32 training batch that completes one real ResNet-50/CIFAR-10 training step on the same GPU?
Each engine is run in a fresh process. A candidate batch counts only if it completes a warmup/capture step and one training step, reports a finite cross-entropy loss, exits cleanly, and stays within the configured physical VRAM limit. The runner finds the boundary by exponential growth followed by binary search, then records both the largest passing batch and the next failing batch.
The result
On this RTX 4080 run, TensorFlow XLA fits the largest batch, PyTorch torch.compile is second, and OpenNN with OPENNN_BATCH_POOL=1 is third. The OpenNN number below is the corrected BinaryFile-cache run; PyTorch and TensorFlow are from the same-machine full 3-way run because their protocol did not change.
| Engine | Max batch | Peak VRAM at max | Next batch | Result |
|---|---|---|---|---|
| OpenNN, BinaryFile cache, batch pool 1 | 4,752 | 15,865 MiB | 4,753 | failed |
| PyTorch torch.compile | 9,216 | 15,820 MiB | 9,217 | failed |
| PyTorch eager | 8,704 | 15,856 MiB | 8,705 | failed |
| TensorFlow XLA | 11,760 | 15,239 MiB | 11,761 | failed |
The headline is therefore:
| Comparison | Ratio |
|---|---|
| TensorFlow XLA vs OpenNN | 2.47x larger batch |
| PyTorch best vs OpenNN | 1.94x larger batch |
| TensorFlow XLA vs PyTorch best | 1.28x larger batch |
This is useful evidence, but it is not an OpenNN marketing win. It shows that for this exact ResNet-50/CIFAR-10 fp32 one-step capacity workload, TensorFlow’s XLA path and PyTorch’s compiled path fit larger batches on the same GPU than the current OpenNN implementation.
What is measured
| Item | Configuration |
|---|---|
| Dataset | CIFAR-10 geometry, 32x32x3 images, 10 classes |
| Network | ResNet-50 v1.5 bottleneck, stages 3-4-6-3 |
| Loss/optimizer | Cross-entropy, Adam, learning rate 0.001 |
| Precision | fp32 |
| Capacity rule | Largest batch that completes forward, backward, and Adam update |
| Search rule | Fresh process per candidate, exponential search plus binary search |
| VRAM rule | Physical GPU memory cap with 256 MiB reserved |
| OpenNN mode | ImageDataset BinaryFile cache, CUDA graph enabled, batch pool set to 1 |
The CIFAR-10 data is used for shape and labels. If a candidate batch is larger than the source dataset size, the Python frameworks repeat CIFAR-10 samples deterministically by modulo indexing. The OpenNN trial builds a temporary image tree of exactly the requested batch size from the same CIFAR-10 training images, lets ImageDataset build/read its BinaryFile image cache, and then runs a single full-batch training step. The trial does not enable OPENNN_GPU_RESIDENT_DATA, so the whole dataset is not staged as a GPU-resident matrix.
Machine and software
| Item | Value |
|---|---|
| GPU | NVIDIA GeForce RTX 4080 |
| Driver | 595.71.05 |
| GPU memory | 16,376 MiB |
| Python | 3.12.3 |
| PyTorch | 2.12.0+cu130 |
| PyTorch CUDA / cuDNN | 13.0 / 92000 |
| TensorFlow | 2.21.0 |
| TensorFlow build CUDA / cuDNN | 12.5.1 / 9 |
| CUDA nvcc | 13.3 |
| OpenNN commit | b7d4255ca23d |
| Full 3-way result JSON | results/gpu-resnet50-max-batch-cifar10-20260622T133809Z.json |
| OpenNN BinaryFile rerun JSON | results/gpu-resnet50-max-batch-cifar10-20260622T135753Z.json |
Why the result matters
The ResNet-50 training-speed benchmark measures throughput at a fixed batch. This benchmark measures a different limit: how much batch state each framework can hold while doing the real training work. That includes activations, gradients, optimizer state, framework workspaces, graph/capture overhead, and any allocator reserve that is needed for the step.
OpenNN’s current result fails just above 4,752 samples because cuDNN workspace allocation or graph capture runs out of usable device memory in the convolution path. PyTorch and TensorFlow both keep going to larger batches on this card. That makes this benchmark a good regression target for future OpenNN memory work: the runner can be rerun after changing convolution workspaces, graph capture strategy, batch buffering, or allocator behavior.
Caveats
- This is a one-step capacity benchmark, not a time-to-quality training result. It shows that a batch completes a real optimizer update with finite loss; it does not mean the largest batch is the best for model quality.
- TensorFlow reserves a large amount of GPU memory up front in this environment. The runner records the observed peak and still gates every candidate by the configured physical VRAM cap.
- OpenNN is reported with
OPENNN_BATCH_POOL=1because this benchmark is about capacity. The default prefetch pool helps throughput-oriented streaming workloads, but it holds extra batch buffers and lowers the maximum batch that fits. The dataset itself is not GPU-resident; it is read through theImageDatasetbinary cache path. - The test uses CIFAR-10 image geometry with a ResNet-50 model. At ImageNet resolution, the memory balance changes substantially.
- We measured the benchmark once on the current machine. Before using it as a public headline, repeat the run and store another JSON artifact.
Reproducing
Build the OpenNN trial target:
cmake --build build-gpu --target opennn_resnet50_maxbatch_trial
If your local GPU build directory is named build, use that directory name instead.
Run the full benchmark from the repository root:
cd docs/benchmarks/resnet50-max-batch
python run_resnet50_maxbatch.py \
--dataset cifar10 \
--precision fp32 \
--engines opennn,pytorch,tensorflow \
--gpu-index 0 \
--require-gpu-idle \
--start-batch 128
The runner writes immutable JSON artifacts under docs/benchmarks/results/. The run used for this note is gpu-resnet50-max-batch-cifar10-20260622T133809Z.json.