Skip to content

[https://nvbugs/6525008][fix] Isolate FlashInfer JIT workspaces for MPI workers - #17233

Open
VALLIS-NERIA wants to merge 6 commits into
NVIDIA:mainfrom
VALLIS-NERIA:agent/nvbug-6525008-flashinfer-workaround
Open

[https://nvbugs/6525008][fix] Isolate FlashInfer JIT workspaces for MPI workers#17233
VALLIS-NERIA wants to merge 6 commits into
NVIDIA:mainfrom
VALLIS-NERIA:agent/nvbug-6525008-flashinfer-workaround

Conversation

@VALLIS-NERIA

@VALLIS-NERIA VALLIS-NERIA commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Description

FlashInfer 0.6.15 writes generated JIT configuration sources before acquiring its compilation lock. Concurrent MPI workers using the shared default workspace can therefore observe a temporarily truncated file and fail compilation with undefined generated types and constants.

For multi-worker MpiPoolSession instances without an explicit workspace, TensorRT-LLM now starts each worker with a rank- and PID-specific temporary FLASHINFER_WORKSPACE_BASE before mpi4py.futures.server imports the user main module. Downloaded/precompiled cubins remain in the shared FLASHINFER_CUBIN_DIR; explicit workspace and cubin paths are propagated unchanged. TRTLLM_FLASHINFER_WORKSPACE_PER_PROCESS=0 disables the workaround, and normal worker shutdown removes the temporary workspace.

The workaround intentionally covers dynamically spawned MpiPoolSession workers. External mpirun/MpiCommSession ranks must set their workspace before importing TensorRT-LLM.

Test Coverage

  • Targeted pre-commit checks for the implementation, tests, and test-list entry.
  • Bootstrap tests cover rank/PID naming, cleanup, explicit workspace preservation, single-worker behavior, and the disable flag.
  • Real two-worker MPIPoolExecutor tests verify distinct JIT workspaces, a shared cubin directory, and parent-only explicit workspace propagation.
  • Registered tests/unittest/llmapi/test_flashinfer_workspace.py in the A100 isolation stage.
  • NSC B200 job 1605074 previously verified four concurrent workers completing the same cold SM100 selective_state_update JIT with distinct workspaces.

The original end-to-end command on NSC/Pyxis could not reach model initialization because dynamic MPI spawn failed with MPI_ERR_SPAWN; the worker bootstrap itself is covered by direct spawn tests.

PR Checklist

  • I reviewed the checklist in the pull request template as appropriate for this change.

GitHub Bot Help

To see a list of available CI bot commands, comment /bot help.

Dev Engineer Review

Code Changes:

  • Added per-worker persistent FlashInfer workspace slots for multi-worker MpiPoolSession instances without explicit workspaces.
  • Uses rank-based slots and nonblocking file locks to prevent concurrent pool collisions.
  • Preserves the shared FlashInfer cubin cache.
  • Supports opt-out with TRTLLM_FLASHINFER_WORKSPACE_PER_PROCESS=0.
  • Forwards FlashInfer environment variables to workers.
  • Leaves explicit workspace and cubin settings unchanged.
  • Documents cache behavior and the scope limitation for externally launched workers and MpiCommSession.

Configuration and Test List:

  • Added unittest/llmapi/test_flashinfer_workspace.py ISOLATION to tests/integration/test_lists/test-db/l0_a100.yml.
  • The test-list entry uses the expected format and targets the multi-device test correctly.

QA Engineer Review

Test Changes:

  • Added tests for rank-based workspace reuse.
  • Added tests for lock release and fallback to an available rank slot.
  • Added tests for explicit workspace preservation and propagation.
  • Added tests for bootstrap configuration, shared cubins, and isolated worker workspaces.
  • Added coverage for multi-worker execution and concurrent SM100 JIT compilation.

Test Coverage:

  • tests/unittest/llmapi/test_flashinfer_workspace.py is included in tests/integration/test_lists/test-db/l0_a100.yml with the ISOLATION tag.
  • Coverage is sufficient for the implemented bootstrap, propagation, isolation, opt-out, and persistent-cache behavior.
  • The original end-to-end command could not complete because dynamic MPI spawn failed with MPI_ERR_SPAWN.

…PI workers (By Agent)

Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
@VALLIS-NERIA

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63608 [ run ] triggered by Bot. Commit: dc6f6a0 Link to invocation

@VALLIS-NERIA
VALLIS-NERIA marked this pull request as ready for review August 4, 2026 03:50
@VALLIS-NERIA
VALLIS-NERIA requested review from a team as code owners August 4, 2026 03:50
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ca5b2ce0-0a60-42d0-b28c-235e331d5dd7

📥 Commits

Reviewing files that changed from the base of the PR and between 086857b and 99a0b24.

📒 Files selected for processing (3)
  • docs/source/llm-api/index.md
  • tensorrt_llm/llmapi/mpi_session.py
  • tests/unittest/llmapi/test_flashinfer_workspace.py

Walkthrough

The change adds FlashInfer workspace-slot locking for MPI workers. Multi-worker pools use the bootstrap and forward FlashInfer environment variables. Explicit workspaces remain unchanged. Tests and documentation cover workspace selection, lock release, pool configuration, and isolation settings.

Changes

FlashInfer workspace isolation

Layer / File(s) Summary
Worker bootstrap and workspace slot locking
tensorrt_llm/llmapi/mpi_session.py, tests/unittest/llmapi/test_flashinfer_workspace.py
The bootstrap selects an available rank-based workspace slot with a nonblocking lock. It preserves the default cubin directory, starts the worker server, and releases the lock when the server exits. Tests cover reuse, fallback, and explicit workspace preservation.
MPI pool environment and bootstrap configuration
tensorrt_llm/llmapi/mpi_session.py, tests/unittest/llmapi/test_flashinfer_workspace.py
Multi-worker pools forward FlashInfer variables and use the bootstrap unless isolation is disabled or a workspace is explicitly configured. Tests cover worker-count selection, environment overrides, cubin sharing, and workspace propagation.
Validation and configuration guidance
tests/unittest/llmapi/test_flashinfer_workspace.py, tests/integration/test_lists/test-db/l0_a100.yml, docs/source/llm-api/index.md
The test module is registered for the A100 pre-merge suite. The documentation describes workspace precedence, persistent per-worker slots, isolation disabling, and external worker configuration.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested reviewers: bowenfu, qijune, juney-nvidia

Sequence Diagram(s)

sequenceDiagram
  participant MpiPoolSession
  participant MPIPoolExecutor
  participant FlashInferBootstrap
  participant WorkspaceSlot
  participant MPIWorkerServer
  MpiPoolSession->>MPIPoolExecutor: configure worker launch and FlashInfer variables
  MPIPoolExecutor->>FlashInferBootstrap: start multi-worker bootstrap
  FlashInferBootstrap->>WorkspaceSlot: acquire available rank-based slot
  WorkspaceSlot-->>FlashInferBootstrap: return workspace path
  FlashInferBootstrap->>MPIWorkerServer: set workspace and cubin environment
  MPIWorkerServer-->>FlashInferBootstrap: exit server
  FlashInferBootstrap->>WorkspaceSlot: release lock
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows the required ticket/type format and clearly describes isolating FlashInfer JIT workspaces for MPI workers.
Description check ✅ Passed The description explains the issue, solution, scope, test coverage, limitations, and checklist status with sufficient detail.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unittest/llmapi/test_flashinfer_workspace.py`:
- Around line 27-98: Add the six test cases from
test_configure_flashinfer_workspace_is_opt_in,
test_configure_flashinfer_workspace_is_per_process,
test_configure_flashinfer_workspace_preserves_explicit_value, and the three
parameterized cases of test_mpi_pool_configures_worker_workspace_isolation to an
applicable test-db YAML file such as l0_a100.yml, and add
tests/unittest/llmapi/test_flashinfer_workspace.py to the QA test list when
required for coverage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 843218a1-8531-4445-8842-170cd9c3cdbf

📥 Commits

Reviewing files that changed from the base of the PR and between b008616 and dc6f6a0.

📒 Files selected for processing (4)
  • tensorrt_llm/__init__.py
  • tensorrt_llm/_flashinfer_workaround.py
  • tensorrt_llm/llmapi/mpi_session.py
  • tests/unittest/llmapi/test_flashinfer_workspace.py

Comment on lines +27 to +98
def test_configure_flashinfer_workspace_is_opt_in(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv(workaround._FLASHINFER_WORKSPACE_ENV, raising=False)
monkeypatch.delenv(workaround._FLASHINFER_WORKSPACE_ISOLATION_ENV, raising=False)

workaround._configure_flashinfer_workspace()

assert workaround._FLASHINFER_WORKSPACE_ENV not in os.environ


def test_configure_flashinfer_workspace_is_per_process(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
monkeypatch.delenv(workaround._FLASHINFER_WORKSPACE_ENV, raising=False)
monkeypatch.setenv(workaround._FLASHINFER_WORKSPACE_ISOLATION_ENV, "1")
monkeypatch.setattr(workaround.tempfile, "gettempdir", lambda: str(tmp_path))

workaround._configure_flashinfer_workspace()

get_user_id = getattr(os, "getuid", lambda: 0)
expected = tmp_path / f"trtllm-flashinfer-{get_user_id()}-{os.getpid()}"
assert os.environ[workaround._FLASHINFER_WORKSPACE_ENV] == str(expected)


def test_configure_flashinfer_workspace_preserves_explicit_value(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
explicit_workspace = tmp_path / "explicit"
monkeypatch.setenv(workaround._FLASHINFER_WORKSPACE_ISOLATION_ENV, "1")
monkeypatch.setenv(workaround._FLASHINFER_WORKSPACE_ENV, str(explicit_workspace))

workaround._configure_flashinfer_workspace()

assert os.environ[workaround._FLASHINFER_WORKSPACE_ENV] == str(explicit_workspace)


@pytest.mark.skipif(not ENABLE_MULTI_DEVICE, reason="multi-device required")
@pytest.mark.parametrize(
"n_workers, override, expected",
[
(1, None, None),
(4, None, "1"),
(4, "0", "0"),
],
)
def test_mpi_pool_configures_worker_workspace_isolation(
monkeypatch: pytest.MonkeyPatch,
n_workers: int,
override: str | None,
expected: str | None,
) -> None:
captured: dict[str, object] = {}

class FakeMpiPoolExecutor:
def __init__(self, **kwargs: object) -> None:
captured.update(kwargs)

monkeypatch.delenv(workaround._FLASHINFER_WORKSPACE_ISOLATION_ENV, raising=False)
monkeypatch.setattr(mpi_session, "MPIPoolExecutor", FakeMpiPoolExecutor)
env_overrides = (
{workaround._FLASHINFER_WORKSPACE_ISOLATION_ENV: override} if override is not None else {}
)
session = SimpleNamespace(
n_workers=n_workers,
_env_overrides=env_overrides,
mpi_pool=None,
)

mpi_session.MpiPoolSession._start_mpi_pool(session)

env = captured["env"]
assert isinstance(env, dict)
assert env.get(workaround._FLASHINFER_WORKSPACE_ISOLATION_ENV) == expected

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -F 'tests/unittest/llmapi/test_flashinfer_workspace.py' \
  tests/integration/test_lists || true
rg -n \
  'test_configure_flashinfer_workspace|test_mpi_pool_configures_worker_workspace_isolation' \
  tests/integration/test_lists || true

Repository: NVIDIA/TensorRT-LLM

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- changed-file status ---'
git status --short -- tests/unittest/llmapi/test_flashinfer_workspace.py
git diff --stat -- tests/unittest/llmapi/test_flashinfer_workspace.py

printf '%s\n' '--- test-list files ---'
git ls-files tests/integration/test_lists | sed -n '1,120p'

printf '%s\n' '--- matching module and test names ---'
rg -n -F 'test_flashinfer_workspace.py' tests/integration/test_lists || true
rg -n 'test_configure_flashinfer_workspace|test_mpi_pool_configures_worker_workspace_isolation' \
  tests/integration/test_lists || true

printf '%s\n' '--- nearby test-list entries for unittest modules ---'
rg -n 'tests/unittest/|unittest/llmapi|llmapi' tests/integration/test_lists | sed -n '1,120p' || true

printf '%s\n' '--- test module outline ---'
ast-grep outline tests/unittest/llmapi/test_flashinfer_workspace.py --view expanded

Repository: NVIDIA/TensorRT-LLM

Length of output: 23426


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- test-list documentation ---'
cat -n tests/integration/test_lists/qa/README.md | sed -n '1,180p'
cat -n tests/integration/test_lists/test-db/README.md | sed -n '1,180p'

printf '%s\n' '--- CBTS artifacts ---'
git ls-files | rg -i 'cbts|touchmap|coverage' | sed -n '1,160p' || true
find . -maxdepth 5 -type f \( -iname '*cbts*' -o -iname '*touchmap*' -o -iname '*coverage*' \) -print | sed -n '1,160p'

printf '%s\n' '--- all list references by basename ---'
rg -n -F 'test_flashinfer_workspace' tests/integration/test_lists || true

Repository: NVIDIA/TensorRT-LLM

Length of output: 9667


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n tests/unittest/llmapi/test_flashinfer_workspace.py | sed -n '1,110p'
rg -n 'test_mpi_session.py|flashinfer|workspace_isolation|ENABLE_MULTI_DEVICE' \
  tests/integration/test_lists/test-db tests/integration/test_lists/qa \
  tests/unittest/llmapi/test_flashinfer_workspace.py

Repository: NVIDIA/TensorRT-LLM

Length of output: 8761


Add the new tests to a CI test list.

tests/unittest/llmapi/test_flashinfer_workspace.py is absent from tests/integration/test_lists/test-db/ and tests/integration/test_lists/qa/. Add the six test cases to the applicable test-db YAML file, such as l0_a100.yml, and include the module in QA coverage if required.

Test coverage summary: three workspace tests and three MPI parameter cases cover the new behavior. Coverage verdict: insufficient because the tests are not scheduled by a test list.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/llmapi/test_flashinfer_workspace.py` around lines 27 - 98, Add
the six test cases from test_configure_flashinfer_workspace_is_opt_in,
test_configure_flashinfer_workspace_is_per_process,
test_configure_flashinfer_workspace_preserves_explicit_value, and the three
parameterized cases of test_mpi_pool_configures_worker_workspace_isolation to an
applicable test-db YAML file such as l0_a100.yml, and add
tests/unittest/llmapi/test_flashinfer_workspace.py to the QA test list when
required for coverage.

Source: Path instructions

…ound (By Agent)

Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
@VALLIS-NERIA

Copy link
Copy Markdown
Collaborator Author

/bot run

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/__init__.py`:
- Around line 25-29: Update the workspace initialization around
_FLASHINFER_WORKSPACE_ENV to distinguish explicit caller-provided values from
parent-generated values: preserve explicit values, but clear inherited generated
workspace values before worker import and assign a worker-local workspace when
needed. Add a regression test covering MPIPoolExecutor(env=...) environment
propagation and verifying workers do not reuse the parent-generated
FLASHINFER_WORKSPACE_BASE.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c35474ba-5f63-4991-b98b-0c6a8d1bef92

📥 Commits

Reviewing files that changed from the base of the PR and between dc6f6a0 and eafdb35.

📒 Files selected for processing (3)
  • tensorrt_llm/__init__.py
  • tensorrt_llm/llmapi/mpi_session.py
  • tests/unittest/llmapi/test_flashinfer_workspace.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tensorrt_llm/llmapi/mpi_session.py
  • tests/unittest/llmapi/test_flashinfer_workspace.py

Comment thread tensorrt_llm/__init__.py Outdated
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63654 [ run ] triggered by Bot. Commit: eafdb35 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63608 [ run ] completed with state ABORTED. Commit: dc6f6a0

Link to invocation

…MPI workers (By Agent)

Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
@VALLIS-NERIA

Copy link
Copy Markdown
Collaborator Author

/bot run

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/llmapi/mpi_session.py`:
- Around line 34-40: Update the generated _FLASHINFER_WORKER_BOOTSTRAP so it
checks TRTLLM_FLASHINFER_WORKSPACE_PER_PROCESS before assigning
FLASHINFER_WORKSPACE_BASE, while preserving setdefault behavior for explicit
workspace values and continuing to launch the MPI futures server. Add a
regression test covering the disabled flag and confirming the workspace is not
changed.
- Around line 448-454: Update the environment construction used by the
MPIPoolExecutor call in the MPI session setup to explicitly propagate
FLASHINFER_WORKSPACE_BASE, sourcing it from the configured environment or
requiring it through env_overrides. Preserve existing environment handling and
add a spawn-level test verifying that a value present only in the parent process
reaches workers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 92b9d83f-14d3-4cab-9703-a6da5d2f56cf

📥 Commits

Reviewing files that changed from the base of the PR and between eafdb35 and 106b996.

📒 Files selected for processing (2)
  • tensorrt_llm/llmapi/mpi_session.py
  • tests/unittest/llmapi/test_flashinfer_workspace.py

Comment thread tensorrt_llm/llmapi/mpi_session.py Outdated
Comment thread tensorrt_llm/llmapi/mpi_session.py Outdated
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63670 [ run ] triggered by Bot. Commit: 106b996 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63654 [ run ] completed with state ABORTED. Commit: eafdb35

Link to invocation

@BowenFu

BowenFu commented Aug 4, 2026

Copy link
Copy Markdown

The mechanism itself checks out. mpi4py.futures._core.client_spawn builds args = get_python_flags() + list(python_args) then args.extend(["-m", get_spawn_module()]), so the worker command becomes python <flags> -c "<bootstrap>" -m mpi4py.futures.server. The trailing -m ... is swallowed into sys.argv, but the spawn path is server_main() -> server_main_spawn(), which never reads sys.argv (only server_main_service() does a getopt). So the bootstrap does start the server, and it does run before the main module import. Three things before it lands, though.

The cache relocation is wider than "separate temporary caches." FLASHINFER_WORKSPACE_BASE is the root of FLASHINFER_CACHE_DIR = <base>/.cache/flashinfer, and FLASHINFER_CUBIN_DIR falls back to FLASHINFER_CACHE_DIR / "cubins" unless the flashinfer-cubin package is installed or FLASHINFER_CUBIN_DIR is set explicitly. We pin only flashinfer-python==0.6.15, and nothing in-tree sets FLASHINFER_CUBIN_DIR. So a per-rank/per-PID base means each worker doesn't just recompile locally — it re-downloads cubins from FLASHINFER_CUBINS_REPOSITORY on every run. That converts a warm local cache into per-run network traffic and breaks multi-GPU runs that work today in offline/air-gapped environments. The directories are also never removed, so /tmp accumulates one tree per rank per run. Since this is on by default for every n_workers > 1 MpiPoolSession, I'd want either an opt-out knob (coderabbit's TRTLLM_FLASHINFER_WORKSPACE_PER_PROCESS seems right) or a narrower isolation that keeps the shared artifacts shared — e.g. also export FLASHINFER_CUBIN_DIR pointing at the default location so only the JIT build workspace is per-rank.

MpiCommSession gets no isolation. The mpirun / trtllm-llmapi-launch path uses MPICommExecutor over an existing comm and never spawns, so if the write-before-lock race is real it still fires there — and that's the configuration most likely to hit it. Worth stating in the description whether that's deliberately out of scope.

Test collection on single-device builds. tests/unittest/llmapi/test_flashinfer_workspace.py does from mpi4py.futures import server at module scope, but mpi_session.py only imports mpi4py under if ENABLE_MULTI_DEVICE:. Only the third test carries the skipif, so collection fails before the skip applies. The first test also reaches mpi_session.mpi4py, which doesn't exist when ENABLE_MULTI_DEVICE is false.

Minor: is this still needed once #17175 moves us to flashinfer 0.6.16? If that release fixes the lock ordering, it'd be better to land this with an explicit removal condition than as a permanent workaround.

Holding on the CI signal for now — 63608 and 63654 both ABORTED and 63670 is still running, so no pipeline has completed on any head yet.

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63670 [ run ] completed with state FAILURE. Commit: 106b996
/LLM/main/L0_MergeRequest_PR pipeline #51624 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@VALLIS-NERIA

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63686 [ run ] triggered by Bot. Commit: 106b996 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63686 [ run ] completed with state FAILURE. Commit: 106b996
/LLM/main/L0_MergeRequest_PR pipeline #51638 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@VALLIS-NERIA

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63697 [ run ] triggered by Bot. Commit: 106b996 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63697 [ run ] completed with state FAILURE. Commit: 106b996
/LLM/main/L0_MergeRequest_PR pipeline #51650 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

…y Agent)

Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/unittest/llmapi/test_flashinfer_workspace.py (1)

75-102: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear inherited FlashInfer settings before the parameterized test.

_start_mpi_pool() copies os.environ before it applies env_overrides. An inherited TRTLLM_FLASHINFER_WORKSPACE_PER_PROCESS=0 or FLASHINFER_WORKSPACE_BASE makes the (4, {}, ...) case select no bootstrap. An inherited FLASHINFER_CUBIN_DIR also makes the default-cubin assertion fail. Clear these variables before creating session.

Proposed fix
 def test_mpi_pool_configures_worker_bootstrap(
     monkeypatch: pytest.MonkeyPatch,
     n_workers: int,
     env_overrides: dict[str, str],
     expected: list[str] | None,
 ) -> None:
+    monkeypatch.delenv(_FLASHINFER_WORKSPACE_ENV, raising=False)
+    monkeypatch.delenv(_FLASHINFER_CUBIN_ENV, raising=False)
+    monkeypatch.delenv(
+        "TRTLLM_FLASHINFER_WORKSPACE_PER_PROCESS", raising=False
+    )
     captured: dict[str, object] = {}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/llmapi/test_flashinfer_workspace.py` around lines 75 - 102,
Clear the inherited FlashInfer environment variables before creating the session
in test_mpi_pool_configures_worker_bootstrap:
TRTLLM_FLASHINFER_WORKSPACE_PER_PROCESS, FLASHINFER_WORKSPACE_BASE, and
FLASHINFER_CUBIN_DIR. Preserve env_overrides so each parameterized case still
controls its intended value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/unittest/llmapi/test_flashinfer_workspace.py`:
- Around line 75-102: Clear the inherited FlashInfer environment variables
before creating the session in test_mpi_pool_configures_worker_bootstrap:
TRTLLM_FLASHINFER_WORKSPACE_PER_PROCESS, FLASHINFER_WORKSPACE_BASE, and
FLASHINFER_CUBIN_DIR. Preserve env_overrides so each parameterized case still
controls its intended value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d0d3e63c-b6ef-4bc0-96e6-cc80adf3980c

📥 Commits

Reviewing files that changed from the base of the PR and between 106b996 and 086857b.

📒 Files selected for processing (3)
  • tensorrt_llm/llmapi/mpi_session.py
  • tests/integration/test_lists/test-db/l0_a100.yml
  • tests/unittest/llmapi/test_flashinfer_workspace.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tensorrt_llm/llmapi/mpi_session.py

@VALLIS-NERIA

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63723 [ run ] triggered by Bot. Commit: 086857b Link to invocation

@BowenFu

BowenFu commented Aug 4, 2026

Copy link
Copy Markdown

086857b addresses all three of my earlier points, and I checked each rather than assuming:

  • Cubins stay shared. FLASHINFER_CUBIN_DIR is pinned to ~/.cache/flashinfer/cubins, which is exactly what flashinfer/jit/env.py _get_cubin_dir() falls back to when FLASHINFER_WORKSPACE_BASE is unset — and since the packaged flashinfer_cubin check is priority 1, ahead of the env var, setting it explicitly does not disable the packaged-cubin path either. Correct on both counts. The opt-out knob and the finally: rmtree also land.
  • MpiCommSession is now scoped out explicitly in the description. That was all I wanted.
  • Test collection is fine — mpi4py is an unconditional entry in requirements.txt, so the module-scope from mpi4py.futures import server imports even on an ENABLE_MULTI_DEVICE=False build, and the module-level pytestmark then covers the mpi_session.mpi4py access. Withdrawing that one; and the l0_a100.yml registration answers the collection question separately.

What remains is the cost of the approach, and it got sharper this week.

FLASHINFER_JIT_DIR is FLASHINFER_CACHE_DIR/<version>/<arch>/cached_ops, i.e. under the isolated base. So a fresh mkdtemp() per worker plus the new rmtree at exit means the JIT cache is cold on every run and is now actively deleted afterwards. On main, workers share ~/.cache/flashinfer, so a module compiled on run 1 is reused on run 2. This PR trades a rare write-before-lock race for a guaranteed recompile on every n_workers > 1 MpiPoolSession, on by default — and your own evidence names a cold SM100 selective_state_update JIT, so this path is real, not hypothetical. FLASHINFER_AOT_DIR is outside the isolated base, so prebuilt ops are unaffected; the exposure is exactly the ops that fall through to JIT.

And #17175 makes that worse. It bumps flashinfer to 0.6.16, which adds an on-disk JIT cache for CuTe-DSL kernels (JitSpecCuteDsl, flashinfer-ai/flashinfer#3874) specifically to reload in ~3–30 ms instead of recompiling in each new process. Per-process ephemeral workspaces would throw that away for pool workers on arrival. Worth checking whether the two land in the right order.

I also looked at whether 0.6.16 simply fixes what you are working around: it does not, as far as I can tell. build_and_load gains a double-check inside the lock, but the generated sources are still written by the module generators before the spec is constructed, outside any lock — so your premise survives the bump.

Given that, is a persistent per-rank directory (<shared>/rank<N>) rather than a temp one an option? It keeps the ranks from racing each other, which is the actual bug, while leaving each rank's cache warm across runs. That would also let you drop the rmtree.

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63723 [ run ] completed with state SUCCESS. Commit: 086857b
/LLM/main/L0_MergeRequest_PR pipeline #51676 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@brnguyen2 brnguyen2 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — the comments below are optional touch-ups, not blockers.

The race analysis and the gating (n_workers > 1, explicit-workspace passthrough, kill switch) look right, and I appreciate that the two real-spawn tests actually exercise the python_args path rather than just asserting on the argument list — that's the part most likely to break silently on an mpi4py bump.

Main concern is the cache cost, raised inline: a fresh mkdtemp per worker per run makes every FlashInfer JIT build cold on every launch, for every worker, forever. That's trading a rare truncated-file failure for a guaranteed multi-minute startup regression on any multi-GPU run using the FlashInfer backend. A rank-keyed stable directory (e.g. $FLASHINFER_WORKSPACE_BASE_default/rank<N>) removes the same cross-rank race while keeping the cache warm across runs, at the cost of N copies of the build tree. If there's a reason that doesn't work (concurrent jobs on one node reusing rank IDs?), say so in the commit message — it's the obvious question a future reader will have.

Also worth stating in the code or the NVBug: this is a workaround for a specific upstream bug in flashinfer-python 0.6.15 (requirements.txt:61). A short comment naming the version and the upstream issue makes it removable later instead of permanent.

Secondary: TRTLLM_FLASHINFER_WORKSPACE_PER_PROCESS is a new user-visible knob with no documentation anywhere. One line somewhere discoverable would help support triage.

Comment thread tensorrt_llm/llmapi/mpi_session.py Outdated
_FLASHINFER_WORKER_BOOTSTRAP = (
"import os,shutil,tempfile;"
"from mpi4py import MPI;"
"workspace=tempfile.mkdtemp("

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FLASHINFER_WORKSPACE_BASE is where FlashInfer caches JIT-compiled .so files, not just scratch — see the comment at tests/unittest/auto_deploy/standalone/test_standalone_package.py:250. A fresh mkdtemp per worker plus rmtree on exit means every worker recompiles every kernel on every launch and throws the artifacts away. On a cold cache that's minutes of added startup per run, permanently, for all multi-GPU FlashInfer-backend flows.

A rank-keyed stable path (e.g. ~/.cache/flashinfer-rank{rank} or <default_base>/rank{rank}) removes the same cross-rank race and keeps the cache across runs. Cost is N copies of the build tree, which seems much cheaper than N cold builds per run.

Comment thread tensorrt_llm/llmapi/mpi_session.py Outdated
"prefix=f'trtllm-flashinfer-{MPI.COMM_WORLD.Get_rank()}-{os.getpid()}-');"
"os.environ.setdefault('FLASHINFER_WORKSPACE_BASE',workspace);"
"from mpi4py.futures.server import main\n"
"try:main()\n"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The finally: shutil.rmtree(...) only runs on a clean worker exit. shutdown_abort() in this same file SIGKILLs worker PIDs, and MPI_Abort paths don't unwind either — so on any crash/abort the temp workspace (a full JIT build tree, easily hundreds of MB) is orphaned in /tmp with a random suffix that nothing will ever collect. On a shared node with repeated failing runs that fills /tmp.

If you keep the temp-dir approach, consider mkdtemp(dir=<stable parent>) and having the parent session clean the parent dir on shutdown, so the abort path is recoverable.

Comment thread tensorrt_llm/llmapi/mpi_session.py Outdated
and "FLASHINFER_WORKSPACE_BASE" not in env)
if isolate_workspace:
# Keep downloaded cubins shared; only generated JIT sources race.
env.setdefault(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoding ~/.cache/flashinfer/cubins duplicates FlashInfer's own default-location logic, which is version-dependent and derived from the workspace base upstream. Two risks: (1) if upstream changes the layout (e.g. adds a version subdir), workers silently get a different cubin dir than a non-MPI run, defeating the sharing this is meant to preserve; (2) expanduser resolves the parent's $HOME and passes it as an absolute path — on a multi-node spawn without a shared home, that path may not exist or be writable on the worker node.

Preferably read the default from the installed flashinfer jit env module rather than reconstructing it here.

from types import SimpleNamespace

import pytest
from mpi4py.futures import server

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import runs at collection time, before pytestmark on line 29 is consulted. On a build with ENABLE_MULTI_DEVICE=0 (where mpi4py isn't necessarily installed) this is a collection error, not a skip — and it takes the whole file down. Note test_mpi_session.py deliberately avoids importing mpi4py directly for this reason.

Move it into the helper that needs it, or guard with server = pytest.importorskip("mpi4py.futures.server").

rank = mpi_session.mpi4py.MPI.COMM_WORLD.Get_rank()
assert prefixes == [f"trtllm-flashinfer-{rank}-{os.getpid()}-"]
assert os.environ[_FLASHINFER_WORKSPACE_ENV] == str(workspace)
assert not workspace.exists()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is vacuous: mkdtemp is faked on line 53 and never creates workspace, so it can't exist regardless of whether the finally: rmtree ran. If the intent is to prove cleanup happens, have fake_mkdtemp actually workspace.mkdir() (and drop a file in it) first — then the assertion tests something.

…Agent)

Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
@VALLIS-NERIA
VALLIS-NERIA requested a review from a team as a code owner August 5, 2026 11:03
@VALLIS-NERIA
VALLIS-NERIA requested a review from kaiyux August 5, 2026 11:03
@VALLIS-NERIA

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64033 [ run ] triggered by Bot. Commit: 99a0b24 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64033 [ run ] completed with state SUCCESS. Commit: 99a0b24
/LLM/main/L0_MergeRequest_PR pipeline #51963 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants