10深度学习系统:从算法到硬件

编译器优化

Triton 与 XLA

Triton编程XLA编译算子自动调优图优化

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第10章 编译器优化 - Triton 与 XLA (Compiler Optimizations: Triton and XLA)

一、导读

1.1 本章学习目标

通过本章的学习,读者将能够:

理解深度学习编译器的作用和架构设计

掌握XLA(Accelerated Linear Algebra)的工作原理和优化策略

深入分析Triton编程模型及其与传统CUDA的区别

了解多级中间表示(MLIR)在编译器中的应用

掌握算子融合的原理和实现方法

理解自动调度搜索(Auto-scheduling)的算法和技术

能够使用Triton编写高效的GPU内核

比较不同编译器框架的设计哲学和适用场景

1.2 为什么本章如此重要

深度学习编译器是连接高层框架和底层硬件的关键桥梁。随着硬件多样性和模型复杂性的增加,编译器的重要性日益凸显。

首先,手写优化的GPU代码极其困难且耗时。CUDA编程需要深入了解硬件架构、内存层次和执行模型。Triton等高级编程模型使得编写高效GPU代码变得更加容易,同时保持接近手写的性能。

其次,不同的硬件平台(NVIDIA GPU、AMD GPU、TPU、CPU等)需要不同的优化策略。编译器可以自动适配目标硬件,实现"一次编写,多处运行"的目标。

第三,现代深度学习框架(如JAX、TensorFlow)严重依赖编译器进行优化。XLA是这些框架的核心组件,理解XLA有助于更好地使用这些框架并诊断性能问题。

最后,随着大模型和新型架构的出现,传统的算子库可能无法提供最优实现。编译器可以通过自动搜索找到针对特定形状和硬件的最优调度。

1.3 核心问题引导

在学习本章内容时,请思考以下问题:

深度学习编译器与传统编译器(如GCC)有什么异同?它们面临的独特挑战是什么?

XLA的HLO(High-Level Optimizer)IR如何表示计算图?它进行了哪些关键优化?

Triton的块级编程模型如何简化GPU编程?它与CUDA线程模型的映射关系是怎样的?

MLIR的多级IR设计如何解决不同抽象层次之间的转换问题?

算子融合的收益来自哪里?哪些操作适合融合,哪些不适合?

自动调度搜索(如Ansor)如何在巨大的搜索空间中找到最优解?

JIT(即时编译)与AOT(提前编译)各有什么优缺点?在什么场景下选择哪种?

编译器优化如何与手动优化协同工作?何时应该信任编译器,何时应该手动干预?


二、核心概念详解

深度学习编译器概述

编译器是将高层程序表示转换为底层可执行代码的工具。传统编译器(如GCC、LLVM)针对通用程序设计语言,而深度学习编译器专门针对机器学习工作负载。

深度学习的特殊性:

  • 数据并行性:张量运算天然适合并行执行
  • 规则的计算模式:矩阵乘法、卷积等操作具有规律的数据访问模式
  • 大规模数据:处理的数据量巨大,内存带宽是关键瓶颈
  • 特定的硬件目标:主要针对GPU、TPU等加速器优化

编译器的目标:

正确性:生成的代码必须产生正确的结果

性能:生成的代码应接近或达到手写优化的水平

可移植性:同一份代码可以在不同硬件上高效运行

编译时间:编译过程不应过长,特别是对于JIT场景

编译器架构

典型的深度学习编译器包含以下组件:

前端(Frontend):将高层框架的模型表示转换为编译器的中间表示(IR)。例如,Torch-MLIR将PyTorch模型转换为MLIR,tf2xla将TensorFlow图转换为HLO。

中间表示(IR):编译器内部使用的计算表示。好的IR应该既足够抽象以进行高层优化,又足够具体以生成高效代码。常见的IR包括:

  • HLO(XLA)
  • MLIR(多级IR)
  • Relay/TIR(TVM)
  • FX Graph(PyTorch)

优化器(Optimizer):对IR进行各种变换以提高性能。包括:

  • 常量折叠
  • 死代码消除
  • 算子融合
  • 内存优化
  • 布局转换

后端(Backend):将优化后的IR转换为目标硬件的代码。可以是LLVM IR、CUDA PTX、或直接的机器码。

运行时(Runtime):支持编译后代码的执行,包括内存管理、任务调度等。

XLA(Accelerated Linear Algebra)

XLA是Google开发的领域特定编译器,用于优化线性代数运算。它是TensorFlow和JAX的核心组件。

XLA架构

HLO(High-Level Optimizer)IR:

HLO是XLA的主要中间表示,是一个基于图的表示,节点代表操作,边代表数据流。HLO的特点:

  • 静态形状:所有张量形状在编译时已知
  • 强类型:每个值都有明确的类型
  • 函数式:没有副作用,便于优化

HLO示例:

%param = f32[128,256] parameter(0)
%weight = f32[256,512] parameter(1)
%dot = f32[128,512] dot(%param, %weight), lhs_contracting_dims={1}, rhs_contracting_dims={0}
ROOT %fusion = f32[128,512] fusion(%dot), kind=kLoop

优化Passes:

XLA执行多种优化pass:

代数简化:利用数学恒等式简化表达式,如$x \times 1 = x$、$x + 0 = x$。

常量折叠:预先计算常量表达式,减少运行时计算。

算子融合:将多个操作合并为一个内核。XLA支持多种融合类型:

- kLoop:逐元素操作的融合

- kInput:将输入操作融合到消费者中

- kOutput:将输出操作融合到生产者中

- kCustom:自定义融合

布局优化:选择最优的内存布局以减少数据重排。

缓冲区优化:重用内存缓冲区,减少分配。

GSPMD(Generalized SPMD):

XLA的分布式编译扩展,可以将单个程序的编译结果自动扩展到多设备。GSPMD通过分析操作的并行属性,自动生成所需的通信代码。

XLA的使用

在JAX中使用XLA:

python
import jax
import jax.numpy as jnp

# JIT编译函数
@jax.jit
def matrix_multiply(x, y):
    return jnp.dot(x, y)

# 首次调用触发编译
result = matrix_multiply(jnp.ones((128, 256)), jnp.ones((256, 512)))

在PyTorch中使用TorchXLA:

python
import torch_xla.core.xla_model as xm

device = xm.xla_device()
model.to(device)
# 后续操作由XLA编译和执行

Triton编程模型

Triton是CMU开发并在OpenAI得到广泛应用的GPU编程模型。它的目标是让编写高效GPU代码像编写Python一样简单。

设计哲学

Triton的核心洞察是:大多数深度学习操作都可以表示为块级(block-level)的并行计算。程序员只需要定义每个线程块如何处理一块数据,Triton编译器负责:

  • 将块分配到GPU线程
  • 优化内存访问模式
  • 生成高效的PTX/SASS代码

这与CUDA的线程级编程形成对比。CUDA要求程序员显式管理每个线程的行为,包括线程索引、共享内存分配、同步等。Triton将这些细节抽象掉,让程序员专注于计算逻辑。

Triton程序结构

一个Triton程序包含以下要素:

Kernel定义:

python
@triton.jit
def add_kernel(X, Y, Z, N, BLOCK_SIZE: tl.constexpr):
    # 计算当前程序负责的起始位置
    pid = tl.program_id(0)
    block_start = pid * BLOCK_SIZE
    
    # 创建索引
    offsets = block_start + tl.arange(0, BLOCK_SIZE)
    
    # 加载数据
    mask = offsets < N
    x = tl.load(X + offsets, mask=mask)
    y = tl.load(Y + offsets, mask=mask)
    
    # 计算
    z = x + y
    
    # 存储结果
    tl.store(Z + offsets, z, mask=mask)

关键概念:

  • tl.program_id:当前程序实例的ID
  • tl.arange:创建立即数序列
  • tl.load/tl.store:内存加载/存储,支持掩码
  • tl.constexpr:编译时常量,用于控制流

启动Kernel:

python
grid = lambda META: (triton.cdiv(N, META['BLOCK_SIZE']),)
add_kernel[grid](x_ptr, y_ptr, z_ptr, N, BLOCK_SIZE=1024)

Triton的优势

生产力:代码量比CUDA少得多,易于编写和维护。

性能:自动优化内存访问和寄存器分配,通常能达到手写CUDA的90%以上性能。

灵活性:支持动态形状和控制流,不像某些编译器要求静态形状。

Python集成:可以直接在Python中使用,与PyTorch无缝集成。

Triton vs CUDA

特性TritonCUDA
抽象级别块级线程级
内存管理自动手动
学习曲线平缓陡峭
性能上限高最高
灵活性中等最高
调试难度较低较高

MLIR(Multi-Level Intermediate Representation)

MLIR是LLVM项目的一部分,旨在为机器学习提供灵活的IR基础设施。

多级IR设计

MLIR的核心创新是多级IR:不是使用单一的IR,而是定义多个不同抽象层次的Dialect(方言),并在它们之间进行转换。

常见的Dialects:

  • torch:PyTorch操作
  • mhlo:Mobile HLO,XLA的子集
  • linalg:线性代数操作
  • affine:仿射循环嵌套
  • scf:结构化控制流
  • gpu:GPU操作
  • llvm:LLVM IR

转换流程

典型的编译流程:

Torch IR -> mhlo -> linalg -> affine/scf -> gpu -> llvm -> PTX

每一层都进行特定类型的优化:

  • 高层:算子融合、常量传播
  • 中层:循环变换、内存优化
  • 低层:指令选择、寄存器分配

Pass机制

MLIR使用Pass管理器来组织和执行优化passes。每个pass对IR进行特定变换,如:

  • -canonicalize:规范化IR
  • -cse:公共子表达式消除
  • -fuse-linalg:融合线性代数操作

算子融合(Operator Fusion)

算子融合是编译器最重要的优化之一,它将多个操作合并为一个内核执行。

融合的收益

减少内存访问:未融合时,每个操作的输出都需要写入全局内存,下一个操作再读回。融合后,中间结果保持在寄存器或共享内存中。

减少内核启动开销:每次内核启动都有固定开销(约10-50微秒)。融合减少了启动次数。

提高缓存利用率:融合后的操作可以更好地利用L1/L2缓存。

融合的类型

垂直融合(Vertical Fusion):将生产者-消费者链融合。例如:

relu(bn(conv(x))) -> 融合为一个内核

水平融合(Horizontal Fusion):将独立的操作融合。例如:

y1 = conv1(x)
y2 = conv2(x)
-> 融合为一个内核同时计算两个卷积

元素级融合:逐元素操作(如add、mul、relu)最容易融合,因为它们的数据访问模式简单。

归约融合:涉及归约的操作(如sum、mean)也可以融合,但更复杂。

融合的约束

并非所有操作都可以融合:

  • 形状不匹配:操作需要兼容的形状
  • 数据依赖:存在循环依赖时无法融合
  • 内存限制:融合可能增加寄存器压力
  • 数值精度:某些融合可能改变浮点运算顺序,影响精度

自动调度搜索

自动调度是指编译器自动寻找最优的执行策略,包括:

  • 线程块大小
  • 共享内存使用
  • 循环展开程度
  • 向量化宽度

TVM的Ansor

Ansor是TVM的自动调度器,使用机器学习方法搜索最优调度。

搜索空间:

Ansor定义了一个参数化的搜索空间,包括:

  • 分块因子
  • 并行化策略
  • 向量化
  • 共享内存缓存

搜索算法:

  • 进化搜索:维护一个候选池,通过变异和交叉产生新候选
  • 代价模型:预测每个候选的性能,避免实际执行所有候选
  • 迭代细化:逐步缩小搜索范围

Halide的调度语言

Halide采用另一种方法:程序员提供算法和调度的分离描述。算法定义"算什么",调度定义"怎么算"。

cpp
// 算法
Func f;
Var x, y;
f(x, y) = sin(x) + cos(y);

// 调度
f.parallel(y).vectorize(x, 8);

这种方法结合了程序员的领域知识和编译器的自动化。

JIT vs AOT编译

JIT(Just-In-Time)编译:在运行时编译代码。

优点:

  • 可以利用运行时信息(如实际形状)
  • 便于调试和迭代
  • 支持动态行为

缺点:

  • 首次执行有编译开销
  • 可能占用较多内存(缓存编译结果)

AOT(Ahead-Of-Time)编译:在部署前编译代码。

优点:

  • 无运行时编译开销
  • 可以进行更激进的优化(有更多时间)
  • 更容易分发和部署

缺点:

  • 需要处理动态形状(通常通过填充或模板)
  • 调试较困难

在实践中,训练阶段通常使用JIT(灵活性优先),推理阶段使用AOT(性能优先)。


三、程序员的视角

3.1 在实际项目中的应用

使用Triton自定义操作:当现有库不支持所需操作时,用Triton编写比用CUDA更快。

XLA性能调优:通过XLA_FLAGS调整XLA行为,如启用/禁用特定优化。

查看编译结果:使用--vmodule=compiler=*等标志查看XLA的编译日志,了解进行了哪些优化。

混合使用:对于关键操作,可以手写Triton/CUDA;对于其余部分,依赖编译器优化。

3.2 常见陷阱与误区

过度依赖编译器:编译器虽然强大,但并非万能。对于特别重要的操作,手动优化仍可能带来显著提升。

忽略编译时间:JIT编译可能耗时数秒甚至数十秒。在生产环境中,应考虑缓存编译结果或使用AOT。

动态形状问题:XLA等编译器要求静态形状。动态形状可能导致重新编译,严重影响性能。

融合的反效果:有时融合会增加寄存器压力,导致occupancy下降。需要实际测量验证。

3.3 与其他层级的联系

编译器位于框架和硬件之间,向上提供简洁的API,向下生成高效的机器码。编译器的质量直接影响上层框架的性能和下层硬件的利用率。


四、代码实战

示例1:Triton矩阵乘法

python
import torch
import triton
import triton.language as tl

@triton.jit
def matmul_kernel(
    A_ptr, B_ptr, C_ptr,
    M, N, K,
    stride_am, stride_ak,
    stride_bk, stride_bn,
    stride_cm, stride_cn,
    BLOCK_SIZE_M: tl.constexpr,
    BLOCK_SIZE_N: tl.constexpr,
    BLOCK_SIZE_K: tl.constexpr,
):
    """
    Triton矩阵乘法内核
    
    每个程序实例计算C的一个BLOCK_SIZE_M x BLOCK_SIZE_N的块
    """
    pid = tl.program_id(axis=0)
    
    # 计算该程序负责的C的块的行列索引
    num_pid_n = tl.cdiv(N, BLOCK_SIZE_N)
    pid_m = pid // num_pid_n
    pid_n = pid % num_pid_n
    
    # 创建指针偏移
    offs_am = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
    offs_bn = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
    offs_k = tl.arange(0, BLOCK_SIZE_K)
    
    # A的指针: [BLOCK_SIZE_M, BLOCK_SIZE_K]
    a_ptrs = A_ptr + (offs_am[:, None] * stride_am + offs_k[None, :] * stride_ak)
    # B的指针: [BLOCK_SIZE_K, BLOCK_SIZE_N]
    b_ptrs = B_ptr + (offs_k[:, None] * stride_bk + offs_bn[None, :] * stride_bn)
    
    # 初始化累加器
    accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
    
    # 沿着K维度分块
    for k in range(0, tl.cdiv(K, BLOCK_SIZE_K)):
        # 加载A和B的块
        a = tl.load(a_ptrs, mask=offs_k[None, :] < K - k * BLOCK_SIZE_K, other=0.0)
        b = tl.load(b_ptrs, mask=offs_k[:, None] < K - k * BLOCK_SIZE_K, other=0.0)
        
        # 累加乘积
        accumulator += tl.dot(a, b)
        
        # 移动指针到下一个块
        a_ptrs += BLOCK_SIZE_K * stride_ak
        b_ptrs += BLOCK_SIZE_K * stride_bk
    
    # 计算C的指针
    offs_cm = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
    offs_cn = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
    c_ptrs = C_ptr + stride_cm * offs_cm[:, None] + stride_cn * offs_cn[None, :]
    
    # 存储结果
    c_mask = (offs_cm[:, None] < M) & (offs_cn[None, :] < N)
    tl.store(c_ptrs, accumulator, mask=c_mask)


def matmul_triton(A, B):
    """使用Triton进行矩阵乘法"""
    M, K = A.shape
    K2, N = B.shape
    assert K == K2, "Incompatible dimensions"
    
    C = torch.empty((M, N), device=A.device, dtype=A.dtype)
    
    # 配置网格和块大小
    grid = lambda META: (
        triton.cdiv(M, META['BLOCK_SIZE_M']) * 
        triton.cdiv(N, META['BLOCK_SIZE_N']),
    )
    
    matmul_kernel[grid](
        A, B, C,
        M, N, K,
        A.stride(0), A.stride(1),
        B.stride(0), B.stride(1),
        C.stride(0), C.stride(1),
        BLOCK_SIZE_M=128,
        BLOCK_SIZE_N=128,
        BLOCK_SIZE_K=32,
    )
    
    return C


def test_triton_matmul():
    """测试Triton矩阵乘法的正确性和性能"""
    import time
    
    M, K, N = 1024, 1024, 1024
    A = torch.randn(M, K, device='cuda', dtype=torch.float32)
    B = torch.randn(K, N, device='cuda', dtype=torch.float32)
    
    # 正确性测试
    C_triton = matmul_triton(A, B)
    C_torch = torch.matmul(A, B)
    
    diff = torch.abs(C_triton - C_torch).max().item()
    print(f"Max difference: {diff:.6e}")
    
    # 性能测试
    iterations = 100
    
    torch.cuda.synchronize()
    start = time.time()
    for _ in range(iterations):
        _ = matmul_triton(A, B)
    torch.cuda.synchronize()
    time_triton = (time.time() - start) / iterations * 1000
    
    torch.cuda.synchronize()
    start = time.time()
    for _ in range(iterations):
        _ = torch.matmul(A, B)
    torch.cuda.synchronize()
    time_torch = (time.time() - start) / iterations * 1000
    
    print(f"Triton: {time_triton:.3f} ms")
    print(f"PyTorch: {time_torch:.3f} ms")
    print(f"Speedup: {time_torch/time_triton:.2f}x")


test_triton_matmul()

示例2:XLA编译分析

python
import jax
import jax.numpy as jnp

# 启用XLA编译日志
import os
os.environ['XLA_FLAGS'] = '--xla_dump_hlo_text=true --xla_dump_to=/tmp/xla_dump'

@jax.jit
def transformer_block(x, w_q, w_k, w_v, w_o):
    """简化的Transformer块"""
    # 自注意力
    q = jnp.dot(x, w_q)
    k = jnp.dot(x, w_k)
    v = jnp.dot(x, w_v)
    
    # 注意力得分
    scores = jnp.dot(q, k.T) / jnp.sqrt(q.shape[-1])
    attention = jax.nn.softmax(scores, axis=-1)
    
    # 应用注意力
    output = jnp.dot(attention, v)
    output = jnp.dot(output, w_o)
    
    # 残差连接
    return output + x

# 创建输入
batch_size = 32
seq_len = 128
hidden_dim = 512

key = jax.random.PRNGKey(0)
x = jax.random.normal(key, (batch_size, seq_len, hidden_dim))
w_q = jax.random.normal(key, (hidden_dim, hidden_dim))
w_k = jax.random.normal(key, (hidden_dim, hidden_dim))
w_v = jax.random.normal(key, (hidden_dim, hidden_dim))
w_o = jax.random.normal(key, (hidden_dim, hidden_dim))

# 首次调用触发编译
result = transformer_block(x, w_q, w_k, w_v, w_o)

print("XLA compilation complete")
print(f"Result shape: {result.shape}")

# 检查dump文件
import subprocess
result = subprocess.run(['ls', '/tmp/xla_dump'], capture_output=True, text=True)
print(f"\nDumped files:\n{result.stdout}")

示例3:简单的算子融合演示

python
import torch
import time

def demonstrate_fusion():
    """演示算子融合的效果"""
    
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    
    size = 10000000  # 10M elements
    x = torch.randn(size, device=device)
    
    # 未融合:逐个操作
    torch.cuda.synchronize()
    start = time.time()
    for _ in range(100):
        y1 = x * 2.0
        y2 = y1 + 1.0
        y3 = torch.relu(y2)
        y4 = y3 * 0.5
    torch.cuda.synchronize()
    time_unfused = (time.time() - start) / 100 * 1000
    
    # 融合:单个表达式(PyTorch JIT可能融合)
    @torch.jit.script
    def fused_op(x):
        return torch.relu(x * 2.0 + 1.0) * 0.5
    
    torch.cuda.synchronize()
    start = time.time()
    for _ in range(100):
        y = fused_op(x)
    torch.cuda.synchronize()
    time_fused = (time.time() - start) / 100 * 1000
    
    print(f"Unfused: {time_unfused:.3f} ms")
    print(f"Fused: {time_fused:.3f} ms")
    print(f"Speedup: {time_unfused/time_fused:.2f}x")
    
    # 使用Torch Compile(PyTorch 2.0+)
    if hasattr(torch, 'compile'):
        compiled_op = torch.compile(fused_op)
        
        # 预热
        _ = compiled_op(x)
        
        torch.cuda.synchronize()
        start = time.time()
        for _ in range(100):
            y = compiled_op(x)
        torch.cuda.synchronize()
        time_compiled = (time.time() - start) / 100 * 1000
        
        print(f"Torch Compile: {time_compiled:.3f} ms")


demonstrate_fusion()

五、深入思考

5.1 历史背景

深度学习编译器的发展经历了多个阶段。早期的Theano(2010)已经包含了编译优化的思想。TensorFlow的XLA(2017)将这一理念推向主流。

TVM(2018)展示了自动代码生成的潜力,Halide(2012)证明了调度分离的价值。MLIR(2019)为编译器基础设施带来了模块化革命。

近年来,Triton(2021)通过高级抽象降低了GPU编程门槛,Torch Compile(2023)将编译优化带入PyTorch主流。

5.2 前沿方向

端到端编译:从Python代码直接生成优化的机器码,跳过中间框架。

自适应编译:根据运行时特征动态调整编译策略。

异构编译:同时 targeting CPU、GPU、TPU等多种硬件。

形式化验证:确保编译器优化的正确性,避免引入bug。

AI辅助编译:使用机器学习指导编译决策,如调度搜索、融合策略。

5.3 延伸阅读

  • XLA官方文档 - Google的编译器指南
  • Triton教程 - OpenAI的GPU编程模型
  • MLIR官网 - LLVM的多级IR项目
  • "TVM: An Automated End-to-End Optimizing Compiler for Deep Learning" - TVM论文
  • "Halide: A Language and Compiler for Optimizing Parallelism, Locality, and Recomputation in Image Processing Pipelines"

六、实验笔记

实验1:Triton vs CUDA性能对比

目标:比较Triton和手写CUDA内核的性能差距

步骤:

实现相同功能的Triton和CUDA版本(如矩阵乘法、softmax)

在不同输入大小下测试

分析性能差异的来源

预期结果:Triton应达到CUDA的80-95%性能,但代码量少得多

分析要点:

  • 哪些情况下Triton性能接近CUDA?
  • 哪些情况下差距较大?
  • Triton编译器的优化能力如何?

实验2:XLA优化分析

目标:理解XLA进行的优化变换

步骤:

启用XLA dump功能

分析HLO IR的优化前后差异

识别执行的优化pass

预期结果:可以看到常量折叠、算子融合等优化

分析要点:

  • XLA融合了哪些操作?
  • 是否有未预期的优化或缺失的优化?
  • 如何影响最终性能?

七、本章小结

本章深入探讨了深度学习编译器的核心技术,从XLA的架构到Triton的编程模型,从算子融合到自动调度。我们理解了编译器如何在不同抽象层次之间转换,以及如何生成高效的底层代码。

编译器是现代深度学习系统的关键组件,它使得高层框架能够充分利用底层硬件的能力。随着硬件多样性和模型复杂性的增加,编译器的重要性只会继续提升。


八、习题

解释XLA的HLO IR的特点。为什么它要求静态形状?这对动态模型有什么影响?

Triton的块级编程模型如何简化GPU编程?它与CUDA的线程模型有何对应关系?

MLIR的多级IR设计解决了什么问题?列举至少三个Dialect及其用途。

算子融合的收益主要来自哪里?给出一个融合带来显著加速的例子和一个融合无效的例子。

比较Ansor的自动调度和Halide的手动调度。各有什么优缺点?

JIT编译和AOT编译分别适用于什么场景?在一个完整的机器学习工作流中,如何结合使用两者?

阅读Triton文档,解释tl.constexpr的作用。它与普通变量有什么区别?

XLA的GSPMD如何实现自动并行?它分析了哪些信息来决定并行策略?

设计一个实验来测量算子融合的效果。你需要控制哪些变量?如何隔离融合的影响?

调研新兴的编译器技术(如Mojo、IREE),比较它们与XLA/TVM的设计哲学差异。

10/12编译器优
0%