FlashQLA Documentation

repository·main·Indexed 20 days ago

https://github.com/qwenlm/flashqla

A high-performance linear attention kernel library built on TileLang and optimized for GDN Chunked Prefill. It provides speedups for forward and backward passes on NVIDIA Hopper and Blackwell architectures (SM90, SM100, SM103, SM120). The library features a high-level API via `chunk_gated_delta_rule` and low-level APIs `chunk_gated_delta_rule_fwd` and `chunk_gated_delta_rule_bwd` for custom training loops.

Tokens
1.8K
Snippets
7
Records
8
Agent score
20%

What's inside FlashQLA

  1. Run Tests and Benchmarks

    main

    To verify the installation and run performance comparisons:

    Run Unit Tests:

    python -m pytest tests/test_gdr_unit.py -v

    Run Profiling: Note: Requires flash_linear_attention==0.5.0 for comparison.

    pip install flash_linear_attention==0.5.0
    python profile/profile_gdr.py --set develop
    python profile/profile_gdr.py --set develop --skip-bwd

    Run Benchmarks: Note: Requires flash_linear_attention==0.5.0 and flashinfer-python==0.6.13 for comparison.

    pip install flash_linear_attention==0.5.0 flashinfer-python==0.6.13
    python benchmark/bench_gated_delta_rule.py
  2. Install FlashQLA

    main

    FlashQLA can be installed via pip or built from source.

    Requirements

    • GPU Architecture: SM90, SM100, SM103, or SM120
    • CUDA: 12.8 or above
    • PyTorch: 2.8 or above
    # Install via pip
    pip install flash-qla
    
    # Or build from source
    git clone https://github.com/QwenLM/FlashQLA.git
    cd FlashQLA
    pip install -v .
  3. Use the High-level API: `chunk_gated_delta_rule`

    main

    The high-level API provides a single function to compute the gated delta rule for chunked prefill. This is the recommended way to use FlashQLA for standard forward passes.

    Arguments:

    • q: Query tensor of shape [B, T, H_q, K]
    • k: Key tensor of shape [B, T, H_q, K]
    • v: Value tensor of shape [B, T, H_v, V]
    • g: Gate tensor of shape [B, T, H_v]
    • beta: Beta tensor of shape [B, T, H_v]
    • scale: Scaling factor
    • initial_state (optional): Initial state tensor of shape [B, H_v, K, V]
    • output_final_state: Boolean indicating whether to return the final state
    • cu_seqlens (optional): Cumulative sequence lengths for variable-length sequences
    import torch
    from flash_qla import chunk_gated_delta_rule
    
    o, final_state = chunk_gated_delta_rule(
        q=q,          # [B, T, H_q, K]
        k=k,          # [B, T, H_q, K]
        v=v,          # [B, T, H_v, V]
        g=g,          # [B, T, H_v]
        beta=beta,    # [B, T, H_v]
        scale=scale,
        initial_state=initial_state,   # optional, [B, H_v, K, V]
        output_final_state=True,
        cu_seqlens=cu_seqlens,         # optional, for variable-length sequences
    )
  4. Use the Low-level API for separate Forward and Backward passes

    main

    If you need to manually manage the forward and backward steps (e.g., for custom training loops), use chunk_gated_delta_rule_fwd and chunk_gated_delta_rule_bwd.

    Forward Pass (chunk_gated_delta_rule_fwd): Returns g, A, o, h, final_state.

    Backward Pass (chunk_gated_delta_rule_bwd): Requires the outputs from the forward pass (like A and do) to compute gradients. Returns dq, dk, dv, db, dg, dh0.

    from flash_qla import chunk_gated_delta_rule_fwd, chunk_gated_delta_rule_bwd
    
    # Forward
    g, A, o, h, final_state = chunk_gated_delta_rule_fwd(
        q, k, v, g, beta, scale=scale, initial_state=h0, cu_seqlens=cu_seqlens
    )
    
    # Backward
    dq, dk, dv, db, dg, dh0 = chunk_gated_delta_rule_bwd(
        q, k, v, g, beta, A, do, dht=dht, scale=scale, initial_state=h0, cu_seqlens=cu_seqlens
    )
  5. Prepare chunk indices with prepare_chunk_indices

    main

    The prepare_chunk_indices function generates indices used for chunked kernel execution. It takes cu_seqlens and a chunk_size and returns a 2D tensor where each row contains [start_index_of_chunk, chunk_index]. This is used to map tokens to their respective chunks.

    import torch
    from flash_qla.utils import prepare_chunk_indices
    
    cu_seqlens = torch.tensor([0, 4, 8], dtype=torch.long)
    chunk_size = 2
    indices = prepare_chunk_indices(cu_seqlens, chunk_size)
    # Returns a tensor mapping tokens to chunk starts and IDs
  6. Prepare lens from cumulative sequence lengths with prepare_lens

    main

    The prepare_lens function calculates the individual sequence lengths from a tensor of cumulative sequence lengths (cu_seqlens). It uses the @tensor_cache decorator to optimize repeated calls with the same input.

    import torch
    from flash_qla.utils import prepare_lens
    
    # Example: cu_seqlens representing sequences of length 3 and 5
    cu_seqlens = torch.tensor([0, 3, 8], dtype=torch.long)
    lens = prepare_lens(cu_seqlens)
    # lens will be tensor([3, 5])
  7. Prepare chunk offsets with prepare_chunk_offsets

    main

    The prepare_chunk_offsets function calculates the starting positions of chunks in a flattened buffer. It returns a tuple containing:

    1. chunk_offsets: A torch.LongTensor of the same shape as cu_seqlens indicating the offset for each sequence.
    2. total_chunks: An integer representing the total number of chunks across all sequences.

    This is essential for indexing into global chunk buffers during kernel execution.

    import torch
    from flash_qla.utils import prepare_chunk_offsets
    
    cu_seqlens = torch.tensor([0, 4, 8], dtype=torch.long)
    chunk_size = 2
    offsets, total_chunks = prepare_chunk_offsets(cu_seqlens, chunk_size)
  8. Use the @tensor_cache decorator for tensor-based functions

    main

    The @tensor_cache decorator provides single-entry caching for functions that take torch.Tensor inputs. It stores the most recent result for a specific set of input tensors. If the function is called again with the exact same tensor objects (checked via identity is), it returns the cached result instead of recomputing. This is useful for avoiding redundant kernel preparation steps in iterative loops.

    import torch
    from flash_qla.utils import tensor_cache
    
    @tensor_cache
    def my_tensor_func(x: torch.Tensor) -> torch.Tensor:
        return x * 2
    
    a = torch.randn(10)
    b = my_tensor_func(a)  # Computes
    c = my_tensor_func(a)  # Returns cached result