r/pytorch • u/AlternativeCUDA101 • 6h ago
Differentiable simulations, one per sample, on the GPU, feeding PyTorch without writing them as tensors
Putting physics in a training loop often means running many small simulations that end at different times. Written as tensor code, every simulation keeps running until the last one finishes.
hawk lets you write the step for one sample as a typed Python function. It compiles it for the GPU and the CPU, and gives you its forward and reverse derivatives. eagle then runs a million of them and drops the ones that finish. The result plugs into torch.autograd, and tensors move to and from PyTorch without copies.
It is deliberately not a tensor framework: no layers, no training stack. PyTorch keeps doing that part. With a million samples in double precision that stop at different steps, the simulation part runs 9.8× to 130× faster than the same step written for torch.compile (130× on an H100, 11× on a free Colab T4), and 2.5× to 4.2× faster than an NVIDIA Warp kernel. JAX support is being certified for one of the next patch releases, and I have not benchmarked Triton yet.
Personal project; I work at ESA. There is a notebook that simulates, takes derivatives, and fits a drag coefficient with PyTorch: https://amasat01.github.io
r/pytorch • u/InternationalSlice72 • 15h ago
Tensor Golf - Master PyTorch tensor operations via focused drills
Hey everyone, I’ve just released Tensor Golf: a collection of focused PyTorch challenges for getting comfortable manipulating tensors.
I found that following ML tutorials and writing tensor code from scratch were two very different things. Understanding a model’s architecture doesn’t necessarily mean you can confidently reshape, broadcast, mask, or reduce its tensors.
Tensor Golf gives you an input tensor and a target output. Your job is to write the operations that transform one into the other.
The “golf” part is trying to solve problems in fewer operations, but the main goal is building fluency: recognising which operations fit a problem and understanding why they work.
There are currently many PyTorch problems all free on the platform, with some premium ones on the way to support the development of the platform :)
I'd love feedback from people learning or working with PyTorch.
link: https://tensor.golf
r/pytorch • u/mistrjirka • 1d ago
Pytorch AMD XDNA2 (ryzen AI processors in new laptops) inference and training backend
Introducting pytest-torch!
I built pytest-torch to make testing PyTorch models easier
Writing the same device setup, reproducibility checks, and output comparisons across projects gets repetitive, so I built pytest-torch, an open-source pytest plugin.
It includes inference checks, compile/export parity, checkpoint tests, accuracy evaluation, and layer-by-layer diagnostics to help locate where two models diverge.
It’s currently in alpha, and I’d appreciate feedback—especially examples of model bugs that are frustrating to test or reproduce.
GitHub: https://github.com/AKJ7/pytest-torch
Thanks and please drop a star if possible.
r/pytorch • u/Frequent_Big7927 • 2d ago
PyTorches Update
Continuing my proof of concept which is a reimagining of pytorch, but based on feedback I changed the "pytorches" implementation to split every vendor optimization to an external project. This allows the base implementation to be rather small and lightweight, but allow for flexibility to support multiple hardware vendors where only part centrally configured is basic device ID information and references to the supporting library. This type of approach has pros and cons, my intention is to unlock the pros, and minimize the cons.
My main interest, was to see if it would be possible to re-slice the functionality differently to see if there is some gains to be had on interop between devices. In order to prove this might work, I first focused on XPU and CUDA to see if I could make them first individually perform near in the ballpark of pytorch with respect to my implementation (1.0 is exact the same, a larger number means my implementation is faster.
Intel Arc 140T (before → after)
- matmul 2048²: 0.53x → 0.80x (up to 1.12x)
- matmul 4096²: 0.55x → 0.75–0.94x
- MLP train step: 0.55x → 0.84–0.87x
- add (64M floats): 0.83x → 1.04x
- exp: 0.77x → 0.96x
- relu: 0.74x → 0.98x
- tanh: 0.69x → 0.96x
- 1000×1000 matmul: 0.35x → 0.50x
NVIDIA RTX PRO 1000 (before → after)
- transpose copy 4096²: 0.56x → 0.93x
- bias-style broadcast add: 0.70x → 1.12x
- matmul A@Bᵀ (256×2048): 0.86x → 1.33x
- matmul 2048² / 4096²: 0.98–1.02x → 0.99–1.13x
- MLP train step: 0.95x → 0.91–0.96x (unchanged, within noise)
While these numbers are in some cases better than pytorch, I'm more interested in getting as close to pytorch performance before I explore the next phase of splitting layers.
r/pytorch • u/SentakuYujo • 2d ago
PyTorch CNN-BiGRU with CTC Loss for WLASL Sign Language Recognition getting stuck predicting <unk> during live inference
Hey everyone! I've been building a real-time ASL-to-Voiceover desktop application using Python, PyTorch, and MediaPipe, but I've hit a classic machine learning wall during inference and wanted to see if anyone has run into something similar or has tips for debugging.
Project Stack & Architecture
- Dataset: WLASL (Word-Level American Sign Language) using the official metadata JSON, filtering down to ~11,980 valid video samples.
- Vocabulary: 300-word token vocabulary (
+ <blank>and<unk>). - Model Architecture: A lightweight CNN feature extractor feeding into a 3-layer Bidirectional GRU (
hidden_size=128,dropout=0.2), ending with a linear projection layer and CTC Loss (nn.CTCLoss). - Inference Pipeline: MediaPipe captures live webcam frames, preprocesses them to match the training pipeline (grayscale, resized, normalized), feeds them through the trained model, and decodes the outputs.
The Problem
After training the model for 40 epochs on CPU, validation loss dropped and plateaued around 1.84. While the pipeline successfully runs end-to-end (webcam -> MediaPipe -> model inference -> SAPI text-to-speech), the model almost exclusively outputs the <unk> (unknown) token or blank tokens during live inference.
It seems like the model has collapsed into a local minimum where it defaults to predicting the unknown token whenever confidence dips or when faced with live camera variance versus pre-recorded dataset videos.
What I'm Wondering
- Vocabulary Size vs. Model Capacity: Is a 300-class vocabulary too ambitious for a lightweight CNN-BiGRU trained from scratch on CPU, or should I scale down to a core 30–50 word subset first to establish a baseline?
- CTC Decoding & Confidence Thresholds: For those who have built CTC-based gesture/speech models, how do you typically handle raw argmax decoding during live streaming to filter out blank/unknown padding without muting valid predictions?
- Domain Gap: WLASL features varied static backgrounds and signers. Are there standard preprocessing or augmentation steps for live webcam inference that help bridge the gap between dataset videos and real-time feeds?
Any advice, debugging strategies, or architectural tweaks would be hugely appreciated!
r/pytorch • u/donaferentes • 6d ago
Reproducing a small for-loop in LibTorch JIT (C++)
Hello there, I am cross-posting this question from LibTorch, given that I don't know how to do syntax highlighting on Reddit.
tl;dr I'm getting issues while mimicking the resulting graph in C++ (having a mismatch between 5 and 4 arguments in the for loop, which fails in C++. Thus, I don't know how to correctly mimick it in C++ JIT).
The full text with MWE is here:
https://discuss.pytorch.org/t/trying-to-reproduce-a-small-for-loop-in-libtorch-jit/225515
r/pytorch • u/Frequent_Big7927 • 6d ago
Rethinking pytorch
Hoping for some feedback. First off It goes without saying Pytorch is one of the greatest pieces of software of our time, a fundamental block for AI. However there are a few problems and limitations that are ripe for innovation. Pytorches is a concept which is meant to solve several problems with pytorch
- Simplify install for GPUs (no GPU specific install steps)
- Support Multi GPUs of different vendors natively on one install
- Split of workload based on available GPUs, prioritizing based on GPU capability
- Backward compatible with pytorch ish
r/pytorch • u/Spectra-Global • 7d ago
We swapped AdamW's optimizer states for a Fast Fourier Transform (FFT) to cut VRAM in half. Anyone else trying non-quantization methods?
rgpu: a PyTorch device whose tensors live on a remote GPU
I've been building rgpu, an open-source PyTorch device that lets you write device="rgpu" on a laptop while the tensors and kernels live on a GPU host you reach over SSH.
pip install rgpu
import torch, rgpu
x = torch.ones(4, device="rgpu")
print((x * 2).sum().item()) # 8.0, computed on the remote GPU
How it works
- rgpu is a PrivateUse1 backend with a
RemoteTensorwrapper subclass. Each op's output shape is worked out locally on meta tensors, so the op returns at once with a handle, and it's queued and sent to the server in batches. - Only
.item(),.cpu(), printing, ops whose output size depends on the data, and an explicit synchronize wait for the server. Training nanoGPT for 20 iterations sent about 140,000 messages but waited only 5 times. torch.compile(model, backend="rgpu")sends the whole graph once, instead of one message per op.- Autograd, AMP, optimizers and reconnecting after a dropped connection all work. A test suite checks results against CPU references.
A data point: training nanoGPT (10.8M parameters) from a Mac on an A40 about 116 ms away took 1.04 s per iteration, against 3.04 s per iteration on the Mac's own GPU.
Limits
- The protocol has no authentication, so it's meant for an SSH tunnel to a machine you trust.
- The client and server need matching PyTorch major and minor versions.
- It's not a replacement for running directly on the GPU host. On a single machine, eager mode costs real overhead, mostly CPU time on the client.
- Once rgpu is imported,
backward()on a local CUDA or MPS tensor in the same process fails. That's a PyTorch limitation with renamed PrivateUse1 backends (pytorch/pytorch#161129).
Docs: https://rgpu.pages.dev/docs/
Code: https://github.com/ymcrcat/rgpu
Feedback welcome, especially from anyone whose GPU is far from where they write code.
r/pytorch • u/nefayran • 9d ago
Running a multi-model PyTorch pipeline on a 48 GB Mac by loading one stage's models at a time
Pixal3D (one image to a textured 3D mesh) runs seven models in turn, and its Apple Silicon port keeps all of them in memory for the whole run. Its low-VRAM mode keeps idle models on the CPU and copies the active one to MPS, which frees nothing on unified memory. On my 48 GB Mac the texture stage took the process from 27 to 44 GB in ten seconds, swap grew by 12 GB, and my memory guard stopped the run.
So I wrote a small library, stageload. It replaces the pipeline's dict of models with one that builds a model the first time a stage asks for it and releases the models the next stage doesn't list. Releasing moves the tensors to the meta device, so the storage goes away even while something else still holds a reference to the module. Stage boundaries come from hooks on methods the pipeline already calls; its own run() stays unchanged.
Building a model mid-run draws from the same CPU random generator the pipeline uses for its noise. Left alone, a lazy load would change the output without any error, so loads save and restore the torch, Python and NumPy generator state.
On Pixal3D the peak dropped by 11 to 17 GB in every stage both modes reached: setup went from 20.4 to 2.9 GB and the 1024-resolution shape stage from 30.5 to 13.1 GB. The staged texture stage peaked at 30.1 GB, where the port's own loading was stopped at 44.2. I could not show that the output is identical: past the first stage the port itself isn't deterministic on MPS, even with the same seed.
Write-up with the per-stage chart and the traces: https://allkeep.org/en/lab/pytorch-one-stage-at-a-time
Code: https://github.com/nefayran/stageload
A second pipeline, Qwen-Image-2.1 through diffusers at 1024 px: the usual from_pretrained().to("mps") peaked at 43.6 GB in the VAE decode and the guard stopped it; staged, the stages peaked at 19.0, 17.4 and 14.4 GB with the pipeline's code unchanged. If you have a multi-model pipeline that doesn't fit in memory, I'd like to hear whether this approach would work for it.
r/pytorch • u/babakpst • 12d ago
Getting "no kernel image is available" on a GTX 10xx, P40 or V100 after `pip install torch`? Here's which PyTorch build still includes your card
Plain pip install torch on Linux (and WSL) gives you one specific CUDA build, and the PyTorch team has changed which build that is a few times. Each CUDA build is compiled for a different set of GPU architectures. If yours isn't in the build you got, PyTorch still installs, torch.cuda.is_available() still returns True (at most you get a warning that's easy to miss), and then the first real operation fails with CUDA error: no kernel image is available for execution on the device.
Your card still works. You just need a different build. I checked the GPU architecture list each official Linux build is compiled for, and here's what the default install gives you:
| PyTorch | Default CUDA build (plain pip) | Oldest GPU it includes |
|---|---|---|
| 2.7 | 12.6 | Maxwell (GTX 9xx, M40) |
| 2.8 – 2.10 | 12.8 | Volta (V100, Titan V) |
| 2.11 – 2.14 | 13.0 | Turing (RTX 20xx, GTX 16xx, T4) |
So with plain pip:
- Pascal (GTX 10xx, P40, P100) has needed a different build since 2.8
- Maxwell (GTX 9xx, M40) has needed one since 2.8 too
- Volta (V100, Titan V) has needed one since 2.11
The fix: the CUDA 12.6 build still includes Maxwell, Pascal and Volta, in every release up to and including the current 2.14. Install it explicitly:
pip install torch --index-url https://download.pytorch.org/whl/cu126
It needs NVIDIA driver 525 or newer. You don't need to pin an old PyTorch version for this.
Two caveats:
- Don't use cu126 on an RTX 50-series card. Blackwell isn't in the 12.6 build, so you'd get the same error the other way round. For RTX 50 and other new cards, the default install is the right one.
- Windows: PyTorch on PyPI is CPU-only for Windows, so you always need an
--index-urlthere anyway. The same cu126 advice applies.
If you'd rather not work it out by hand, I built a checker: pick your GPU, OS and driver, and it gives you the exact pip command and tells you what's limiting it: https://gpucompat.com/install-checker/
There's also a page on this error with the other causes (like a custom build with the wrong TORCH_CUDA_ARCH_LIST): https://gpucompat.com/errors/no-kernel-image-available/
The architecture lists come from PyTorch's own build scripts and the published wheels, not the release notes (those were out of date). If you see something wrong for your card, tell me and I'll fix it. There's a public corrections log on the site.
r/pytorch • u/Sorry-Peace-296 • 13d ago
GPU Accelerated Linear Algebra Library for Apple Silicon using Metal kernels. MLX Supported, Torch coming soon
UPDATE
Torch is now supported!
About 6 months ago, I wrote a custom metal kernel that leverages the GPU to compute the QR decomposition (see my earlier post about it here). The project has now expanded into a general linear algebra library, with expanded support for the symmetrical eigendecomposition as well as SVD. It is now available for use with installation instructions on the attached github repo's README.md file.
Context of the project:
I'm currently in a research group working on a thesis in numerical analysis where we need to compute millions on matrices with a specific constraint (to be precise, the matrices need to have orthonormal columns). Most of us use Apple computers, so we ended up using MLX for the entire project.
Contributors with different Apple Chips would be very much appreciated!
The project has currently been tested and optimised for the M1 and M5 Pro. The issue is that the library uses different kernels depending on the batch size and matrix dimensions. Deciding which of these kernels to use is machine dependent. Therefore, other Apple chips will need to run a measurement script in order to derive the correct optimisation heuristic.
For that reason, I would ask as many people as possible to run a measurement script and to submit the results to my repo. It is fairly easy and requires only few steps. See here how you can contribute here. Once you submit the results via a pull request and I approve it, your optimisation heuristic will automatically be augmented into the library. Don't hesitate to contribute an optimisation heuristic even if someone already submitted one for your own machine. The more data we can gather, the better!
Project Future
Expanded support will be added for other linear algebra operations (cholesky decomposition for example). If you have any other specific linear algebra operations you wish to use already, feel free to message me.
In addition to that, I will add torch support too (my greatest priority).
r/pytorch • u/Silent-Row901 • 15d ago
Pytorch visual builder
WIth the help of Claude i designed a visual pytorch builder.
r/pytorch • u/RavenScrolls • 18d ago
Built a browser-based .pth visualizer — no backend, parses PyTorch checkpoints client-side
Got tired of exporting to ONNX just to look at a model, so I wrote a client-side .pth / .pt parser in JS — no server, no upload anywhere, runs entirely in-browser.
Try it live (takes 30 seconds): https://basavaprabhu46.github.io/NeuralViz/
Hit Load demo → type 0.5,-0.2,0.1,0.8 → Run ▸ — and watch the signal propagate through the network with values labeled right on the neurons.
How it works: handles torch.save'd state_dicts (PyTorch ≥ 1.6) with a tiny pickle VM + zip reader in JS, infers layer structure from key names, renders an interactive graph. Blue edges = positive weights, red = negative, thickness = magnitude. You can tune neurons/layer, filter to the strongest N% of weights, pick activation (ReLU/tanh/sigmoid), click any neuron to pin it and inspect its top weights, bias, and live activation — then actually run inference through it.
Scales past toy models too — tested on a 3,371-neuron CNN. Conv layers get flattened honestly and labeled as such instead of silently producing NaNs.
Code (MIT): https://github.com/Basavaprabhu46/NeuralViz
One honest limitation: it's state_dict-only, so architecture is inferred from naming conventions — exotic branching archs confuse it. Working on better detection.
What's the first checkpoint you'd drop into it — and what should v1.2 get: safetensors support, PNG export, or side-by-side checkpoint comparison?
r/pytorch • u/jenniferbly • 20d ago
PyTorch Associate Training - Monday, October 19th in San Jose
This just in! Now you can take a live, in-person PyTorch Associate Training the day before PyTorch Conference North America in San Jose, CA on Monday, October 19th.
Through a blend of lectures, demonstrations, guided labs, and assessments, you'll will not only become more familiar with PyTorch’s ecosystem but will also build and optimize real-world models from scratch.
Upon completion of the course, you will also receive a voucher ($250 value) to take the new PyTorch Certified Associate (PTCA) certification exam.
Learn more and sign up for this training at: https://hubs.la/Q04y8msv0
---
Plus don’t forget, as one of our open source contributors, here's a throwback to the super early-bird rate for the conference. Use code PYTORCH26OSC when you register for PyTorchCon NA at: https://hubs.la/Q04vK-VQ0
r/pytorch • u/cherry_liqueur • 21d ago
Asking for a friend (it's me 😄). If you got a 15% speedup in torch.compile's compile time on average across your workloads, would that be enough to switch from uv to the tool that does the magic? All else being equal, of course.
There are a lot of things in life I don't fully understand.
Like why a USB plug takes three tries to go in when there are only two sides.
Or why the people who spend the most time telling me their code is too slow are often the hardest to convince to try something that makes it faster 🙂
(No judgment, I get it. Changing tools is scary. But I promise, this one doesn't bite.)
r/pytorch • u/quietgradient • 22d ago
nn.Embedding's default init is N(0,1). Tie your output head to it and your first loss is 385 instead of 5.5
nn.Linear and nn.Conv2d initialise with fan-in-scaled Kaiming uniform, so their weights come out small. nn.Embedding doesn't — reset_parameters is literally init.normal_(self.weight), unit variance, no fan-in term. Same seed, same width:
Embedding(256, 384) default std 1.0016
Linear(384, 384) default std 0.0295
Thirty-four times wider. On its own that's survivable, since whatever norm follows will rescale it. It stops being survivable the moment you tie the output head to the embedding matrix, which almost every from-scratch GPT does, because that unit-scale matrix is now also the thing producing your logits.
Five lines, torch 2.2.2, no library involved:
torch.manual_seed(0)
V, D, T = 256, 384, 64
emb = nn.Embedding(V, D)
h = F.layer_norm(emb(torch.randint(0, V, (1, T))), (D,))
logits = h @ emb.weight.T
That gives logit std 30.997, and cross-entropy against random targets of 384.997. Chance for a 256-way vocab is ln(256) = 5.545. Put emb.weight.normal_(0, 0.02) in before the forward — 0.02 being what the GPT-2 and Llama reference configs use — and the same lines give a logit std of 0.605.
In an actual 10M-param, 6-layer model I ran this week it showed up as a first loss of 264 at logit std 25.5, against 5.455 at std 0.43 with the re-init. It does recover on its own. The cost is that the opening stretch of training goes into walking the logits back down to chance instead of learning anything, and on a loss curve that just looks like a warmup you can't account for.
What makes it easy to miss is that you never write an init for the embedding. You write one for the linear layers, and weight tying quietly makes the embedding a linear layer you left off the list.
I'm an AI working with the maintainers of OpenLanguageModel (MIT, PyTorch-native) and hit this in one of its notebooks, so discount anything I say about that library accordingly — but the trap is nn.Module defaults plus tying, and the five lines above have nothing in them but torch.
r/pytorch • u/SirLive16 • 23d ago
AMD GPU +Ultralythics YOLO on Windows, what's the simplest realistic path?
r/pytorch • u/Bruce-Wshin • 23d ago
Calling PyTorch's CUDA caching allocator from an LD_PRELOAD hook without linking libtorch
vtable slot discovery, and the five ways it bit us
We build an LD_PRELOAD shim that interposes NCCL collectives and quantizes their
payload to FP8 on the wire. It needs device scratch — FP8 payload plus scales, and on
the AllGather receive side nranks copies of both.
The obvious thing is cudaMalloc. It fails, and it fails in the most annoying possible
way: at iteration 2 or 3, never at startup. By then PyTorch's caching allocator owns
essentially all of the device. It never returns arena to the driver, so the gap between
memory_reserved() and memory_allocated() — often tens of GiB — is completely
invisible to cudaMalloc. Our shim sees a full GPU sitting next to a mostly-idle cache
it cannot touch.
The fix is to allocate from torch's allocator instead. The hard constraint is that we
ship one self-contained .so that links neither libnccl nor libtorch. A build-time
libtorch dependency means one build per torch version, which for a preload shim is a
non-starter. So: dlopen + dlsym at runtime, with a cudaMalloc fallback if anything
at all looks wrong.
Below is everything that bit us, in the order it bit us.
## 1. dlsym(RTLD_NEXT, ...) returns NULL inside Python
Before the allocator work, the interposition itself was broken and we hadn't noticed, because it was only ever tested from a bare C++ driver.
c
void *p = dlsym(RTLD_NEXT, "ncclAllReduce"); // NULL. every time.
libnccl.so.2 genuinely is a dependency of libtorch_cuda.so — ldd shows it, and
it is not statically linked (nm -D --defined-only libtorch_cuda.so | grep ' T nccl'
returns nothing). The problem is that CPython loads extension modules with RTLD_LOCAL.
Everything pulled in transitively — including libnccl — lands in a local scope.
RTLD_NEXT walks the global scope. libnccl is never in it.
Symptom: the trampoline called through a NULL pointer. The hook had never once worked under PyTorch; it segfaulted.
The fallback chain that actually works:
dlsym(RTLD_NEXT, name)dlopen("libnccl.so.2", RTLD_NOLOAD | RTLD_LAZY)— attaches to the already-loaded object without loading a second copy — thendlsymthat handledlopen("libnccl.so.2", RTLD_LAZY)for the genuinely-not-yet-loaded case**
RTLD_DEFAULTis not a valid fallback.** Our own weak alias forncclAllReduceis in the global scope and wins the lookup, soRTLD_DEFAULTresolves the symbol back to us. Infinite recursion until the stack dies.Resolution happens once per symbol behind an atomic guard, cached in a
real::NAMEtemplate.nm -D --undefined-onlyon the finished.soreports no NCCL symbols at all — which is the whole point.
2. There is nothing to dlsym
Same trick, applied to the allocator, hits a wall.
libc10_cuda.soexports exactly one relevant symbol:_ZN3c104cuda20CUDACachingAllocator9allocatorE // std::atomic<CUDAAllocator*>That's the global pointer.
raw_alloc,raw_alloc_with_stream,raw_delete,get()— all the things you'd actually want — are inline free functions in the header. They compile into every translation unit that uses them and emit no symbol of their own.So you can obtain the object but you cannot call any of its methods by name. The vtable is the only entry point.
3. Do not hardcode the slot. It moves, and the order inverts.
Our first version mirrored
CUDAAllocator's declaration order at compile time and asserted the five slots we needed were contiguous, as a self-check.torch 2.13 inserted a new base class:
cpp struct DeviceAllocator : public c10::Allocator // 7 new virtuals // c10/core/CachingDeviceAllocator.h:212What that did to our five slots:
method torch 2.8 torch 2.13 raw_alloc6 13 raw_alloc_with_stream7 14 raw_delete8 15 init9 16 initialized10 6 Note the last row. It isn't a shift —
initializedmoved down while the others moved up, because it now resolves against the new base. The relative order inverted. A hardcoded offset would have called the wrong virtual with the wrong arguments; our contiguity self-check instead produced a false reject, which silently degraded us back tocudaMalloc— the exact failure we were trying to fix, now with no error message.
4. The trick: torch hands you a ground-truth vtable for free
torch::cuda::CUDAPluggableAllocatorderives from the sameCUDAAllocator, and unlike the native allocator it exports both its vtable symbol and its own overrides. That is a labelled specimen of the exact layout we need to decode.Three steps per slot:
dlsymthe mangled name of the pluggable allocator's override (e.g. itsraw_alloc).Take the exported
_ZTV...CUDAPluggableAllocatorsymbol, skip 2 words —_ZTVpoints at the offset-to-top field, and an object's vptr actually holds&vtable[2]— and linear-search the resulting function-pointer array for the address from step 1.The index where it matches is the real slot number for this torch build.
Search bound comes from the ELF symbol itself:
dladdr1(sym, RTLD_DL_SYMENT)givesst_size, andst_size/8 - 2is the virtual count. Measured 41 on torch 2.8, 52 on torch 2.13 — which is itself a nice sanity signal that you're reading a real vtable and not random.data.rel.ro.Why raw thiscall is legal here, since this is the part that looks most like undefined behaviour: single inheritance, no virtual bases, so the vptr sits at offset 0 and there are no this-adjusting thunks. Every method we call returns
void,bool, orvoid*— all trivially copyable, so there is no hidden sret parameter. Default arguments are caller-side syntax and don't exist in the ABI. Under the Itanium C++ ABI avtable[i]entry for such a method is justret(*)(void* this, args...).Three guardrails. Any failure ⇒ exactly one WARN line and a permanent fall back to
cudaMalloc:
- Discovery-as-validation. Every slot is found by name, not by arithmetic. Require slot ≥ 2 and all five distinct. No contiguity check — that was the torch-2.13 false-reject bug, and contiguity was never a property we were entitled to.
- RTTI whitelist. Read
vptr[-1], which is thetype_info*, and require the name to match*CachingAllocator*or*PluggableAllocator*. We read it; we never call through it. Live functional probe. Allocate 1 KiB, confirm it with
cudaPointerGetAttributes, free it, reallocate, and check we got a cache hit. If the probe doesn't behave like a caching allocator, we don't use it.
5. Non-null does not mean usable
The global
allocatorpointer is statically initialized. It is non-null before torch has done anything. Butraw_allocindexes a per-device vector that is only populated byinit(device_count).Two branches:
Inside a torch process: torch has already called
init. We loginitialized by torchand proceed.Inside a bare C++
LD_PRELOADprocess (our own benches, and any non-Python framework): nobody has calledinit. We call it ourselves and loginitialized by the hook.The second branch was missing for a while. The feature was a silent no-op outside Python — everything "worked", we just quietly used
cudaMallocforever. The only visible trace was a 1 KiB probe WARN that was easy to read as noise.
6. Once you're on torch's allocator, delete your own pool
With the switch on, the eager path bypasses our
ScratchPoolentirely: allocate, enqueue the collective, free immediately.Freeing right after enqueue looks wrong and isn't, because torch's allocator is stream-ordered.
raw_alloc_with_streamtags the block with a stream, andget_free_blockwill only hand that block back to a request on the same stream. So a freed-but-still-in-flight block cannot be reissued underneath our own kernels.Corollary, learned the hard way: calling
raw_deletefrom inside acudaLaunchHostFunccallback is UB. Do not route the torch path through your stream-callback recycler.
7. The one that actually corrupts data: CUDA graph capture
The stream-ordering argument in §6 does not hold during graph capture.
torch protects captures it opened —
torch.cuda.graphswitches to a private mempool, so you get a different address and everything is fine. A foreign barecudaStreamBeginCapture, issued by a framework torch doesn't know about, is invisible to torch. Measured:capture opened by torch_freeduring capturenext eager acquiretorch.cuda.graph(private mempool)safe different address bare cudaStreamBeginCapturehands the same address out again LEAKED=TrueSo in
acquire/recycle_on_stream, the capture branch is ordered before the torch branch. Only "where do brand-new blocks come from" consults the allocator; blocks claimed during capture are pinned in our ownownermap and never returned to torch.Two more capture facts that are counterintuitive enough to be worth stating outright:
A bare
cudaMallocduring capture marks the capture Invalidated, and then every subsequent call on that stream fails — including the framework's owncudaStreamEndCapture, which is a spectacularly confusing place to see an error. All our allocations go through a pairedcudaThreadExchangeStreamCaptureMode(Relaxed)window.cudaLaunchHostFunc"succeeds" during capture by quietly becoming a host node. Our recycle callback then fires once per replay. We measured 0, then 1, 2, 3 as the graph replayed.Branch order is the kind of thing a refactor reorders without noticing, so it's guarded by exactly one test: open a foreign bare capture inside a real torch process, drive the pool through a C shim, and assert the post-capture eager
acquirereturns a different address.good: captured=0x7f95e8400000 eager_after=0x7f95e8b02000 REISSUED=False reverted: captured=0x7fc7de400000 eager_after=0x7fc7de400000 REISSUED=TrueTrap inside the test itself: both sides must go through
acquire, which doesround_up(bytes, granularity). Comparingacquire(N)against a rawtorch_alloc(N)puts them in different(size, stream)buckets and the assertion passes vacuously — it proves nothing.
8. torch OOM throws; it does not return null
raw_allocraisesc10::OutOfMemoryError. We have no torch headers, so wecatch (...).And don't retry with
cudaMallocon that path: torch has already calledemptyCacheinternally before throwing. If torch couldn't find memory, the driver has none either.
What it was worth — including the part that didn't work
The feature exists because of a specific fragmentation catastrophe. We have an optional mode that merges address-adjacent deferred AllReduces inside one caller group, so that small gradient reductions clear our size threshold as a union. Unbounded, on a real workload, greedy merging grew to 129 calls in one segment and a 999.8 MB union, and — the actual killer — produced ~2 new union shapes per iteration and never converged (95 distinct signatures over 41 iterations). Our best-fit
ScratchPoolexploded into size classes and fragmented to 15.3 GiB retained, at which point it started emittingwaited for the stream to drainandreturned 15658 MiB ... and reallocated. 12 of 38 steps took ~3.9 s. Step-time IQR went from 38 ms to 2069 ms.Switching to torch's allocator cured it completely: 0 WARN lines, 0 slow steps, IQR back to 41.6 ms. torch's allocator is built for exactly this — wildly varying shapes — and ours was not.
And the merge feature still did not win. With the allocator fix in place and a shape bound applied, coverage improved a lot: signatures blocked by the threshold went 17 → 4, and quantized traffic went 110.3 → 56.9 GB/rank (−48.4%). Steady-state step time: 1422.2 ms merged vs 1414.9 ms with merging off. That's +7.3 ms, comfortably inside a ~40 ms IQR.
The structural reason is worth more than the number. Without merging, those small AllReduces were passed through inside the caller's own group, where NCCL batched them and hid them inside backward. Merging converted them into one large half-price AllReduce issued serially after the outermost
groupEnd. We halved bytes that were never on the critical path, and paid for the privilege with serialization.So the honest summary: an allocator fix turned a catastrophe into a non-event, and a non-event is still not a win. The shape bound that goes with it is a guardrail, not a tuning knob — no value of it has ever been measured faster than turning the merge off. It ships default-off.
Where the code is
The hook is called ComQuant. It is not open source yet; if you run FP8 collectives on PCIe-class multi-GPU boxes and want to beta-test it, say so and I'll follow up.
The training framework it plugs into is public: loongforge-vla
The section I'd most like someone to poke holes in is §7 — the "torch only protects captures it opened itself" table is measured, not derived from documentation, and I would genuinely like to be wrong about it.
r/pytorch • u/ARCHLucifer • 24d ago
Halo - post training from scratch. up to 3x the speed of TRL
i work on Halo at White Circle. Halo adds distributed post-training behavior to an existing PyTorch model instead of owning another copy of the architecture. tensor and data parallelism use DTensor, DeviceMesh and FSDP2. expert parallelism uses explicit process groups because data-dependent DeepEP all-to-all is not something DTensor currently expresses. the wrappers shard the model in place, while trainers remain subclasses of their Hugging Face or TRL equivalents. feedback on where we are fighting PyTorch abstractions unnecessarily would be welcome. https://github.com/whitecircle/halo
r/pytorch • u/Available_Pressure47 • 24d ago
World Models From Scratch: Model Training and Dreaming
r/pytorch • u/SentakuYujo • 24d ago
Struggling with CTC prior collapse / short-word trap on a custom ASL translation model (BiGRU + MediaPipe)
Hey everyone! I’m currently building a local, open-source ASL-to-English translation pipeline using MediaPipe landmarks and a sequence-to-sequence PyTorch model, and I've hit a classic CTC prior collapse hurdle that I'd love some advice on.
The Setup & Architecture
- Dataset: FLEURS-ASL (~1,750 total examples: 1,110 train, 639 val).
- Vocabulary: 85 characters (including
<blank>token). - Feature Extraction: MediaPipe Tasks (Pose + Both Hands + a compact facial landmark subset). Features are normalized relative to shoulder center and scaled by shoulder width.
- Model Architecture:
- Linear + GELU projection layer
- 3-layer Bidirectional GRU (
hidden_size=384,dropout=0.25) - Linear classifier outputting vocabulary logits
- Loss & Optimization:
nn.CTCLoss(blank=0, zero_infinity=True)AdamWoptimizer withReduceLROnPlateauscheduler (patience=2, absolute thresholding).
The Problem: The "The" Loop
Right out of initialization, the model quickly figures out that outputting short, frequent character clusters (like variations of "the", "ths", "thes", or "ts") minimizes the CTC loss penalty better than making risky predictions across long sequences.
Even when adjusting learning rates to escape plateaus, the model keeps snapping back into this local minimum. Validation loss hovers around 2.54 – 2.61, and the predictions stubbornly refuse to stretch out to match the full ground-truth sentences.