TT-Forge AI Compiler Stack

repository·main·Indexed 43 days ago

https://github.com/tenstorrent/tt-forge

An open-source AI compiler stack for Tenstorrent hardware. It includes frontends for PyTorch, JAX, and ONNX (via TT-XLA and TT-Forge-ONNX), an MLIR-based compiler (TT-MLIR), and TT-Lang, a Python DSL for custom kernel development. The ecosystem supports single-chip and multi-chip configurations for running and training AI workloads on Wormhole and Blackhole architectures.

Tokens
69.1K
Snippets
132
Records
208
Agent score
56%

What's inside TT-Forge

  1. Introduction to TT-Lang

    main

    TT-Lang is a Python-based Domain Specific Language (DSL) used to express kernel programs for Tenstorrent hardware. It is designed to be tightly integrated with TT-NN, allowing users to mix existing TT-NN operations with custom kernel programs.

    The programming model focuses on:

    • Explicit data movement: Controlling how data moves between memory and compute.
    • Explicit compute threads: Defining specific threads for computation and data movement.
    • Explicit synchronization: Using primitives like dataflow buffers and semaphores to manage execution schedules and performance.
  2. Overview of TT-Forge Sub-Projects

    main

    TT-Forge is composed of several specialized sub-projects depending on your workflow:

    • TT-XLA: Primary frontend for PyTorch and JAX models. Supports single and multi-chip via the PJRT interface.
    • TT-Forge-ONNX: TVM-based frontend for ONNX, TensorFlow, and PaddlePaddle (single-chip only).
    • TT-MLIR: The core MLIR-based compiler that defines TTIR, TTNN, and TTKernel dialects.
    • TT-Lang: A Python DSL for writing custom high-performance kernels (early preview).
    • TT-Blacksmith: Provides optimized training recipes and experiments for PyTorch, JAX, and Lightning.
    • TT-Forge-Models: A library of 800+ model variants with standardized loaders.
  3. Overview of TTNN operations library

    main

    TTNN is a high-level operations library for Tenstorrent hardware that provides a PyTorch-like API for tensor creation, manipulation, and computation. TTNN operations run individually, meaning each op call triggers one kernel launch. For scenarios requiring multiple ops to be fused into a single kernel, use TT-Lang instead.

    Key Concepts

    • Tensors: Must be moved to the device using ttnn.to_device(tensor, device) before computation can occur.
    • Layouts: Supports ttnn.ROW_MAJOR_LAYOUT and ttnn.TILE_LAYOUT. Note that ttnn.TILE_LAYOUT (32x32 tiles) is required for most compute operations.
    • Memory Configs:
      • ttnn.DRAM_MEMORY_CONFIG: The default configuration for large storage.
      • ttnn.L1_MEMORY_CONFIG: Fast but limited memory (~1.5MB per core).
    • Data Types: Common types include ttnn.bfloat16, ttnn.float32, ttnn.bfloat8_b, and ttnn.uint32.
  4. Overview of TT-Forge-ONNX Model Demos

    main

    The tt-forge-onnx demos directory provides example implementations of popular deep learning models to showcase how to run inference using TT-Forge-ONNX. These examples cover Computer Vision (CNN) and Natural Language Processing (NLP) models.

    Critical Limitation: TT-Forge-ONNX does not support multi-chip configurations. It is intended for single-chip projects only.

  5. Explore TT-Lang reference models

    main

    The tt-lang-models repository contains several high-performance implementations of various model architectures optimized for Tenstorrent hardware using TT-Lang. Key models include:

    • DFlash: A lightweight cross-attention draft model for speculative decoding. It uses TT-Lang kernels (RoPE, RMSNorm, SiLU, residual adds) to achieve significant decoding speedups.
    • Engram: A port of the DeepSeek Engram conditional memory module to TT-Lang on Wormhole, utilizing streaming dataflow kernels and PipeNet for inter-core boundary sharing.
    • nanochat: An inference implementation written entirely in TT-Lang.
    • Oasis: Real-time world generation using the Oasis 500M diffusion transformer, running end-to-end inference in a single captured trace.
    • Qwen-Image: A TTNN + TT-Lang implementation of Qwen-Image 20B image generation.
    • Micelle MD: Cell-list molecular dynamics implementation featuring Ewald electrostatics and on-device Verlet integration.
    • Toy World Model: A Pong world model based on a diffusion transformer running on a single Blackhole card.
  6. Overview of Programming a Mesh of Devices with TT-NN

    main

    TT-NN supports multi-device operations using a Single-Program Multiple-Device (SPMD) technique. This allows you to scale single-device application code to multiple connected devices by parallelizing computations across them.

    To scale models, TT-NN uses three core abstractions:

    1. MeshDevice: A "virtual device" abstraction that defines a logical 2-D mesh of connected physical devices. Operations targeting a MeshDevice are distributed via SPMD across all devices in that mesh.
    2. Input Data Distribution: The mechanism for distributing host-memory data to the on-device memory of the MeshDevice. When an operation is distributed, the code within a single-device scope operates only on its local shard of the data.
    3. MeshTensor: A collection of tensor shards distributed across the 2D mesh. While a standard Tensor is an N-dimensional matrix, a MeshTensor represents the global view of data spread across the MeshDevice.
  7. What is a MeshDevice and how does it work?

    main

    A MeshDevice is a virtual device abstraction that bundles multiple physical devices into a single logical unit to enable parallel execution across a mesh topology. It is natively supported at the runtime level to reduce dispatch overhead via unified command queues.

    Key Optimizations:

    • Kernel Compilation Broadcasting: Compilation artifacts are automatically broadcast to all devices in the mesh to avoid redundant per-device compilation.
    • Data Broadcasting: Replicated tensors are efficiently broadcast across the mesh topology with minimal host-side processing.
    • Unified Command Dispatch: Uses mesh-aware command queues to coordinate lock-step parallel execution.

    Important Note: MeshDevice does not hide the distributed nature of computation. Users must explicitly manage:

    • Data Distribution: Specifying how tensors are sharded or replicated using mesh mappers.
    • Collective Communication: Using CCL operations (e.g., all-gather, reduce-scatter) for device communication.
    import ttnn
    
    # Example: Instantiating a MeshDevice over a 1x2 Wormhole configuration
    mesh_device = ttnn.open_mesh_device(ttnn.MeshShape(1, 2))
  8. What is a MeshDevice?

    main
    A MeshDevice is a logical abstraction representing a 2-D mesh of connected physical devices. Instead of managing individual physical devices manually, you interact with the MeshDevice to distribute computations. When you run an operation on a MeshDevice, the TT-NN runtime uses the Single-Program Multiple-Device (SPMD) model to execute that operation across all physical devices captured within the mesh topology.
  9. What is Tensor Sharding?

    main

    Tensor sharding is the process of dividing a large tensor into smaller, non-overlapping pieces (shards) and distributing them across multiple memory banks. This is essential for high performance on Tenstorrent devices.

    Memory Layouts

    • Interleaved (Default): Data is split into pages and distributed evenly across all available memory banks in a round-robin fashion. This is a non-sharded layout.
    • Sharded: A tailored layout (Height, Width, Block, or ND) that can significantly improve performance by enhancing data locality and reducing inter-core communication overhead.
  10. What is Metal Trace and when to use it

    main

    Metal Trace is a performance optimization feature designed to eliminate host overhead involved in constructing and dispatching operations. It is most effective when the host time required to dispatch a model exceeds the device time needed to execute the operations (i.e., the model is host-bound).

    How it works: Metal Trace records the commands for dispatching operations into a DRAM buffer. During execution, these recorded commands are replayed from the buffer.

    Key Constraints:

    • Static Parameters: All operation parameters, including input/output tensor shapes and memory addresses, must be static. It cannot be used directly for generative models with changing sequence lengths without advanced techniques like multiple traces.
    • Ideal Use Cases: Statically sized inputs/outputs, such as image or sequence classification.
    • Requirements: You must use program cache and ensure target operations are already compiled before capturing the trace.
  11. Manage Grid and Node Coordinates

    main

    A grid is the space of nodes (Tensix Cores) where a kernel is executed. In single-chip mode, the grid is 2D. In multi-chip mode, it includes additional dimensions for connectivity (e.g., same card, same host).

    Use ttl.grid_size(dims) to get the total size of the grid in a specific dimensionality. If the requested dims is smaller than the actual grid rank, the highest rank dimension is flattened. If dims is larger, higher ranks are padded with 1.

    Use ttl.node(dims) to get the zero-based coordinates of the current node. If dims is smaller than the grid rank, the highest rank is flattened. If dims is larger, higher ranks are padded with 0.

    # Grid size examples
    # For (8, 8) single chip or SPMD grid:
    x_size = ttl.grid_size(dims=1)          # returns 64
    x_size, y_size = ttl.grid_size(dims=2) # returns (8, 8)
    x_size, y_size, z_size = ttl.grid_size(dims=3) # returns (8, 8, 1)
    
    # Node coordinate examples
    # For (8, 8) single chip or SPMD grid:
    x = ttl.node(dims=1)          # returns x in [0, 64)
    x, y = ttl.node(dims=2)       # returns (x, y) in [0, 8) x [0, 8)
    x, y, z = ttl.node(dims=3)    # returns (x, y, 0) in [0, 8) x [0, 8) x [0]
  12. Use output_tensor for performance and tracing

    main

    Most TTNN operations accept an output_tensor or optional_output_tensor parameter. Instead of the op allocating a new tensor for the result, it writes into the provided pre-allocated tensor. This is critical for:

    • Performance: Avoiding repeated allocation/deallocation overhead.
    • Tracing: Pre-allocating all tensors is required before trace capture.
    • Scratch buffers: Reusing the same memory across different operations or loop iterations.
    # Pre-allocate a scratch tensor
    scratch = ttnn.zeros_like(x, device=device, memory_config=ttnn.DRAM_MEMORY_CONFIG)
    
    # Reuse it across ops
    ttnn.relu(x, output_tensor=scratch)
    ttnn.add(scratch, bias, output_tensor=scratch)