NVIDIA / NVIDIA/TransformerEngine

[Bug] Triton mask-map moe_permute ignores num_out_tokens

Open
#3,346 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
3.5k
Forks
831
Avg merge
3d 11h
Merged PRs (30d)
65

Description

Summary

The Triton path behind moe_permute(..., map_type="mask") sizes its output at exactly num_out_tokens rows but never enforces that limit: _permute_kernel receives num_out_tokens explicitly marked # pylint: disable=unused-argument, the row-id map assigns destination rows as an unbounded cumulative sum over the routing map, and nothing clamps them. If the routing map routes more tokens than num_out_tokens, the excess rows are written past the end of the allocation.

The CUDA path behind the same public API (map_type="index") implements the capacity limit — moe_permute_row_map has an explicit idx >= num_out_tokens branch that marks over-capacity entries -1, and every consumer skips -1 (common/permutation/permutation.cu lines 28–34) — so the two backends of one entry point disagree, and a caller expecting the documented capacity-drop behaviour from the mask path gets out-of-bounds writes. #2876 added a positivity guard for num_out_tokens, but a positive value smaller than routing_map.sum() still reaches the kernel unchecked, and the tests always construct the map so that routing_map.sum() == num_out_tokens, which is why this has not been seen.

The kernel was added in #1373.

Steps to reproduce

import torch
import transformer_engine.pytorch  # noqa
from transformer_engine.pytorch.permutation import moe_permute

routing_map = torch.zeros(8, 4, dtype=torch.int32, device="cuda")
routing_map[:, 0] = 1
routing_map[:4, 1] = 1          # 12 routed entries
inp = torch.randn(8, 16, device="cuda", dtype=torch.bfloat16)
out, row_id_map = moe_permute(inp, routing_map, num_out_tokens=9, map_type="mask")
# out has 9 rows; the kernel writes 12

Under compute-sanitizer --tool memcheck (with PYTORCH_NO_CUDA_MEMORY_CACHING=1 so the caching allocator's slack does not absorb it), this reports 48 invalid global writes at the permute store (transformer_engine/common/triton/permutation.py:303) — the three over-capacity rows times the 16-element hidden size — and the run then ends in an unspecified launch failure. After the fix: 0 errors, the in-capacity rows are unchanged, and the row map's per-token n_routed sums to num_out_tokens.

Environment

  • TransformerEngine commit 07e281f2 (main); transformer-engine-cu12 2.17.0 wheel + source checkout for the Python/Triton code
  • NVIDIA L40S (sm_89), driver CUDA 12.4
  • torch 2.13.0+cu126, triton 3.7.1, Python 3.12

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in transformer_engine/common/triton/permutation.py at _permute_kernel and the permute store around line 303, then compare the capacity handling in common/permutation/permutation.cu. Reproduce the issue with the provided 12-entry routing map and num_out_tokens=9 under compute-sanitizer. Done means no invalid writes, unchanged in-capacity rows, and row_id_map n_routed sums to num_out_tokens.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, pytorch
Domain
backend, machine-learning, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.