<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://photoszzt.github.io/feed.xml" rel="self" type="application/atom+xml"/><link href="https://photoszzt.github.io/" rel="alternate" type="text/html" hreflang="en"/><updated>2026-06-19T07:21:09+00:00</updated><id>https://photoszzt.github.io/feed.xml</id><title type="html">Zhiting’s space</title><subtitle>Zhiting&apos;s space is a personal website dedicated to sharing my thoughts, experiences, and projects. </subtitle><entry><title type="html">Piper</title><link href="https://photoszzt.github.io/blog/2026/piper/" rel="alternate" type="text/html" title="Piper"/><published>2026-06-18T23:39:58+00:00</published><updated>2026-06-18T23:39:58+00:00</updated><id>https://photoszzt.github.io/blog/2026/piper</id><content type="html" xml:base="https://photoszzt.github.io/blog/2026/piper/"><![CDATA[<p>Overview / Takeaway</p> <p>Piper is a PyTorch distributed training system that separates strategy specification from runtime execution, letting users express composed strategies such as PP + DP/EP + ZeRO without hard-coding each combination into the framework. Its core abstraction is a global training DAG that explicitly represents compute, communication, placement, streams, and ordering constraints, enabling joint scheduling across parallelism dimensions. The main result is flexibility without giving up baseline performance: Piper matches common strategies such as ZeRO-1 and 1F1B while enabling DualPipe-like schedules with 6-30% throughput gains, plus PP x ZeRO combinations that other evaluated systems either cannot run or do not memory-optimize correctly.</p> <p>Abstract</p> <ol> <li> <p>Distributed training needs composed strategies Large-scale training increasingly combines data, pipeline, expert parallelism, and memory optimizations such as ZeRO, but deployed foundation-model systems often depend on experts who manually design both high-level sharding and low-level execution.</p> </li> <li> <p>Piper decouples strategy from runtime Piper exposes model annotations and scheduling directives that transform a unified IR, described as “a unified global training DAG that represents all computation and communication”. The runtime then executes per-device plans without being specialized to a fixed strategy.</p> </li> <li> <p>Performance parity plus composed-strategy gains Piper maintains parity on widely used strategies such as ZeRO and enables further gains through joint scheduling of communication and compute in strategies like DeepSeek-V3’s DualPipe.</p> </li> </ol> <p>1 Introduction</p> <ol> <li> <p>High-level and low-level strategy jointly determine throughput A training strategy decomposes into a high-level parallelism plan and a per-device low-level execution plan. The high-level plan sets lower bounds on per-device memory, compute, and communication load; the low-level plan determines how close execution gets to that bound.</p> </li> <li> <p>Existing systems either require experts or expose limited strategy spaces Human-engineered systems such as DeepSeek-V3’s DualPipe require custom codesign of PP, EP, and intra-GPU resource usage. General frameworks such as Megatron, DeepSpeed, and TorchTitan expose knobs, but they tend to dispatch each parallelism dimension independently, which makes composed scheduling hard.</p> </li> <li> <p>DualPipe motivates local microbatch overlap DualPipe shares a GPU between forward and backward microbatches to hide EP communication, but that conflicts with framework assumptions that each microbatch owns the full GPU. Compiler systems like JAX/XLA offer generic tensor placement but lack arbitrary PP scheduling and user control over streams/resources.</p> </li> <li> <p>Piper’s goal is extensibility The target is “to build a system that minimizes the effort needed to specify and implement an arbitrary distributed training strategy”. Piper does this with a scheduling API, a global DAG IR, and a strategy-agnostic runtime.</p> </li> <li> <p>Reported contributions are API, IR, runtime, and evaluation Piper contributes a user scheduling interface, a unified global training DAG for joint compute/communication scheduling, an efficient distributed runtime, and an evaluation showing parity on common strategies plus better performance and memory efficiency on composed ones.</p> </li> </ol> <p>2 Background</p> <ol> <li> <p>DP and ZeRO reduce redundant state differently In DP, every worker stores a full model replica, computes local gradients, and averages gradients with allreduce or allgather/reduce-scatter. ZeRO reduces redundant state by sharding optimizer state, gradients, and/or weights, rematerializing state with allgather and resharding after use.</p> </li> <li> <p>TP, EP, and CP put communication on the critical path Tensor, expert, and context parallelism shard weights or activations and require collectives according to the sharding plan. Unlike DP, their collectives execute directly on the batch’s critical path.</p> </li> <li> <p>PP needs microbatch schedules Pipeline parallelism shards layers across workers, then uses microbatches to overlap execution across devices. Its performance depends on bubbles, communication overhead, and schedule design; PP is naturally MPMD because ranks run different operations in different orders.</p> </li> <li> <p>Composed strategies must handle heterogeneous submodules The DualPipe-like example combines PP across layers, EP for expert layers, and DP for non-expert attention layers. This matters more as models become heterogeneous, such as Qwen3-Next’s diverse attention layers and multimodal models with modality- specific components.</p> </li> </ol> <p>3 Challenges</p> <ol> <li> <p>High-level strategy must include intra-device parallelism As communication overhead grows, scheduling must include communication/compute overlap within a GPU. DualPipeV shows that overlapping forward and backward microbatches helps, but overlapping forward-forward or backward-backward can introduce bubbles that erase gains.</p> </li> <li> <p>Low-level scheduling faces resource contention Multiple parallelism dimensions introduce operations that compete for GPU memory, network bandwidth, streams, and communicators. In the DualPipe-style example, a DP allreduce can interfere with EP all-to-all; measured EP communication slowed by 1.46x under background DP allreduces.</p> </li> <li> <p>Communication partitioning is a tradeoff Using separate streams can avoid sequential delay but create bandwidth interference; putting DP and EP communication on the same stream can delay critical all-to-alls; partitioning DP allreduces into smaller pieces may reduce interference but can lower communication efficiency.</p> </li> <li> <p>Runtime must be strategy-agnostic but still efficient The runtime must avoid CPU scheduling overhead, allocate memory/streams/communicators efficiently, and jointly schedule communication from different parallelism dimensions. Near capacity, PyTorch’s memory allocator can stall waiting for in-flight work, so ZeRO can improve throughput as well as memory.</p> </li> <li> <p>Existing frameworks have incomplete PP x ZeRO behavior The paper reports that general frameworks do not fully support all ZeRO levels with PP, likely because ZeRO hooks interact poorly with PP’s repeated layer execution across microbatches. Piper’s unified DAG supports 3-8x larger batch sizes in the PP x ZeRO case study.</p> </li> </ol> <p>4 Design</p> <ol> <li> <p>Piper has compiler and runtime components The compiler translates annotated models and user schedules into a distributed execution plan; the runtime executes that plan on distributed workers. The design avoids hard-coding specific combinations of parallelism strategies.</p> </li> <li> <p>The IR is a global training DAG The DAG contains Chunk nodes for compute and Comm nodes for point-to-point or collective communication. Nodes have device placement, stream assignment, exec functions, and edges for data dependencies.</p> </li> <li> <p>Annotations define schedulable regions Users annotate meaningful model regions, such as PP stages or expert MLP blocks. Piper converts these into Chunks that can later be placed, replicated, sharded, split, or ordered.</p> </li> <li> <p>Scheduling directives transform the DAG The main directives are Place, Replicate, Shard, Split, and Order. Filters select Chunks by dimensions such as PP, EP, MB, or PASS, where PASS can distinguish forward, backward, backward-input, and backward-weight phases.</p> </li> <li> <p>Place and Replicate insert communication Place assigns nodes to devices and inserts send/recv at cross-device boundaries. Replicate synchronizes gradients with allreduce by default or reduce-scatter when gradient sharding is enabled, with optional streams and bucket sizes.</p> </li> <li> <p>Shard, Split, and Order express expert parallelism, microbatching, and schedules Shard inserts all-to-all before and after matched Chunks, enabling EP when combined with Replicate. Split duplicates a sub-DAG into microbatches. Order adds temporal dependencies and can express overlapped sub-DAGs through nested filter lists.</p> </li> <li> <p>DualPipe can be specified concisely The simplified DualPipe schedule uses streams for PP, EP, and DP communication; places two PP stages across devices; replicates non-expert chunks; shards expert chunks; splits into two microbatches; and orders microbatches so one PP stage overlaps forward and backward work.</p> </li> </ol> <p>4.2 Piper Compiler</p> <ol> <li> <p>Compilation starts from TorchDynamo graph capture Piper extracts a PyTorch fx.Graph, initially treating all tensor operators for a forward-backward pass as one Chunk. Annotation boundaries split this graph into subgraphs, each becoming a Chunk’s forward exec function; PyTorch autograd supplies backward execution.</p> </li> <li> <p>Model-state buckets are tied to Chunks The compiler uses tensor operator dependencies to associate parameters, gradients, and optimizer state with each Chunk. Currently, a state bucket can only be associated with Chunks that share the same placement.</p> </li> <li> <p>Directives become graph rewrites The compiler mechanically applies user scheduling directives, inserting Comm nodes such as all-to-all and allreduce. It then removes unnecessary parameter allgathers or gradient reduce-scatters when consecutive Chunks use the same state bucket.</p> </li> <li> <p>The final DAG includes data and temporal constraints The compiler output is a distributed DAG with explicit communication, placement, streams, model data dependencies, and Order dependencies. Missing stream assignments default to the compute stream.</p> </li> </ol> <p>4.3 Piper Runtime</p> <ol> <li> <p>The centralized scheduler creates per-device partial orders The scheduler decomposes the global DAG into one unique sub-DAG per PP rank, with workers sharing a PP rank executing SPMD. Tasks on the same stream are totally ordered; tasks on different streams are ordered only when data or temporal dependencies require it.</p> </li> <li> <p>Independent tasks are scheduled by a simple dependency heuristic For overlapping sub-DAGs, Piper creates one queue per stream, repeatedly chooses a ready task with the most downstream dependencies, and appends it to that task’s stream queue. This works well for symmetric DualPipe-style forward/backward overlap.</p> </li> <li> <p>Workers manage streams, communicators, and memory Each Ray actor loads its model weights and dispatches Chunks/Comms according to the scheduler’s plan. Cross-stream dependencies use CUDA events and stream waits, while independent tasks proceed concurrently.</p> </li> <li> <p>Worker dispatch prioritizes communication carefully Piper prioritizes send communication first, defers receive communication to reduce P2P interference, and among other communication tasks prioritizes critical-path operations over reductions. Deterministic ordering avoids collective deadlocks.</p> </li> <li> <p>Separate P2P streams and communicators reduce PP bubbles Piper uses separate send and receive streams plus separate communicators. This avoids requiring a single global P2P order and only requires that downstream workers process data in the same order upstream workers produce it.</p> </li> <li> <p>Memory management explicitly controls state and activations Piper allocates flat buffers for parameter and gradient buckets, stores persistent sharded state for ZeRO, materializes temporary full buffers when needed, and releases buffers after the last consumer completes. Intermediate activations are freed once their final downstream task is scheduled.</p> </li> </ol> <p>5 Implementation</p> <ol> <li> <p>Piper is a TorchDynamo backend It hooks into arbitrary PyTorch code but currently requires fully traceable models so it can partition fx.Graphs at compile time.</p> </li> <li> <p>Annotations are Python context managers Annotations attach metadata during execution; graph capture records that metadata and uses it to segment the graph into Chunks.</p> </li> <li> <p>Runtime execution uses Ray The compiler and centralized scheduler run in a driver process; each worker is a Ray actor.</p> </li> </ol> <p>6 Evaluation</p> <ol> <li> <p>Evaluation compares against three general-purpose frameworks Piper is evaluated against Megatron-LM 0.18.0, DeepSpeed 0.18.9, and TorchTitan 0.2.2 on 4 AWS EC2 NVIDIA 8xA100 nodes with NVLink and EFA.</p> </li> <li> <p>Common PP schedules are competitive For Qwen3 1B with PP-8 x DP/EP-4 across 32 A100s and Qwen3 9B with PP-4 x DP/EP-4 across 16 A100s, Piper supports 1F1B and interleaved 1F1B. TorchTitan schedule builders were adapted to Piper in 29 LoC and 38 LoC.</p> </li> <li> <p>TorchTitan loses performance from memory and stream behavior TorchTitan’s larger DP memory footprint causes PyTorch CUDA allocator delays, and its interleaved schedule is 14% worse than its 1F1B schedule because sends and receives share one stream. Piper-interleaved-1F1B is 5% higher throughput than Piper- 1F1B due to lower memory use and separate send/receive streams.</p> </li> <li> <p>Megatron benefits from fused kernels Megatron’s single-device stage forward takes about 30 ms for Qwen3 1B, versus 40 ms in Piper. Piper’s Chunk abstraction is orthogonal to fused kernels, so those optimizations could be integrated.</p> </li> <li> <p>PP x ZeRO support differs sharply DeepSpeed and Megatron support PP x ZeRO-1 but not ZeRO-2/3 with PP. TorchTitan claims ZeRO-2/3 support, but gradient and weight states do not reshard between all microbatches, so memory savings are much smaller. Piper supports all PP x ZeRO combinations.</p> </li> <li> <p>ZeRO-1 throughput is similar across systems On Qwen3 1B with DP-2 across 2 A100s, ZeRO-1 throughput is Piper 8641 tokens/s ±701, TorchTitan 8637 ±977, DeepSpeed 9352 ±52, and Megatron 9942 ±1106.</p> </li> <li> <p>Piper gets the expected ZeRO memory savings For Qwen3 9B with 8-way PP and 4-way DP across 32 A100s, PP x ZeRO-1 variants OOM even at the smallest batch size. TorchTitan OOMs at batch size 8 for ZeRO-2 and 16 for ZeRO-3, while Piper runs up to batch size 32 for ZeRO-2 and 40 for ZeRO-3, corresponding to 8x and 3.3x larger batch sizes.</p> </li> <li> <p>DualPipeV is easier to express and faster in Piper The DualPipeV schedule builder from TorchTitan was adapted to Piper in 63 LoC using Order for microbatch overlap. On Qwen3 1B, Piper-DualPipeV improves 13% over Piper-1F1B, while TorchTitan-DualPipeV improves only 3% over TorchTitan-1F1B.</p> </li> <li> <p>Qwen3 9B DualPipeV shows gains where TorchTitan OOMs TorchTitan could not run the 9B DualPipeV case due to OOM. Piper-DualPipeV improves 10% over Piper interleaved 1F1B and 6% over Megatron interleaved 1F1B, while Megatron still benefits from fused kernels that Piper could potentially incorporate.</p> </li> <li> <p>Scalability is reasonable Piper scales Qwen3 1B across 2-, 4-, and 8-way PP and 2- and 4-way DP, with global batch size scaled linearly. Figure 9 shows throughput increasing close to the theoretical curve, reaching roughly 390k tokens/s for PP=8, DP=4.</p> </li> </ol> <p>7 Related Work</p> <ol> <li> <p>General frameworks expose fixed strategy sets Megatron, DeepSpeed, and TorchTitan implement common DP/ZeRO, TP/EP/CP, and PP mechanisms, but each dimension dispatches eagerly with little synchronization across dimensions, making joint scheduling of memory and communication bandwidth difficult.</p> </li> <li> <p>Compiler systems inspire Piper but lack low-level user scheduling JAX/XLA and GSPMD-inspired systems provide tensor sharding annotations and compiler-inserted communication, but are limited in arbitrary PP scheduling and do not expose per-device resources such as GPU streams to users.</p> </li> <li> <p>DSLs and scheduling systems overlap with Piper’s goals CoCoNeT, AutoSP, DynaFlow, Slapo, and TVM relate through annotation, compiler support, or schedule languages. Piper extends these ideas to distributed tensor programs with explicit intra-device parallelism and a flexible runtime.</p> </li> <li> <p>Auto-parallelism systems need broader execution support Many auto-parallel systems restrict the search space to remain tractable and may miss strategies such as full communication/compute overlap. Piper aims to serve as a common runtime for such systems by supporting richer composed strategies through a unified IR.</p> </li> <li> <p>nnScaler is closest but lacks intra-device parallelism nnScaler allows generic constraints similar to Piper directives, but does not support intra-device parallelism. Future Piper work could integrate profile-guided and dynamic approaches from systems such as DeepCompile and Tessera.</p> </li> </ol> <p>8 Conclusion</p> <ol> <li> <p>Piper’s central contribution is explicit strategy representation Piper decouples distributed execution strategy from model and runtime using a unified global training DAG. By making placement, granularity, and ordering explicit, it can express PP, DP, EP, and ZeRO combinations without runtime specialization.</p> </li> <li> <p>The interface supports both expert schedules and automated search Piper is positioned as a programmable substrate for “both expert-designed schedules and future automated search over a rich space of composed strategies”.</p> </li> </ol>]]></content><author><name></name></author><category term="paper-note"/><category term="paper-note"/><summary type="html"><![CDATA[Piper]]></summary></entry><entry><title type="html">Sharpen the Spec, Cut the Code A Case for Generative File System with SYSSPEC</title><link href="https://photoszzt.github.io/blog/2026/sysspec/" rel="alternate" type="text/html" title="Sharpen the Spec, Cut the Code A Case for Generative File System with SYSSPEC"/><published>2026-03-01T23:39:58+00:00</published><updated>2026-03-01T23:39:58+00:00</updated><id>https://photoszzt.github.io/blog/2026/sysspec</id><content type="html" xml:base="https://photoszzt.github.io/blog/2026/sysspec/"><![CDATA[<h3 id="sharpen-the-spec-cut-the-code-a-case-for-generative-file-system-with-sysspec-link">Sharpen the Spec, Cut the Code: A Case for Generative File System with SYSSPEC <a href="https://www.usenix.org/system/files/fast26-liu-qingyuan.pdf">link</a></h3> <h4 id="design">Design</h4> <ul> <li>(1) Functionality specifications, which use concepts like Hoare logic (pre/post-conditions) and invariants to describe the behavior of individual modules. <ul> <li>Specification methods <ul> <li>pre- and post-conditions, following Hoare Logic, define the contractual obligations for each function by specifying the required state before execution and the guaranteed state upon completion.</li> <li>invariants are properties that must hold true across all state transitions, ensuring the module’s integrity</li> <li>a system algorithm outlines the high-level logic for how a function should achieve its state transition, guiding the LLM’s implementation strategy. A high-level intent is often sufficient. The intent can be regarded as a lightweight system algorithm that, expressed in natural language, guides the LLM in generating the desired implementation.</li> </ul> </li> </ul> </li> <li>(2) Modularity specifications, which decompose the system into distinct components and use a rely-guarantee discipline [21] to ensure they can be composed correctly and developed independently. <ul> <li>(1) module implementations must respect their declared dependencies (Rely)</li> <li>(2) provide guarantees about their behavior (Guarantee)</li> <li>(3) compose through logical implication of these contracts</li> </ul> </li> <li>(3) Concurrency specifications, which explicitly define locking protocols and other concurrency-related behaviors that are notoriously difficult for LLMs to infer on their own. <ul> <li>During code generation, our toolchain first directs the LLM to generate a correct sequential version of the code, focusing only on the primary functionality.</li> <li>In a second pass, using the dedicated concurrency specification to instrument the code with the required locking and other concurrent behaviors</li> </ul> </li> </ul> <h4 id="the-sysspec-toolchain">The SYSSPEC Toolchain</h4> <ul> <li>SpecCompiler <ul> <li>Two-phase prompting - First phase, it generates a correct sequential implementation of the module, focusing only on its core functionality. - Second phase, it uses the dedicated concurrency specification to instrument this sequential code with the necessary locking and concurrent behaviors.</li> <li>Retry-with-feedback loop used within each phase: a CodeGen agent generates the implementation, and a separate, reasoning-focused SpecEval agent reviews the output against the specification. If the SpecEval agent identifies a flaw, it does not simply report failure; instead, it generates specific, actionable feedback (e.g., “The case where function foo() fails is not handled”). This feedback is then appended to the original prompt, and the CodeGen agent retries. This refinement cycle continues until the generated code satisfies the specification or an attempt-limit is reached.</li> </ul> </li> </ul>]]></content><author><name></name></author><category term="paper-note"/><category term="paper-note"/><summary type="html"><![CDATA[Sharpen the Spec, Cut the Code A Case for Generative File System with SYSSPEC]]></summary></entry><entry><title type="html">attention math</title><link href="https://photoszzt.github.io/blog/2026/attention-math-note/" rel="alternate" type="text/html" title="attention math"/><published>2026-03-01T23:39:58+00:00</published><updated>2026-03-01T23:39:58+00:00</updated><id>https://photoszzt.github.io/blog/2026/attention-math-note</id><content type="html" xml:base="https://photoszzt.github.io/blog/2026/attention-math-note/"><![CDATA[<h3 id="attention-math">Attention math</h3> <h4 id="why-frac1sqrtdk">Why $frac{1}{\sqrt(dk)}$</h4> <p>X, Y are two independent random variable. E[X] = 0, E[Y] = 0. Var[X] = 1, Var[Y] = 1.</p> <p>$Var[XY] = E[X]^2 \dot Var[Y] + E[Y]^2 \dot Var[X] + Var[X] \dot Var[Y]$</p> <p>Since E[x] and E[Y] = 0, $Var[XY] = Var[X] * Var[Y]$</p> <p>For $C = Q * K^T$, $C_{ij} = \sum_{k=1}^{N} Q_{ik} * K_{kj}$. Each entry is a sum of N products of random variables.</p> <p>$Var[QK^T] = dk * 1 * 1$, where N = dk, Var[Q] = 1, Var[k] = 1.</p> <p>We want to find a value to scale the $QK^T$ so that Var[a \dot QK^T] = 1$</p> <p>$Var[aX] = a^2 * Var[X]$, $Var[aQK^T] = a^2 * Var[QK^T] = a^2 * dk = 1$. $a = \frac{1}{\sqrt(dk)}$</p>]]></content><author><name></name></author><category term="paper-note"/><category term="paper-note"/><summary type="html"><![CDATA[reading notes on attention math]]></summary></entry><entry><title type="html">Agentic Context Engineering</title><link href="https://photoszzt.github.io/blog/2026/ace/" rel="alternate" type="text/html" title="Agentic Context Engineering"/><published>2026-02-17T23:39:58+00:00</published><updated>2026-02-17T23:39:58+00:00</updated><id>https://photoszzt.github.io/blog/2026/ace</id><content type="html" xml:base="https://photoszzt.github.io/blog/2026/ace/"><![CDATA[<h3 id="agentic-context-engineering-evolving-contexts-for-self-improving-language-models-link">Agentic Context Engineering: Evolving Contexts for Self-Improving Language Models <a href="https://arxiv.org/pdf/2510.04618">link</a></h3> <h4 id="motivation">Motivation</h4> <p>Contexts should function not as concise summaries, but as comprehensive, evolving playbooks—detailed, inclusive, and rich with domain insights.</p> <h4 id="component">Component</h4> <p><img width="645" height="262" alt="image" src="https://github.com/user-attachments/assets/f0d8e1a4-b893-47f7-9992-f9714c6abea6"/></p> <ul> <li>Generator: produces reasoning trajectories.</li> <li>Reflector: distills concrete insights from successes and errors <ul> <li>separates evaluation and insight extraction from curation, improving context quality and downstream performance.</li> </ul> </li> <li>Curator: integrates these insights into structured context updates.</li> </ul> <h4 id="incremental-delta-updates">Incremental Delta Updates</h4> <ul> <li>context as a collection of structured, itemized bullets, rather than a single monolithic prompt. <ul> <li>(1) metadata: including a unique identifier and counters tracking how often it was marked helpful or harmful.</li> <li>(2) content: capturing a small unit such as a reusable strategy, domain concept, or common failure mode.</li> </ul> </li> <li>Properties: <ul> <li>(1) localization, so only the relevant bullets are updated.</li> <li>(2) fine-grained retrieval, so the Generator can focus on the most pertinent knowledge.</li> <li>(3) incremental adaptation, allowing efficient merging, pruning, and de-duplication during inference.</li> </ul> </li> <li>Incrementally produces compact delta contexts: small sets of candidate bullets distilled by the Reflector and integrated by the Curator.</li> </ul> <h4 id="grow-and-refine">Grow-and-Refine</h4> <ol> <li>bullets with new identifiers are appended, while existing bullets are updated in place (e.g., incrementing counters).</li> <li>A de-duplication step then prunes redundancy by comparing bullets via semantic embeddings. This refinement can be performed proactively (after each delta) or lazily (only when the context window is exceeded), depending on application requirements for latency and accuracy.</li> </ol>]]></content><author><name></name></author><category term="paper-note"/><category term="paper-note"/><summary type="html"><![CDATA[Evolving Contexts for Self-Improving Language Models]]></summary></entry><entry><title type="html">perftracker</title><link href="https://photoszzt.github.io/blog/2026/perftracker/" rel="alternate" type="text/html" title="perftracker"/><published>2026-02-17T23:39:58+00:00</published><updated>2026-02-17T23:39:58+00:00</updated><id>https://photoszzt.github.io/blog/2026/perftracker</id><content type="html" xml:base="https://photoszzt.github.io/blog/2026/perftracker/"><![CDATA[<h3 id="perftracker-online-performance-troubleshooting-for-large-scale-model-training-in-production-link">PerfTracker: Online Performance Troubleshooting for Large-scale Model Training in Production <a href="https://arxiv.org/pdf/2506.08528">link</a></h3> <h4 id="observation">Observation</h4> <p>LMT function (Python functions, GPU/CPU kernel functions, memory operations, etc.) executions exhibit two significant characteristics.</p> <ul> <li>Within a single worker, most low-level functions are executed repeatedly. This is because training involves many identical iterations, and models are typically composed of repetitive submodules (e.g., transformer blocks).</li> <li>Runtime behaviors of functions are highly identical across workers, because modern parallelisms (e.g., at data, pipeline, tensor, and expert levels) distribute workloads evenly with frequent synchronization operations.</li> <li>Most performance issues can be diagnosed by observing abnormal function runtime behaviors in comparison to all other function executions</li> </ul> <h4 id="insight">Insight</h4> <ul> <li>(1) Performance issues can be observed by profiling the behavior of function executions</li> <li>(2) We can troubleshoot performance issues using differential observability that localizes the offending function executions with abnormal behavior (e.g., low average GPU-NIC throughput without fluctuation)</li> <li>(3) We do not need to analyze fine-grained raw observability data of all the functions; instead, we only need to summarize their runtime behavior patterns.</li> </ul> <h4 id="design">Design</h4> <p><img width="1625" height="553" alt="image" src="https://github.com/user-attachments/assets/a422f84c-4d1d-40bf-8272-7a3b34ce8f12"/></p> <ul> <li>(1) detecting performance degradation of LMT to trigger online profiling (per worker).</li> <li>(2) summarizing runtime behavior patterns of each function from raw profiling data (per worker).</li> <li>(3) a centralized localization algorithm that pinpoints the root-cause function based on the behavior patterns (global).</li> </ul> <h5 id="detecting-performance-degradation">Detecting Performance Degradation</h5> <ul> <li>Indicators of iteration time: A PyTorch training iteration always involves several dataloader.next() calls, followed by several optimizer.step() calls (the number depends on training parameters like pipeline parallelism). The duration from the first dataloader.next() to the last optimizer.step() is regarded as the duration of a complete training iteration.</li> <li>After detecting 𝑀 (=10 in practice) identical sequences starting with dataloader.next() and ending with optimizer.step(), this sequence is defined as the training iteration sequence.</li> <li>Performance degradation detection <ul> <li>(1) The average duration of the recent 𝑁 (=50 in practice) iterations exceeds the recent shortest iteration time by more than 5%.</li> <li>(2) The current training iteration sequence has not yet been fully matched, but the time elapsed since the last event received is at least 5× the average iteration duration (indicating the training is blocked) <ul> <li>If PerfTracker fails to match a training iteration after 𝐾 (=200 in practice) consecutive event receptions, it goes back to the previous iteration detection phase to redetect the training iteration sequence.</li> </ul> </li> </ul> </li> <li>Profile generation (default 20s) <ul> <li>Torch Profiler: function execution events for Python functions, CPU operations, memory operations, and CUDA kernels</li> <li>nsys to sample hardware metrics at 10 kHz, such as GPU, DRAM, NVLink, PCIe, and the network.</li> </ul> </li> </ul> <h5 id="summarization">Summarization</h5> <ul> <li>(1) finding the function execution events on the critical path, including GPU computation kernels, collective communication functions, memory operations, Python functions, and all other functions executed in LMT</li> <li>(2) clustering all execution events of each function (for Python functions, the entire call stack must be identical to be considered the same function), then defining several patterns to summarize the behavior of each function</li> </ul> <h5 id="localization">Localization</h5> <p>Performance issues</p> <ul> <li>(1) a common problem of all LMT workers, like hardware misconfigurations and low-efficiency code implementation</li> <li>(2) a special problem on only a part of the workers, like hardware issues</li> </ul>]]></content><author><name></name></author><category term="paper-note"/><category term="paper-note"/><summary type="html"><![CDATA[Online Performance Troubleshooting for Large-scale Model Training in Production]]></summary></entry><entry><title type="html">GEPA</title><link href="https://photoszzt.github.io/blog/2026/gepa/" rel="alternate" type="text/html" title="GEPA"/><published>2026-02-15T23:39:58+00:00</published><updated>2026-02-15T23:39:58+00:00</updated><id>https://photoszzt.github.io/blog/2026/gepa</id><content type="html" xml:base="https://photoszzt.github.io/blog/2026/gepa/"><![CDATA[<h3 id="gepa-reflective-prompt-evolution-can-outperform-reinforcement-learning-link">GEPA: REFLECTIVE PROMPT EVOLUTION CAN OUTPERFORM REINFORCEMENT LEARNING <a href="https://arxiv.org/pdf/2507.19457">link</a></h3> <h4 id="motivation">Motivation</h4> <p>How can we extract maximal learning signal from every expensive rollout to enable effective adaptation of complex, modular AI systems in low-data or budgetconstrained settings?</p> <h4 id="overview">Overview</h4> <p><img width="1142" height="873" alt="image" src="https://github.com/user-attachments/assets/438a4f32-40c9-489f-825e-06b8da2e40ae"/></p> <h4 id="genetic-prompt-evolution">Genetic prompt evolution</h4> <ol> <li>Onitializing a candidate pool P, where a candidate is a concrete instantiation of the learnable parameters of the compound system, ⟨Π, Θ⟩Φ. Initially, the candidate pool consists only of the base system’s parameters as the sole candidate.</li> <li>Proposes increasingly effective candidates by modifying existing ones through mutation or crossover, informed by learning signals from newly gathered rollouts and while tracking each new candidates’ ancestry. Each new candidate inherits learning signals from its parents, as well as signals from the current rollout.</li> <li>During each iteration, GEPA identifies promising candidates from the candidate pool (candidate selection), proposes a new candidate—possibly by mutating prompts in a module based on reflective feedback or by performing crossover between two candidates—and evaluates this new variant on a minibatch of tasks. If the newly proposed candidate demonstrates improved performance relative to its parent(s) on the local minibatch, then GEPA adds the new candidate to the candidate pool P. This involves tracking internal data structures including tracking the ancestry of the new candidate, along with the full evaluation of the new candidate on a Dpareto, a validation set used for candidate selection.</li> <li>After the budget is depleted, GEPA returns the candidate with the best aggregate performance on Dpareto.</li> </ol> <p><img width="1146" height="923" alt="image" src="https://github.com/user-attachments/assets/6511125b-88cd-4d8d-9549-aef24882d989"/></p> <h4 id="reflection-using-natural-language-feedback">Reflection using natural language feedback</h4> <ol> <li>Given a selected candidate to mutate in the current iteration of the optimization loop, GEPA updates the system with the candidate parameters, selects a target module within the system to improve (via round robin to ensure all modules receive updates), and generates a few rollouts over a minibatch sampled from the training dataset, recording their outcomes (success/failure).</li> <li>By examining the execution traces of the system, GEPA identifies the target module’s inputs, outputs, and reasoning. With this, GEPA uses an LLM to reflectively examine this information, attributing successes or failures to elements of the module’s prompt (or omission thereof), and propose new instructions for the target module.</li> <li>A new candidate is then proposed as a copy of the current candidate, with the target module’s prompt updated to the new proposed prompt.</li> </ol> <h4 id="system-aware-merge">System Aware Merge</h4> <p><img width="592" height="997" alt="image" src="https://github.com/user-attachments/assets/39916d6b-56ac-4ca3-ae30-7bbc46b5c7d7"/></p> <h4 id="pareto-based-candidate-selection">Pareto-based candidate selection</h4> <ol> <li>Identifies the highest score achieved for each individual training instance across all candidates in the pool, creating a “Pareto frontier” of scores achieved by the optimization process so far.</li> <li>Compiles a list of candidates that achieve the best score on at least one training task. This filters the pool down to candidates that incorporate “winning” strategies, preserving every valuable insight discovered in any reflective mutation.</li> <li>Prunes candidates that are strictly dominated: for instance, if Candidate 2 has the best score on Task 1 only, but Candidate 3 achieves that same best score on Task 1 and the best on Task 2, Candidate 2 is removed.</li> <li>Stochastically samples a candidate from this pruned list, assigning higher selection probability to candidates that achieved the best score across more training instances.</li> </ol>]]></content><author><name></name></author><category term="paper-note"/><category term="paper-note"/><summary type="html"><![CDATA[REFLECTIVE PROMPT EVOLUTION CAN OUTPERFORM REINFORCEMENT LEARNING]]></summary></entry><entry><title type="html">MDP</title><link href="https://photoszzt.github.io/blog/2026/mdp/" rel="alternate" type="text/html" title="MDP"/><published>2026-01-04T00:00:00+00:00</published><updated>2026-01-04T00:00:00+00:00</updated><id>https://photoszzt.github.io/blog/2026/mdp</id><content type="html" xml:base="https://photoszzt.github.io/blog/2026/mdp/"><![CDATA[<h3 id="note-to-concepts-in-mathematical-foundations-of-reinforcement-learning-link">Note to concepts in Mathematical Foundations of Reinforcement Learning <a href="https://github.com/MathFoundationRL/Book-Mathematical-Foundation-of-Reinforcement-Learning/tree/main">link</a></h3> <ul> <li>State: describes the agent’s status with respect to the environment.</li> <li>State space: The set of all the states is called the state space, denoted as S = {s1, . . . , $s_n$}.</li> <li>Action:</li> <li>Policy: tells the agent what action to take in a given state.</li> <li>Reward: After executing an action at a state, the agent obtains a reward, denoted as r, as feedback from the environment. The reward is a function of the state s and action a. Hence, it is also denoted as r(s, a).</li> <li>Trajectory: state-action-reward chain.</li> <li>Return of the the trajectory: the sum of all rewards received along the trajectory.</li> <li>Discounted return with discount rate $\gamma$: $\sum_{t=0}^{\infty} \gamma^t r(s_t, a_t)$</li> <li>Episode: When interacting with the environment by following a policy, the agent may stop at some <em>terminal states</em>. The resulting trajectory is called an episode (or a trial ).</li> </ul> <h3 id="markov-decision-process">Markov Decision Process</h3> <ul> <li>Sets: <ul> <li>State space: the set of all states, $S$.</li> <li>Action space: a set of actions, $A(s)$, associated with each state $s \in S$.</li> <li>Reward set: a set of rewards, denoted as $R(s, a)$, associated with each state-action pair (s, a)</li> </ul> </li> <li>Model: <ul> <li>State transition probability: In state $s$, when taking action $a$, the probability of transitioning to state s′ is $p(s’|s,a)$.</li> </ul> <p>\(\sum_{s′ \in S} p(s′|s, a) = 1\)</p> <ul> <li>Reward probability: In state $s$, when taking action $a$, the probability of obtaining reward $r$ is $p(r|s, a)$. For any $(s, a)$,</li> </ul> \[\sum_{r \in R(s,a)} p(r|s, a) = 1\] </li> <li> <p>Policy: In state s, the probability of choosing action $a$ is $\pi(a|s)$. For any $s \in S$,</p> \[\sum_{a \in A(s)} \pi(a|s) = 1\] </li> <li> <p>Markov property: memoryless property of a stochastic process.</p> \[p(s_{t+1}|s_t, a_t, s_{t-1}, a_{t-1}, \ldots, s_0, a_0) = p(s_{t+1}|s_t, a_t)\] \[p(r_{t+1}|s_t, a_t, s_{t-1}, a_{t-1}, \ldots, s_0, a_0) = p(r_{t+1}|s_t, a_t)\] </li> </ul> <h3 id="state-value">State value:</h3> <ul> <li>Discounted return along the trajectory: $G_t = \sum_{k=0}^{\infty} \gamma^k R_{t+k+1}$, $\gamma \in [0, 1)$ is the discount rate.</li> <li> <p>State value:</p> \[\begin{aligned} v_{\pi}(s) &amp;= \mathbb{E}[G_t | S_t = s] \\\\ &amp;= \mathbb{E}[R_{t+1} + \gamma G_{t+1} | S_t = s] \\\\ &amp;= \mathbb{E}[R_{t+1}|S_t=s] + \gamma \mathbb{E}[G_{t+1}|S_t=s] \end{aligned}\] </li> </ul>]]></content><author><name></name></author><category term="math-for-rl"/><category term="math-for-rl"/><summary type="html"><![CDATA[Markov Decision Process]]></summary></entry><entry><title type="html">Megascale</title><link href="https://photoszzt.github.io/blog/2025/megascale/" rel="alternate" type="text/html" title="Megascale"/><published>2025-12-20T00:00:00+00:00</published><updated>2025-12-20T00:00:00+00:00</updated><id>https://photoszzt.github.io/blog/2025/megascale</id><content type="html" xml:base="https://photoszzt.github.io/blog/2025/megascale/"><![CDATA[<ol> <li>Fault-tolerant training <ul> <li> <p>A driver process identifies the failed pod by heartbeat. Driver pauses the training, run self diagnostic test, identify faulty node, driver submits the IP addresses of the nodes to be blocked, along with the information of the Pods running on them, to</p> <p>Kubernetes evicts the faulty nodes and replenishes the cluster with an equivalent amount of healthy ones, which pass our diagnostic tests.</p> </li> </ul> </li> <li>Data collection <ul> <li> <p>IP address, the Pod name, hardware information, current status of the</p> <p>training processes, stdout/stderr logs of training processes, RDMA traffic metrics(look for significant decline or abnormal fluctuation),</p> </li> </ul> </li> <li>Diagnostic Tests</li> <li>Intra-host test <ol> <li>The Loopback test measures the loopback bandwidth from all RDMA NICs (RNICs) to various intra-host endpoints, including memory nodes and GPUs. It conducts a full-mesh test within the host, covering all possible link combinations.</li> <li>RNICto-RNIC test examines the connectivity and bandwidth performance between different RNICs on the same host.</li> </ol> </li> <li>NCCL tests <ol> <li>all-to-all test among the GPUs within a single node</li> <li>all-reduce test with neighboring machines under the same ToR switch</li> </ol> </li> <li>Fast checkpoint <ol> <li>First step: each GPU worker writes its on-chip states to the host memory, and then continues the training process</li> <li>Second step: a background process takes over, asynchronously transferring the state from the host memory to a distributed file system (HDFS in our deployment) for centralized maintenance</li> </ol> </li> <li>Fast recovery: <ol> <li>Observation: Multiple GPU workers often share the same state partition, e.g., the workers in the same data parallel group</li> <li>A single worker in the group to read the shared state partition from HDFS, then broadcasts the state partition to all other GPU workers that share the same data.</li> </ol> </li> <li>Performance Diagnosis with CUDA Event Monitor <ol> <li>Observation: MFU for various training tasks gradually declines over time</li> <li>Method: records the execution time of critical code segments on each machine rank during a run</li> <li>Visualization: <ol> <li>Heat map to show time consumption differences between machines from various dimensions <ol> <li>latency data of the computation phase (forward and backward) across devices and average the latency across steps</li> </ol> </li> <li>The event timeline on machines in a trace format from different distributed views (data parallelism, pipeline parallelism, tensor parallelism).</li> </ol> </li> <li>Implementation: <ol> <li>timer data is wrote to a local file in a line-by-line format,</li> <li>synchronizes this log file with a Kafka queue in real-time.</li> <li>the analytical database remains updated by consuming data from this Kafka queue</li> </ol> </li> </ol> </li> <li>3D Parallel Training Visualization <ol> <li>Each GPU worker logs its own ongoing event upon communication timeout. These logs are then used to construct a visual representation of data dependencies based on the logical topology in the 3D parallel setting.</li> </ol> </li> <li>Collective Communication Group Initialization: <ol> <li>Use Redis for initialization store instead of TCPStore</li> <li>Reduce global barrier</li> </ol> </li> <li>Data pipeline <ol> <li>Asynchronous data preprocessing: While the GPU workers are synchronizing gradients at the end of each training step, the data preprocessing for the subsequent step can start, which hides the preprocessing overhead.</li> <li>Redundant data loader elimination: GPU workers within the same machine are in the same tensor parallel group. Their inputs for each iteration are inherently identical. Only need one copy of data in CPU memory instead of each GPU worker copies memory by itself.</li> </ol> </li> <li>Network <ol> <li>NCCL tune retransmit timer and retry count for fast recovery (doesn’t mention what param and tune to what)</li> </ol> </li> </ol>]]></content><author><name></name></author><category term="paper-review"/><category term="paper-review"/><summary type="html"><![CDATA[Scaling Large Language Model Training to More Than 10,000 GPUs]]></summary></entry><entry><title type="html">Mycroft</title><link href="https://photoszzt.github.io/blog/2025/mycroft/" rel="alternate" type="text/html" title="Mycroft"/><published>2025-12-16T00:00:00+00:00</published><updated>2025-12-16T00:00:00+00:00</updated><id>https://photoszzt.github.io/blog/2025/mycroft</id><content type="html" xml:base="https://photoszzt.github.io/blog/2025/mycroft/"><![CDATA[<h3 id="mycroft-tracing-dependencies-in-collective-communication-towards-reliable-llm-training-link">Mycroft: Tracing Dependencies in Collective Communication Towards Reliable LLM Training <a href="https://arxiv.org/pdf/2509.03018">link</a></h3> <p>Summary: Mycroft, a novel, lightweight distributed tracing and root cause analysis system designed to enhance reliability in Large Language Model (LLM) training by addressing issues within collective communication libraries (CCLs). These libraries often act as “black boxes,” making troubleshooting difficult and leading to wasted resources due to failures and slowdowns in large-scale training jobs. Mycroft functions by implementing Coll-level tracing, which captures fine-grained control and data dependencies—specifically Flow-level and Chunk-level traces—to expose internal communication states with minimal performance overhead. The system uses a real-time trigger mechanism to detect anomalies quickly and a dependency-driven analysis to efficiently pinpoint the root cause of both gray failures and performance degradation in distributed LLM training environments, showing effectiveness when deployed at scale, such as at ByteDance.</p> <p><img width="1155" height="671" alt="image" src="https://github.com/user-attachments/assets/9f314554-8ca0-4176-966d-6dfdfa436777"/></p> <h4 id="instrumentation">Instrumentation</h4> <p>Trace point format</p> <ul> <li>Metadata: IP, comm_id, Gid, GPU_id, Channel_id, QP_id</li> <li>Operation: timestamps, op_name, op_seq, msg_size</li> <li>Chunk: stuck_time, total_chunks, GPU_ready, RDMA_transmitted, RDMA_done</li> </ul> <p>Tracepoints</p> <ul> <li>completion log: when a CollOp finishes. Include metadata (start and end timestamps, bytes transmitted, local and remote NIC information, and other CollOp metadata).</li> <li>realtime state log: every 100ms while a CollOp is in progress. <ul> <li>Includes data transmission progress across all devices, covering Stream Multiprocessor (SM) copies and RDMA writes.</li> <li>Generated in each window until the CollOp completes or the NCCL proxy thread exits or crashes.</li> </ul> </li> </ul> <p>Real time trigger</p> <ul> <li>sampling a subset of ranks across the cluster and monitoring all CollOps.</li> <li>At least one rank per DP group to ensure coverage in common LLM parallelism architectures.</li> <li>Limit sampling to at most 10 ranks.</li> </ul> <div class="language-golang highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// LogData represents the aggregated trace data for a specific IP within a time window.</span>
<span class="c">// In the source, this includes "completion logs" and "real-time state logs" [4].</span>
<span class="k">type</span> <span class="n">LogData</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">IP</span>             <span class="kt">string</span>
	<span class="n">CompletedOps</span>   <span class="kt">int</span>     <span class="c">// Count of completed Coll Ops</span>
	<span class="n">HasRealTimeLog</span> <span class="kt">bool</span>    <span class="c">// True if "real-time state log" exists (indicates active running state) [3]</span>
	<span class="n">Throughput</span>     <span class="kt">float64</span> <span class="c">// Current throughput measurement</span>
	<span class="n">OpInterval</span>     <span class="kt">float64</span> <span class="c">// Current interval between operations</span>
<span class="p">}</span>

<span class="c">// TriggerMechanism maintains the state for Mycroft's anomaly detection.</span>
<span class="k">type</span> <span class="n">TriggerMechanism</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">NormalThroughput</span> <span class="kt">float64</span> <span class="c">// Baseline for healthy throughput [2]</span>
	<span class="n">NormalInterval</span>   <span class="kt">float64</span> <span class="c">// Baseline for healthy operation interval [2]</span>
	<span class="n">Delta</span>            <span class="kt">float64</span> <span class="c">// Time window size (e.g., seconds) [1]</span>
<span class="p">}</span>

<span class="c">// NewTriggerMechanism initializes the trigger system.</span>
<span class="k">func</span> <span class="n">NewTriggerMechanism</span><span class="p">(</span><span class="n">delta</span> <span class="kt">float64</span><span class="p">)</span> <span class="o">*</span><span class="n">TriggerMechanism</span> <span class="p">{</span> <span class="k">return</span> <span class="o">&amp;</span><span class="n">TriggerMechanism</span><span class="p">{</span> <span class="n">Delta</span><span class="o">:</span> <span class="n">delta</span><span class="p">}}</span>

<span class="c">// Trigger implements Algorithm 1: Trigger Mechanism [1].</span>
<span class="c">// Input: Sampled IP list (S_ips), time (t)</span>
<span class="c">// Output: Trigger type (string) and Abnormal IP (string), or empty if normal.</span>
<span class="k">func</span> <span class="p">(</span><span class="n">tm</span> <span class="o">*</span><span class="n">TriggerMechanism</span><span class="p">)</span> <span class="n">Trigger</span><span class="p">(</span><span class="n">sampledIPs</span> <span class="p">[]</span><span class="kt">string</span><span class="p">,</span> <span class="n">t</span> <span class="kt">float64</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
	<span class="c">// Line 2: logs &lt;- Acquire(S_ips, t - Delta, t) [1]</span>
	<span class="n">logs</span> <span class="o">:=</span> <span class="n">tm</span><span class="o">.</span><span class="n">Acquire</span><span class="p">(</span><span class="n">sampledIPs</span><span class="p">,</span> <span class="n">t</span><span class="o">-</span><span class="n">tm</span><span class="o">.</span><span class="n">Delta</span><span class="p">,</span> <span class="n">t</span><span class="p">)</span>

	<span class="k">for</span> <span class="n">ip</span><span class="p">,</span> <span class="n">log</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">logs</span> <span class="p">{</span>
		<span class="c">// Line 3: "if no Coll Ops completed in logs" [1]</span>
		<span class="c">// Context from Section 4.3: Mycroft checks if the rank "stalls mid-operation</span>
		<span class="c">// with real-time state log but without producing completion log" [3].</span>
		<span class="k">if</span> <span class="n">log</span><span class="o">.</span><span class="n">HasRealTimeLog</span> <span class="o">&amp;&amp;</span> <span class="n">log</span><span class="o">.</span><span class="n">CompletedOps</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
			<span class="c">// Line 4: return failure trigger, abnormal IP [1]</span>
			<span class="k">return</span> <span class="s">"failure_trigger"</span><span class="p">,</span> <span class="n">ip</span>
		<span class="p">}</span>

		<span class="c">// Line 6: "if throughput drops by half or Coll Op interval doubles" [2]</span>
		<span class="c">// These heuristics are configurable but based on practical experience [3].</span>
		<span class="n">isThroughputDrop</span> <span class="o">:=</span> <span class="n">tm</span><span class="o">.</span><span class="n">NormalThroughput</span> <span class="o">&gt;</span> <span class="m">0</span> <span class="o">&amp;&amp;</span> <span class="n">log</span><span class="o">.</span><span class="n">Throughput</span> <span class="o">&lt;</span> <span class="p">(</span><span class="n">tm</span><span class="o">.</span><span class="n">NormalThroughput</span><span class="o">*</span><span class="m">0.5</span><span class="p">)</span>
		<span class="n">isIntervalSpike</span> <span class="o">:=</span> <span class="n">tm</span><span class="o">.</span><span class="n">NormalInterval</span> <span class="o">&gt;</span> <span class="m">0</span> <span class="o">&amp;&amp;</span> <span class="n">log</span><span class="o">.</span><span class="n">OpInterval</span> <span class="o">&gt;</span> <span class="p">(</span><span class="n">tm</span><span class="o">.</span><span class="n">NormalInterval</span><span class="o">*</span><span class="m">2.0</span><span class="p">)</span>

		<span class="k">if</span> <span class="n">isThroughputDrop</span> <span class="o">||</span> <span class="n">isIntervalSpike</span> <span class="p">{</span>
			<span class="c">// Line 7: return straggler trigger, abnormal IP [2]</span>
			<span class="k">return</span> <span class="s">"straggler_trigger"</span><span class="p">,</span> <span class="n">ip</span>
		<span class="p">}</span>

		<span class="c">// Line 8: update normal throughput and Coll Op interval [2]</span>
		<span class="c">// Updates the baseline for the next window if no anomalies are found.</span>
		<span class="n">tm</span><span class="o">.</span><span class="n">NormalThroughput</span> <span class="o">=</span> <span class="n">log</span><span class="o">.</span><span class="n">Throughput</span>
		<span class="n">tm</span><span class="o">.</span><span class="n">NormalInterval</span> <span class="o">=</span> <span class="n">log</span><span class="o">.</span><span class="n">OpInterval</span>
	<span class="p">}</span>

	<span class="c">// Return empty strings if no active abnormal trigger is found.</span>
	<span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="s">""</span>
<span class="p">}</span>
</code></pre></div></div> <h4 id="root-cause-analysis">Root cause analysis</h4> <p><img width="613" height="696" alt="image" src="https://github.com/user-attachments/assets/4e2b754f-2de2-479a-83d4-7a3b9e0891a6"/></p> <h5 id="principle">Principle</h5> <table> <thead> <tr> <th>Level</th> <th>Problem</th> <th>Rule</th> </tr> </thead> <tbody> <tr> <td>Chunk-level</td> <td>Failure</td> <td>Each rank should transmit the same amount of data</td> </tr> <tr> <td>Chunk-level</td> <td>Performance</td> <td>Each component should finish within expected execution time</td> </tr> <tr> <td>Flow-level</td> <td>Failure</td> <td>Each flow should complete</td> </tr> <tr> <td>Flow-level</td> <td>Performance</td> <td>Each flow should take similar execution time</td> </tr> <tr> <td>Flow-level</td> <td>Performance</td> <td>Each flow should start and end at similar time</td> </tr> </tbody> </table> <h5 id="local-and-remote-root-cause">local and remote root cause</h5> <table> <thead> <tr> <th>State</th> <th>Condition</th> <th>Local cause</th> <th>Remote cause</th> </tr> </thead> <tbody> <tr> <td>Not started</td> <td>GPUReadyChunks = RDMATransmittedChunks = RDMADoneChunks = 0</td> <td>Uninitialized</td> <td>Blocked</td> </tr> <tr> <td>Not transmitted</td> <td>GPUReadyChunks &gt; RDMATransmittedChunks</td> <td>RDMA Issue</td> <td>Receiver not ready</td> </tr> <tr> <td>Not delivered</td> <td>RDMATransmittedChunks &gt; RDMADoneChunks</td> <td>RDMA Issue</td> <td>Receiver failed</td> </tr> <tr> <td>GPU not ready</td> <td>GPUReadyChunks = RDMATransmittedChunks = RDMADoneChunks &gt; 0</td> <td>GPU issue</td> <td>-</td> </tr> </tbody> </table> <h4 id="deployment">Deployment</h4> <p>Data volume: a training job utilizing 10,000 GPUs generates approximately 3 TB of data per day. This data is retained for one day before being discarded.</p> <p>Debugging toolset <img width="1175" height="419" alt="image" src="https://github.com/user-attachments/assets/b7a230e2-c32b-4c50-be66-1c57877f4560"/></p> <ul> <li>Dump Python stacks of each rank using py-spy to identify the data loader or checkpoint stuck <ul> <li>Stack traces of all relevant Python programs are dumped automatically when Mycroft detects a trigger. Stacks are then grouped by process commands across all GPUs and mapped into a topology grid, where each block represents a GPU rank with its current Python call stack. In this grid, identical call stacks will be marked in the same color. <ul> <li>This is particularly useful as stuck threads typically have different call stacks from the rest, making them stand out on the grid for ease of troubleshooting.</li> </ul> </li> </ul> </li> <li>Dump Python CollOps collected by Flight Recorder to identify the synchronization issues <ul> <li>Stores the traces of the latest N CollOps in a ring buffer.</li> <li>Each CollOp trace includes the CollOp ID, the sizes of input and output tensors, execution state, and communication process group ID.</li> <li>Extract CollOp stacks to analyze the rank synchronization issue by figuring out the device with the last operation on each CUDA stream in a process group, and aggregating all the trace stacks to visualize and identify abnormal devices.</li> </ul> </li> </ul>]]></content><author><name></name></author><category term="paper-review"/><category term="paper-review"/><summary type="html"><![CDATA[Mycroft Tracing Dependencies in Collective Communication Towards Reliable LLM Training]]></summary></entry><entry><title type="html">ByteRobust</title><link href="https://photoszzt.github.io/blog/2025/byteroburst/" rel="alternate" type="text/html" title="ByteRobust"/><published>2025-11-22T23:39:58+00:00</published><updated>2025-11-22T23:39:58+00:00</updated><id>https://photoszzt.github.io/blog/2025/byteroburst</id><content type="html" xml:base="https://photoszzt.github.io/blog/2025/byteroburst/"><![CDATA[<h3 id="robust-llm-training-infrastructure-at-bytedance-link">Robust LLM Training Infrastructure at ByteDance <a href="https://arxiv.org/abs/2509.16293">link</a></h3> <h4 id="control-plane">Control plane</h4> <ol> <li>Robust controller <ol> <li>Robust controller react to events from proactive realtime check: <ol> <li>High confidence event for a specific machine: force drain node, evict the machine, skip stop time diagnostics <ol> <li>GPU unavailability</li> <li>Disk fault</li> </ol> </li> <li>Network issue: tolerate several alerts before evict the machine. Policy: twice within 5 mins empirically <ol> <li>NIC and Network switch flapping can auto recover</li> </ol> </li> <li>If the restarted job fails again after machine eviction, it goes to a stop-time check procedure.</li> </ol> </li> <li>Robust controller analyze the logs <ol> <li>User space error: traceable to specific code modules from logs and exit codes, python exception -&gt; code rollback</li> <li>Training crashes/abnormal metrics, e.g., NaN losses, arise without a clear culprit -&gt; stop time check</li> <li>Performance anomalies: 0 RDMA traffic within 10 mins, low TensorCore utilization -&gt; aggregation analysis</li> </ol> </li> </ol> </li> <li>Runtime analyzer <ol> <li>Aggregation analysis <ol> <li>Parses process trees in each training pod to identify training related processes (torchrun, dataloader, checkpoint process)</li> <li>Stack traces from these identified processes are aggregated into multiple groups via string matching to differentiate abnormal sources <ol> <li>The dominant groups are deemed healthy.</li> <li>Remaining groups are classified as outliers.</li> </ol> </li> <li>Find shared parallel groups for those outliers and isolate the corresponding machines</li> </ol> </li> <li>Fail slow (MFU decline): <ol> <li>Repeat aggregation every 10s, flag the parallel group with the most outliers at each round.</li> <li>Parallel group with the highest cumulative flag count across 5 rounds is marked as the degrader for over-eviction.</li> </ol> </li> </ol> </li> </ol> <h4 id="data-plane-robust-agent-in-each-training-pod">Data plane (Robust agent in each training pod)</h4> <ol> <li>Monitor <ol> <li>System inspection (Proactive realtime check): <ol> <li>Network: NIC down or jitter, packet loss rate, switches down</li> <li>GPU: status of DCGM service, PCIe bandwidth, memory row remapping, and GPU temperature, etc.</li> <li>Host: OS kernel event (Xid in dmesg)</li> </ol> </li> <li>Metics collection <ol> <li>Workload specific training metrics from wandb: loss, gradient norm, MFR, etc <ol> <li>Fatal signal: 5× increase in loss/gradient norms, NaN values</li> </ol> </li> <li>stdout/stderr logs and process exit codes, which serve as hints for diagnostics</li> <li>Events: Significant declines serve as a signal for potential job hangs and MFU declines. <ol> <li>CUDA</li> <li>RDMA: traffic</li> <li>Host</li> <li>Storage</li> </ol> </li> </ol> </li> </ol> </li> <li>Diagnoser <ol> <li>NaN loss diagnosis <ol> <li>Standard GPU and network tests first (EUD and NCCL tests). <ol> <li>Intra machine all-to-all test to verify bandwidth</li> <li>Inter machine all-gather NCCL test to verify connectivity and integrity of data transfer with “neighboring” machines.</li> </ol> </li> <li>Bitwise alignment test <ol> <li>Each machine initiates a reference model whose structure matches that of the target training job (dense models or MoE models).</li> <li>Load predefined weights, employs a specific parallelism configuration (e.g., TP=2, PP=2, DP=2 or EP=2, PP=2, DP=2), and executes one training step on fixed input to ensure reproducibility.</li> <li>The outputs from all machines are collected and analyzed to verify bit-wise accuracy.</li> <li>Machines that yield incorrect results are promptly isolated and removed.</li> <li>If this test does not identify any defective machines, reattempt and rollback are sequentially employed to settle potential transient failures and human errors.</li> </ol> </li> </ol> </li> <li>Job hang and MFU declines</li> </ol> </li> <li>On demand tracer</li> <li>CKPT manager</li> </ol>]]></content><author><name></name></author><category term="paper-review"/><category term="paper-review"/><summary type="html"><![CDATA[Robust LLM Training Infrastructure at ByteDance]]></summary></entry></feed>