pytorch

Stars and mentions

Last 30 days, Oct 4 – Nov 2

GitHub stars1,439
mentions54
04182123164Oct 4Oct 18Nov 2
UTC days · repositories pytorch owns18 repositories

Package downloads

Cumulative recorded downloads · since 2025-10-26

npm36.9K
PyPI540.6M
0135.2M270.3M405.5M540.6MOct 4Oct 18Nov 2
Oct 29npm: 1/4 packages observed · PyPI: 3/10 packages observed

Across 14 known packages in repositories pytorch owns. Cumulative totals include recorded downloads before the selected range. Missing observations leave gaps; totals may be incomplete. PyPI excludes known mirrors.

Top repositories

pytorch’s repositories ranked by new stars, as on trending.

Loading

Contributes to

Repositories outside pytorch’s account where pytorch opened or reviewed pull requests in the past 365 days.

Who noticed pytorch

Everyone who starred or forked pytorch’s repositories since the archive began in 2011.

Notable accounts

Ordered by the stars their own repositories have received.

Biggest fans

Accounts that starred the most of these repositories.

Breakout weeks

Each repository’s best week of new stars, with the release published around it.

Mentions

51posts
Nov 30 posts
  1. So Astra misunderstood a request of mine and pasted all of my human prompts into a GitHub comment and... uhhh.... I kinda like it? LMAO pytorch#202678 t.co

    35 likes · 1,344 views
  2. Reddit · · r/GitGems · u/DegTrader · pytorch/pytorch

    Turning the GitHub commit DAG into a benchmark for coding agents The commit DAG is a partial order, and most agent benchmarks throw that structure away. Independent commits form an antichain; every valid execution order is a linear extension of the DAG. Let L(G) be the number of linear extensions and E the set of all permutations. Then: P(valid) = L(G) / |E| and the structural uncertainty of a task is H(G) = log L(G) - E[log L]. That gives two clean scores. Agent score: normalized edit distance between the order your agent produced and a reference topological order. Task difficulty: log L(G), the number of ways the work could legitimately have been sequenced. It also lets you build evals straight from real GitHub history: take a merged PR series, strip the solution, hand the agent only the issue text, then score the commit order it produces. No synthetic repos required. A large AI codebase makes a good corpus for this because the DAG is dense but not arbitrary: pytorch/pytorch Curious whether anyone has scored agent trajectories this way instead of on test pass rate alone.

    1 point
  3. Building a PyTorch Container Image PyTorch is a flexible machine learning library for building deep learning models that can perform a wide range of tasks such as image recognition and language processing. This guide walks through cloning the PyTorch source repository, building a container image for either CPU or GPU systems, and pushing the finished image to a container registry. By the end, you'll have a custom PyTorch container image built from source and published to a registry, ready to pull into any Docker-compatible environment. --- Prerequisites - A server with Docker installed (a GPU-enabled server with the NVIDIA Container Toolkit if you're building the GPU image). - Access to the server using SSH as a non-root user with sudo privileges. - Start the Docker service. ```console $ sudo systemctl start docker ``` - Add the non-root user to the Docker group. For example, linuxuser. ```console $ sudo usermod -aG docker linuxuser ``` - Switch to the user: ```console $ su - linuxuser ``` 1. Set Up the Server 1. Create a new directory to store your PyTorch project files: $ mkdir llama-project 2. Switch to the directory: $ cd llama-project 3. Clone the PyTorch project repository using Git: $ git clone https://github.com/pytorch/pytorch 4. List all files and verify that a new pytorch directory is available: $ ls 5. Switch to the pytorch project directory: $ cd pytorch 6. List all directory files and verify the available Dockerfile resources: $ ls -a The PyTorch project directory includes the following Dockerfile resources: - Dockerfile: Contains the PyTorch build context for CPU systems. - docker.makefile: Contains advanced GPU build context and image automation directives with support for custom dependency packages. Use these resources in the next sections to build a CPU or GPU system container image. 2. Build a PyTorch Container Image for CPU Systems Follow the steps below to build a new PyTorch container image for CPU systems using the Dockerfile resource on your server. 1. Build a new PyTorch container image using the Dockerfile resource. Replace pytorch-image with your desired image name: $ docker build -f Dockerfile -t pytorch-image . 2. View all Docker images and verify that your new PyTorch image is available: $ docker images Output: REPOSITORY TAG IMAGE ID CREATED SIZE pytorch-image latest d3674f1db4d5 2 hours ago 11.9GB 3. Build a PyTorch Container Image for GPU Systems Follow the steps below to build a new PyTorch container image for GPU systems using docker.Makefile, which contains the CUDA dependency build context compatible with NVIDIA GPU servers. 1. Build a new PyTorch container image for GPU using the docker.Makefile: $ docker build -f Dockerfile -t pytorch-image . Wait at least 2 hours for the build process to complete. When successful, your output should look like the one below: #29 29224.1 copying torch/include/ATen/ops/_sparse_coo_tensor_with_dims_ops.h -> build/lib.linux-x86_64-cpython-310/torch/include/ATen/ops #29 29224.1 copying torch/include/ATen/ops/absolute_compositeimplicitautograd_dispatch.h -> build/lib.linux-x86_64-cpython-310/torch/include/ATen/ops #29 29224.1 copying torch/include/ATen/ops/gradient_ops.h -> build/lib.linux-x86_64-cpython-310/torch/include/ATen/ops #29 29224.1 copying torch/include/ATen/ops/crow_indices_compositeexplicitautograd_dispatch.h -> build/lib.linux-x86_64-cpython-310/torch/include/ATen/ops #29 29224.1 copying torch/include/ATen/ops/isinf.h -> build/lib.linux-x86_64-cpython-310/torch/include/ATen/ops #29 29224.1 copying torch/include/ATen/ops/_fore #29 29224.1 [output clipped, log limit 2MiB reached] #29 29235.3 warning: no previously-included files matching '*.o' found anywhere in distribution #29 29235.4 warning: no previously-included files matching '*.dylib' found anywhere in distribution #29 29235.7 warning: no previously-included files matching '*.swp' found anywhere in distribution #29 DONE 29239.6s #30 [dev 1/1] COPY --from=build /opt/conda /opt/conda #30 DONE 10.8s #31 exporting to image #31 exporting layers #31 exporting layers 41.0s done #31 writing image sha256:d3674f1db4d5e9585a5e5b64a0cdfcb0c7d312358e267000e23b6fa9d8e7e917 done #31 naming to docker.io/root/pytorch:v1.8.0-rc1-38416-g4dc160864b3-cuda12.1-cudnn8-devel done #31 DONE 41.0s 2. View all Docker images on the server and verify that a new root/pytorch image is available: $ docker images Output: REPOSITORY TAG IMAGE ID CREATED SIZE root/pytorch v1.8.0-rc1-38416-g4dc160864b3-cuda12.1-cudnn8-devel d3674f1db4d5 3 hours ago 20.9GB 3. Verify the PyTorch GPU image's target CUDA version and confirm your system uses that driver version or higher: $ cat docker.Makefile | grep CUDA_VERSION Output: CUDA_VERSION_SHORT ?= 12.1 CUDA_VERSION ?= 12.1.1 BASE_DEVEL = nvidia/cuda:$(CUDA_VERSION)-cudnn$(CUDNN_VERSION)-devel-ubuntu22.04 Based on the above output, NVIDIA GPU servers with CUDA version 12.1 or higher can run your PyTorch GPU container image. 4. Upload the PyTorch Container Image to a Container Registry 1. Open your container registry's management panel and copy the registry URL, username, and API key (or password) to use when accessing the registry. 2. Switch to your server terminal session and log in to your container registry. Replace your-registry-url, exampleregistry, exampleuser, and registry-password with your actual registry details: $ docker login https://your-registry-url/exampleregistry -u exampleuser -p registry-password 3. Tag your PyTorch container image with your desired registry tag. For example, tag your PyTorch GPU image ID with a registry repository tag such as your-registry-url/exampleregistry/pytorch-gpu-image: $ docker tag d3674f1db4d5 your-registry-url/exampleregistry/pytorch-gpu-image The above command tags the Docker image with container ID d3674f1db4d5 with your registry tag. 4. View all Docker images on the server and verify that the new tagged image is available: $ docker images Output: REPOSITORY TAG IMAGE ID CREATED SIZE root/pytorch v1.8.0-rc1-38416-g4dc160864b3-cuda12.1-cudnn8-devel d3674f1db4d5 4 hours ago 20.9GB your-registry-url/exampleregistry/pytorch-gpu-image latest d3674f1db4d5 4 hours ago 20.9GB 5. Push the tagged image to your container registry repository: $ docker push your-registry-url/exampleregistry/pytorch-gpu-image 6. Open your container registry's management panel and check the repositories list to verify that the new repository is available. Next Steps - Automate the build and push steps in a CI/CD pipeline so new PyTorch images are rebuilt on every commit. - Pull the image into a Kubernetes cluster or Docker Compose stack to run inference or training workloads. - Trim the final image size with multi-stage builds if you only need the runtime (not the full build toolchain). - Pin the CUDA and cuDNN versions in your Dockerfile to keep builds reproducible across environments. For the full guide with additional tips, visit the original article on Vultr Docs.

    5 reactions
  4. YouTube · · YouTube description · Just Known Dev · pytorch/pytorch

    Your GitHub Actions Jobs May Now Run on AWS | Dev News Five developer stories and some updates in about four minutes: what happened, why it matters, and what to do. 1. GitHub Actions: eligible Linux jobs on standard 2- and 4-core runners may now run on AWS as well as Azure (automatic on Free, Pro and Team; Enterprise Cloud opts in). They can use new outbound IPs, so allowlists built on Azure ranges, including Entra Conditional Access, can block them: sync with the actions list from api.github.com/meta. Included Actions usage is now a dollar amount ($12 Free, $18 Pro/Team, $300 Enterprise Cloud a month), and Copilot cloud sandboxes draw from it, with billing from November. macos-14 jobs fail during brownouts Oct 29 and Oct 30, 14:00 to 24:00 UTC; the image retires Nov 2. 2. CISA added Linux kernel CVE-2026-46300 ("Fragnesia", per Red Hat) to its Known Exploited Vulnerabilities list, due Oct 31, saying it can be chained with PHP2Shell to plant web shells and run commands as root. Patch PHP (8.5.12 / 8.4.27 / 8.3.36 / 8.2.35) and the host kernel (fixed upstream in 5.10.257, 5.15.208, 6.1.174, 6.6.141, 6.12.91, 6.18.33, 7.0.10, or your distro's update). PHP2Shell's federal deadline is Oct 29. 3. Jenkins fixed 15 core vulnerabilities (13 High), including a setup-wizard race that lets an unauthenticated attacker create an admin account on a new instance. Upgrade to 2.588 or LTS 2.580.2. 4. Apache Kafka disclosed 12 vulnerabilities. A Kafka 4.1–4.3.0 broker using SASL/OAUTHBEARER with no JWKS endpoint URL (the default) skips signature checks and accepts unsigned tokens (CVE-2026-110985). Kafka Streams apps and consumers using ListSerde can be crashed by a crafted record. Set sasl.oauthbearer.jwks.endpoint.url, then upgrade brokers and client libraries to 4.2.2, 4.3.2 or 4.4.0. 5. PyTorch 2.15 drops Python 3.10, needs NVIDIA driver 580+ and a Turing (sm_75) or newer GPU for the default CUDA wheels, needs macOS 15 for MPS, and writes distributed checkpoints 2.14 can't load. Pin torch. Updates: Node.js 26 'Lithium' became LTS on Oct 28 (26.13.0); the first Node 27 alpha slipped and is expected shortly. Tailscale 1.104.4 fixes its SSH and Serve flaws. On Oct 30, Amazon Pinpoint's console and engagement features end, and Cloud Run functions decommission Node.js 20. ⏱ Chapters 0:00 Intro 0:13 Today's rundown 0:43 GitHub Actions jobs may now run on AWS 1:29 PHP2Shell gets root: a kernel bug joins KEV 1:59 Jenkins fixes 15 core flaws 2:23 Kafka brokers can accept unsigned tokens 2:57 PyTorch 2.15 drops Python 3.10 3:23 Updates 3:48 Sign-off 📰 Sources GitHub Actions multi-cloud runners: github.blog Copilot cloud and the dollar-based Actions pool: github.blog GitHub Docs, included usage: docs.github.com macos-14 retirement: github.blog CISA KEV catalog (feed): cisa.gov CVE-2026-46300 record: cveawg.mitre.org Red Hat on CVE-2026-46300: access.redhat.com Palo Alto advisory: security.paloaltonetworks.com Jenkins Security Advisory 2026-10-28: jenkins.io Apache Kafka CVE list: kafka.apache.org Kafka advisories on oss-security (Oct 28): openwall.com PyTorch 2.15 release notes: github.com/pytorch/pytorch/releases/tag/v2.15.0 Node.js 26.13.0: nodejs.org Node.js 27 alpha release PR: node#67062 Tailscale security bulletins: tailscale.com Amazon Pinpoint end of support: docs.aws.amazon.com Cloud Run functions runtime support: cloud.google.com Just Known Daily: developer news, every day. From unknown to known. A channel by Just Unknown. #technews #github #devops #cybersecurity #pytorch #developers

    56 views
  5. What is the cheapest way to fine-tune Llama on an H100? The cheapest way to fine-tune Llama on one H100 is the lightest method that fits (LoRA for Llama 3.1 8B, QLoRA for a 70B), a checkpoint every few minutes and billing only while the job runs. Nodus lists the H100 SXM at $2.60/hr on its pricing page, stops billing when the job finishes and restores the latest checkpoint on a new machine if the GPU is reclaimed. In our worked example, that keeps a 50-million-token Llama 3.1 8B LoRA run at $8.35, where an idle hour or one unprotected preemption pushes the same run to $10.95 or $15.50. We build Nodus, so weigh that. Memory and speed figures below come from Hugging Face, torchtune and Unsloth, other platforms' prices link to their own pages, and every cost shows its arithmetic. Which fine-tuning method fits Llama on one H100? LoRA and QLoRA fit the 8B comfortably, and only QLoRA fits a 70B. The figures are Hugging Face's own estimates from its Llama 3.1 launch post, which describes them as approximate: | Method | Llama 3.1 8B | Llama 3.1 70B | Fits one H100 80 GB? | |---|---|---|---| | Full fine-tuning | 60 GB | 500 GB | 8B only, with memory-saving settings | | LoRA (16-bit base) | 16 GB | 160 GB | 8B yes, 70B no | | QLoRA (4-bit base) | 6 GB | 48 GB | Both | !Approximate training memory from Hugging Face: Llama 3.1 8B needs 6 GB for QLoRA, 16 GB for LoRA and 60 GB for full fine-tuning; Llama 3.1 70B needs 48 GB, 160 GB and 500 GB. On one 80 GB H100, the 70B has exactly one option: QLoRA. The full fine-tuning figure deserves a second look. The textbook cost of mixed-precision training with the AdamW optimizer is 16 bytes per parameter, as laid out in the ZeRO paper. Llama 3.1 8B has 8,030,261,248 parameters, so that is 128 GB before activations. Hugging Face's lower 60 GB, and torchtune's 18.9 GiB on a 24 GB RTX 4090 in its README, depend on lower-precision optimizer states and other memory savers. For the 70B, Unsloth reports that QLoRA of Llama 3.3 70B fits in 41 GB in its Llama 3.3 post. A 16-bit LoRA needs two or more H100s, which our 70B VRAM guide works through in detail. Is QLoRA cheaper than LoRA for Llama 3.1 8B on an H100? Usually not. QLoRA saves memory, and on an 80 GB card the 8B does not need the saving, while the 4-bit base can slow every step. torchtune measured both on the same hardware. For Llama 3.1 8B on an RTX 4090, LoRA trained at 3,083 tokens per second and QLoRA at 2,413, so QLoRA needed 28% more GPU time (3,083 / 2,413 = 1.28). For Llama 3.2 3B on an A100, the gap was small: 8,210 against 8,035 tokens per second. These are torchtune's numbers on those GPUs, not H100 measurements. !torchtune throughput: Llama 3.1 8B on an RTX 4090 trained at 3,083 tokens/s with LoRA and 2,413 with QLoRA; Llama 3.2 3B on an A100 trained at 8,210 and 8,035 tokens/s. QLoRA's slowdown ranged from 2% to 22% in torchtune's tests, and you pay for every extra second. GPU time is the bill, so the method that fits with the most headroom and runs fastest is the cheap one. For an 8B on an H100, that is 16-bit LoRA. Measure both on 100 steps if your sequences are long, because the trade can shift. What does a Llama fine-tune cost per finished run? It costs the hourly rate times every hour you are billed, not only the training hours. We use one formula: cost per finished run = rate x (training tokens / tokens per second / 3,600 + startup + idle + redone hours) Here is a worked example, built from stated assumptions rather than a measured run. The job is a LoRA fine-tune of Llama 3.1 8B: 25 million tokens of data, 2 epochs, so 50 million training tokens. For speed we use torchtune's 4,699 tokens per second for Llama 3.1 8B LoRA on one RTX A6000. We found no published H100 figure for this exact setup, so the A6000 number is a stand-in: replace it with the tokens per second you measure. We also assume 15 minutes of billed startup and teardown, as in our H100 and B200 cost-per-job post. Training takes 50,000,000 / 4,699 = 10,641 seconds, or 2.96 hours. At $2.60/hr that is $7.70 of training plus $0.65 of startup, before anything goes wrong: | Scenario | Billed hours | Cost | |---|---|---| | Clean run, billing stops when the job ends | 2.96 + 0.25 = 3.21 | $8.35 | | Machine left on for 1 hour after training | 3.21 + 1 = 4.21 | $10.95 | | Preempted at 2.5 hours, no usable checkpoint | 3.21 + 2.5 redone + 0.25 restart = 5.96 | $15.50 | | Preempted at 2.5 hours, checkpoint every 10 minutes | 3.21 + at most 0.17 redone + 0.25 restart = 3.63 | $9.44 | The rate is listed at $2.60/hr on the Nodus pricing page for the H100 SXM 80 GB. Prices checked October 28, 2026. !Cost per finished Llama 3.1 8B LoRA run at $2.60/hr: $8.35 for a clean run, $10.95 with one idle hour, $15.50 after a preemption with no checkpoint, and $9.44 after a preemption with 10-minute checkpoints. One preemption without a checkpoint adds $7.15 to the run; with 10-minute checkpoints it adds $1.09. The lesson for Llama on an H100 is that the waste lines are bigger than any rate difference you can shop for. A one-hour idle tail adds $2.60, or 31% of this run. A lost run nearly doubles it. Why are LoRA checkpoints cheap enough to save every few minutes? Because LoRA trains a tiny fraction of the model, its recovery checkpoint is small. Llama 3.1 8B has 32 layers, a hidden size of 4,096, key and value projections of 1,024 and an MLP size of 14,336, per the model config (we read an ungated copy on Hugging Face). A rank-16 adapter on all seven linear layers adds 16 x (in + out) parameters per layer: 16 x [(4,096 + 4,096) x 2 + (4,096 + 1,024) x 2 + (4,096 + 14,336) x 3] = 1,310,720 per layer Over 32 layers that is 41,943,040 trainable parameters, 0.52% of the model. A full fine-tune has to save optimizer state for all 8 billion parameters, so frequent checkpoints get expensive. With LoRA they barely register, which is why the 10-minute row in the table above is realistic. Nodus resumes from full checkpoints with optimizer, scheduler and RNG state, as the training guide describes. On a reclaim notice it also asks the job for an urgent checkpoint, and a checkpoint committed during that notice counts, per the attempts and recovery docs. Our spot preemption guide covers the same pattern for your own scripts. How do you run a Llama LoRA job on Nodus? First, get access to the weights. Llama models on Hugging Face are gated: you accept Meta's license on the model page, such as meta-llama/Llama-3.1-8B-Instruct, then use a Hugging Face token from that account. The terms are in the Llama 3.1 Community License. Then install the CLI, sign in and store the token as a Secret. These commands are copied from the Nodus training and secrets docs: $ pip install nodus-compute $ nodus login nodus secret create hf-token --from-literal HF_TOKEN=hf_xxx The manifest below is the LoRA example from the training guide with four changes: the Llama model pinned to its current Hugging Face commit, method, epochs, and one H100 under resources. The managed sft runtime publishes presets only for Qwen models, so a Llama run names its GPU. TrainingJobs are Beta, and we have not run this exact manifest, so start with the dry run. apiVersion: nodus.dev/v1beta1 kind: TrainingJob metadata: name: llama-8b-lora spec: runtime: nodus/sft model: uri: hf://meta-llama/Llama-3.1-8B-Instruct@0e9e39f249a16976918f6564b8830bc894c89659 secret: hf-token # only for gated repositories data: volume: support-chats # a Volume holding chats.jsonl path: chats.jsonl format: JSONL columns: {prompt: question, completion: answer} parameters: method: LoRA epochs: 2 lora: {r: 16, alpha: 32} resources: {gpu: {type: [h100-sxm-80g], count: 1}} maxCostUSD: "20.00" $ nodus apply -f sft.yaml --dry-run -o yaml # status.estimate: cost to completion, first hold, start time $ nodus apply -f sft.yaml The runtime saves a recovery checkpoint every saveSteps steps (200 by default), so set it to land roughly every 10 minutes once you know your step time. nodus describe tj/llama-8b-lora shows tokens per second while it trains, which is the number to put into the formula above. The maxCostUSD cap suspends the job at a checkpoint if spending reaches it, per the sft runtime reference and the budgets docs. Where are Modal or RunPod the cheaper choice? Both can beat Nodus on this run, depending on how you work. Modal includes $30 a month of free compute on its Starter plan, and charges $0.001097 per second for an H100, or $3.9492/hr, according to Modal's pricing page. The clean run above bills 3.21 hours, which is $12.68 at that rate. That fits inside the monthly credit, so an occasional small Llama fine-tune can cost you nothing out of pocket on Modal. Modal restarts a preempted function on the same input, and you write the checkpoints yourself, per its preemption docs. RunPod lists the H100 SXM at $2.69/hr on Community Cloud and $3.99/hr on Secure Cloud on its pricing page. If you stop the Pod the moment training ends, the clean run costs $8.63 on Community Cloud, 28 cents more than our example. Pods are billed by the second until they stop, per RunPod's Pod pricing docs, so one forgotten hour makes it $11.32. RunPod also offers a form-driven Axolotl fine-tuning flow in its fine-tuning docs, and a Pod gives you a full machine to work in interactively. Pick Modal if your runs are small and occasional, or your code already runs as Modal functions. Pick RunPod if you want a machine you drive by hand and you reliably shut it down. Pick Nodus if your runs are long enough that idle time and preemption are the real cost. Our fine-tuning platform comparison covers per-token APIs as well. Which setup should you pick? Use this rule, in order: 1. Llama 3.1 8B: 16-bit LoRA on one H100. Do not pay QLoRA's slowdown for memory you have. If you do not need an H100's speed, a 24 GB card is cheaper; our Unsloth guide shows the break-even. 2. A 70B Llama on one H100: QLoRA. A 16-bit LoRA needs two or more GPUs. 3. Full fine-tuning: only after LoRA has measurably fallen short. For comparison, the Nodus sft runtime's preset for a full fine-tune of Qwen3-8B is 8 x H100. 4. Every run: billing that stops when training stops, a checkpoint at least every 10 minutes, and 100 measured steps before you commit the full budget. FAQ Can I use a fine-tuned Llama commercially? Yes for most companies, under the Llama 3.1 Community License. If you distribute the model or a product built on it, the license requires "Built with Llama" to be displayed and the model's name to start with "Llama". Companies above 700 million monthly active users need a separate license from Meta. How long a context can I fine-tune a 70B Llama on with one 80 GB GPU? Unsloth reports up to 89,000 tokens of context for Llama 3.3 70B QLoRA on an 80 GB GPU with its long-context settings, against 6,900 with Hugging Face plus Flash Attention 2. Context length drives activation memory, so it is often what pushes a 70B run off a single GPU. Should I pick a B200 instead of an H100 for Llama fine-tuning? Only if it finishes faster than its price ratio. The B200 is listed at $5.50/hr on the Nodus pricing page, 2.1 times the H100's $2.60, so it has to train more than 2.1 times faster to cost less per finished run. Does Nodus pick the GPU if I leave resources out? For Llama, the sft runtime falls back to its default: one RTX 4090, L40S, RTX A6000 or A100 80 GB. That suits QLoRA of an 8B but not a 70B, so name the H100 explicitly when you need 80 GB. Originally published on the Nodus blog.

  6. TorchTitan's October update is out. I fixed 2 bugs in its quick-start script --- title: "TorchTitan's October update is out. I fixed 2 bugs in its quick-start script" published: true description: "Step 3 of the torchtitan quick start runs download_hf_assets.py. I found a tuple-shaped error and a list-mutation bug in it. PR #5621 fixed both." tags: pytorch, python, opensource, machinelearning cover_image: raw.githubusercontent.com --- <!-- Images are pushed to Sonica-B/Sonica-B under blog/, so the URLs below resolve. The interactive version of figure 1 is visibility/anatomy/titan-card-punch/titan-card-punch.html. --> TorchTitan Update, October 21, 2026. During PyTorch Conference North America in San Jose, the PyTorch team published TorchTitan Update: New SOTA Models, Performance at Scale, TitanRL. It walks through new support for DeepSeek-V4, Kimi K3 and Qwen3.5, introduces TitanRL for reinforcement learning, and reports DeepSeek-V3 671B training at 2,694,493 tokens per second on 512 GB300 GPUs. The post ends by telling you to clone the repo and follow the quick start. Step 3 of that quick start downloads a tokenizer with scripts/download_hf_assets.py. I found two bugs in that script, and a test that passed the whole time had been hiding one of them. I opened PR #5621 on October 25, and it merged on October 27. The update covers the scale end of torchtitan. My side quest happened at the other end, in the script a new user runs before training a real model. The short version The download function was handed a list of what to fetch and wrote into that list instead of its own copy, so the caller's list came back one entry longer. Call it twice with the same list and the second call rejects the entry the first call added. The same function also raised an error built from three separate strings, so its message printed as a tuple with literal \n in it, and the test that checked the message could not tell. The fix copies the list and builds one message. The concept, animated !Animated figure. Two frames on a plate, the caller and download_hf_assets(). The caller's asset_types list is a tray with one chip, "tokenizer"; a wire from the function's parameter asset_types to that tray reads "same object". The function's .append("additional_patterns") drops a second chip into the caller's tray; on the second call the ASSET_PATTERNS table (tokenizer, safetensors, index, config) rejects it with ValueError: Unknown asset type additional_patterns. After #5621 a copy, list(asset_types), appears inside the function, the wire points to it, and the caller's tray keeps one chip. Figure 1. Trays are list objects and chips are their entries; the wire shows which object the parameter name asset_types refers to. The three states: the first call before the fix, the second call with the same list, and a call after the fix. download_hf_assets.py is the torchtitan script that downloads a model's tokenizer, config, safetensors index or weights from the Hugging Face Hub before training. You tell it which assets you want as a list, and when you also pass extra file patterns it adds a placeholder entry, additional_patterns, to that list so the download loop picks them up. The catch is in the word "that". If you passed a string, the function wrapped it in a fresh list and the placeholder went into the fresh list. If you passed a list, the placeholder went into your list. Your list is now ['tokenizer', 'additional_patterns']. The first call still works, because the table of known assets is rebuilt every call and the placeholder is matched during that same call. The second call, with the same list, meets an entry nobody asked for, finds no pattern for it, and stops: Unknown asset type additional_patterns. Anyone looping over several repos with one shared list of asset types hit this on the second repo. The second bug, and why a passing test hid it I found the function through a typo. I typed --assets tokenizers, with a stray "s", and instead of a readable message I got this: ValueError: ('Unknown asset type tokenizers. Available uses: --assets tokenizer safetensors index config \n', "Or specify exact patterns to download. Example: --additional_patterns '*.safetensors' README.md '*.json' \n", 'Or use --all to download all files') That is a tuple, with literal \n escapes inside it. The error was raised with three comma-separated strings, and when an exception has more than one argument Python prints the whole tuple as its message. The existing test for this case used assertIn on a piece of the message. A substring of a tuple's printed form is still a substring, so the test stayed green for as long as the bug existed. A short repro with the Hub calls mocked confirmed both bugs: it printed args: 3, and after one call my list read ['tokenizer', 'additional_patterns']. Before writing any code, I searched torchtitan's issues and PRs for the function and both messages. Neither bug had been reported. The fix, in one idea !Still from the third state: a second tray labelled list(asset_types), the copy, sits inside the function holding "tokenizer" and "additional_patterns", the wire from the parameter points to it, and the caller's tray holds "tokenizer" alone. Figure 2. After #5621 the function copies the list first. The copy gets the placeholder entry and the download loop reads the copy; the caller's list is exactly as it was passed in. Work on a copy. The function now copies the list it was given before adding anything to it, so the caller's list is untouched: list(asset_types) instead of using the caller's object. And the error is now one message built from one string, so it prints as three readable lines. Two edits in one file, 35 lines added and 8 removed across the script and its tests. The tests changed with it. The existing one now asserts that the exception has exactly one argument before it checks the text, which is what would have caught the tuple. A new one calls the function twice with one list and additional_patterns=["*.txt"], then checks that the list is still ["tokenizer"] and that both calls downloaded the same files. What changed for torchtitan users A mistyped asset type now prints three readable lines: Unknown asset type tokenizers. Available uses: --assets tokenizer safetensors index config, then the --additional_patterns example, then the --all option. Scripts that loop over several repos with one shared asset_types list now work past the first repo. Which files get downloaded does not change. The one behaviour change is for a caller that relied on the mutation. I searched the repo for callers of the function and found none that did. test_tokenizer.py passes a string, which still gets wrapped in a list. I ran everything on Windows 11 with Python 3.11 and torch 2.16.0.dev20261024+cpu. pre-commit passed flake8, ufmt, pydoclint, codespell and Pyrefly on both files. The lychee link checker needs /bin/bash, so it could not start on Windows. I did not run anything on Linux or GPU, and the PR description says so. Thank you, tianyu-l Thank you to @tianyu-l for approving PR #5621 and merging it on October 27, two days after I opened it. It was my first PR to the repo, so CI also needed a maintainer to approve the workflow runs before it could start. Thanks as well to the PyTorch distributed team at Meta who maintain torchtitan. The repo merged 392 PRs in the 30 days before I opened mine, and outside fixes still got a first review within 0 to 2 days. I modeled my PR description on #5040, an earlier outside fix to the same function by u7k4rs6. Because #5040 had just touched that function, I picked the smallest change over a rewrite. What I took away I don't count a test as protection until I have watched it fail on the bug. The old test stayed green for as long as the tuple message existed. So after writing the new assertions, I reverted only the script fix and kept the tests: 2 failed and 11 passed. With the fix back, all 13 passed, along with the 40 tests in tests/unit_tests/cpu/scripts. That revert is the cheapest check I know, and it changes what I can put in a PR. "Tests pass" becomes "these 2 tests fail on main and pass with the change," which a reviewer can verify without taking my word for it. Listing what I did not run does the same job for the parts I could not test. I'm an ML engineer with a DevOps background, and I'm open to ML engineering roles. My code is on GitHub as Sonica-B, my projects are at shreyaboyane.dev, and I'm on LinkedIn. <!-- optional disclosure she may keep: An AI coding agent helped me probe the package and draft this fix; I reviewed and verified the change before submitting. -->

  7. NCCL Roadmap Nov 2026 - Jan 2027 の解説 はじめに ! NCCL 奥がふかすぎる。。。。にゃ。。。 NVIDIA が公開した NCCL Roadmap: Nov 2026 - Jan 2027 (GitHub Issue #2486) から、大規模分散学習・推論に携わるエンジニアが押さえるべきポイントを解説します。前回のロードマップの解説は「NCCL Roadmap Aug-Oct 2026 の解説」、そこで挙げた項目の現在までの実装状況は「NCCL 2.32.3 でロードマップはどう実装されたか」にあります。GIN の基本は前回の解説を前提にします。 この記事では、新しい概念の GENK と Flow、AWS EFA と GIN の現状、注目機能 2 つを扱います。調査は 2026 年 10 月 29 日に行い、まだリリースされていないコードは、staging ブランチを次のリリースの候補とみなして読んでいます。 実装状況の確かめ方 NVIDIA/nccl、aws-ofi-nccl、amazon-contributing/DeepEP、NVIDIA/nccl-extensions の公開ブランチを手元に取得し、commit の履歴とソースを読んで確かめました。PR と Issue の状態 (open か、マージされたか、閉じた日) は GitHub で確かめています。記事のソースへのリンクは、確かめたときの commit に固定しています。 NCCL は master、staging、dev の 3 本のブランチを公開していて、staging には 2.32.5-1 の版番号を付けた commit があり、その後に 2.32.6a0 に上がっています。ロードマップは「Coming Soon: NCCL 2.32 Update 1」の版番号を書いていませんが、この記事では staging のコードをその候補として読みます。 今回のロードマップの項目のうち、この記事で扱うものと、前回のロードマップの近い項目は次のとおりです。対応は筆者の読みです。 今回のロードマップの項目 前回のロードマップの近い項目 General Kernels (GENK) Unify legacy kernels using Device API Flow Device API More Device API primitives GIN Backend Improvements GIN backend improvements CFT を使う階層型カーネル Compute Fabric Transport (CFT) through Device API New Enqueue Pipeline Legacy and symmetric-kernel host-side unification ロードマップには、内部のメモリの削減、RAS の進み具合の監視、RMA の profiling など、この記事で扱わない項目もあります。 Device API とは何か NCCL の API の全体像 ロードマップの項目を説明する前に、まず背景知識として NCCL の API を整理します。NCCL の API は、Host API、Device API、plugin API の 3 つに分けられます。Host API と Device API の仕様は NCCL の公式文書にあり、この記事の表はその章立てに沿っています。plugin API は公式文書の API の章には無く、リポジトリの plugins ディレクトリに plugin の種類ごとの説明があります。 API 何をするか 例 Host API CPU から呼んで、communicator (通信に参加する GPU の集まり) の作成や集団通信を NCCL に頼みます。集団通信では、NCCL がアルゴリズムを選び、NCCL のカーネルを GPU で起動します。利用者の計算は別のカーネルです ncclCommInitRank、ncclAllReduce Device API 利用者が書いた CUDA カーネルの中から、ほかの GPU との通信を直接呼びます (2.28 から)。計算と通信を 1 つのカーネルに入れられます。使う前の準備には Host API を使います GIN、LSA plugin API NCCL のほうから呼ぶ、下の部品の差し替え口です。ネットワークへの送受信や、アルゴリズムの選択の調整などを、NCCL の外の実装に任せます net、GIN。説明は plugins にあります。aws-ofi-nccl は EFA 用の plugin 誰が NCCL を呼ぶか NCCL を呼ぶのは、多くの場合アプリ自身ではなく、間に入るフレームワークやライブラリです。主な呼び手を、ソースで確かめた範囲で並べます。 呼び手 どう呼ぶか 使う API PyTorch の torch.distributed (backend "nccl") C++ の ProcessGroupNCCL が、ncclAllReduce などを直接呼びます Host API PyTorch の backend "nccl4py" (torch.distributed._nccl4py) Python で書いた backend が、nccl4py を通して呼びます。init_process_group("nccl4py") で選びます Host API nccl4py NVIDIA の公式の Python の bindings。Cython で libnccl を呼び、PyTorch の tensor や CuPy の配列をそのまま渡せます。実験的な CuTe DSL の部品もあります 主に Host API vLLM の PyNccl Python の ctypes で libnccl を読み込んで呼びます Host API PyTorch の symmetric memory の演算、DeepEP、NCCL EP 自分の CUDA カーネルの中から通信を呼びます。準備には Host API で window (次の節で説明します) を登録し、device communicator を作ります Device API と Host API aws-ofi-nccl など NCCL のほうから呼ばれます。NCCL が EFA などのネットワークに送るときの下の部品です。InfiniBand には NCCL に組み込みの経路もあります plugin API Device API は何のためにあるか Host API で通信を頼むと、通信は NCCL のカーネル 1 回分の単位になり、利用者の計算とは別のカーネルになります。計算の途中で決まった相手に少しずつ送りたい場合や、計算と通信を細かく重ねたい場合には、この単位が粗すぎます。たとえば MoE のトークンの振り分けでは、どのトークンをどの GPU に送るかが計算の途中で決まります。 Device API は、利用者のカーネルの中から、ほかの GPU のメモリへ直接データを動かせるようにするための API です。NCCL の Device API の例は、計算と通信を分けて順に行う従来のやり方に対して、カーネルの中で両方を細かく混ぜることで、待ち時間を減らし処理量を上げられると説明しています。 GPU のカーネルからほかの GPU に届く経路はいくつかあり、Device API は経路ごとに部品を用意しています。同じノードで NVLink や PCIe の P2P でつながった相手には、相手のメモリを普通の読み書きの命令で触れます (LSA)。NVLink のスイッチが複数の宛先への書き込みや足し合わせを引き受けられるシステムでは、Multimem を使います。ノードをまたぐ相手には、NIC を通して put (相手のメモリへの書き込み) や get (相手のメモリからの読み出し) を出します (GIN)。どちらも、相手が何も呼ばない片側通信です。CFT は、相手のメモリを番地 (ポインタ) で触る代わりに、CUDA が用意する宛先の番号 (logical endpoint) とオフセットを指定して put や get を出す部品で、Blackwell 以降の GPU で使えます。これらと組み合わせて使う、足し合わせやコピーの部品 (reduce と copy) も用意されています。 GIN の仕組みは、前回の解説の「GIN とは何か」で説明しました。LSA は後の GENK と Flow の節で、CFT は注目機能の節で扱います。Multimem と reduce と copy の部品は、この記事では詳しく扱いません。 どの部品を使う場合も、準備は利用者が CPU で Host API を呼んで行います。通信に使うバッファを、全ランクで同じ形に対応付けた領域 (symmetric memory window) として登録し、カーネルが使う資源を指定して device communicator を作り、それを渡して自分のカーネルを起動します。手順の例は NCCL の文書にあります。GIN を使うカーネルは、NCCL を更新したら作り直す必要があると文書は書いています。次の節からは、この Device API の上に加わる新しい部品とカーネルを見ていきます。 GENK と Flow とは何か ! GENK と Flow は、まだリリースされておらず staging と dev にだけあるコードです。この節は staging の実装から読み取った内容で、リリースまでに変わる可能性があります。staging で試すには、ビルドのときに SYM_GENK=1 を指定し、実行のときに NCCL_SYM_GENK_ENABLE=1 を設定する必要があります。 概要 Flow は、前の節の LSA と GIN の上に作られた、集団通信の部品を提供する Device API です。ロードマップは Flow を「LSA と GIN の両方で動く、高水準の Device API」と説明しています。LSA は前の節で触れた、相手の GPU のメモリを普通の読み書きの命令で触る部品です。 GENK (General Kernels) は、NCCL が自分の Ring と Tree の集団通信を、この Flow の上で作り直したカーネルの一群です。Ring では AllGather、ReduceScatter、Broadcast、Reduce、AllReduce を、Tree では AllReduce を実装しています。 前回のロードマップには「Unify legacy kernels using Device API」という項目がありました。筆者は、GENK をこの項目につながる取り組みと読んでいます。今回のロードマップは、選んだプラットフォームで GENK を既定にし、古いカーネルを置き換えるとしています。あわせて性能モデルを 1 つにまとめ、どの実装を選ぶかを同じ物差しで決めるとしています。 staging では、NCCL 自身が同じ Flow で集団通信を組んでいます。Flow がリリースに入れば、利用者も NCCL の内部と同じ部品で通信カーネルを書けます。 NCCL の集団通信には、GENK の前から 2 種類のカーネルがあります。 legacy kernels: NCCL に最初からある Ring や Tree のカーネルです。経路の違いは、CPU の側が準備する transport (NVLink や PCIe の P2P、ネットワークなど) が受け持ち、ネットワークでは CPU の proxy スレッドが NIC に渡します。 symmetric カーネル: 全ランクが同じ形で登録したバッファ (symmetric memory window) を集団通信に渡したときに使える、新しいカーネルです。相手のバッファの番地が分かるので、Device API の LSA などで相手のメモリを直接読み書きします。いまあるものは、NVLink の直結を前提に、場面ごとに作り込まれています (表の「特化した symmetric カーネル」)。 GENK は、symmetric カーネルと同じ Device API の作りで、legacy kernels と同じくらい広い経路で動くものです。Ring で隣の GPU にデータを渡す場面で比べると、次のようになります。 隣の GPU との経路 legacy kernels 特化した symmetric カーネル GENK NVLink 動く 動く 動く (LSA) PCIe の P2P 動く 使えない 動く (LSA) 別のノード (ネットワーク) 動く 一部の種類だけ動く (GIN) 動く (GIN) 表は、Hopper 以降の GPU で GENK を有効にした場合についてソースから読んだもので、動かして確かめたものではありません。PCIe の行は CUDA P2P が使える場合、GENK のネットワークの行は GIN Proxy (GPU の代わりに CPU のスレッドが NIC に送る GIN のバックエンド) が使える場合です。 特化した symmetric カーネルは、PCIe だけでつながったノードでは候補から外れます。GENK では、隣の GPU ごとに Flow が LSA か GIN かを実行時に選ぶので、同じカーネルのまま NVLink でも PCIe でもネットワークでも動きます。staging のカーネルの選択にも「特化したカーネルは NVLink の直結を要するが、GENK は CUDA P2P の LSA や GIN も使える」というコメントがあります。 利用者の側の例もあります。NCCL の Device API の例には、Transformer の RMSNorm の計算を ReduceScatter と AllGather の通信と 1 つのカーネルにまとめるものがあり、いまは LSA、Multimem、GIN を直接使って、受け取り、足し合わせ、送り出しを書いています。Flow は、NCCL 自身が集団通信に使うこの受け取り、足し合わせ、送り出しの部分を、部品として提供します。 NCCL の文書の Device API の章には、Flow と GENK の説明はまだありません。Flow の使い方は、ヘッダの説明にまとまっていて、Flow processor を足した commit が入口になります。GENK は、カーネルを足した commit と、NCCL の選択に組み込んだ commit が出発点です。 図で [API] のタグを付けた Flow processor、LSA connection、GIN connection は、アプリのカーネルからも呼べる Device API です。cost model、legacy kernels、specialized symmetric kernels、GENK は NCCL の中の部品です。 GIN の signal とは GIN の put は片側通信で、受け手は何も呼ばないので、データがいつ届いたかを自分では知れません。signal は、それを知らせるための、受け手の側に置かれた数です。送り手が put に signal を付けると、データが届いたあとに受け手の数が増え (たとえば 1 増え)、受け手はその数が目標に達するまで待ってから読みます。GIN の基本は、前回の解説の「GIN とは何か」で説明しました。 signal には strong と weak の 2 種類があります。weak signal が「読んでよい」と約束するのは、その signal を付けた put のデータだけです。strong signal は、同じ相手に同じ context (GIN の通信の通り道の 1 本) で先に出した put のデータもすべて読めると約束します。そのため、最後の put にだけ signal を付ければ済みます。 EFA の送り方の SRD は、書き込みが届く順番を保ちません。そのため、最後の put に付けた signal が届いても、前の put のデータが届いたとは限りません。EFA の上の経路のうち、GPU から直接送る EFA GDA が提供するのは weak signal だけです (次の節で説明します)。一方、Flow の GIN の接続は strong signal を必要とします。 AWS EFA 関連アップデートと現在の課題 EFA の 2 つの GIN の経路と signal の種類 ! GENK と Flow の GIN の接続は strong signal を必要とします。EFA の上で strong signal を提供するのは GIN Proxy だけで、GPU から直接送る EFA GDA は weak signal しか提供しません。そのため、EFA の複数ノードで GENK を使うなら Proxy の経路になります。 EFA で GIN を使うときは、使う経路 (GIN のバックエンド) が、カーネルの必要とする種類の signal を提供するかを先に確かめます。NCCL は、カーネルが求める種類の signal を提供できないバックエンドを使わず、提供できる別のバックエンドがあればそちらに切り替え、無ければエラーにすると説明しています。EFA で使う GIN の経路として、ここでは 2 つを比べます。1 つは NCCL の GIN Proxy で、CPU のスレッドが aws-ofi-nccl の RMA の機能を通して EFA に送ります。もう 1 つは EFA GDA で、GPU のカーネルが EFA の送信キューに直接依頼を書きます。aws-ofi-nccl の文書によると、EFA GDA は NCCL_GIN_TYPE=5 で明示したときに使われます。同じ文書は、明示しなければ NCCL 2.31 は EFA で Proxy を使うと書いています。インスタンスやドライバなどの要件も同じ文書にあります。 2 つの経路は、提供する signal が違います。NCCL の Proxy は、strong signal と VA signal (登録した仮想アドレスに置く signal) を使えると報告します。EFA GDA については、aws-ofi-nccl が strong signal も VA signal も使えないと報告し、NCCL の側も番号で指す indexed signal だけを受け付けます。EFA GDA の signal は、EFA の NIC が届いた書き込みを数えるカウンタで作られています。aws-ofi-nccl の文書は、signal と counter の合計が EFA のデバイスあたり 512 個までであることも、制限に挙げています。 図の矢印は、使い手が必要とする signal をバックエンドが提供するかを示します。矢印はソースと文書から描いたもので、どの経路が選ばれるかや、動かした結果を示すものではありません。図の DeepEP の ordered は届く順番を前提にする上流のカーネル、unordered は順番を前提にしない amazon-contributing のフォークのカーネルです。 GENK はノードをまたぐ通信に使う GIN のバックエンドとして Proxy を指定しているので、staging のコードを読む限り、GENK が EFA の上で複数ノードの通信をするなら Proxy の経路を通ります。一方、aws-ofi-nccl の文書は、EFA GDA を使うときに symmetric の GIN カーネルを無効にするよう書いていて、この設定 (NCCL_SYM_GIN_KERNELS_ENABLE=0) にすると複数ノードの GENK は候補から外れます。つまり、EFA GDA を使う設定 (NCCL_GIN_TYPE=5 と NCCL_SYM_GIN_KERNELS_ENABLE=0) では複数ノードの GENK は使われず、複数ノードで GENK を試すときは、この 2 つの環境変数を設定せずに Proxy の経路で動かします。 aws-ofi-nccl v1.22.0 で、EFA GDA の Get がタグの付いた版で使えるようになった Get は、GPU のカーネルが、ほかの GPU のメモリの中身を、持ち主に何もさせずに読み出す操作です。たとえば、手元の番号を見て、ほかの GPU が持つ表の行を取り出すような使い方ができます。Get の意義は「NCCL 2.32.3 でロードマップはどう実装されたか」で説明しました。 その記事の時点では、NCCL 2.32.3 には Get が入っていましたが、aws-ofi-nccl の側の対応はタグの無い master にしかありませんでした。2026 年 10 月 22 日 (日本時間) の v1.22.0 は、NCCL 2.32 が使うキューの形への対応と、Get の完了を数える修正を含む最初のタグです。これで、NCCL 2.32 と aws-ofi-nccl v1.22.0 という、タグの付いた版どうしに、EFA GDA の Get に要る変更がそろいました。v1.22.0 のリリースノートは Get に触れておらず、筆者は動かして確かめていません。 ただし、EFA GDA の Get には、完了を知らせる signal や counter を付けられません。読み出しが終わったかは、出した依頼をまとめて待つ Flush で確かめます。 MoE の通信ライブラリ (DeepEP と NCCL EP) の動き MoE のトークンを担当の expert に配り (dispatch)、計算結果を集める (combine) 通信のライブラリにも動きがありました。DeepEP と NCCL EP はこの同じ役割の候補で、どちらも下で NCCL を使います。DeepEP は 2026 年 9 月 29 日の V2.5 で NVSHMEM への依存を取り除き、ノードをまたぐ通信は NCCL の GIN だけになりました。要件は NCCL 2.32.3 以降です。EFA で DeepEP を動かす取り組みは amazon-contributing/DeepEP のフォークにあり、その詳細は別の記事で整理してみます。 NVIDIA の NCCL EP は NVIDIA/nccl-extensions に移り、NCCL の staging からは、nccl_ep と nccl_m2n のディレクトリが消されました。 注目機能 2 選 CFT を使う階層型カーネル CFT (Compute Fabric Transport) は NCCL 2.31 で Device API に加わった部品で、嬉しさは 2 つあります。1 つ目は、カーネルから直接届く相手を、LSA の範囲より広げられる見込みがあることです。LSA で届く相手は、CUDA P2P で相手のメモリを普通の読み書きの命令で触れる相手 (LSA team) に限られます。CFT は相手を番地ではなく CUDA が配る宛先の番号で指すので、届く範囲は、CUDA が「この番号で届く」と報告する範囲で決まります。前回のロードマップも、CFT を、logical endpoint で届く範囲の相手を宛先の番号で指す通信の形と説明していました。ただし、リリースされた版では、CFT で届く相手の集まり (CFT team) の大きさは既定で LSA team と同じです。 2 つ目は、受け手に届いたことを早く知らせる書き込みがあることです。CFT の普通の put や reduce で受け手に知らせるときは、データを書いたあと、書き込みの完了を確定させ (fence)、合図のフラグを書きます。これが counted な書き込みで、フラグの代わりに、NCCL_WIN_CFT_COUNTED を付けて登録した window の中のカウンタを使います。カウンタはデータより後に進むことが保証されているので、送り手は fence とフラグの書き込みを省けます。staging の文書は、これを低遅延のための操作と説明しています。受け手は、カウンタを見てからデータを読む間に fence を入れる必要があります。 仕組みは次のとおりです。CFT の宛先の番号は、CUDA 13.3 で加わった logical endpoint という仕組みです。NCCL は CPU の側で、宛先の番号を確保し、そこに各 GPU のバッファを結び付けておきます。カーネルは、送りたいデータを shared memory に置き、宛先の番号とオフセットを指定して put を出し、そのあと完了を確かめます。 dev では、CUDA 13.4 以降で CUDA が「logical endpoint で届く範囲」を報告すると、CFT team をその範囲から作ります。その範囲は LSA の範囲の倍数でなければならず、LSA より広い範囲を 1 つの CFT team にできる作りです。NCCL の文書は、CFT がどの物理的な経路を通るかを書いていません。NCCL EP の CFT を使う経路は、1 つの NVLink のドメインの中だけを対象にしています。 ロードマップは、この CFT を使って、AllGather、ReduceScatter、AlltoAll の階層型カーネルを作るとしています。階層型は、近い GPU の間と遠い GPU の間で通信を分ける作りで、LSA の範囲と、それより広い CFT team の範囲を使い分けることになると筆者は読んでいます。master、staging、dev を調べた範囲では、CFT の宛先の集まりの階層の種類は 2.32.3 のコードに定義されていますが、それを使う集団通信のカーネルは見当たりませんでした。 CFT とは CFT が使えるのは、ハードウェア、ドライバ、CUDA が logical endpoint と fabric 用の命令に対応したシステムだけで、例の barrier のプログラムは CUDA 13.3 と SM_100 を要件に挙げています。CFT team を CUDA の報告する範囲 (clique) から作る変更は、dev の 2026 年 9 月 28 日の commit で入り、master と staging にはまだありません。 NCCL EP には、CFT の counted な書き込みで低遅延の dispatch を送る経路が、NCCL_EP_LL_CFT=1 で選んだときだけ使われる形で、2026 年 9 月 16 日の commit から main に入っています。 New Enqueue Pipeline: 呼び出しから起動までを 3 つの層に分ける enqueue は、アプリが呼んだ集団通信や put を、カーネルやコピーの起動に変える NCCL の中の流れです。ロードマップは、これを管理 (Management)、準備 (Preparation)、スケジューリング (Scheduling) の 3 層に分け、制御の流れを簡単にし、カーネルの最適化をしやすくするとしています。 管理と準備の層を含む基本の枠組みは 2.31.2 から入っていて、その時点のスケジューリングの層は既存の経路に戻すだけでした。いまの staging と dev でも、新しい流れは既定で無効です。dev では、Preparation の tuning ですべての集団通信のアルゴリズムやプロトコルを先に選び、classification でその結果だけを使って起動の経路を振り分ける形にしました。経路ごとのスケジューラは、それぞれの仕事を stream ごとに 1 つのキューにまとめます。dev の commit では、新しい流れの symmetric のスケジューラが起動するのは特化した symmetric カーネルだけで、GENK は自分用の device communicator の準備が要るので、従来の流れの symmetric カーネルのスケジューラから起動されます。 まとめ staging の GENK は、Hopper 以降の GPU でビルドと環境変数で有効にすると NVLink、PCIe、ネットワークのどの経路でも同じカーネルが候補になり、EFA の複数ノードでは strong signal を提供する GIN Proxy の経路を使います。CFT を使う階層型カーネルは部品だけが公開コードにあり、New Enqueue Pipeline は枠組みが既定で無効のまま dev で進んでいます。

  8. I have contacted @nvidia to work out the proper fix. I opened an issue in @PyTorch pytorch#202035 & wrote a mitigation for pytorch, a byte-span guard before cuDNN's backward github.com/speridlabs/cudnn-sdpa-research/blob/ma… Meanwhile i recommend pass the incoming gradient through .contiguous() or force FLASH_ATTENTION. The full story is here speridlabs.com

    6 likes · 67 views
  9. MPS backend out of memory: the "max allowed" number on every Mac size --- title: "MPS backend out of memory: the \"max allowed\" number on every Mac size" published: true description: "Read off 167 public error reports: what \"max allowed\" is on 8, 16, 24, 32, 36, 48, 64 and 128 GB Macs, and how it went up on the bigger Macs from late 2025." tags: pytorch, python, macos, machinelearning series: Big models on a 48 GB Mac canonical_url: allkeep.org cover_image: allkeep.org --- The PyTorch error on a Mac carries one number that people search for. This one is from a 16 GB Mac (AudioLDM2 #20): RuntimeError: MPS backend out of memory (MPS allocated: 18.02 GB, other allocations: 107.10 MB, max allowed: 18.13 GB). Tried to allocate 4.50 MB on private pool. In my earlier note I measured where it comes from on my 48 GB Mac. "max allowed" is PYTORCH_MPS_HIGH_WATERMARK_RATIO, 1.7 by default, times the GPU working set macOS recommends, which torch.mps.recommended_max_memory() returns. One machine can't tell you what that number is on the others, so I read it off other people's errors. Where the numbers come from On 24 October I searched GitHub for issues that quote the error with its "max allowed" value. That gave 250 issues and pull requests, 167 of them with a number in the text. Most values fall into a few groups, and where a reporter named their Mac's memory, the groups line up with the size. Divided by 1.7, each value gives the working set macOS gave the GPU on that machine. | Mac memory | max allowed, GiB | GPU working set, GiB | share of memory | reports | seen | |---|---|---|---|---|---| | 8 GB | 9.07 | 5.33 | 67 % | 27 | 2023 – 2026 | | 16 GB | 18.13 | 10.67 | 67 % | 18 | 2023 – 2026 | | 24 GB | 27.20 | 16.00 | 67 % | 9 | Jan 2025 – Jan 2026 | | 24 GB | 30.19 | 17.76 | 74 % | 3 | Dec 2025 – Sep 2026 | | 32 GB | 36.27 | 21.33 | 67 % | 6 | 2024 – 2025 | | 36 GB | 45.90 | 27.00 | 75 % | 3 | 2024 – 2026 | | 48 GB | 63.65 | 37.44 | 78 % | 4, plus mine | Sep – Oct 2026 | | 64 GB | 81.60 | 48.00 | 75 % | 7 | Dec 2023 – Apr 2025 | | 64 GB | 88.13 | 51.84 | 81 % | 6 | Mar – Oct 2026 | | 128 GB | 163.20 | 96.00 | 75 % | 4 | 2024 – 2025 | | 128 GB | 182.78 | 107.52 | 84 % | 2 | Dec 2025 – Jan 2026 | !The share of memory macOS gives the GPU, by Mac size: two thirds up to 32 GB and three quarters above in reports until late 2025; 74 %, 78 %, 81 % and 84 % on 24 GB, 48 GB, 64 GB and 128 GB Macs in reports since late 2025 Older PyTorch versions label these numbers "GB" and newer ones "GiB". Both are the binary unit: 1.7 × two thirds × 16 GiB is exactly 18.13. What changed Until late 2025 the rule looks simple. macOS gave the GPU two thirds of memory on Macs up to 32 GB and three quarters above that. Reports from late 2025 on show larger shares on the 24 GB, 64 GB and 128 GB Macs. The three 64 GB reports that name their system all say macOS 26. The 8 and 16 GB values haven't moved. My own 48 GB Mac runs macOS 26.5.2 and returns 37.44 GiB, 78 %. Two limits on what this shows: the newer rows rest on two to six reports each, and I can't tell from them whether macOS or the chip made the difference. What it means when you see the error At the default ratio the limit is above physical memory on every row of the table: 1.7 × two thirds is 113 % of memory, and 1.7 × 84 % is 143 %. By the time a job reaches "max allowed", it has been swapping for a while. That matches my own measurement: on my Mac, swap began between 33.5 and 36 GB of footprint, far below the 63.65 GiB limit. The advice at the end of the error, a ratio of 0.0, removes the limit, so the job can swap further. If you'd rather get a clean error than a swapping Mac, set the limit below your memory, for example a ratio of 1.0, which is the working set itself. Set the low ratio with it: on PyTorch 2.14, a high ratio below 1.4 set on its own fails with "invalid low watermark ratio 1.4". PYTORCH_MPS_HIGH_WATERMARK_RATIO=1.0 PYTORCH_MPS_LOW_WATERMARK_RATIO=0.8 python your_script.py To see your own Mac's working set: import torch print(torch.mps.recommended_max_memory() / 2**30, "GiB") The numbers that don't fit About a third of the reports show other limits, such as 1.70 GiB, 3.40 GiB, 6.77 GiB or 3.00 GiB. Those aren't the Mac's default. An app or a user set its own ratio or cap: one video tool caps MPS at 3 GiB even on a 128 GB Mac. Another 20 reports sit at 7.93, and I couldn't tie that value to one Mac size. If your number is far below the table, look in the tool's settings before you blame the Mac. Sources Two reports for each row. The full list of 167 is in the data file. - 8 GB, 9.07: noScribe #84, pytorch #105839 - 16 GB, 18.13: pinokio #941, AudioLDM2 #20 - 24 GB, 27.20: pytorch #145374, qwen3-optimizer-study #13 - 24 GB, 30.19: ml-bokeh #3, ComfyUI #17264 (ratio 1.0, so 17.76) - 32 GB, 36.27: ComfyUI-LivePortraitKJ #82, instructlab #2568 - 36 GB, 45.90: ACE-Step-1.5 #282 - 48 GB, 63.65: trellis2-mlx #1, ask-physics #24 - 64 GB, 81.60: bobo-fun #8, ComfyUI-CogVideoXWrapper #312 - 64 GB, 88.13: EtherReel-releases #7, ComfyUI #17309 - 128 GB, 163.20: ComfyUI #9308, lm-evaluation-harness #1835 - 128 GB, 182.78: ComfyUI #11584, ComfyUI-WanVideoWrapper #1856

    1 reaction · 1 comment
  10. MultiheadAttentionの高速経路がVRAMを18GB食っていた話 TL;DR nn.MultiheadAttention は、eval かつ勾配なしの推論でマスクを渡すと、need_weights=False を付けていても SDPA を使わず、ヘッド数 × P × P の注意行列を丸ごと作る Xiaomi-Lens-0 では flash-attn が無いときの代替経路がまさにこれで、3 MP の画像でマスク生成段の追加 VRAM が 18.02 GB、8 MP では 1 回 30.75 GiB の確保で OOM していた 既に実装されていた SDPA 経路を代替に回す 1 ファイルの PR で 2.28 GB (約 87% 減) になり、翌日マージされた。学習時やテストでは再現しない罠なので、オプション依存を外した推論経路は別にベンチするべきだ Xiaomi-Lens-0#3 背景: なぜ掘ったのか Xiaomi-Lens-0 は Qwen3.5-4B ベースのマルチモーダルモデルで、文章で答えながら <|mask|> トークンをピクセル単位のマスクに変換する。マスクを作るデコーダの PatchSelfAttention は、flash-attn の FA2、PyTorch の SDPA、nn.MultiheadAttention の 3 実装を同じ重みで切り替えられる。 SeerRay-Lab/Xiaomi-Lens-0 既定は flash で、flash-attn が無い・GPU が sm80 未満・CPU のどれかなら MultiheadAttention に落ちる作りだった。Windows では flash-attn のビルドが面倒なので、筆者の環境はまさにその代替経路を通っていた。大きめの画像を入れたら落ちた、というのが入口だ。 筆者が一番引っかかったのは、該当コードに need_weights=False がちゃんと付いていたことだ。ドキュメントを読む限り、これで SDPA に乗るはずだった。正しく書かれたコードが 18 GB を食うなら、話は Xiaomi-Lens-0 だけでは終わらない。 環境 項目 GPU 計測 (PR) 仕組みの再現 ハード RTX 5090 32 GB CPU 2 コア / メモリ 7 GB OS Windows 11 Linux PyTorch 2.14.1+cu130 2.14.1 (CPU 実行) その他 Transformers 5.4.0、flash-attn なし fp32 何が起きていたか PR に載せた数字で、モデル読み込み後の状態から、マスク生成段 (infer.py の run_forward) が追加で使った VRAM のピークだ。アプリ全体の使用量ではない。 入力 パッチ数 P MultiheadAttention SDPA 0.5 MP 1,944 0.86 GB 0.46 GB 1 MP 3,952 2.58 GB 0.83 GB 2 MP 7,776 8.53 GB 1.55 GB 3 MP 11,616 18.02 GB 2.28 GB 画像が 6 倍で 21 倍。P は画像を 16×16 画素に切った区画数なので、二次で伸びているのは一目で分かる。16 ヘッド × P × P を bf16 で持つと 3 MP で約 4.32 GB で、18.02 GB はその約 4 枚分に相当する。8 MP (3840×2560) では P が約 3.2 万になり、1 枚分だけで 30.75 GiB の確保を要求して OOM した。 ! 8 MP の数字は ComfyUI 経由の別テストで、PatchSelfAttention をバイト単位で同一にコピーして測った。SDPA 側は完走し、リクエスト全体のピークは 15.05 GB、約 6 秒だった。表とは測定範囲が違う。 測り方の骨組みはこうなる。 def mask_pass_extra_gb(model, processor, mask_id, image, text, device): inputs, _ = build_inputs(processor, image, text, device) ids, _, n_masks = generate_completion( model, processor, inputs, max_new_tokens=2048, mask_token_id=mask_id) assert n_masks > 0 torch.cuda.synchronize() base = torch.cuda.memory_allocated() torch.cuda.reset_peak_memory_stats() run_forward(model, inputs, ids, mask_id, device) # ここが mask pass torch.cuda.synchronize() return (torch.cuda.max_memory_allocated() - base) / 1e9 犯人探し: need_weights=False は何を約束していたか ドキュメントの約束 PyTorch 2.14 の nn.MultiheadAttention には、こう書いてある。 Set need_weights=False to use the optimized scaled_dot_product_attention (和訳: 最適化された scaled_dot_product_attention を使うには need_weights=False を設定せよ) 同じページに、推論専用の fast path に入る条件も並んでいる。自己注意であること、batch_first の 3 次元入力、autograd 無効 (inference_mode か no_grad)、.eval() 済み、などだ。infer.py は model.eval() の後に torch.inference_mode() で推論するので、全部満たす。 docs.pytorch.org ソースの現実 fast path の CUDA 実装 native_multi_head_attention_cuda を読むと、SDPA へ進む条件の先頭がこれだった。 if (!mask.has_value() && no_seq_len_1_nested && マスクがあれば SDPA には行かず、bmm_nt でスコア行列を作って masked_softmax をかける。直前のコメントには、形が [B, num_head, T, T] と明記されている。 github.com/pytorch/pytorch/blob/main/aten/src/ATe… そして Xiaomi-Lens-0 のデコーダは、パディング用の key_padding_mask を常に渡していた。つまり need_weights=False の約束は fast path に入った時点で守られない。ドキュメントのその一文は、fast path の外側 (Python 側の F.multi_head_attention_forward) か、CUDA でマスクを渡さない場合にだけ成り立つ、と読むのが正確だと筆者は考えている。 プロファイラで裏を取る ソースを読んだだけでは心許ないので、CPU で同じ呼び方を 4 条件で回し、呼ばれた aten 演算を拾った。 import torch import torch.nn as nn from torch.profiler import ProfilerActivity, profile E, H, P = 256, 16, 512 # 経路の確認が目的なので小さめ KEYS = ("scaled_dot_product", "softmax", "aten::bmm", "native_multi_head") mha = nn.MultiheadAttention(E, H, batch_first=True) x = torch.randn(1, P, E) for mode in ("eval", "train"): getattr(mha, mode)() for mask in (None, torch.zeros(1, P, dtype=torch.bool)): with torch.no_grad(), profile(activities=[ProfilerActivity.CPU]) as prof: mha(x, x, x, need_weights=False, key_padding_mask=mask) ops = sorted({e.key for e in prof.key_averages() if any(k in e.key for k in KEYS)}) print(mode, "mask" if mask is not None else "none", ops) eval none ['aten::_native_multi_head_attention', 'aten::_softmax', 'aten::bmm'] eval mask ['aten::_masked_softmax', 'aten::_native_multi_head_attention', 'aten::bmm'] train none ['aten::_scaled_dot_product_flash_attention_for_cpu', 'aten::scaled_dot_product_attention'] train mask ['aten::_scaled_dot_product_flash_attention_for_cpu', 'aten::scaled_dot_product_attention'] eval・マスクありの _masked_softmax には [1, 16, 512, 512] が渡っていた。CPU の fast path はマスクが無くても行列を作る点で CUDA と挙動が違うが、マスクありの推論で行列を作る点は同じだ。 ここで一番意外だったのは train の 2 行だ。同じ層・同じ引数でも、学習モードなら SDPA に乗る。 ! 学習ループや、勾配を有効にしたままの単体テストでは、この二次メモリは再現しない。eval() と inference_mode() を揃えた本番同等の経路でしか出てこない。 CPU で量を確かめる 経路が分かっても、それがメモリの主犯かは量を測らないと言えない。forward 1 回ごとに別プロセスを起こし、/proc/self/clear_refs でピーク常駐メモリ (VmHWM) をリセットしてから増分を取った。SDPA 側は Xiaomi-Lens-0 の _sdpa_self_attention と同じ組み立て方 (MHA の射影重みを流用し、パディングを -inf の加算マスクにする) にしている。 peak_mem.py (全文)import sys import torch import torch.nn as nn import torch.nn.functional as F E, H = 1024, 16 # head_dim = 64。実モデルの寸法ではない def sdpa_like_repo(mha, x, kpm): # Xiaomi-Lens-0 の _sdpa_self_attention と同じ組み立て B, P, _ = x.shape q, k, v = F.linear(x, mha.in_proj_weight, mha.in_proj_bias).chunk(3, -1) q, k, v = (t.view(B, P, H, E // H).transpose(1, 2) for t in (q, k, v)) bias = torch.zeros(B, 1, 1, P, dtype=x.dtype).masked_fill( kpm[:, None, None, :], float("-inf")) out = F.scaled_dot_product_attention(q, k, v, attn_mask=bias) return F.linear(out.transpose(1, 2).reshape(B, P, E), mha.out_proj.weight, mha.out_proj.bias) def mib(field): return next(int(l.split()[1]) for l in open("/proc/self/status") if l.startswith(field)) / 1024 if name == "main": path, P = sys.argv[1], int(sys.argv[2]) torch.manual_seed(0) mha = nn.MultiheadAttention(E, H, batch_first=True).eval() x, kpm = torch.randn(1, P, E), torch.zeros(1, P, dtype=torch.bool) base = mib("VmRSS") open("/proc/self/clear_refs", "w").write("5") # ピーク (VmHWM) をリセット with torch.inference_mode(): if path == "mha": mha(x, x, x, need_weights=False, key_padding_mask=kpm) else: sdpa_like_repo(mha, x, kpm) print(f"{path} P={P}: +{mib('VmHWM') - base:.0f} MiB") P 16 × P² × 4 B MultiheadAttention SDPA 1,024 64 MiB 172 MiB 44 MiB 2,048 256 MiB 580 MiB 64 MiB 3,072 576 MiB 1,245 MiB 84 MiB 4,096 1,024 MiB 2,165 MiB 104 MiB 2 回回して同じ値だった。MultiheadAttention は理論値の約 2 倍 (スコア行列と softmax 出力) に収束していき、SDPA は P 1,024 あたり 20 MiB の直線だ。GPU の実測と同じ形が CPU でも出た。 PR の 4 点に二次式と一次式を当てはめると、32 GB クラスの GPU で MultiheadAttention 経路が実用になるのは 3 MP 前後まで、SDPA 経路なら上限の 8.4 MP まで追加 6 GB 程度という見積もりになる (4 MP 以上は外挿)。 fit_scaling.pyimport numpy as np P = np.array([1944, 3952, 7776, 11616], dtype=float) mha_gb = np.array([0.86, 2.58, 8.53, 18.02]) sdpa_gb = np.array([0.46, 0.83, 1.55, 2.28]) (a, b), *_ = np.linalg.lstsq(np.stack([P**2, P], 1), mha_gb, rcond=None) (c, d), *_ = np.linalg.lstsq(np.stack([P, np.ones_like(P)], 1), sdpa_gb, rcond=None) print("| 画像 | P | MHA予測 | SDPA予測 |") print("|---|---|---|---|") for mp in (0.5, 1, 2, 3, 4, 6, 8): p = mp * 1e6 / 256 print(f"| {mp} MP | {p:,.0f} | {a*p*p + b*p:.1f} GB | {c*p + d:.2f} GB |") 直し方: 新しく書かない 選択肢は 3 つあった。 MultiheadAttention の呼び方を工夫して fast path を外す。勾配の有無に依存する裏技で、PyTorch の版で挙動が変わりうるので却下 マスクを渡さない。パディングを含む入力で意味が変わるので却下 既にあった SDPA 経路を代替に回す。同じ重みで、環境変数 XIAOMI_LENS_DECODER_ATTN=sdpa で選べる状態だった 3 を選んだ。差分は実質この入れ替えだけだ (docstring 省略、シグネチャは 1 行に畳んでいる)。 def _self_attention(self, normed, key_padding_mask): - if _DECODER_ATTN == "sdpa": - return self._sdpa_self_attention(normed, key_padding_mask) - if _DECODER_ATTN == "mha": - return self._mha_self_attention(normed, key_padding_mask) if _DECODER_ATTN == "flash": output = self._flash_self_attention(normed, key_padding_mask) if output is not None: return output - return self._mha_self_attention(normed, key_padding_mask) - return self._sdpa_self_attention(normed, key_padding_mask) XIAOMI_LENS_DECODER_ATTN=mha で旧挙動を明示的に選べるようにし、警告文も「falling back to SDPA」に直した。環境変数は、それを知っている人しか救わない。既定の代替経路を変えれば、知らない人にも届く。 出力は変わったか PR では 6 ケース (猫、コーヒーカップ、23 枚の硬貨、ロケット、旗、推論を伴う依頼) で、トークン ID、ボックス、全マスクロジットを比べた。 比較 結果 新しい既定 vs 旧コードで sdpa を明示 6/6 ビット単位一致 新コードで mha を明示 vs 旧既定 6/6 ビット単位一致 SDPA vs MHA のトークン・ボックス 6/6 同一 SDPA vs MHA のマスク IoU (閾値 0.5) 0.9976 〜 0.9998 上 2 行は配線の確認、下 2 行が経路差だ。境界にわずかな差が出るのは丸めによるもので、6 ケースは網羅的な品質評価ではない。CPU でも同じ重みで比べると、最大絶対差は fp32 で 5.22e-08、bf16 で 4.88e-04、NaN なしだった。 compare_out.pyimport torch import torch.nn as nn from peak_mem import E, H, sdpa_like_repo torch.manual_seed(0) mha = nn.MultiheadAttention(E, H, batch_first=True).eval() for dtype in (torch.float32, torch.bfloat16): m = mha.to(dtype) x = torch.randn(2, 1024, E, dtype=dtype) kpm = torch.zeros(2, 1024, dtype=torch.bool) kpm[1, 900:] = True # 2 枚目だけ末尾 124 パッチが padding with torch.inference_mode(): ref = m(x, x, x, need_weights=False, key_padding_mask=kpm)[0] out = sdpa_like_repo(m, x, kpm) diff = (ref.float() - out.float()).abs() print(f"{str(dtype):15s} max|diff|={diff.max():.2e} " f"mean|diff|={diff.mean():.2e} nan={out.isnan().any().item()}") 自前で MHA から SDPA へ移植するなら、真偽値マスクの向きに注意したい。key_padding_mask は True が無視、SDPA の attn_mask は True が参加で、逆向きだ。そのまま渡すと結果が壊れる (下のスクリプトで最大差 3.46)。Xiaomi-Lens-0 は加算マスクに変換していたので無事だった。 import torch import torch.nn.functional as F torch.manual_seed(0) q = k = v = torch.randn(1, 2, 4, 8) pad = torch.tensor([False, False, False, True]).view(1, 1, 1, 4) # True = padding ref = F.scaled_dot_product_attention(q, k, v, attn_mask=torch.zeros(1, 1, 1, 4).masked_fill(pad, float("-inf"))) for name, m in (("反転して渡す", ~pad), ("そのまま渡す", pad)): print(name, (F.scaled_dot_product_attention(q, k, v, attn_mask=m) - ref).abs().max().item()) 考察: 代替経路はなぜ放置されるのか 今回の件で筆者が学んだのは、オプション依存の代替経路は、二重に見えにくいということだ。 1 つ目は、開発者の手元では本命 (flash-attn) が入っているので、代替経路を通らない。2 つ目は、代替経路を通っても、学習や勾配ありのテストでは fast path に入らないので、問題が出ない。代替経路が二次メモリを食うのは「flash-attn が無い」かつ「本番同等の推論」の交差点だけで、そこは作者が最も踏まない場所だ。 同じ交差点は他のモデルにもある。標準の nn.TransformerEncoderLayer も、src_key_padding_mask を渡した推論で同じ形の行列を作った。手元のモデルを点検するために、推論と同じ inference_mode で 1 回回し、最後の 2 次元が等しい大きなテンソルを拾う関数を書いた。 import torch from torch.profiler import ProfilerActivity, profile WATCH = ("softmax", "bmm", "baddbmm", "matmul") def find_square_attention(run, min_len: int = 256): with torch.inference_mode(), profile( activities=[ProfilerActivity.CPU], record_shapes=True ) as prof: run() hits = set() for ev in prof.events(): if any(w in ev.name for w in WATCH): for shape in ev.input_shapes: if len(shape) >= 3 and shape[-1] == shape[-2] >= min_len: hits.add((ev.name, tuple(shape))) return sorted(hits) if name == "main": from peak_mem import sdpa_like_repo mha = torch.nn.MultiheadAttention(1024, 16, batch_first=True).eval() enc = torch.nn.TransformerEncoderLayer(1024, 16, batch_first=True).eval() x, pad = torch.randn(1, 1024, 1024), torch.zeros(1, 1024, dtype=torch.bool) print("MHA ", find_square_attention(lambda: mha(x, x, x, need_weights=False, key_padding_mask=pad))) print("Enc ", find_square_attention(lambda: enc(x, src_key_padding_mask=pad))) print("SDPA", find_square_attention(lambda: sdpa_like_repo(mha, x, pad))) MHA [('aten::_masked_softmax', (1, 16, 1024, 1024)), ('aten::bmm', (16, 1024, 1024))] Enc [('aten::_masked_softmax', (1, 16, 1024, 1024)), ('aten::bmm', (16, 1024, 1024))] SDPA [] この関数を書く途中で、筆者は一度だまされている。.eval() を呼び忘れた層で試して空リストが返り、「問題なし」と結論しかけた。検査そのものを本番条件で回さないと、この罠は検査をすり抜ける。 PR で意識したこと 修正自体は数行でも、メンテナが短時間で「正しい」と判断できる材料がないとマージされない。PR 本文は「問題 / 変更 / 計測 / 出力比較」に分け、次の 3 点を明記した。 測った範囲: 追加ピークであって全体の VRAM ではない 逃げ道: mha を明示すれば旧挙動に戻せる 出力差の性質: 配線の一致 (ビット単位) と、経路差の大きさ (IoU) を分けて示す 10 月 19 日に出して、20 日にマージされた。 まとめ need_weights=False は、推論時の fast path にマスクを渡すと SDPA を保証しない。ソースの分岐はマスクの有無で決まっている Xiaomi-Lens-0 では代替経路を SDPA に替えるだけで、3 MP の追加 VRAM が 18.02 GB から 2.28 GB になった 代替経路の問題は、本命が入った手元と、勾配ありのテストの両方で隠れる。本番と同じ eval と inference_mode で、オプション依存を外して 1 回測るのが一番安い対策だ 参考 PyTorch 2.14 torch.nn.MultiheadAttention ── fast path の条件と need_weights の説明 PyTorch 2.14 scaled_dot_product_attention ── 真偽値マスクの向き、dropout、実装の自動選択 pytorch/pytorch attention.cu ── fast path がマスクありで SDPA に進まない分岐 (2026-10-24 時点の main) SeerRay-Lab/Xiaomi-Lens-0 PR #3 ── 修正、計測、出力比較の原本 総当たり表のたとえで記号と式を 1 つずつ追った入門寄りの解説は、Qiita に書いた。 qiita.com x.com

Trending awards, every completed day and week
Gold
31st place finishes
Silver
152nd place finishes
Bronze
663rd place finishes
Awarded repositories
2Owned by pytorch now

Repositories

Ordered by gold, then silver, then bronze.

  1. pytorchBest: 1st by stars, 1,046 stars31565
  2. ELFBest: 3rd by forks, 71 forks001

A repository earns an award for finishing first, second or third in a completed trending day or week, ranked by new stars, new forks or pull request authors. Awards follow a repository through renames and transfers and count toward its current owner. Weeks run Monday to Sunday, UTC.