Skip to content

simple_trainer: non_blocking batch copies, fewer loss.item() calls, uint8 images from the loader - #1087

Open
kleinicke wants to merge 2 commits into
nerfstudio-project:mainfrom
fastforks:pr/trainer-no-syncs-v2
Open

kleinicke wants to merge 2 commits into
nerfstudio-project:mainfrom
fastforks:pr/trainer-no-syncs-v2

Conversation

@kleinicke

Copy link
Copy Markdown
Contributor

Two small commits touching examples/simple_trainer.py and examples/datasets/colmap.py.

  1. Batch tensors are copied with non_blocking=True (the loader already pins memory) and loss.item() is only called every 10 steps for the progress bar. Only the progress-bar text refreshes less often; tensorboard scalars are still written every tb_every steps.
  2. Dataset returns the decoded uint8 image in the default (uncached) loader too, as the --cache_images path of [perf] 3DGS training (simple_trainer.py) on 8x B200: 13.9 ms to 9.8 ms per step, from decoding one image per step #1062 already does; the trainer converts to float on the GPU, so the host-to-device copy is 4x smaller. Pixel values are identical. All consumers in the repo (simple_trainer, simple_trainer_2dgs, the val loop) divide by 255.0, and colmap.py's __main__ casts to uint8; third-party code that assumed float32 from Dataset would need .float().

garden/4, 7k steps, default flags, seed 42, RTX 5060 Ti, 5 runs each, interleaved:

train time (median, min-max) PSNR (mean ± sd) peak allocated
main 221.4 s (220.0-226.3) 26.22 ± 0.04 3.93 GB
commit 1 218.2 s (216.5-219.8) 26.25 ± 0.02 3.93 GB
commits 1+2 209.4 s (207.1-211.3) 26.24 ± 0.03 3.94 GB

So commit 1 alone is a small gain (~1.5 %, close to the run-to-run spread) and most of the ~6 % total comes from commit 2. PSNR differences are within run-to-run noise. Rasterization still synchronizes once per step (Intersect.cpp reads the intersection count on the host), so this only removes the blocking copies and the per-step item() calls.

kleinicke and others added 2 commits October 14, 2026 02:45
The batch comes from a pin_memory loader, so the host-to-device copies can be
non_blocking instead of waiting for the previous step's GPU work. loss.item()
(and the depth / pose error items) only runs every 10 steps for the progress
bar; tensorboard scalars are unaffected.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The default (uncached) path converted the image to float32 in the DataLoader
worker, so the host-to-device copy moved 4x the bytes. Return the decoded
image as the cached path of nerfstudio-project#1062 already does; simple_trainer converts to
float on the device. Every consumer in the repo divides by 255.0 (simple_trainer,
simple_trainer_2dgs, the val loop) or casts back to uint8 (colmap.py __main__).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant