# XTC, A Research Platform for Optimizing AI Workload Operators

Hugo Pompougnac, Christophe Guillon, Sylvain Noiry, Alban Dutilleul, Guillaume Iooss, Fabrice Rastello

► To cite this version:

Hugo Pompougnac, Christophe Guillon, Sylvain Noiry, Alban Dutilleul, Guillaume Iooss, et al., XTC, A Research Platform for Optimizing AI Workload Operators. CORSE - Compiler Optimization and Run-time Systems. 2025. hal-05460129

HAL Id: hal-05460129

https://inria.hal.science/hal-05460129v1

Submitted on 15 Jan 2026

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

<figure><img src="image_1.png" /></figure>

Distributed under a Creative Commons CC BY 4.0 - Attribution - International License
# XTC, A Research Platform for Optimizing AI Workload Operators

**Hugo Pompougnac**

Univ. Grenoble Alpes, Inria, CNRS,
Grenoble INP, LIG, 38000 Grenoble,
France

hugo.pompougnac@inria.fr

**Christophe Guillon**

Univ. Grenoble Alpes, Inria, CNRS,
Grenoble INP, LIG, 38000 Grenoble,
France

christophe.guillon@inria.fr

**Sylvain Noiry**

Univ. Grenoble Alpes, Inria, CNRS,
Grenoble INP, LIG, 38000 Grenoble,
France

sylvain.noiry@inria.fr

**Alban Dutilleul**

Univ. Grenoble Alpes, Inria, CNRS,
Grenoble INP, LIG, 38000 Grenoble,
France

alban.dutilleul@inria.fr

**Guillaume Iooss**

Univ. Grenoble Alpes, Inria, CNRS,
Grenoble INP, LIG, 38000 Grenoble,
France

guillaume.iooss@inria.fr

**Fabrice Rastello**

Univ. Grenoble Alpes, Inria, CNRS,
Grenoble INP, LIG, 38000 Grenoble,
France

fabrice.rastello@inria.fr

## Abstract

Achieving high efficiency on AI operators demands precise control over computation and data movement. However, existing scheduling languages are locked into specific compiler ecosystems, preventing fair comparison, reuse, and evaluation across frameworks. No unified interface currently couples scheduling specification from code generation and measurement. We introduce XTC, a platform that unifies scheduling and performance evaluation across compilers. With its common API and reproducible measurement framework, XTC enables portable experimentation and accelerates research on optimization strategies.

## 1 Introduction

For performance engineers and researchers, achieving high efficiency on AI workloads operators such as matrix multiplication or convolution is a demanding task. It involves finding a delicate balance between computation and data movement to ensure that hardware units are continuously utilized with minimal stalls and idle time [1].

### 1.1 Automation or manual tuning ?

It is therefore crucial to structure code so that each hardware resource remains continuously engaged in useful computation. Typically, the affine loop nests implementing an operator are transformed through a series of optimizations to enable vectorization, software pipelining, multi-core multithreading and other parallelism-based improvements. In addition to exposing parallelism, transformations must preserve and enhance data locality by carefully orchestrating data transfers through the cache hierarchy [2].

Existing approaches span a continuum between fully automated compiler heuristics and expert-driven manual tuning. Compiler-based optimizations offer higher productivity but often fail to achieve peak efficiency when heuristics break down, for instance during auto-vectorization. Conversely, hand-tuned kernels – written in assembly or with intrinsics

<figure><img src="image_2.png" /><figcaption>**Figure 1.** XTC's high-level components and their interactions. XTC allows to decouple research on scheduling strategies from code generation, validation and measurement.</figcaption></figure>

and delivered as hardware-specific libraries for compute-intensive tasks, see for instance MKL[3] – reach the highest performance but at the cost of portability, maintainability, and development effort. Bridging this gap requires exposing compiler optimization decisions to experts through interfaces that are both controllable and portable.

### 1.2 Scheduling languages

Scheduling languages [4] advance this goal by allowing experts to script optimization transformations. They provide a programmable interface for triggering transformations such
as tiling or fusion, thereby reducing reliance on opaque compiler heuristics. These transformations can be manipulated either directly by an engineer or through autotuning.

Although existing scheduling languages share similar abstractions and primitives, each remains closely tied to a specific compilation ecosystem. For example, TVM's Tensor Expression (TE) scheduling interface [5] and the Halide language [6] are tightly integrated into their respective frameworks. Similarly, MLIR's Transform dialect [7] is an integral component of MLIR [8]. Being part of an intermediate representation, the latter was not designed to be exposed to programmers and is challenging to interact with.

In practice, final performance of generated code depends both on the richness of exposed transformations and on the quality of heuristics embedded in opaque compiler passes. In other words, from a research perspective, it is important to decouple the scheduling components from the code generation ones.

There is currently no unified, user-facing API flexible enough to decouple scheduling specification from code generation. Consequently, adopting one of these languages typically limits access to features available in other software stacks. Even comparing technologies against each other is difficult due to the lack of a shared representation and infrastructure, particularly for obtaining comparable performance metrics.

## 2 Contributions

To address these limitations, we propose XTC – a research and prototyping platform designed to decouple scheduling and code generation, therefore allowing the researcher to experiment at the level of scheduling strategies. As shown in Figure 1, it provides three entry points (in blue) to control operator scheduling while leveraging the code generation infrastructure of mainstream toolchains. XTC then provides a multi-platform measurement framework (in green) exposing a range of software and hardware performance counters.

**Unified API for scheduling.** We introduce a unified API that abstracts core components from multiple scheduling languages, enabling seamless integration of diverse scheduling compilers. On top of this API, we expose a lightweight, higher-level declarative scheduling language to simplify manual experimentation.

**Unified API for measurement.** We distribute XTC with a controlled measurement setup that minimizes variability and exposes detailed hardware performance metrics. This cross-platform harness – to our knowledge, the first to access hardware performance counters on Apple Silicon CPUs in addition to (i) x86 and non-Apple ARM CPUs, and (ii) NVIDIA GPUs – ensures reproducible and quantitative comparisons across compilation pipelines. Using this infrastructure, we compare results against optimized C baselines and peak performance bounds, validate cross-backend consistency by

```c
1 for (int I = 0; I < 256; I++)
2 for (int J = 0; J < 258; J++)
3 for (int K = 0; K < 512; K++)
4 C[I][J] += A[I][K] * B[K][J];
```

<caption>**Figure 2.** A non-optimized matrix multiplication in C.</caption>

replaying identical schedules through multiple code generators, evaluate performance models against actual measurements, and demonstrate end-to-end gains within the Aidge [9] framework on complete neural networks.

**Integration with state-of-the-art backends.** The unified API interfaces with existing scheduling frameworks such as TVM and MLIR, allowing researchers to leverage the rapidly evolving infrastructure of large and possibly complex software ecosystems. The interface also supports user-defined backends, facilitating the evaluation of research prototypes.

**Space exploration.** We provide interfaces for automating design space exploration and evaluation, enabling experts to connect high-level scheduling strategies with custom sampling and predictive models. This interface makes it straightforward to reproduce the search spaces of state-of-the-art autotuners, such as Ansor[10].

By integrating a unified API, interoperable backends, autotuning interfaces, and a cross-platform measurement runtime, XTC enables reproducible performance research and facilitates the comparison of scheduling strategies across hardware and software stacks.

## 3 A unified API for scheduling AI workloads

This section explains how to schedule an operator with XTC. We first show how to attach a schedule to an operator, then present the scheduling primitives exposed by XTC's API.

### 3.1 Schedules and operators

XTC provides a fixed set of common AI operators (matmul, conv2d, relu, padding, transpose) which share hyper rectangular and unordered iteration spaces. They are combined into computation graphs and optimized through classic loop-nest transformations (strip-mining, splitting, reordering, etc.). In this framework, given a loop nest that implements a linear algebra operator, the goal of scheduling is to transform the loops in order to make more effective use of the target resources.

As a running example, consider the matrix multiplication in Figure 2, for which we want to generate an optimized implementation as shown in Figure 3 (in C for the sake of readability). The outer loop *I* stays unchanged (line 1). We split *J*'s iteration space at 256, yielding a main block [0, 256) (lines 2–32) and a short remainder [256, 258) (lines 33–35). This ensures the main block has an extent along *J* that is a
```c
1 for (int I = 0; I < 256; I+=1) {
2     for (int J = 0; J < 256; J+=16) {
3         VLOAD(vectC00, C[I][J]);
4         VLOAD(vectC01, C[I][J+8]);
5         for (int K = 0; K < 512; K+=4) {
6             VBROADCAST(vectA00, A[I, K]);
7             VLOAD(vectB00, B[K, J]);
8             VFMADD(vectC00, vectA00, vectB00);
9             VLOAD(vectB01, B[K, J+8]);
10            VFMADD(vectC01, vectA00, vectB01);
11         }
12         VBROADCAST(vectA01, A[I, K+1]);
13         VLOAD(vectB00, B[K+1, J]);
14         VFMADD(vectC00, vectA01, vectB00);
15         VLOAD(vectB01, B[K+1, J+8]);
16         VFMADD(vectC01, vectA01, vectB01);
17         VBROADCAST(vectA02, A[I, K+2]);
18         VLOAD(vectB00, B[K+2, J]);
19         VFMADD(vectC00, vectA02, vectB00);
20         VLOAD(vectB01, B[K+2, J+8]);
21         VFMADD(vectC01, vectA02, vectB01);
22         VBROADCAST(vectA03, A[I, K+3]);
23         VLOAD(vectB00, B[K+3, J]);
24         VFMADD(vectC00, vectA03, vectB00);
25         VLOAD(vectB01, B[K+3, J+8]);
26         VFMADD(vectC01, vectA03, vectB01);
27     }
28 };
29
30 VSTORE(C[I][J], vectC00);
31 VSTORE(C[I][J+8], vectC01);
32 }
33 for (int J = 256; J < 258; J++)
34     for (int K = 0; K < 512; K++)
35     C[I][J] += A[I][K] * B[K][J];
```

<caption>**Figure 3.** The target implementation of the matrix multiplication in Figure 2. CPU-dependent instructions (i.e. intrinsics) are hidden behind C macros.</caption>

multiple of 16, enabling vector-friendly register tiles (line 2). With 8-wide SIMD, a 16-element tile emits two full vectors per inner step. Along the reduction dimension *K*, we use a register-tile size of 4 (line 5). We then permute loops so that *K* precedes *J*, fully unroll *K*, and vectorize *J* to execute the inner kernel with SIMD instructions.

Hand-writing such C implementation – even with pre-processor helpers – is error-prone and brittle, and profiling variants (e.g., without unrolling along *K*, with tiling along *I*, and so on) requires revisiting non-trivial code.

In contrast, Figure 4 expresses the same transformation sequence with the XTC Python API. The operator (mm, for matrix multiplication) is defined in lines 4–8. Lines 25–28 trigger compilation and execution. Lines 13–23 specify the loop transformations; parameters (tile sizes, loop reordering, vectorization, etc.) are exposed as Python knobs. We also introduce a root label to disambiguate branches created by explicit splits (e.g. 'J[0]', 'J[1]'). Before any split, the root is the operator id (mm0 here).

This representation of the scheduling state relies on a single Python/C++ object (the variable *sch*, defined on line 11), inspired by TVM schedules.

By comparison, the MLIR Transform dialect operates at a lower level. The state is distributed across SSA values that are

```python
import xtc graphs.xtc op as 0
from xtc backends.mlir import Backend

I, J, K, dtype = 256, 258, 512, "float32"
a = 0. tensor((I, K), dtype, name="A")
b = 0. tensor((K, J), dtype, name="B")
with O graph(name="mm graph") as gb:
    0.mm(a, b, name="mm0")
impl = Backend(gb graph)
sch = impl.get_scheduler()

sch dims = ['I', 'J', 'K']
sch.split(root="mm0", dim="J",
    segments=("J[0":"0", "J[1":"256】))
sch.strip_mine(root="J[0]", dim="K", tiles={"K1": 4})
sch.strip_mine(root="J[0]", dim="J", tiles={"J1": 16})
sch.unroll(root="J[0]", unrolls={"J1": 16, "K1": 4})
sch vectorize(root="J[0]", axes=["J1"])
sch interchange(root="mm0",
    permutation=["I","J[0]","J[1]"])
sch interchange(root="J[0]",
    permutation=["K","K1","J1"])
sch interchange(root="J[1]", permutation=["K"])

comp = impl.get_compile()
module = comp.compile(sched)
executor = module.getpar 执行器
res = executor.execute()
```

<caption>**Figure 4.** The scheduling of a matrix multiplication using the XTC API (MLIR backend).</caption>

created, consumed, and sometimes invalidated throughout the transformation sequence; see Figure 5. These values may denote the operator, the loops materialized along the way, or the enclosing function. Such IR-centric interface also surfaces IR constraints (SSA discipline, types) and adds boilerplate. By comparison, XTC’s API makes the schedules easier to express and reason about. In our framework, Transform scripts are generated when targeting MLIR rather than written by hand.

## 3.2 Scheduling primitives

As illustrated in Table 1, the API of XTC exposes ten scheduling primitives. This scheduling interface is an extensible proposal, designed to adapt to new needs. In particular, achieving high performance on GPUs will require new primitives that are not yet implemented (although XTC already supports profiling on GPUs).

Each primitive corresponds to a well-known transformation on loop nests or memory layouts, enabling the systematic construction of high-performance kernels. We describe them below, highlighting their semantics and their correspondence to the backends primitives we leverage.

**Strip mine.** Strip-mining partitions the iteration domain of an affine loop into regular blocks of fixed size. Consider a loop with induction variable *i* iterating from a lower bound *lb* to an upper bound *ub* with step *n*. Strip-mining with a factor *s*, replaces this loop with two nested loops: the outer loop, with induction variable *i*<sub>0</sub>, iterates from *lb* to *ub* in steps of *n* × *s*; the inner loop, with induction variable *i*<sub>1</sub>,
```python
1 func.func @myfun(
2   %A: memref<256x512xf32> {llvm.noalias},
3   %B: memref<512x258xf32> {llvm.noalias},
4   %C: memref<256x259xf32> {llvm.noalias}
5 ){
6   linalg.matmul {mymm}
7   ins(%A, %B: memref<256x512xf32>, memref<512x258xf32>)
8   outs(%C: memref<256x258xf32>)
9   return
10 }
11
12 tr.named_sequence @_tr_main(%arg0: !tr.op {tr.readonly}) {
13   %mm = tr.structured.match attributes {mymm} in %arg0: (!tr.op) -> !tr.op
14   %t_mm, %loop = tr.structured tile_using_for %fst sizes [1,0,0]
15   %fst, %snd = tr.structured.split %t_mm after 256 {dimension = 1 : i64} : !tr.op
16   %t_mm0, %loop1, %loop2 = tr.structured tile_using_for %first sizes [0,16,4]
17   : (!tr.op) -> (!tr.op, !tr.op, !tr.op)
18   %t_mm4, %loop5 = tr.structured tile_using_for %t_mm0 sizes [0,0,1]
19   : (!tr.op) -> (!tr.op, !tr.op)
20   %t_mm6, %loop7, %loop8 = tr.structured tile_using_for %snd sizes [0,1,1] : (!tr.op) -> (!tr.op, !tr.op, !tr.op)
21   tr.structuredvectorize %t_mm4 : !tr.op
22   %f = tr.get幻parent_op %loop {isolated_fromAbove} : (!tr.op) -> !tr.op
23   tr.apply_patterns to %f {
24     tr.apply_patterns. vector.reduction_to_contract
25     tr. apply_patterns. vector. transfer_permutation_patterns
26   } : !tr.op
27   tr. apply_patterns to %f {
28     tr. apply_patterns. vector. lower_outerproduct
29     tr. apply_patterns. vector. lower_contraction
30   } : !tr.op
31   tr. loop. unroll %loop2 {factor = 4 : i64} : !tr.op
32   tr. yield
33 }
```

<table><caption>**Figure 5.** An example of an MLIR Transform dialect script applied to a matrix multiplication equivalent to the one in Figure 2. The transformation state is carried by MLIR variables. In blue, the evolving state of the linalg.matmul operator being optimized. In green, the evolving state of the low-level loops (scf.for) materialized during the tiling. In red, the evolving state of the function containing the operator (func.func @myfun).</caption><thead><tr><th>XTC primitive</th><th>TVM/TE counterpart</th><th>MLIR transform dialect counterpart</th></tr></thead><tbody><tr><td>Strip mine</td><td>split</td><td>tile_using_for (1D)</td></tr><tr><td>Interchang</td><td>reorder</td><td>Implicitly carried by the dataflow of the script</td></tr><tr><td>Unroll</td><td>unroll</td><td>loop.unroll</td></tr><tr><td>Vectorize</td><td>vectorize</td><td>vectorize + apply_patterns</td></tr><tr><td>Parallelize</td><td>fuse + parallel</td><td>tile_using_forall (1D)</td></tr><tr><td>Split</td><td>loop_partition</td><td>split_handle + split</td></tr><tr><td>Pack</td><td>cache_read + compute_at</td><td>pack</td></tr><tr><td>Bufferize</td><td>cache_write + compute_at</td><td>pack</td></tr><tr><td>Fuse</td><td>compute_at</td><td>fuse_into_containing_op</td></tr></tbody></table>

<caption>**Table 1.** The scheduling primitives exposed in XTC and their counterparts in TVM/TE and the MLIR Transform dialect. The implementations colored in gray are still experimental.</caption>

iterates from 0 to (s - 1) with step n. The original induction variable is reconstructed as i = i0 + i1. If (ub - lb) is not an exact multiple of (n × s), the last block may contain fewer iterations. Note that tiling – which improves locality and reuse, boosting arithmetic intensity – is a generalization of strip-mining: it performs multi-dimensional strip-mining and groups the resulting inner loops in an arbitrary order. dimension).

**Interchange.** Loop interchange reorders the loops within a nest while respecting data-dependence constraints and preserving the association of each loop with its root. It then

changes the iteration order, potentially improving data locality or enabling vectorization.

**Split.** Splitting divides a loop’s iteration space into multiple contiguous regions defined by explicit split points. For a loop iterating over `[lb, ub)` with step n, and a set of split points `s1, s2, ..., sk` satisfying `lb < s1 < ... < sk < ub`, the transformation produces `k + 1` loops, each iterating over one segment: `[lb, s1]`, `[s1, s2]`, ..., `[sk, ub]`. Each loop executes the same body over its restricted range. Unlike strip-mining, splitting does not introduce additional nesting. It is particularly useful to isolate specific regions of the iteration space
in order to apply specific transformations to them – for example, to vectorize sections whose size is a multiple of the SIMD width.

**Unroll.** Loop unrolling expands a loop by replicating its body as many times as there are iterations, handling the induction variable explicitly within the inlined code. Unrolling exposes instruction-level parallelism and reduces loop-control overhead.

**Vectorize.** Vectorization maps the computation of a loop or a loop nest onto the SIMD resources of the target hardware (and implicitly unrolls the concerned dimensions). The loops must first be tiled so that each iteration block matches the SIMD register width. When supported, this primitive also triggers FMA formation.

**Parallelize.** Parallelization distributes loop iterations across multiple threads or cores. The parallelized loop must correspond to an inherently parallel dimension and typically appear among the outermost loops. An MLIR compiler such as `mlir-opt` targets OpenMP directives, whereas TVM uses pthreads by default.

**Pack/Bufferize.** *Packing* is applied at a certain loop level, on one of the input tensors. It allocates a new local memory and copies the elements that will be used by the operator below the loop, in the order of their access. This transformation is a trade-off between improving the spatial locality and the effectiveness of the hardware prefetcher, and the time and memory spent performing this reordered copy. We allow the padding of the new local memory, in order to reduce the conflict misses. *Bufferization* can be seen as a symmetry of packing, with the same trade-off. A new local buffer is created at the specified loop level, that will be used to store the newly computed data of the output tensor. Once the computation of the loop is done, the data from the local buffer is copied to the output tensor, while modifying its ordering to fit the original layout.

**Fuse.** *Fusion of producer* rematerializes the computation of the producer of the input buffer. *Fusion of consumer* on the other side brings computation of the consumer operation.

**Implementation limitations.** TVM and MLIR both distinguish between the level of abstract tensors (TE in TVM, tensor dialect in MLIR) and that of access to persistent memory (TIR in TVM, `memref` dialect in MLIR). In practical terms, pack and `fuse_into_containing_op` are not provided at the `memref` level but rather at the tensor level. Similarly, `loop_partition` is not provided at the TE level but at the TIR level. In XTC, support for TIR on the TVM side, and for `lincalg` on tensors on the MLIR side, is still experimental and is not exposed at the moment.

<figure><img src="image_3.png" /><figcaption>**Figure 6.** XTC's backend structure. Green boxes represent classes applying transformations, blue boxes are the input or produced objects.</figcaption></figure>

# 4 Infrastructure

This section presents the components involved in compiling an operation graph together with its schedule, the components responsible for evaluating the generated code, and the interactions between them.

## 4.1 XTC backends

Our interface currently supports two scheduling pipelines:

* TVM schedules on Tensor Expressions (TE), where tensors are manipulated directly (rather than through TVM-IR, which materializes control flow);

* the MLIR Transform dialect applied to operations on `MemRef` values (i.e. explicit persistent memory).

We refer to these different pipelines as *backends*. A backend serves as an abstraction layer for any compilation framework capable of processing a graph of operations and its associated schedule, each expressed in the framework's native representation. A backend applies the schedule and generates an executable for performance evaluation.

In practical terms, both MLIR- and TVM-based pipelines ultimately generate LLVM IR, which is eventually processed using the LLVM toolchain (opt, llc). They can alternatively produce C source code for export. However, XTC, while emitting optimized C code, does not enforce vectorization and alias information. This aspect should be improved as it is of interest to provide efficient C code generation for a number of tools (intrumentation, analysis, debugging).

From a software architecture perspective, when a Graph is created, its associated Scheduler records the scheduling API calls and builds an internal representation of the schedule. It then applies this schedule and compiles the resulting program into an executable Module.

Integrating a new backend into XTC requires providing a class that implements the Scheduler interface and producing an executable that conforms to XTC's ABI: a function
<figure><img src="image_4.png" /><figcaption>**Figure 7.** Implementation of the existing TVM and MLIR backends in XTC. The custom MLIR target shows how an accelerator with an offloading runtime could be added. Blue boxes are the input/produced objects. The TVM boxes are in colored in purple, while the MLIR ones are in cyan.</figcaption></figure>

named after the graph and taking as parameters the graph’s inputs and outputs, each passed as a contiguous raw pointer.

Figure 6 illustrates the general structure of a backend. Each component is detailed below.

**Schedule.** The unified scheduling API is implemented by subclassing the abstract Scheduler class. Each subclass generates backend-specific scheduling instructions. For example, calling unroll emits a loop. unroll Transform operation in the MLIR backend. The resulting instructions form a Schedule object, which is later consumed by the compiler to transform the graph.

**Compiler.** The Compiler takes as input a Graph and its associated Schedule. It lowers the graph into the backend’s intermediate representation and applies the schedule. A target-specific lowering pipeline then generates the final artifact: an executable, a shared library, or C source code.

**Module.** A Module encapsulates the code produced by the compiler and exposes the necessary runtime facilities. On the host machine, it loads the generated shared library and manages argument passing and result retrieval. XTC also supports alternative runtimes, such as accelerator offloading.

## 4.2 Validation and measurement harness

Each Module provides an Executor and an Evaluator. The Executor validates that the optimized operator produces results consistent with the reference implementation. The Evaluator generates input tensors, executes the compiled code, and collects performance metrics. Profiling may rely on the system’s monotonic clock or on hardware counters.

**Accessing CPU hardware counters.** The evaluation harness is designed as a portable platform for measuring hardware performance counters. This abstraction permits users

to easily focus on analyzing execution characteristics to drive the optimization process.

To initiate measurement, we use human-readable event names to open the corresponding hardware events. On all supported platforms, the implementation offers comparable measurement capabilities:

* On GNU/Linux, event-to-code translation relies on the libpfm4 library, with counter access managed through the Perf interface.

* On macOS, we leverage Apple’s undocumented KPerf system interface and the KPep database for translation.

Upon completion of the target execution, the harness halts the counters counting and retrieves the final event counts.

**Profiling GPU code.** Performance measurement on GPU relies both on profiling the host and the accelerator. XTC currently only supports NVIDIA GPUs accelerators for the MLIR backend. The experimental code generation for GPU leverages the dialects and associated runtime already existing in upstream MLIR. Thus, it reuses most of our infrastructure built for the MLIR backend. The resulting code has a host entry point and handles offloading of compute kernels. The XTC’s host runtime has been augmented with hooks that interact with additional performance measurement libraries. From the user point of view, there is a single interface for performance counters, with GPU-related ones being prefixed to discriminate them from the host ones. The current implementation supports modern Nvidia GPUs having a compute capability higher or equal to 7.5, and it relies on the Nvidia CUpti library.

## 5 XTC for research

In this section, we show how XTC can be used as a research platform. Studying the behavior of a given operator or graph on a given hardware target, as we do in § 3 and § 6, is the
<figure><img src="image_5.png" /><figcaption>**Figure 8.** The reimplementation of the example in Figure 4 using our declarative, high-level language.</figcaption></figure>

most straightforward use case. Beyond that, we describe two other possible projects: the implementation of a high-level declarative scheduling language, and the implementation of exploration strategies for autotuning.

## 5.1 Implementing a new scheduling language

The XTC API can serve as a foundation for defining higher-level scheduling languages. Indeed, current scheduling languages remain imperative and sequential: users must specify transformations step-by-step, dealing more with compiler internals rather than with the operator's logic or hardware behavior. The search space becomes the sequences of transformations rather than the mapping of computations onto hardware resources, complicating the exploration strategies of both experts and autotuning algorithms.

Figure 8 illustrates the declarative language we defined on top of the XTC API. It is basically a reimplementation of the schedule presented in Figure 4. In this framework, instead of describing a sequence of transformations, one directly specifies the structure of the target loop nest. The low-level transformations required to obtain this loop nest are then inferred automatically.

The language itself is implemented as a dictionary; in this way, it can be expressed in Python, but also in MLIR, as a dictionary of attributes. The keys of the dictionary are used to declare loops. Given a dimension *D* and an integer *N*, the declaration *D#N* designates a tile of size *N* along *D* (lines 6 and 7). Given a dimension *D* and two integers *A* and *B*, the declaration *D[A : B]* designates the region of a split of size (*A* − *B*) along *D* (lines 4 and 9). A split declaration carries an inner schedule that describes the scheduling of the operator below this new root (lines 4–8 and 9–11), which does not need to be explicitly named. A bare dimension (lines 3, 5, 10) designates the outermost loop along a dimension, if the latter is not a split. The order of declarations in the dictionary determines the order of loops in the target code. Finally, unrolling, vectorization, or parallelization appear as annotations on these declarations. In practice, they can be represented as values of the dictionary (lines 6 and 7).

This higher-level form of notation allows the programmer to think in terms of *target code* rather than transformations, and thus to reason more naturally about the mapping between the code and the machine's hardware resources (the innermost tile being mapped on vector registers, etc.). This approach notably removes the need to explicitly maintain the temporary state of the transformations (and their association with a root), whether that state is distributed across multiple variables – as in the Transform dialect – or carried by a Python/C++ object, as in our scheduling API or in TVM.

## 5.2 Defining scheduling strategies

While providing different levels of abstraction for scheduling particular kernels, there is a need for constructing general scheduling strategies which apply to any operation and can be used to build features, performance models and design space exploration.

To this end, XTC provides a base *Strategy* interface which allows to define a design space based on some scheduling template. The design space can be for instance a sparse multi-dimensional grid from which one must be able to sample, or get default values inferred from builtin heuristics possibly dependent on a given optimization level. The implementation relies on the scheduling primitives shown in § 3 to generate a schedule given a sample.

The provided method `sample(num:int) -> list[Sample]` can be used for sampling and prediction. When a candidate is chosen, for instance by some externally defined acquisition function, code generation is done through the method `generate(sch:Scheduler, sample:Sample)` which sets the scheduler in the desired state. The method `default_schedule opt_level:int)` returns a heuristically determined default given the target properties.

For instance, inspired by the sketches defined by An-sor [10], this allows to add a flexible and customizable strategy *StrategyPRT* which represent tilings, packing, write caches and fusion. The strategy is defined as a list of tokens given as a string which represents an outer-inner view of the scheduled operation. Each token express some free choices of tiles over multiple dimensions, buffers or fused computations.

In this terminology, the available tokens are, given *Pvars*<sub>1..p</sub> the list of parallel dimensions and *Rvars*<sub>1..r</sub> the list of reduction dimensions:

* T: tiling all dimensions;

* P: tiling all *Pvars*<sub>1..p</sub>;

* R: tiling all *Rvars*<sub>1..r</sub>;

* U: tiling all dimensions with free order;

* O: tiling with order *Pvars*<sub>1</sub>, *Rvars*<sub>1</sub>, *Pvars*<sub>2..p</sub>;

* W: create optionally a write buffer for the output;

* B: create optionally packed buffers for inputs;

* F: optionally fuse some consumers.
<figure><img src="image_6.png" /><figcaption>**Figure 9.** Example of samples evaluation for the PPWRPRP strategy.</figcaption></figure>

One can then instantiate `StrategyPRT("PPWRPRP")` to represent a design space equivalent to the Ansor sketches for CPU.

Figure 9 illustrates how to generate samples from this strategy when scheduling a matrix multiplication and use a simple random search for evaluating the performance of the graph. It also shows the equivalent scheduling primitive sequence for an example sample.

# 6 Evaluation

We present in this section several use cases of XTC as a research platform. We demonstrate: (i) its performance relative to hand-written C code, (ii) its ability to leverage multiple compilation technologies, (iii) its use for evaluating performance models, and (iv) its integration within an existing machine learning framework.

## 6.1 Evaluating XTC against optimized C code

We first compare the performance of scheduling an operator with XTC against a hand-written, parameterized C implementation of matrix multiplication using the Goto strategy [2]. This strategy tiles the operands across cache levels, based on a fixed, vectorized inner register. We use a register tile of size 4 × 32, while leaving the outer tile sizes free under divisibility constraints. Both operands are 1024 × 1024 matrices, yielding a search space of 594 schedule instances.

<figure><img src="image_7.png" /><figcaption>**Figure 10.** Correlation between XTC with TVM backend and hand optimized C version of the GOTO strategy on a [1024, 1024] × [1024, 1024] matrix multiplication.</figcaption></figure>

<figure><img src="image_8.png" /><figcaption>**Figure 11.** Correlation for two backends on a [512, 128] × [128, 1024] matrix multiplication with TU strategy and vector constraint</figcaption></figure>

We then compare the performance of this C implementation with the corresponding XTC implementation compiled through the TVM backend.

As shown in Figure 10, performance is comparable to hand-written C code with vector intrinsics. This is not a new result, though, from a research perspective, it confirms that, at least at the operator level, using XTC for defining scheduling strategies is a competitive alternative to hand written C code. In this particular case, writting and debugging the C code template for this single strategy took days.

## 6.2 Evaluating integration of backends into XTC

This section discusses how the current integrations of distinct backends into XTC compare against each other. The objective here is not to benchmark XTC against other frameworks, but to exhibit differences between compiler technologies that XTC leverages on.

We consider a single operation and use the previously described `StrategyPRT` to generate 100 random schedules. The strategy is constrained so that the inner tile is always vectorizable. Each schedule is compiled using both the TVM and MLIR backends, and we measure the resulting execution times to evaluate their correlation.
<figure><img src="image_9.png" /><figcaption>**Figure 12.** Correlation for two backends on a [112, 112, 16] × [7, 7, 3] step 2 conv2d with PPRPRP strategy and vector constraint</figcaption></figure>

As shown in Figure 11, when scheduling a matrix multiplication with the TU strategy, we get highly correlated execution times over the explored space. The few cases where relative performance significantly differs are due to varying unrolling in the backends.

Conversely, we conducted an experiment consisting of scheduling a convolution using the PPRPRP strategy. The result is shown in Figure 12. In this case, the MLIR backend lags far behind the TVM backend across all samples. This is due to limitations in the vectorization pass of mlir-opt, which appears to be disabled when access functions are non-trivial. We were therefore able to identify this issue (and apply a pre-pass to simplify the access functions). Of course, other MLIR-based compilers (such as IREE [11] or xDSL[12]) might behave differently, but this is a typical use case of XTC where one may seek to identify the limitations of a backend, and optionally improve it.

## 6.3 Evaluating a cache model with XTC

This section demonstrates how XTC can be used to evaluate performance models through its search strategies and integrated hardware counter instrumentation.

Our case study is a fully associative cache model inspired by the one IOOPT[13] leverages as its cost function. Using XTC, we assess the model's ability to predict L1 cache misses for different operator implementations on an Apple M4 Max. Figure 13 and Table 2 show the correlation between predicted and measured L1 cache misses on a range of schedule instances for a matrix multiplication.

The model exhibits moderate correlation with measured data, capturing high-miss schedules more accurately than efficient ones. However, its overall optimistic prediction struggles to classify across efficient schedules and to model finer-grained hardware effects.

<table><caption>**Table 2.** Correlation statistics for the cache model</caption><thead><tr><td>Correlation Type</td><td>Statistic (r or ρ)</td></tr></thead><tbody><tr><td>Pearson (r)</td><td>0.534</td></tr><tr><td>Spearman (ρ)</td><td>0.492</td></tr></tbody></table>

<figure><img src="image_10.png" /><figcaption>**Figure 13.** Correlation between L1 cache misses predicted by a full-associative cache model and the corresponding hardware counter on [1024, 1024] × [1024, 1024] matrix multiplication samples generated by XTC with the Goto [2] strategy running on the Apple M4 Max.</figcaption></figure>

## 6.4 Evaluating XTC within a full graph export

Finally, we demonstrate how XTC can be integrated within a complete inference pipeline. As a proof of concept, we connected XTC to the Aidge [9] framework, enabling mixed generation of C++ templates and compiled neural network subgraphs. In this setup, XTC compiles selected subgraphs – identified as candidates for optimization – while the remaining parts are generated through Aidge's standard C++ flow. Thanks to this approach, one can benefit from a compiler backend without requiring the full support for all ML operations. In this particular experiment, we generated inference code for common convolutional networks (Squeezenet [14], Resnet18 [15], and Resnet50) using XTC with the TVM backend to compile subgraphs with pad, convolution and dense operators. We show results for two different CPU architectures, an Intel Core-i5-1135G7 (4.1GHz) and an ARM RPI4 Cortex-A72 (1.5GHz) for single core inference.

In Figure 14, we see that using XTC for mapping subgraphs achieve between ×15 and ×30 speedup on the Intel machine (resp. between ×2 and ×4 speedup on the ARM machine).

Obviously, there is a large potential for improvements achievable with optimized libraries or ML graph compiler compared to C++ code. With the XTC setup, one can experiment optimization of operations in context and extract subgraphs for fusion strategies.

# 7 Related works

This section does not attempt to provide an exhaustive survey of autotuning strategies based on scheduling languages; such coverage is available in [4]. Instead, we focus on the design rationale behind XTC and its positioning relative to existing mainstream infrastructures.
<figure><img src="image_11.png" /><figcaption>**Figure 14.** Speedup of partial compilation with XTC against generic C++ export (cpp), in emit C mode (xtc-c) and fully compiled mode (xtc)</figcaption></figure>

A family of scheduling frameworks exposes purely func-
tional primitives. Lift [16] and Elevate [17] rely on rewrite
rules and strategy combiners; MDH [18] uses algebraic
homomorphisms to express high-level decompositions. XTC
intentionally operates at a lower level. Its primitives denote
concrete loop and memory transformations that correspond
to those exposed by TVM.

TVM [5] builds on the distinction between algorithm spec-
ification and scheduling strategy introduced by Halide [6],
and generalizes this approach across a wide range of hard-
ware targets. It is a tensor compiler renowned for producing
high-quality code for many kinds of tensor computations. In
addition, TVM clearly separates graph-level and operator-
level optimizations, enabling end-to-end model execution
while supporting fine-grained operator tuning.

Implementing optimization schemes not natively supported
by TVM's Tensor Expression (TE) language – such as GotoBLAS-
style [2] packing or split transformations [19] – is non-trivial.
TVM's scheduling language enables experts to define high-
level search spaces via templates, specifying tiling depths, tile
sizes, and other structural parameters. AutoTVM [20] then
orchestrates sampling, lowering, execution, and modeling to
identify performant configurations within a combinatorial

search space. A key limitation of AutoTVM is its reliance on
dense hyper-rectangular search spaces, which necessitates
*ad-hoc* encodings of sparse constraints (e.g. divisibility). De-
spite this, TVM remains a preferred platform for research
due to its code quality and accessible development model.

AutoTVM's requirement for per-operator templates scales
poorly to workloads comprising hundreds of operator vari-
ants. This limitation led to the development of Ansor [10],
which shifts the template definition to the target architec-
ture rather than the operator-architecture pair. While this
improves scalability, it couples search strategies tightly to the
search space through hard-coded hyperparameters, making
the infrastructure less extensible. Ansor's emphasis on gen-
erality also encourages the use of scheduling languages that
mirror underlying loop transformations. This design choice
eliminates the need to translate abstract search points into
concrete transformation plans, enhancing extensibility and
portability. In theory, adding a new transformation directly
extends the search space. This approach also facilitates for-
mal reasoning, as proofs can be constructed at the granular-
ity of individual transformations. Consequently, frameworks
such as Exo [21, 22], OptiTrust [23], and Polymorphous [24]
adopt this one-to-one mapping between scheduling language
constructs and transformation operations. However, this de-
sign introduces control into the search space, thereby compli-
cating optimization. Moreover, the abstraction is not directly
aligned with the runtime behavior, making metric derivation
and interpretation more opaque. This hinders the expression
of expert knowledge – such as inter-operator transfer tuning
– that could otherwise help structure or prune the search
space for faster convergence.

XTC adopts the philosophy proposed by Mary Hall et al. in [4], which calls, among other things, for an effort to (i) unify scheduling languages, and (ii) raise the level of abstraction of scheduling languages. The scheduling API we propose aims to address these two challenges by providing unified, general scheduling primitives to serve as the bedrock for higher-level representations (e.g. describing the loop structure of the target code), and for search strategies that are more easily adaptable to backend-specific constraints.

8 Conclusion

We introduced XTC, a research platform for experiment-
ing with scheduling and performance optimization across
compiler frameworks. By decoupling scheduling from code
generation, XTC enables fair comparison, reproducible mea-
surement, and rapid prototyping of optimization strategies.
Our results show that XTC matches hand-tuned performance,
reveals backend limitations, and allows performance mod-
els evaluation. Beyond its immediate utility, XTC promotes
a modular vision of compiler research where scheduling
abstractions, autotuners, and evaluation tools interoperate
seamlessly to accelerate innovation.
## References

[1] A. Yasin, "A top-down method for performance analysis and counters architecture," in *2014 IEEE International Symposium on Performance Analysis of Systems and Software (ISPASS)*, 2014, pp. 35-44.

[2] K. Goto and R. A. v. d. Geijn, "Anatomy of high-performance matrix multiplication," *ACM Trans. Math. Softw.*, vol. 34, no. 3, May 2008. [Online]. Available: https://doi.org/10.1145/1356052.1356053

[3] Intel Corporation, *Intel® oneAPI Math Kernel Library for Linux*\* Developer Guide (2022.2), Intel Corporation, 2022, developer Guide, version 2022.2. [Online]. Available: https://cdrdv2-public.intel.com/742092/onemkl-developer-guide-2022-2-linux.pdf

[4] M. Hall, C. E. Oancea, A. Elster, A. Rasch, S. Joshi, A. M. Tavakkoli, and R. Schulze, "Scheduling language chronology: Past, present, and future," *ACM Trans. Archit. Code Optim.*, Jun. 2025, just Accepted. [Online]. Available: https://doi.org/10.1145/3743135

[5] T. Chen, T. Moreau, Z. Jiang, L. Zheng, E. Yan, M. Cowan, H. Shen, L. Wang, Y. Hu, L. Ceze, C. Guestrin, and A. Krishnamurthy, "Tvm: an automated end-to-end optimizing compiler for deep learning," in *Proceedings of the 13th USENIX Conference on Operating Systems Design and Implementation*, ser. OSDI'18. USA: USENIX Association, 2018, p. 579-594.

[6] J. Ragan-Kelley, C. Barnes, A. Adams, S. Paris, F. Durand, and S. Amarasinghe, "Halide: a language and compiler for optimizing parallelism, locality, and recomputation in image processing pipelines," *SIGPLAN Not.*, vol. 48, no. 6, p. 519-530, Jun. 2013. [Online]. Available: https://doi.org/10.1145/2499370.2462176

[7] M. P. Lücke, O. Zinenko, W. S. Moses, M. Steuwer, and A. Cohen, "The mlir transform dialect: Your compiler is more powerful than you think," in *Proceedings of the 23rd ACM/IEEE International Symposium on Code Generation and Optimization*, ser. CGO '25. New York, NY, USA: Association for Computing Machinery, 2025, p. 241-254. [Online]. Available: https://doi.org/10.1145/3696443.3708922

[8] C. Lattner, J. A. Pienaar, M. Amini, U. Bondhugula, R. Riddle, A. Cohen, T. Shpeisman, A. Davis, N. Vasilache, and O. Zinenko, "MLIR: A compiler infrastructure for the end of moore's law," *CoRR*, vol. abs/2002.11054, 2020. [Online]. Available: https://arxiv.org/abs/2002.11054

[9] "Aidge", 2025. [Online]. Available: https://eclipse.dev/aidge/index.html

[10] L. Zheng, C. Jia, M. Sun, Z. Wu, C. H. Yu, A. Haj-Ali, Y. Wang, J. Yang, D. Zhuo, K. Sen, J. E. Gonzalez, and I. Stoica, "Ansor: generating high-performance tensor programs for deep learning," in *Proceedings of the 14th USENIX Conference on Operating Systems Design and Implementation*, ser. OSDI'20. USA: USENIX Association, 2020.

[11] H.-I. C. Liu, M. Brehler, M. Ravishankar, N. Vasilache, B. Vanik, and S. Laurenzo, "Tinyiree: An ml execution environment for embedded systems from compilation to deployment," *IEEE Micro*, vol. 42, no. 5, p. 9-16, Sep. 2022. [Online]. Available: https://doi.org/10.1109/MM.2022.3178068

[12] M. Fehr, M. Weber, C. Ulmann, A. Lopoukhine, M. P. Lücke, T. Degioanni, C. Vasiladiotis, M. Steuwer, and T. Grosser, "xdsl: Sidekick compilation for ssa-based compilers," in *Proceedings of the 23rd ACM/IEEE International Symposium on Code Generation and Optimization*, ser. CGO '25. New York, NY, USA: Association for Computing Machinery, 2025, p. 179-192. [Online]. Available: https://doi.org/10.1145/3696443.3708945

[13] A. Olivry, G. Iooss, N. Tollenaere, A. Rountev, P. Sadayappan, and F. Rastello, "ioopt: automatic derivation of i/o complexity bounds for affine programs," in *Proceedings of the 42nd ACM SIGPLAN International Conference on Programming Language Design and Implementation*, ser. PLDI 2021. New York, NY, USA: Association for Computing Machinery, 2021, p. 1187-1202. [Online]. Available: https://doi.org/10.1145/3453483.3454103

[14] F. N. Iandola, S. Han, M. W. Moskewicz, K. Ashraf, W. J. Dally, and K. Keutzer, "Squeezenet: Alexnet-level accuracy with 50x fewer parameters and ~0.5mb model size," 2016. [Online]. Available: https://arxiv.org/abs/1602.07360

[15] K. He, X. Zhang, S. Ren, and J. Sun, "Deep residual learning for image recognition," 2015. [Online]. Available: https://arxiv.org/abs/1512.03385

[16] B. Hagedorn, L. Stoltzfus, M. Steuwer, S. Gorlatch, and C. Dubach, "High performance stencil code generation with lift," in *Proceedings of the 2018 International Symposium on Code Generation and Optimization*, ser. CGO '18. New York, NY, USA: Association for Computing Machinery, 2018, p. 100-112. [Online]. Available: https://doi.org/10.1145/3168824

[17] B. Hagedorn, J. Lenfers, T. Kundefedhler, X. Qin, S. Gorlatch, and M. Steuwer, "Achieving high-performance the functional way: a functional pearl on expressing high-performance optimizations as rewrite strategies," *Proc. ACM Program. Lang.*, vol. 4, no. ICFP, Aug. 2020. [Online]. Available: https://doi.org/10.1145/3408974

[18] A. Rasch, R. Schulze, D. Shabalin, A. Elster, S. Gorlatch, and M. Hall, "(de/re)-compositions expressed systematically via mdh-based schedules," in *Proceedings of the 32nd ACM SIGPLAN International Conference on Compiler Construction*, ser. CC 2023. New York, NY, USA: Association for Computing Machinery, 2023, p. 61-72. [Online]. Available: https://doi.org/10.1145/3578360.3580269

[19] N. Tollenaere, G. Iooss, S. Pouget, H. Brunie, C. Guillon, A. Cohen, P. Sadayappan, and F. Rastello, "Autotuning convolutions is easier than you think," *ACM Trans. Archit. Code Optim.*, vol. 20, no. 2, Mar. 2023.

[20] T. Chen, L. Zheng, E. Yan, Z. Jiang, T. Moreau, L. Ceze, C. Guestrin, and A. Krishnamurthy, "Learning to optimize tensor programs," in *Proceedings of the 32nd International Conference on Neural Information Processing Systems*, ser. NIPS'18. Red Hook, NY, USA: Curran Associates Inc., 2018, p. 3393-3404.

[21] Y. Ikarashi, G. L. Bernstein, A. Reinking, H. Genc, and J. Ragan-Kelley, "Exocompilation for productive programming of hardware accelerators," in *Proceedings of the 43rd ACM SIGPLAN International Conference on Programming Language Design and Implementation*, ser. PLDI 2022. New York, NY, USA: Association for Computing Machinery, 2022, p. 703-718. [Online]. Available: https://doi.org/10.1145/3519939.3523446

[22] Y. Ikarashi, K. Qian, S. Droubi, A. Reinking, G. L. Bernstein, and J. Ragan-Kelley, "Exo 2: Growing a scheduling language," in *Proceedings of the 30th ACM International Conference on Architectural Support for Programming Languages and Operating Systems, Volume 1*, ser. ASPLOS '25. New York, NY, USA: Association for Computing Machinery, 2025, p. 426-444. [Online]. Available: https://doi.org/10.1145/3669940.3707218

[23] G. Bertholon, A. Charguéraud, T. Kundefedhler, B. Bytyqi, and D. Rouhling, "Interactive source-to-source optimizations validated using static resource analysis," in *Proceedings of the 13th ACM SIGPLAN International Workshop on the State Of the Art in Program Analysis*, ser. SOAP 2024. New York, NY, USA: Association for Computing Machinery, 2024, p. 26-34. [Online]. Available: https://doi.org/10.1145/3652588.3663320

[24] J. Zhao, S. A. Vahabpour, X. Yue, K.-T. A. Wang, and T. S. Abdelrahman, "PolyMorphous: An MLIR-Based Polyhedral Compiler with Loop Transformation Primitives," in *2025 IEEE International Parallel and Distributed Processing Symposium (IPDPS)*. Los Alamitos, CA, USA: IEEE Computer Society, Jun. 2025, pp. 383-394. [Online]. Available: https://doi.ieeecomputersociety.org/10.1109/IPDPS64566.2025.00041