Anjiang Wei, Allen Nie, Thiago Teixeira, Rohan Yadav, Wonchan Lee, Ke Wang, Alex Aiken — Stanford, NVIDIA, Google, 2025

LLM Optimizers via Agent-System Interfaces

Scientists need supercomputers but can't write mapper code. This paper uses LLMs + a DSL + structured profiling feedback to automatically optimize parallel program mappings, beating expert-tuned code by up to 1.34x and OpenTuner by 3.8x.

Prerequisites: What an LLM is + Basic idea of parallel computing + What optimization means
10
Chapters
3
Simulations

Chapter 0: The Problem

You're a physicist simulating combustion chemistry. Your simulation runs on a supercomputer with hundreds of GPUs, dozens of CPU cores, and multiple tiers of memory. The physics code is done. It works. But it runs 10x slower than it should because your tasks are landing on the wrong processors and your data is sitting in the wrong memory.

The bottleneck isn't the algorithm. It's the mapper — a piece of system code that decides which task runs on which processor and which data lives in which memory. Getting this right can yield an order-of-magnitude speedup. Getting it wrong means your simulation takes days instead of hours.

The mapper problem is universal. Every task-based parallel programming system — Legion, StarPU, Chapel, Ray, Pathways — needs mappers. The scientific computations that dominate the world's most powerful supercomputers all depend on mapping decisions. And right now, making those decisions takes a systems expert days of manual tuning.

Why is this so hard? Three reasons compound:

The Mapping Decision Space

Each task must be assigned to a processor, each data region to a memory, and each memory layout configured. Click "Randomize" to see how different mappings affect simulated throughput.

Traditional auto-tuning approaches like OpenTuner treat this as a black-box optimization: try a mapping, measure the throughput, try another. But with a search space this large, scalar feedback is almost useless. After 1000 iterations, OpenTuner is still 3.8x slower than the approach in this paper using just 10 iterations.

Why is mapper optimization especially hard to automate with traditional methods like OpenTuner?

Chapter 1: The Key Insight

Here is the central idea: give an LLM a high-level DSL to write mappers in, run the mapper, and feed back structured profiling data — not just a scalar throughput number — so the LLM knows what to fix.

This is the Agent-System Interface (ASI): an abstraction layer between the LLM agent and the parallel runtime system. It has two parts:

Domain-Specific Language (DSL)
Abstracts away hundreds of lines of C++ into a few lines of declarative code. Defines a structured search space the LLM can actually explore. 14x fewer lines of code on average.
AutoGuide
Interprets raw execution output (cryptic error messages, timing data) into actionable feedback: what went wrong, why, and what to try next.
Why this works and black-box doesn't: OpenTuner sees "throughput = 0.3 GFLOPS." The LLM optimizer sees "throughput = 0.3 GFLOPS. Memory layout mismatch on task3: stride does not match expected value. Suggestion: adjust layout constraints or move task3 to a different processor type." One is a number. The other is a diagnosis.

The combination is powerful for a surprising reason. The DSL is a brand-new language with zero examples in any LLM's training data. C++ has millions of examples. Yet LLMs generate correct DSL mappers at an 80% success rate on the first try, versus 0% for C++. The abstraction makes the task tractable even without prior exposure.

The full system runs for just 10 iterations. In 10 minutes per application, it finds mappers that surpass expert-written mappers by up to 1.34x — the same mappers that took human experts days to write.

Generative optimization, not reinforcement learning. Traditional auto-tuners like OpenTuner use RL: try something, get a scalar reward, update a policy. This paper uses generative optimization: an LLM proposes code, gets rich textual feedback, and reasons about what to change. The LLM is the optimizer. The feedback is language, not a number.
What are the two components of the Agent-System Interface?

Chapter 2: What Are Mappers?

Before we can optimize mappers, we need to understand what decisions they encode. A mapper is a piece of code that makes three kinds of assignments for every task and data region in a parallel program:

1. Processor Selection

Each task can run on a GPU, a CPU, or the OpenMP runtime. This isn't a one-size-fits-all decision. Small tasks may prefer CPUs because GPU kernel launch overhead dominates. Tasks with huge memory footprints may need CPUs when GPU memory is insufficient. Compute-dense tasks belong on GPUs. The right choice depends on the task's specific characteristics.

2. Memory Placement

Each data region must live somewhere in the memory hierarchy. The options for GPU-targeted tasks include:

Each choice trades off speed, capacity, and data transfer cost. And this decision is per-task, per-argument — the same data region might need to be in different places for different tasks.

3. Memory Layout

How data is arranged in memory affects cache efficiency dramatically:

4. Index Mapping

When a parallel for-loop launches thousands of tasks over partitioned data, an index mapping function determines which partition runs on which processor. Think of it as a function from a task index (e.g., grid point [i, j]) to a processor index (e.g., GPU 3 on node 2). A bad mapping means data shuffles between GPUs on every iteration. A good mapping keeps communication local.

The search space is exponential. If you have T tasks, each with 3 processor choices, A data arguments each with 3 memory choices, and L layout options — the space is 3T × 3T×A × LT×A×P before even counting index mapping functions. For real applications, random mappers fail 82% of the time with runtime errors from invalid combinations.

This is why expert-written mappers represent the gold standard. A systems researcher who understands both the application and the machine can make informed choices about all these dimensions simultaneously. The question this paper asks: can an LLM match that expertise?

Why do 82% of randomly generated mappers fail with runtime errors?

Chapter 3: The Agent-System Interface

This is the core contribution. The Agent-System Interface (ASI) sits between the LLM and the Legion parallel runtime, making it possible for the LLM to generate and iterate on mapper code.

The DSL: From C++ to Clarity

Here's what a mapper looks like in the original C++ API versus the DSL. The same mapping — running task0 on GPUs, placing ghost regions in ZeroCopy memory, using C-order SOA layout with 64-byte alignment, and distributing tasks cyclically across GPUs:

DSL vs C++ Mapper Code

The DSL expresses the same mapping decisions in a fraction of the code. Click "Toggle" to compare. The highlighted lines in the DSL correspond to 126+ lines of C++ system code.

DSL Statement Types

The DSL has four statement types, each controlling one dimension of the mapping decision:

# 1. Processor selection: which processor type runs this task
Task task0 GPU;

# 2. Memory placement: where this data lives
Region * ghost_region GPU ZCMEM

# 3. Memory layout: how data is arranged
Layout * * * C_order SOA Align==64

# 4. Index mapping: custom distribution function
def cyclic(Task task):
    ip = task.ipoint;
    mgpu = Machine(GPU);
    node_idx = ip[0] % mgpu.size[0];
    gpu_idx = ip[0] % mgpu.size[1];
    return mgpu[node_idx, gpu_idx];

IndexTaskMap task4 cyclic

Each statement is independent. The processor choice for task0 doesn't constrain the memory choice for task1. This separation of concerns is the key design principle: it eliminates the tangled dependencies in C++ mapper code and gives the LLM independent knobs to turn.

The Compiler

Behind the scenes, a compiler translates each DSL statement into the low-level C++ system API calls that the Legion runtime actually executes. The LLM never sees the C++. It writes DSL, the compiler handles the rest.

14x fewer lines of code. Across all benchmarks, the DSL achieves an average 14x reduction in code length compared to equivalent C++ mappers. This isn't just aesthetic — it's the reason LLMs can generate correct mappers at all. Despite the DSL having zero examples in any training corpus, LLMs generate correct DSL at 80% success rate versus 0% for C++.
Why fewer lines matters for LLMs: The semantic gap between "align all data to 64 bytes with Fortran ordering" and Layout * * * Align==64 F_order is tiny. The gap between that same English description and the C++ implementation — which requires a sequence of API calls to enforce alignment and ordering constraints — is enormous. The DSL collapses that gap.
Why do LLMs generate correct DSL code (80% success) despite never seeing DSL examples in training, while failing at C++ mappers (0% success) despite C++ being widely represented?

Chapter 4: The Optimization Loop

The optimization process is iterative: the LLM proposes a mapper, runs it, reads the feedback, and proposes a better one. Here's how it works step by step.

Inputs

The LLM agent receives two things before it starts:

These define the search space. The agent knows what processors exist and what tasks need mapping.

The Loop

1. Generate
LLM writes DSL mapper code, making decisions about processor selection, memory placement, layout, and index mapping for each task.
2. Compile
DSL compiler translates to C++ system code. If the DSL has syntax errors, the compiler reports them immediately.
3. Execute
The application runs with the generated mapper on the actual hardware. Performance is deterministic (all randomness is controlled).
4. AutoGuide
Raw execution output is interpreted into structured feedback: error explanations + performance analysis + modification suggestions.
5. Update
LLM reads the enriched feedback and generates a new mapper. Repeat from step 1.

Modular Code Generation

The agent doesn't generate the entire mapper as one monolithic block. Instead, the task is decomposed into independent segments — one for processor selection, one for memory placement, one for layout, one for index mapping. The agent decides what to generate for each segment separately.

This decomposition works because the DSL enforces it. Since the DSL separates mapping decisions by design, the agent can modify processor selection without touching memory placement. In C++, these decisions are entangled in the same functions, so changing one risks breaking another. The DSL's separation of concerns directly enables modular optimization.

The agent is implemented in the Trace framework, which treats each code segment as an independent optimization variable. This aligns with the DSL's design: each decision dimension is a separate knob the optimizer can turn.

The entire process takes 10 iterations and completes in about 10 minutes per application. For context, expert mapper development takes days.

Why is the mapper generation task decomposed into independent segments rather than generated as one block?

Chapter 5: Structured Feedback

This is where the paper diverges most sharply from traditional auto-tuning. Instead of just feeding back a throughput number, AutoGuide interprets raw execution output into three layers of feedback.

Layer 1: Raw Execution Output

The runtime produces raw output: either an error message (if the mapper crashed) or performance metrics (execution time, throughput). This is what OpenTuner sees. It's almost useless for guiding the next iteration.

Layer 2: Explain

AutoGuide pattern-matches on the raw output to explain what went wrong. Examples:

Raw OutputExplanation
Assertion failed: stride does not match expected valueMemory layout is unexpected — the data arrangement doesn't match what the task expects.
Invalid mapper output from map_task: GPU has no affinityTask was assigned to a GPU that can't access the required data — the memory placement is incompatible with the processor selection.
Execution time: 0.03s(No error to explain — passes through to suggestion layer.)

Layer 3: Suggest

Based on the explanation, AutoGuide suggests specific mapper modifications:

ExplanationSuggestion
Memory layout mismatchAdjust layout constraints or move tasks to different processor types.
GPU has no affinityChange memory placement to FrameBuffer or ZeroCopy for the affected region.
Execution time: 0.03s (but slow)Move more tasks to GPU to reduce execution time.
AutoGuide is not an LLM. It's implemented as keyword matching over raw execution output. Simple pattern matching, no neural network. The domain heuristics that systems researchers carry in their heads — "most tasks run faster on GPUs," "stride mismatches mean layout is wrong" — are encoded as rules. This is crucial: it means the feedback is reliable and deterministic, not hallucinated.

The Ablation Proves It Matters

The authors tested four feedback configurations on three benchmarks:

Each layer of feedback adds value. The progression from raw output to explanation to suggestion mirrors how a human expert debugs: first you see the symptom, then you diagnose the cause, then you know what to change. AutoGuide automates this reasoning chain so the LLM doesn't have to rediscover systems knowledge from scratch each time.
How is AutoGuide implemented, and why is that design choice important?

Chapter 6: Results

The system is evaluated on 9 benchmarks: 3 scientific computing workloads (Circuit, Stencil, PENNANT) and 6 parallel matrix multiplication algorithms (Cannon's, SUMMA, PUMMA, Johnson's, Solomonik's, COSMA). Hardware: 2 Intel 10-core CPUs, 256 GB memory, 4 NVIDIA Tesla P100 GPUs. LLM: GPT-4o.

vs. Expert-Written Mappers

The expert-written mappers are the gold standard — created by domain scientists who spent years mastering both the application and the programming framework. The best mappers found by the LLM optimizer match or surpass every single expert mapper:

Performance vs Expert Mappers (Normalized Throughput)

Throughput normalized to expert-written mappers (1.0 = expert level). Values above 1.0 mean the LLM found a faster mapping. Click benchmarks to highlight.

The largest gain is on Circuit: 1.34x speedup over the expert mapper. The improvement comes from a subtle memory placement decision — the LLM placed two data collections in GPU FrameBuffer instead of ZeroCopy memory. This trades slightly higher inter-GPU communication for much faster memory access, a net win.

vs. OpenTuner (RL-based)

OpenTuner uses reinforcement learning with scalar throughput feedback. The comparison is stark:

3.8x faster with 100x fewer iterations. This is the headline result. The LLM optimizer with structured feedback finds better mappers in 10 iterations than a traditional RL auto-tuner finds in 1000. Rich feedback beats brute-force search by a wide margin.

vs. Random Mappers

Random mappers sample uniformly from the DSL-defined search space. Out of 90 random mappers (10 per benchmark), 74 (82.2%) crash with runtime errors. The ones that survive perform poorly. This confirms that the search space is hostile — most of it is invalid — and any effective method must navigate it intelligently.

What was the LLM optimizer's key advantage over OpenTuner?

Chapter 7: Analysis

What does the LLM actually change when it optimizes a mapper? The case studies reveal specific, interpretable optimization patterns.

Circuit: Memory Placement Insight

The expert mapper placed two data collections in GPU ZeroCopy memory (shared CPU-GPU, no explicit transfer). The LLM discovered that moving them to GPU FrameBuffer (dedicated GPU memory, fastest access) yields 1.34x speedup. Why?

The LLM found this through iteration: it tried FrameBuffer, saw the speedup in the profiling data, and kept it. A human expert chose ZeroCopy as the "safe" default. The LLM wasn't biased by convention.

COSMA: Index Mapping Optimization

The expert mapper for COSMA (a matrix multiplication algorithm) achieved 1.31x less throughput than the LLM's mapper. The difference was in the index mapping function — how matrix subblocks are distributed across GPUs. The LLM discovered a distribution that reduces inter-GPU communication by keeping related subblocks on the same GPU.

Common Patterns the LLM Discovers

The LLM isn't doing anything magical. Every optimization it finds is something a skilled systems engineer could discover. The advantage is speed and systematicness: the LLM tries all the knobs in 10 iterations, while a human might try 3-4 configurations per day and miss the non-obvious combinations.

DSL Code Generation Success

The authors tested LLM code generation across 10 natural-language mapping strategies:

SettingC++ SuccessDSL Success
Single trial0%80%
Iterative refinement (10 tries)0%100%
Zero percent for C++. Even with iterative refinement and compiler feedback, the LLM could not produce a single correct C++ mapper across 10 strategies. The low-level system API is simply too complex. The DSL doesn't just make optimization easier — it makes generation possible at all.
What optimization pattern gave the largest speedup (1.34x) on the Circuit benchmark?

Chapter 8: The DSL Design

The DSL isn't just "simpler C++." It embodies specific design principles that make it suitable as an LLM-agent interface. Understanding these principles is important because they generalize: if you want an LLM to optimize any complex system, the interface design matters as much as the LLM itself.

Principle 1: Declarative, Not Imperative

The DSL says what to achieve, not how to implement it. Task task0 GPU means "run task0 on a GPU." The compiler handles the 50+ lines of C++ needed to actually implement GPU task scheduling. This means the LLM's output is a specification, and correctness is the compiler's responsibility.

Principle 2: Separation of Concerns

Each DSL statement controls one dimension independently. Processor selection doesn't affect memory placement syntax. Layout decisions don't constrain index mapping. This orthogonality means the LLM can reason about each dimension separately and iterate on one without destabilizing others.

Principle 3: Wildcard Matching

The * wildcard applies a decision to all matching entities. Region * ghost_region GPU ZCMEM applies to all tasks using ghost_region. Layout * * * applies to all tasks, all data, all processors. This gives the LLM a coarse-to-fine control strategy: start with broad defaults, then specialize.

Principle 4: Structured Search Space

Each statement has a finite set of valid options. Processor selection: {CPU, GPU, OMP}. Memory type: {FBMEM, ZCMEM, SYSMEM}. Layout order: {C_order, F_order}. Layout type: {SOA, AOS}. The DSL doesn't allow arbitrary code for these decisions — it constrains the LLM to valid choices. Only index mapping functions allow arbitrary arithmetic.

Principle 5: Expressiveness Where It Matters

The DSL is restricted for most decisions (finite option sets) but expressive for index mapping (arbitrary functions). This matches the problem structure: processor and memory choices are categorical decisions, but the best index mapping might require custom arithmetic. The def statement lets the LLM write real computation:

def block_cyclic(Task task):
    ip = task.ipoint;
    mgpu = Machine(GPU);
    # Block-cyclic distribution:
    # group 4 consecutive indices, then cycle
    block = ip[0] / 4;
    gpu = block % mgpu.size[1];
    node = (block / mgpu.size[1]) % mgpu.size[0];
    return mgpu[node, gpu];
The design lesson for AI-system interfaces: Constrain the easy decisions (finite option sets the LLM can't mess up), leave room for creativity on the hard decisions (custom functions where the LLM can express novel strategies), and always separate concerns so changes are modular. This pattern — structured scaffolding with expressive escape hatches — shows up in every successful LLM-for-systems paper.
Why does the DSL use finite option sets for most decisions but allow arbitrary functions for index mapping?

Chapter 9: Connections

This paper sits at the intersection of several active research threads. Each connection reveals a broader pattern about how LLMs can improve systems.

Generative Optimization Family

The core technique — LLM proposes code, gets rich feedback, iterates — appears in several recent systems:

LLMs for Systems

The DSL Pattern

The idea of creating a domain-specific interface for LLM agents appears across domains:

The meta-pattern: When you want an LLM to optimize a complex system, three things matter: (1) a high-level interface that abstracts away implementation details (DSL), (2) rich feedback beyond scalar metrics (AutoGuide), and (3) iterative refinement with the LLM as the optimizer (generative optimization). This three-part recipe appears in every successful LLM-for-systems paper, even when the domain is completely different.
AspectThis Paper
TargetParallel program mappers (Legion)
LLMGPT-4o
InterfaceCustom DSL (14x code reduction)
FeedbackAutoGuide: Explain + Suggest
Iterations10 (vs 1000 for OpenTuner)
Best speedup1.34x over expert mappers
vs RL baseline3.8x faster (at 10 vs 1000 iters)
Code gen successDSL: 80% first try, 100% iterative
Time savingsDays to minutes
What three-part recipe do successful LLM-for-systems papers share?