BladeDISC Documentation

repository·main·Indexed 21 days ago

https://github.com/alibaba/bladedisc

An end-to-end dynamic shape compiler for machine learning workloads that provides performance optimizations for TensorFlow and PyTorch across various CPU and GPU backends. It includes torch_blade for PyTorch acceleration via TorchScript compilation and FoldAcc for optimizing AlphaFold models using Automatic Mixed Precision (AMP) and Tensor Parallelism.

Tokens
34.2K
Snippets
98
Records
172
Agent score
75%

What's inside BladeDISC

  1. What is BladeDISC?

    main

    BladeDISC is an end-to-end DynamIc Shape Compiler designed for machine learning workloads. It provides transparent performance optimization for TensorFlow and PyTorch workloads on GPGPU and CPU backends.

    Key characteristics include:

    • Native Dynamic Shape Support: Optimized for both static and dynamic shape scenarios.
    • MLIR-based: Built on the MLIR framework and closely related to the mlir-hlo project.
    • Flexible Deployment: Supports both Plugin Mode (running as a plugin within the original framework runtime) and Standalone Mode (AOT compilation into a self-contained binary).
  2. Walkthrough of the BladeDISC Pass Pipeline

    main

    The BladeDISC compilation process follows a multi-stage pass pipeline that transforms high-level TensorFlow/PyTorch IR into optimized machine code for both CPU and GPU. The pipeline is divided into several major phases:

    1. TF-to-HLO Passes: Converts TensorFlow dialect to HLO (High-Level Optimizer) dialect. This phase includes standard MLIR-HLO pipelines and BladeDISC-specific passes like DiscLowerTfPass (for custom ops like RandomUniform and TopK) and ReviseArgumentsForStaticRankPass.
    2. HLO Graph Optimization & Placement: Optimizes the HLO graph and decides where operations should execute. Key components include the ShapeSimplifier pass (for shape propagation and constraint insertion) and Placement Passes (which mark shape-calculation ops to be executed on the CPU).
    3. Bufferize Passes: Transitions the IR from the 'tensor world' to the 'buffer world' by explicitly emitting allocation and deallocation logic. This includes converting mhlo to lmhlo (the bufferized representation) and assigning memory spaces (CPU vs. GPU).
    4. LHLO Graph Optimization Passes: Performs optimizations on the bufferized representation, most notably the Fusion Pass (using 'base' or 'stitch' strategies) and the Speculation Pass (generating multiple kernel versions for different runtime conditions like vectorization or implicit broadcasts).
    5. Runtime & Library Call Related Passes: Integrates the Runtime Abstraction Layer (RAL). It injects the RAL context and rewrites custom call ops into disc_ral.dispatch ops to ensure a stable ABI and manage stateful resources.
    6. CodeGen Passes: Lowers lmhlo.fusion ops into nested loops. This phase is backend-aware (CPU vs. GPU) and uses backbone passes like DiscLhloLegalizeRootsToParallelLoopsPass and InputInlineFusionPass to generate schedules.
    7. Loops to GPU / GPU Module to CUBIN: For GPU backends, this phase tiles loops, maps them to GPU blocks/threads, and lowers the GPU dialect to vendor-specific dialects (NVVM for CUDA or ROCm for AMD) before compiling to a binary blob.
    8. Host Side Passes: Generates the scheduling logic (kernel launching, data movement, synchronization) and lowers the final IR to the LLVM dialect for binary generation.
  3. What is Speculation in BladeDISC?

    main
    Because shapes are unknown at compile time, standard optimizations like data vectorization or schedule selection are difficult. BladeDISC uses a process called speculation: it generates multiple versions of kernels at compile time and generates host-side code to select and launch the most appropriate kernel version at runtime based on the actual shapes.
  4. Use the Quantizer class for PyTorch quantization

    main

    The primary interface for the toolkit is the Quantizer class. You instantiate a Quantizer with configuration (such as target backend type or excluded module types) and use it to generate different types of proxy models depending on your stage in the workflow:

    • Calibration Proxy: Created via Quantizer.calib(model). Used to calibrate parameters by running forward passes with typical data.
    • QAT Proxy: Created via Quantizer.qat(model). Used for Quantization-Aware Training.
    • Quantized Model Proxy: Created via Quantizer.quantize(model). Represents the final quantized model ready for inference or export.
    from torch_quant import Quantizer
    
    model = MyModel() # torch.nn.Module
    quantizer = Quantizer()
    
    # For calibration
    calib_model = quantizer.calib(model)
    
    # For QAT
    qat_model = quantizer.qat(model)
    
    # For final quantized model
    quant_model = quantizer.quantize(model)
  5. How the compiler interacts with RAL via Context Injection

    main

    To simplify the compiler's core optimization logic, BladeDISC uses Context Injection. All RAL APIs are required to take a context object as their first argument.

    During compilation, a transformation pass rewrites the entry function and all related functions to ensure the context is passed through. In the MLIR intermediate representation, this is modeled using the disc_ral dialect and the disc_ral.RalExecutionContextType type, which eventually lowers to a pointer in LLVM IR.

  6. How inputs and outputs are bound in RAL

    main

    To maintain a stable ABI and hide the implementation details of memory structures (like MemRef), the compiler rewrites the entry function's inputs and outputs. Instead of receiving raw buffers, the function receives a context and uses recv_input and send_output API calls to interact with the RAL.

    This design allows for partial execution: the compiler can place recv_input calls such that the binary starts executing as soon as specific inputs are ready, or send_output calls to stream results back before all computations are finished.

    // Original IR
    func @main(%arg0 : memref<?x?xf32>, %arg1 : memref<?x?xf32>) -> memref<?x?xf32> {
      %ret = alloc(...)
      use(%arg0, %arg1, %ret, ...)
      return %ret : memref<?x?xf32>
    }
    
    // After RAL conversion
    func @main(!disc_ral.context %ctx) {
      %arg0 = disc_ral.recv_input(%ctx, 0) // receive the first input
      %arg1 = disc_ral.recv_input(%ctx, 1) // receive the second input
      %ret = alloc(...)
      use(%arg0, %arg1, %ret, ...)
      disc_ral.send_output(%ctx, 0, %ret) // send the first output
    }
  7. How TorchBlade handles Lists and Dicts

    main

    TorchBlade's support for Python data structures like List and Dict is limited to cases where elements can be analyzed statically at conversion time:

    • Supported: List[Scalar] and List[Tensor] where elements are known statically. In these cases, TorchBlade imitates the list operations during conversion so the List structure itself does not need to be preserved in the IR.
    • Unsupported: If elements cannot be analyzed statically, or if using Dict, the operations will fallback to the PyTorch runtime.
  8. Understand the Shape Optimization Pass workflow

    main

    The shape optimization pass in BladeDISC is a tensor-level, reenterable pass designed to improve shape information within the IR. It operates in two primary stages:

    1. Stage One: Explicit Materialization: Materializes shape computation IR on the tensor level. This includes (partial) shape inference (e.g., calculating the output shape of a concat operation) and enabling optimization opportunities for shape computation IR.
    2. Stage Two: Analysis and Optimization: An iterative process that performs canonicalization, loads existing shape constraint IR, conducts global shape analysis (finding more constraints and injecting constraints implied by mhlo op definitions), performs global shape optimization (using the same SSA value for symbolic equal dimensions), and finally saves the updated information back into the IR.