diff --git a/v3/gpu-systems/flash-attention-triton/flash-attention-triton_SOLN.ipynb b/v3/gpu-systems/flash-attention-triton/flash-attention-triton_SOLN.ipynb index ba0e100..daa867d 100644 --- a/v3/gpu-systems/flash-attention-triton/flash-attention-triton_SOLN.ipynb +++ b/v3/gpu-systems/flash-attention-triton/flash-attention-triton_SOLN.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "a14a63c5", "metadata": {}, "source": [ "# Implement FlashAttention-2 in Triton — Solution\n", @@ -79,9 +80,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, + "id": "e1ce38d2", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Triton is available!\n", + "CUDA available: True\n" + ] + } + ], "source": [ "import torch\n", "import torch.nn.functional as F\n", @@ -102,9 +113,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, + "id": "cdf6b637", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Q shape: torch.Size([2, 4, 128, 64])\n", + "K shape: torch.Size([2, 4, 128, 64])\n", + "V shape: torch.Size([2, 4, 128, 64])\n", + "\n", + "Full attention matrix would be: 2 x 4 x 128 x 128\n", + "= 512.0 KB\n" + ] + } + ], "source": [ "# Test data\n", "torch.manual_seed(42)\n", @@ -129,7 +154,8 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, + "id": "bdb3477a", "metadata": {}, "outputs": [], "source": [ @@ -152,7 +178,8 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, + "id": "072c33a3", "metadata": {}, "outputs": [], "source": [ @@ -232,82 +259,121 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, + "id": "422ab2cd", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Triton flash attention kernel defined.\n" + ] + } + ], "source": [ - "# Triton kernel reference (requires GPU to actually run)\n", - "# This shows what the GPU kernel would look like\n", - "\n", "if TRITON_AVAILABLE:\n", + " DEVICE = triton.runtime.driver.active.get_active_torch_device()\n", + " \n", " @triton.jit\n", " def flash_attention_kernel(\n", - " Q_ptr, K_ptr, V_ptr, O_ptr,\n", - " stride_qb, stride_qh, stride_qn, stride_qd,\n", - " stride_kb, stride_kh, stride_kn, stride_kd,\n", - " stride_vb, stride_vh, stride_vn, stride_vd,\n", - " stride_ob, stride_oh, stride_on, stride_od,\n", - " N, D: tl.constexpr,\n", - " BLOCK_Q: tl.constexpr, BLOCK_KV: tl.constexpr,\n", - " ):\n", - " \"\"\"\n", - " FlashAttention-2 Triton kernel.\n", - " Each program processes one (batch, head, q_block) tile.\n", - " \"\"\"\n", - " # Program IDs\n", + " Q_block_ptr,\n", + " K_block_ptr,\n", + " V_block_ptr,\n", + " output_ptr,\n", + " Q_strideBH, Q_strideN, Q_strideD,\n", + " K_strideBH, K_strideN, K_strideD,\n", + " V_strideBH, V_strideN, V_strideD,\n", + " output_strideBH, output_strideN, output_strideD,\n", + " N:tl.constexpr, \n", + " D:tl.constexpr, \n", + " BLOCK_Q: tl.constexpr,\n", + " BLOCK_KV: tl.constexpr,\n", + " ): \n", + " scale = 1.0 / tl.sqrt(float(D)) \n", + " n_blocks = tl.cdiv(N , BLOCK_KV) \n", + " running_max = tl.full((BLOCK_Q,1), float('-inf'), dtype=tl.float32)\n", + " running_sum = tl.zeros((BLOCK_Q,1),dtype=tl.float32)\n", + " running_output = tl.zeros((BLOCK_Q,D),dtype=tl.float32)\n", + "\n", " q_block_idx = tl.program_id(0)\n", - " bh_idx = tl.program_id(1) # combined batch*head index\n", - " \n", - " scale = 1.0 / tl.sqrt(float(D))\n", - " \n", - " # Offsets for this Q block\n", - " q_offset = q_block_idx * BLOCK_Q\n", - " q_range = q_offset + tl.arange(0, BLOCK_Q)\n", + " bh_idx = tl.program_id(1)\n", + " \n", + " q_start = Q_block_ptr + Q_strideBH * bh_idx\n", + " q_range = q_block_idx * BLOCK_Q + tl.arange(0, BLOCK_Q)\n", " d_range = tl.arange(0, D)\n", - " q_mask = q_range[:, None] < N\n", - " \n", - " # Load Q block into SRAM\n", - " q_ptrs = Q_ptr + bh_idx * stride_qh + q_range[:, None] * stride_qn + d_range[None, :] * stride_qd\n", - " Q_block = tl.load(q_ptrs, mask=q_mask, other=0.0)\n", + " q_offsets = q_range[:,None]*Q_strideN+d_range[None,:]*Q_strideD\n", + " q_mask = q_range[:,None]