Repository navigation
Convert uint8 batches to float on the device when running on CUDA - #255
Merged
Merged
Conversation
PTDetector._process_batch_group() converted each batch to float32 and divided by 255 on the CPU, then copied it to the GPU. On CUDA devices, the uint8 tensor is now copied first: 4x fewer bytes, and a pass over every pixel moves off the CPU. The division uses a 0-dim tensor on the device rather than a Python float, because CUDA computes x / scalar as x * (1 / scalar), which differs from the CPU result in the last bit for 126 of the 256 pixel values; dividing by a device tensor gives exactly the CPU's float32 values. Other devices keep the existing code path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 17, 2026
Owner
|
Thanks! Merging via PR 259 (minor comment edits only, and merging main). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Speeds up GPU inference by converting uint8 → float32 on the device instead of the CPU; outputs are unchanged.
PTDetector._process_batch_group()converts each batch to float32 and divides by 255 on the CPU, then copies it to the device. On CUDA devices, this PR copies the uint8 tensor first and converts it on the GPU: the copy is 4x smaller, and the per-pixel conversion no longer runs on the CPU inside the inference loop.The division uses a 0-dim tensor on the device rather than the Python float
255.0. CUDA computesx / scalarasx * (1 / scalar), which differs from the CPU result in the last bit for 126 of the 256 pixel values; dividing by a device tensor is a true division and gives exactly the CPU's float32 values (checked for all 256 values). Other devices (CPU, MPS, DirectML) keep the existing code path, since I couldn't test them. If you can test on MPS or DirectML, a 256-entry lookup table computed on the CPU (lut[x.long()]) would be exact on any device.Measurements
MDv1000-redwood,
run_detector_batchCLI, 500 images from LILA (mixed resolutions), an NVIDIA GPU + 8 vCPU (Xeon Gold 6248R VM). Images/s excluding start-up (from runs on 100 and 500 images):--batch_size 8--use_image_queue --preprocess_on_image_queue, with #256--batch_size 8, with #256Single runs; run-to-run spread on this machine is a few percent. The gain is largest once the queue no longer limits throughput (#256).
Equivalence
The
imageslist of the output file is identical to main's for default and--batch_size 8, and for augmented inference (the md_tests augmentation step, 47 images; dividing by the Python float instead changed 5 of them in the last digit). With--use_image_queue --preprocess_on_image_queue --batch_size 8, main itself is not deterministic run to run (batches follow the order in which images arrive from the loaders; two runs of main differ on 22 of 500 images, confidence in the last digit, no box changes), and this PR differs from main by the same amount.Tests
Run on main and with all three of my PRs applied (#255, #256, #257), on the same machine:
pytest -vv: 24 passed, 4 skipped, on both.python -m megadetector.utils.md_tests --max_coord_error 0.01 --max_conf_error 0.01 --skip_download_tests --warning_mode(GPU, CPU, and video tests): all 35 steps run on both, and the comparison lines against the expected results are the same. 40 of the 41 result files are identical to main's; the remaining one (worker-side preprocessing with batched inference, the non-deterministic case above) differs on 1 of 47 images by 0.001 in confidence.--warning_modeis there because, independently of these PRs, md_tests fails on main at "Running MD on a folder of videos": DSCF0028.MP4 is reported with a confidence error of 0.667, although every box above 0.5 matches the expected results at IoU ≥ 0.998 with confidence within 0.021 (and the assertion message names two boxes with confidence 0.005). Presumably the frames decode slightly differently here: opencv-python 5.0.0 (FFmpeg avcodec 62.28.101), torch 2.14.1+cu130, NVIDIA driver 580.105.08, Ubuntu 22.04, Python 3.11.14.This came out of benchmarking
run_detector_batchend to end (related to the TODO item "Performance evaluation for batching, preprocessing"); happy to share the full numbers. Prepared with help from Claude Code; I reviewed the diff and the measurements.