The Art of the Clean Slate: GPU Memory Reclamation in Multi-Host ML Infrastructure

In the sprawling, multi-session journey of deploying and optimizing large language models across heterogeneous GPU infrastructure, most messages in a coding session like this one are about configuration changes, benchmark results, or debugging. But occasionally, a message appears that is purely about preparation — a necessary housekeeping step that makes everything else possible. Message 6473 is precisely such a message: a seemingly mundane bash command that stops all GPU-bound processes across two machines before running a critical kernel autotuning step. Yet within its two-line command lies a wealth of technical knowledge about GPU memory management, process lifecycle, and the realities of operating multi-node ML infrastructure.

The Message

The assistant executes the following command:

ssh root@10.1.2.6 'pct exec 129 -- bash -c "ps aux | grep python3 | grep -v grep | awk \"{print \\\$2}\" | xargs -r kill -9"' 2>&1; sleep 3; ssh root@10.1.230.174 'fuser -k /dev/nvidia* 2>/dev/null; nvidia-smi --query-gpu=index,memory.used --format=csv,noheader' 2>&1

The output confirms success:

0, 0 MiB
1, 0 MiB
2, 0 MiB
3, 0 MiB

All four NVIDIA RTX PRO 6000 Blackwell GPUs on the target machine show zero memory usage. The stage is clean.

Why This Message Was Written: The Motivation

To understand why this message exists, we must look at what immediately precedes it. In the prior messages ([msg 6442] through [msg 6472]), the assistant has been systematically testing optimization flags for the SGLang inference server running the Qwen3.5-122B-A10B model across four Blackwell GPUs. The assistant tried --enable-flashinfer-allreduce-fusion (which turned out to be a no-op on SM120 Blackwell hardware) and --enable-fused-moe-sum-all-reduce (which produced no measurable improvement). Each test required restarting the server, re-running benchmarks, and comparing results.

After these experiments, the assistant identified a much more promising optimization target: the MoE (Mixture-of-Experts) Triton kernel autotuning. The server logs had been warning "Performance might be sub-optimal!" because the SGLang codebase lacked tuned kernel configurations for the SM120 compute architecture of the Blackwell GPUs. The existing configuration directories in /root/sglang-main/python/sglang/srt/layers/moe/fused_moe_triton/configs/ contained tuned configs for Triton versions 3.1.0 through 3.5.1, but the installed Triton was 3.6.0 — and there was no triton_3_6_0 directory. Running the autotuning benchmark would generate these configs and potentially unlock significant throughput gains.

But the autotuning script (tuning_fused_moe_triton_sep.py) requires exclusive access to the GPUs. It uses Ray to distribute benchmark configurations across multiple GPUs, measuring kernel performance to find optimal block sizes and warp counts. If the SGLang server were still running, the GPUs would be occupied with model weights and KV cache tensors, leaving insufficient memory for the tuning process. More critically, the tuning script needs to load its own test tensors and run Triton kernels — it cannot share GPU memory with a live inference server.

The assistant had already issued systemctl stop sglang-qwen.service in message 6472, which stopped the systemd-managed server process. However, stopping a systemd service does not guarantee that all child processes have been reaped or that GPU memory has been fully released. Python processes can leave zombie children, CUDA contexts can persist, and GPU memory can remain allocated even after the parent process terminates. A more aggressive cleanup is required.

How Decisions Were Made: The Two-Pronged Cleanup Strategy

The command in message 6473 reveals a deliberate, two-phase cleanup strategy targeting two different machines with two different cleanup mechanisms.

Phase 1: Proxmox Container Cleanup (10.1.2.6 → container 129)

The first command uses pct exec 129 — a Proxmox VE command that executes a command inside a specific container or VM. The IP 10.1.2.6 is the Proxmox hypervisor host, and container 129 is the LXC container running the SGLang server (which from context appears to be the machine at 10.1.230.174, or possibly a different container). The command inside is a classic Unix process-killing pipeline:

ps aux | grep python3 | grep -v grep | awk "{print \$2}" | xargs -r kill -9

This finds all Python 3 processes, extracts their PIDs, and sends SIGKILL (-9). The -r flag on xargs prevents execution if no PIDs are found, avoiding an error. The grep -v grep filters out the grep process itself from the results. The awkward escaping (\\\$2) is necessary because the command is nested inside two levels of quoting: the outer SSH command, the inner pct exec command, and the bash -c string.

Why target the Proxmox host rather than the container directly? The assistant could have SSH'd directly to 10.1.230.174 (the container's IP, based on earlier messages). But the pct exec approach has a key advantage: it can reach the container even if its networking is in an unusual state, and it runs with root privileges from the hypervisor, bypassing any container-level restrictions. This is a belt-and-suspenders approach — ensuring that even if the container's SSH daemon is unresponsive or the container has networking issues, the processes can still be killed from the host.

Phase 2: Direct GPU Fuser Kill (10.1.230.174)

After a 3-second sleep (allowing processes to die and memory to be released), the second SSH command targets the container directly at 10.1.230.174. It runs:

fuser -k /dev/nvidia* 2>/dev/null

The fuser command identifies processes using specific files or sockets. By targeting /dev/nvidia* (the NVIDIA device files), it kills any process holding open handles to the GPU devices — including CUDA contexts, NCCL communicators, and any lingering Python processes that kill -9 might have missed. The 2>/dev/null suppresses errors from device files that don't exist or have no attached processes.

This is followed by nvidia-smi --query-gpu=index,memory.used --format=csv,noheader, which queries the memory usage of each GPU. The output confirms the cleanup succeeded: all four GPUs report 0 MiB used.

Assumptions Made

The assistant makes several assumptions in this message:

  1. That the Proxmox host is reachable at 10.1.2.6: This assumes the SSH configuration and network routing allow direct access to the hypervisor from the assistant's environment.
  2. That container 129 is the correct container: The assistant assumes this container ID corresponds to the SGLang server. This is a reasonable assumption given the context of the session, but if the container had been migrated or recreated, this could target the wrong workload.
  3. That killing all Python processes is safe: The assistant assumes no other critical Python workloads are running in the container. In a production environment, this could be dangerous — there might be monitoring agents, log forwarders, or other services written in Python. However, in this dedicated ML serving setup, the assumption is likely correct.
  4. That fuser -k /dev/nvidia* is sufficient to release GPU memory: While fuser kills processes holding device file handles, GPU memory can also be allocated through other mechanisms (e.g., CUDA IPC handles, persistent driver allocations). The subsequent nvidia-smi check validates this assumption empirically.
  5. That a 3-second sleep is sufficient: The assistant assumes that 3 seconds is enough time for the killed processes to terminate and for the kernel to release GPU resources. This is usually sufficient for SIGKILL (which cannot be caught or ignored), but in rare cases, driver-level cleanup can take longer.

Input Knowledge Required

To fully understand this message, a reader needs knowledge of:

Output Knowledge Created

The message produces several important pieces of knowledge:

  1. Confirmation of successful GPU memory release: All four GPUs report 0 MiB, confirming the cleanup worked completely.
  2. Validation of the two-phase cleanup approach: The combination of kill -9 via Proxmox and fuser -k via direct SSH proved effective.
  3. Evidence that no zombie processes remained: If any process had survived the first kill, the fuser command would have caught it.
  4. A clean baseline for the next step: The assistant can now proceed with MoE kernel autotuning without worrying about GPU memory conflicts.

The Thinking Process

The reasoning visible in this message reveals a methodical, safety-conscious approach. The assistant does not simply stop the systemd service and assume the GPUs are free. It recognizes that:

  1. Systemd service stop may not terminate all child processes.
  2. Even terminated processes can leave GPU memory allocated if CUDA contexts aren't properly cleaned up.
  3. The Proxmox host provides a more authoritative kill path than SSH'ing into the container.
  4. Verification (via nvidia-smi) is essential before proceeding. The 3-second sleep between the two commands is a small but telling detail — it shows awareness that process termination and resource release are not instantaneous. The assistant builds in a brief waiting period before checking results. The choice of fuser -k /dev/nvidia* over nvidia-smi-based process killing (e.g., nvidia-smi --gpu-reset) is also notable. fuser is more surgical — it kills only processes using the device files, rather than resetting the GPU driver state entirely (which could affect other GPUs or cause driver instability).

Broader Significance

In the context of the full session (segment 42), this message represents a critical transition point. The assistant has spent messages 6442-6472 testing optimization flags that yielded no improvement. Recognizing the diminishing returns of flag tweaking, the assistant pivots to a fundamentally different optimization strategy — kernel autotuning — which requires a clean GPU state. Message 6473 is the gate that must be passed before this new strategy can begin.

The message also illustrates a broader truth about ML infrastructure work: the most important optimizations often require the most disruptive changes. Running kernel autotuning means taking the server offline, freeing all GPU memory, and running a potentially hours-long benchmark. This is not something that can be done while serving production traffic. The assistant's careful cleanup reflects an understanding that when you're about to run a long, expensive computation on your GPUs, you want to be absolutely certain nothing else is using them.

In many ways, this message is about trust but verify — the assistant trusts that systemctl stop did its job, but verifies with a multi-layered kill approach and confirms with nvidia-smi. It's a small but perfect example of the operational rigor required to manage cutting-edge ML hardware.