The Vanishing Sitecustomize: Debugging NCCL Environment Propagation in a Python Venv

The Message

ssh root@10.1.230.174 'PYTHONSTARTUP=/dev/null /root/ml-env/bin/python3 -c "import site; site.main(); import os; print(os.environ.get(\"NCCL_PROTO\", \"NOT SET\"))"'

Output: NOT SET

This single, seemingly trivial diagnostic command — executed in message [msg 4823] — represents a critical juncture in a multi-hour debugging session. The assistant was locked in a battle with Python's environment propagation machinery, trying to force NCCL tuning environment variables into spawned worker processes for a speculative decoding setup. The command's output, NOT SET, confirmed that yet another approach had failed, and forced a fundamental rethinking of how environment variables flow through Python's multiprocessing spawn mechanism.

The Debugging Context: Why This Message Exists

To understand why this particular command was written, we must step back into the broader narrative. The assistant was deploying an EAGLE-3 speculative decoding system on top of a massive 1-trillion-parameter Mixture-of-Experts model (Kimi K2.5) running across 8 PCIe-connected GPUs. The hardware had no NVLink interconnect — all GPU-to-GPU communication went through PCIe, which is dramatically slower. This made NCCL tuning absolutely critical for performance.

Earlier in the session ([msg 4798] through [msg 4822]), the assistant had discovered a painful regression: the EAGLE-3 speculation setup that previously showed 94 tok/s was no longer reproducible. The stable baseline had settled at 82-83 tok/s, and EAGLE-3 with 2-step speculation was delivering only 59-61 tok/s — a 27% degradation compared to running without speculation at all. The root cause was traced to the verify step: in EAGLE-3 mode, verifying 3 tokens through the 1T MoE model took approximately 30ms per cycle, compared to roughly 12ms for a single-token decode in baseline mode. This 2.5x slowdown was the difference between speculation being beneficial and being harmful.

The assistant hypothesized that NCCL tuning environment variables — specifically NCCL_PROTO=LL, NCCL_ALGO=Ring, NCCL_P2P_LEVEL=SYS, NCCL_MAX_NCHANNELS=16, NCCL_BUFFSIZE=16777216, and NCCL_NTHREADS=512 — were not propagating to the spawned worker processes that handled the target model's verify forward pass. These variables are critical for PCIe-only multi-GPU setups because they force NCCL to use the LL (Low Latency) protocol with Ring algorithm and system-level P2P, which dramatically reduces communication overhead compared to the default settings that assume NVLink is available.

The Assumptions Under Test

The command in message [msg 4823] was testing a specific hypothesis: that a sitecustomize.py file placed in the venv's site-packages directory would be automatically loaded by Python's site module during interpreter startup, and that it would set the NCCL environment variables before any NCCL communicator was initialized.

This assumption rested on several layers of reasoning:

  1. Python's site module loads sitecustomize.py automatically. The standard Python startup sequence imports the site module, which searches for a sitecustomize.py file in the site-packages directories and executes it. This is a well-known mechanism for injecting environment-wide configuration.
  2. A venv's site-packages directory is a valid location for sitecustomize.py. The assistant had verified in [msg 4817] that the venv's site-packages path (/root/ml-env/lib/python3.12/site-packages) was in the list returned by site.getsitepackages(). This suggested the file would be found.
  3. Setting env vars in sitecustomize.py would affect spawned children. The theory was that if the environment variables were set at the earliest possible moment — before any NCCL code ran — they would be inherited by child processes created via multiprocessing with spawn context, because spawn uses execv which inherits the parent's C-level environment.
  4. No other mechanism was interfering. The PYTHONSTARTUP=/dev/null prefix was added to eliminate the possibility that a PYTHONSTARTUP script was somehow interfering with site module initialization or overwriting environment variables after sitecustomize.py ran.

The Failure and Its Significance

The output NOT SET was unambiguous: the sitecustomize.py file was not being loaded, or if it was loaded, its environment variable assignments were not persisting. This was a significant finding because it ruled out what seemed like the most reliable approach for ensuring NCCL tuning applied to all spawned processes.

The failure of sitecustomize.py to load in a venv context is a subtle but important Python behavior. While site.getsitepackages() includes the venv's site-packages directory for the purpose of adding it to sys.path, the sitecustomize.py loading mechanism has historically been more restrictive. In some Python versions and venv configurations, sitecustomize.py is only loaded from the system site-packages directories, not from venv-local ones. This is a deliberate security and isolation measure — venvs are meant to be self-contained environments, and allowing arbitrary code execution from sitecustomize.py within a venv could undermine that isolation.

The assistant had previously tried several other approaches that also failed:

The Thinking Process Visible in This Message

The command reveals a meticulous, hypothesis-driven debugging methodology. The assistant is working through a decision tree:

  1. Hypothesis: sitecustomize.py isn't being loaded. Test: Run Python with -c and check if the env var is set. Result: NOT SET — hypothesis confirmed.
  2. Refined hypothesis: Maybe PYTHONSTARTUP is interfering. Test: Set PYTHONSTARTUP=/dev/null to disable any startup script. Result: Still NOT SETPYTHONSTARTUP is not the issue.
  3. Refined hypothesis: Maybe site.main() isn't being called (though it normally is during startup). Test: Explicitly call site.main() in the script to force site module processing. Result: Still NOT SET — the file genuinely isn't being loaded. The use of PYTHONSTARTUP=/dev/null is a particularly elegant debugging technique. Rather than trying to figure out what PYTHONSTARTUP might be set to, the assistant simply neutralizes it by pointing it at /dev/null. This eliminates an entire class of potential interference in one line.

Input Knowledge Required

To fully understand this message, one needs:

  1. Python's site module mechanics: Understanding that sitecustomize.py is loaded during interpreter startup to allow site-wide customization, and that its loading depends on the site module's search path.
  2. Python venv behavior: Venvs have nuanced interactions with the site module. The sitecustomize.py loading behavior differs between system Python and venv Python, and can be affected by flags like --system-site-packages and --without-pip.
  3. NCCL environment variables: The specific variables being set (NCCL_PROTO, NCCL_ALGO, NCCL_P2P_LEVEL, etc.) control NVIDIA's Collective Communications Library's behavior for multi-GPU communication. These are critical for performance on systems without NVLink.
  4. Multiprocessing spawn mechanics: Python's spawn start method creates a new Python interpreter process that inherits the parent's environment. Environment variables set via os.environ in the parent should be visible to children, but only if they are set before the child is created.
  5. The broader EAGLE-3 speculative decoding context: The urgency behind this debugging comes from the need to make speculative decoding viable on PCIe-only hardware. Every millisecond saved in the verify step directly translates to throughput gains.

Output Knowledge Created

This message produced a crucial piece of negative knowledge: sitecustomize.py in a venv's site-packages directory is not reliably loaded by Python's site module, even when explicitly triggered via site.main(). This finding forced the assistant to abandon the sitecustomize.py approach and seek alternative solutions.

The practical output was a narrowing of the solution space. The assistant now knew that:

Conclusion

Message [msg 4823] is a masterclass in systematic debugging. A single command, carefully constructed with PYTHONSTARTUP=/dev/null and an explicit site.main() call, definitively ruled out a promising approach to a critical performance problem. The NOT SET output was not just a test result — it was a forcing function that pushed the debugging session toward a deeper understanding of Python's environment propagation architecture and ultimately toward the correct solution. In the high-stakes world of speculative decoding performance optimization, where every millisecond counts, knowing what doesn't work is often as valuable as knowing what does.