Instructions to use replicate/flash-attn4 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Kernels
How to use replicate/flash-attn4 with Kernels:
# !pip install kernels from kernels import get_kernel # a version (or an explicit revision) is required; see the "Files and versions" tab for the available ones kernel = get_kernel("replicate/flash-attn4", version=1) - Notebooks
- Google Colab
- Kaggle
Download build/torch-cuda/copy_utils.py from replicate/flash-attn4: direct link, hf CLI and curl.
- Browser
- Download file 12.7 kB
-
https://huggingface.co/replicate/flash-attn4/resolve/main/build/torch-cuda/copy_utils.py
- Command line
-
hf download hf://replicate/flash-attn4/build/torch-cuda/copy_utils.py
-
curl -L -o copy_utils.py https://huggingface.co/replicate/flash-attn4/resolve/main/build/torch-cuda/copy_utils.py
12.7 kB
| # Copyright (c) 2025, Wentao Guo, Ted Zadouri, Tri Dao. | |
| import math | |
| from typing import Optional, Type, Callable | |
| import cutlass | |
| import cutlass.cute as cute | |
| from cutlass import Float32, Int32, const_expr | |
| from cutlass.cute.nvgpu import cpasync | |
| import cutlass.utils.blackwell_helpers as sm100_utils | |
| from cutlass.cutlass_dsl import T, dsl_user_op | |
| from cutlass._mlir.dialects import llvm | |
| import cutlass.pipeline | |
| def cvt_copy( | |
| atom: cute.CopyAtom, | |
| src: cute.Tensor, | |
| dst: cute.Tensor, | |
| *, | |
| pred: Optional[cute.Tensor] = None, | |
| loc=None, | |
| ip=None, | |
| **kwargs, | |
| ) -> None: | |
| assert isinstance(src.iterator, cute.Pointer) and src.memspace == cute.AddressSpace.rmem | |
| if const_expr(src.element_type != dst.element_type): | |
| src_cvt = cute.make_fragment_like(src, dst.element_type, loc=loc, ip=ip) | |
| src_cvt.store(src.load().to(dst.element_type)) | |
| src = src_cvt | |
| cute.copy(atom, src, dst, pred=pred, loc=loc, ip=ip, **kwargs) | |
| def load_s2r(src: cute.Tensor, *, loc=None, ip=None) -> cute.Tensor: | |
| dst = cute.make_fragment_like(src, src.element_type, loc=loc, ip=ip) | |
| cute.autovec_copy(src, dst, loc=loc, ip=ip) | |
| return dst | |
| def get_copy_atom( | |
| dtype: Type[cutlass.Numeric], num_copy_elems: int, is_async: bool = False, *, loc=None, ip=None | |
| ) -> cute.CopyAtom: | |
| num_copy_bits = const_expr(min(128, num_copy_elems * dtype.width)) | |
| copy_op = cpasync.CopyG2SOp() if is_async else cute.nvgpu.CopyUniversalOp() | |
| return cute.make_copy_atom(copy_op, dtype, num_bits_per_copy=num_copy_bits) | |
| def make_tmem_copy( | |
| tmem_copy_atom: cute.CopyAtom, num_wg: int = 1, *, loc=None, ip=None | |
| ) -> cute.CopyAtom: | |
| num_dp, num_bits, num_rep, _ = sm100_utils.get_tmem_copy_properties(tmem_copy_atom) | |
| assert num_dp == 32 | |
| assert num_bits == 32 | |
| tiler_mn = (cute.make_layout((128 * num_rep * num_wg // 32, 32), stride=(32, 1)),) | |
| layout_tv = cute.make_layout( | |
| ((32, 4, num_wg), (num_rep, 32)), stride=((0, 1, 4 * num_rep), (4, 4 * num_rep * num_wg)) | |
| ) | |
| return cute.make_tiled_copy(tmem_copy_atom, layout_tv, tiler_mn) | |
| def copy( | |
| src: cute.Tensor, | |
| dst: cute.Tensor, | |
| *, | |
| pred: Optional[cute.Tensor] = None, | |
| num_copy_elems: int = 1, | |
| is_async: bool = False, | |
| loc=None, | |
| ip=None, | |
| **kwargs, | |
| ) -> None: | |
| copy_atom = get_copy_atom(src.element_type, num_copy_elems, is_async) | |
| cute.copy(copy_atom, src, dst, pred=pred, loc=loc, ip=ip, **kwargs) | |
| def tiled_copy_1d( | |
| dtype: Type[cutlass.Numeric], num_threads: int, num_copy_elems: int = 1, is_async: bool = False | |
| ) -> cute.TiledCopy: | |
| num_copy_bits = num_copy_elems * dtype.width | |
| copy_op = cpasync.CopyG2SOp() if is_async else cute.nvgpu.CopyUniversalOp() | |
| copy_atom = cute.make_copy_atom(copy_op, dtype, num_bits_per_copy=num_copy_bits) | |
| thr_layout = cute.make_layout(num_threads) | |
| val_layout = cute.make_layout(num_copy_elems) | |
| return cute.make_tiled_copy_tv(copy_atom, thr_layout, val_layout) | |
| def tiled_copy_2d( | |
| dtype: Type[cutlass.Numeric], major_mode_size: int, num_threads: int, is_async: bool = False | |
| ) -> cute.TiledCopy: | |
| num_copy_bits = math.gcd(major_mode_size, 128 // dtype.width) * dtype.width | |
| copy_elems = num_copy_bits // dtype.width | |
| copy_op = cpasync.CopyG2SOp() if is_async else cute.nvgpu.CopyUniversalOp() | |
| copy_atom = cute.make_copy_atom(copy_op, dtype, num_bits_per_copy=num_copy_bits) | |
| gmem_threads_per_row = major_mode_size // copy_elems | |
| assert num_threads % gmem_threads_per_row == 0 | |
| thr_layout = cute.make_ordered_layout( | |
| (num_threads // gmem_threads_per_row, gmem_threads_per_row), | |
| order=(1, 0), | |
| ) | |
| val_layout = cute.make_layout((1, copy_elems)) | |
| return cute.make_tiled_copy_tv(copy_atom, thr_layout, val_layout) | |
| def atomic_add_fp32x4( | |
| a: Float32, b: Float32, c: Float32, d: Float32, gmem_ptr: cute.Pointer, *, loc=None, ip=None | |
| ) -> None: | |
| gmem_ptr_i64 = gmem_ptr.toint(loc=loc, ip=ip).ir_value() | |
| # cache_hint = cutlass.Int64(0x12F0000000000000) | |
| llvm.inline_asm( | |
| None, | |
| [ | |
| gmem_ptr_i64, | |
| Float32(a).ir_value(loc=loc, ip=ip), | |
| Float32(b).ir_value(loc=loc, ip=ip), | |
| Float32(c).ir_value(loc=loc, ip=ip), | |
| Float32(d).ir_value(loc=loc, ip=ip), | |
| ], | |
| # [gmem_ptr_i64, Float32(a).ir_value(loc=loc, ip=ip), cache_hint.ir_value()], | |
| "{\n\t" | |
| # ".reg .b128 abcd;\n\t" | |
| # "mov.b128 abcd, {$1, $2, $3, $4};\n\t" | |
| ".reg .v4 .f32 abcd;\n\t" | |
| # "mov.b128 abcd, {$1, $2, $3, $4};\n\t" | |
| "mov.f32 abcd.x, $1;\n\t" | |
| "mov.f32 abcd.y, $2;\n\t" | |
| "mov.f32 abcd.z, $3;\n\t" | |
| "mov.f32 abcd.w, $4;\n\t" | |
| "red.global.add.v4.f32 [$0], abcd;\n\t" | |
| # "red.global.add.L2::cache_hint.v4.f32 [$0], abcd, 0x14F0000000000000;\n\t" | |
| "}\n", | |
| # "red.global.add.L2::cache_hint.f32 [$0], $1, 0x12F0000000000000;", | |
| # "red.global.add.L2::cache_hint.f32 [$0], $1, $2;", | |
| "l,f,f,f,f", | |
| # "l,f,l", | |
| has_side_effects=True, | |
| is_align_stack=False, | |
| asm_dialect=llvm.AsmDialect.AD_ATT, | |
| ) | |
| def set_block_rank( | |
| smem_ptr: cute.Pointer, peer_cta_rank_in_cluster: Int32, *, loc=None, ip=None | |
| ) -> Int32: | |
| """Map the given smem pointer to the address at another CTA rank in the cluster.""" | |
| smem_ptr_i32 = smem_ptr.toint(loc=loc, ip=ip).ir_value() | |
| return Int32( | |
| llvm.inline_asm( | |
| T.i32(), | |
| [smem_ptr_i32, peer_cta_rank_in_cluster.ir_value()], | |
| "mapa.shared::cluster.u32 $0, $1, $2;", | |
| "=r,r,r", | |
| has_side_effects=False, | |
| is_align_stack=False, | |
| asm_dialect=llvm.AsmDialect.AD_ATT, | |
| ) | |
| ) | |
| def store_shared_remote_fp32x4( | |
| a: Float32, | |
| b: Float32, | |
| c: Float32, | |
| d: Float32, | |
| smem_ptr: cute.Pointer, | |
| mbar_ptr: cute.Pointer, | |
| peer_cta_rank_in_cluster: Int32, | |
| *, | |
| loc=None, | |
| ip=None, | |
| ) -> None: | |
| remote_smem_ptr_i32 = set_block_rank( | |
| smem_ptr, peer_cta_rank_in_cluster, loc=loc, ip=ip | |
| ).ir_value() | |
| remote_mbar_ptr_i32 = set_block_rank( | |
| mbar_ptr, peer_cta_rank_in_cluster, loc=loc, ip=ip | |
| ).ir_value() | |
| llvm.inline_asm( | |
| None, | |
| [ | |
| remote_smem_ptr_i32, | |
| remote_mbar_ptr_i32, | |
| Float32(a).ir_value(loc=loc, ip=ip), | |
| Float32(b).ir_value(loc=loc, ip=ip), | |
| Float32(c).ir_value(loc=loc, ip=ip), | |
| Float32(d).ir_value(loc=loc, ip=ip), | |
| ], | |
| "{\n\t" | |
| ".reg .v4 .f32 abcd;\n\t" | |
| "mov.f32 abcd.x, $2;\n\t" | |
| "mov.f32 abcd.y, $3;\n\t" | |
| "mov.f32 abcd.z, $4;\n\t" | |
| "mov.f32 abcd.w, $5;\n\t" | |
| "st.async.shared::cluster.mbarrier::complete_tx::bytes.v4.f32 [$0], abcd, [$1];\n\t" | |
| "}\n", | |
| "r,r,f,f,f,f", | |
| has_side_effects=True, | |
| is_align_stack=False, | |
| asm_dialect=llvm.AsmDialect.AD_ATT, | |
| ) | |
| def cpasync_bulk_s2cluster( | |
| smem_src_ptr: cute.Pointer, | |
| smem_dst_ptr: cute.Pointer, | |
| mbar_ptr: cute.Pointer, | |
| size: int | Int32, | |
| peer_cta_rank_in_cluster: Int32, | |
| *, | |
| loc=None, | |
| ip=None, | |
| ): | |
| smem_src_ptr_i32 = smem_src_ptr.toint(loc=loc, ip=ip).ir_value() | |
| smem_dst_ptr_i32 = set_block_rank( | |
| smem_dst_ptr, peer_cta_rank_in_cluster, loc=loc, ip=ip | |
| ).ir_value() | |
| mbar_ptr_i32 = set_block_rank(mbar_ptr, peer_cta_rank_in_cluster, loc=loc, ip=ip).ir_value() | |
| llvm.inline_asm( | |
| None, | |
| [ | |
| smem_dst_ptr_i32, | |
| smem_src_ptr_i32, | |
| mbar_ptr_i32, | |
| Int32(size).ir_value(loc=loc, ip=ip), | |
| ], | |
| "cp.async.bulk.shared::cluster.shared::cta.mbarrier::complete_tx::bytes [$0], [$1], $3, [$2];", | |
| "r,r,r,r", | |
| has_side_effects=True, | |
| is_align_stack=False, | |
| asm_dialect=llvm.AsmDialect.AD_ATT, | |
| ) | |
| def cpasync_bulk_g2s( | |
| gmem_ptr: cute.Pointer, | |
| smem_ptr: cute.Pointer, | |
| tma_bar_ptr: cute.Pointer, | |
| size: int | Int32, | |
| *, | |
| loc=None, | |
| ip=None, | |
| ): | |
| gmem_ptr_i64 = gmem_ptr.toint(loc=loc, ip=ip).ir_value() | |
| smem_ptr_i32 = smem_ptr.toint(loc=loc, ip=ip).ir_value() | |
| mbar_ptr_i32 = tma_bar_ptr.toint(loc=loc, ip=ip).ir_value() | |
| llvm.inline_asm( | |
| None, | |
| [gmem_ptr_i64, smem_ptr_i32, mbar_ptr_i32, Int32(size).ir_value()], | |
| "cp.async.bulk.shared::cta.global.mbarrier::complete_tx::bytes [$1], [$0], $3, [$2];", | |
| "l,r,r,r", | |
| has_side_effects=True, | |
| is_align_stack=False, | |
| asm_dialect=llvm.AsmDialect.AD_ATT, | |
| ) | |
| def cpasync_reduce_bulk_add_f32( | |
| smem_ptr: cute.Pointer, | |
| gmem_ptr: cute.Pointer, | |
| store_bytes: int | Int32, | |
| *, | |
| loc=None, | |
| ip=None, | |
| ): | |
| smem_ptr_i32 = smem_ptr.toint(loc=loc, ip=ip).ir_value() | |
| # cache_hint = cutlass.Int64(0x14F0000000000000) # EVICT_LAST | |
| llvm.inline_asm( | |
| None, | |
| [gmem_ptr.llvm_ptr, smem_ptr_i32, Int32(store_bytes).ir_value()], | |
| "cp.reduce.async.bulk.global.shared::cta.bulk_group.add.f32 [$0], [$1], $2;", | |
| "l,r,r", | |
| # [gmem_ptr.llvm_ptr, smem_ptr_i32, Int32(store_bytes).ir_value(), cache_hint.ir_value()], | |
| # "cp.reduce.async.bulk.global.shared::cta.bulk_group.L2::cache_hint.add.f32 [$0], [$1], $2, $3;", | |
| # "l,r,r,l", | |
| has_side_effects=True, | |
| is_align_stack=False, | |
| asm_dialect=llvm.AsmDialect.AD_ATT, | |
| ) | |
| def cpasync_bulk_get_copy_fn( | |
| src_tensor: cute.Tensor, | |
| dst_tensor: cute.Tensor, | |
| single_stage: bool = False, | |
| **kwargs, | |
| ) -> Callable: | |
| # src_is_smem = const_expr( | |
| # isinstance(src_tensor.iterator, cute.Pointer) | |
| # and src_tensor.memspace == cute.AddressSpace.smem | |
| # ) | |
| group_rank_src = const_expr(cute.rank(src_tensor) - (1 if not single_stage else 0)) | |
| group_rank_dst = const_expr(cute.rank(dst_tensor) - (1 if not single_stage else 0)) | |
| # ((atom_v, rest_v), STAGE), ((atom_v, rest_v), RestK) | |
| src = cute.group_modes(src_tensor, 0, group_rank_src) | |
| dst = cute.group_modes(dst_tensor, 0, group_rank_dst) | |
| def copy_bulk(src_idx, dst_idx, **new_kwargs): | |
| size = const_expr(cute.size(src.shape[:-1]) * src.element_type.width // 8) | |
| cpasync_bulk_g2s( | |
| src[None, src_idx].iterator, | |
| dst[None, dst_idx].iterator, | |
| size=size, | |
| **new_kwargs, | |
| **kwargs, | |
| ) | |
| def copy_bulk_single_stage(**new_kwargs): | |
| size = const_expr(cute.size(src.shape) * src.element_type.width // 8) | |
| cpasync_bulk_g2s(src.iterator, dst.iterator, size=size, **new_kwargs, **kwargs) | |
| return copy_bulk if const_expr(not single_stage) else copy_bulk_single_stage | |
| def tma_get_copy_fn( | |
| atom: cute.CopyAtom, | |
| cta_coord: cute.Coord, | |
| cta_layout: cute.Layout, | |
| src_tensor: cute.Tensor, | |
| dst_tensor: cute.Tensor, | |
| filter_zeros: bool = False, | |
| single_stage: bool = False, | |
| **kwargs, | |
| ) -> Callable: | |
| src_is_smem = const_expr( | |
| isinstance(src_tensor.iterator, cute.Pointer) | |
| and src_tensor.memspace == cute.AddressSpace.smem | |
| ) | |
| smem_tensor, gmem_tensor = (src_tensor, dst_tensor) if src_is_smem else (dst_tensor, src_tensor) | |
| group_rank_smem = const_expr(cute.rank(smem_tensor) - (1 if not single_stage else 0)) | |
| group_rank_gmem = const_expr(cute.rank(gmem_tensor) - (1 if not single_stage else 0)) | |
| # ((atom_v, rest_v), STAGE), ((atom_v, rest_v), RestK) | |
| s, g = cpasync.tma_partition( | |
| atom, | |
| cta_coord, | |
| cta_layout, | |
| cute.group_modes(smem_tensor, 0, group_rank_smem), | |
| cute.group_modes(gmem_tensor, 0, group_rank_gmem), | |
| ) | |
| if const_expr(filter_zeros): | |
| s = cute.filter_zeros(s) | |
| g = cute.filter_zeros(g) | |
| src, dst = (s, g) if src_is_smem else (g, s) | |
| def copy_tma(src_idx, dst_idx, **new_kwargs): | |
| cute.copy(atom, src[None, src_idx], dst[None, dst_idx], **new_kwargs, **kwargs) | |
| def copy_tma_single_stage(**new_kwargs): | |
| cute.copy(atom, src, dst, **new_kwargs, **kwargs) | |
| return (copy_tma if const_expr(not single_stage) else copy_tma_single_stage), s, g | |
| def tma_producer_copy_fn(copy: Callable, pipeline: cutlass.pipeline.PipelineAsync): | |
| def copy_fn(src_idx, producer_state: cutlass.pipeline.PipelineState, **new_kwargs): | |
| copy( | |
| src_idx=src_idx, | |
| dst_idx=producer_state.index, | |
| tma_bar_ptr=pipeline.producer_get_barrier(producer_state), | |
| **new_kwargs, | |
| ) | |
| return copy_fn | |