burn_cubecl/kernel/attention/
base.rs1#[cfg(feature = "autotune")]
2use crate::kernel::attention::attention_autotune;
3use crate::{CubeBackend, ops::numeric::empty_device_dtype, tensor::CubeTensor};
4use burn_backend::cubecl::dtype_to_storage_type;
5use burn_backend::{
6 DType, Shape,
7 ops::{AttentionModuleOptions, attention::attention_fallback},
8};
9use cubek::attention::forward::{
10 definition::{
11 AccumulatorPrecision, AttentionGlobalTypes, AttentionOptions, AttentionSetupError,
12 },
13 launch,
14 routines::blackbox_accelerated::BlackboxAcceleratedStrategy,
15};
16
17#[derive(Debug)]
18pub enum AttentionStrategy {
20 FlashBlackboxAccelerated(BlackboxAcceleratedStrategy),
22
23 FlashUnit,
25
26 Fallback,
28
29 #[cfg(feature = "autotune")]
31 Autotune,
32}
33
34impl Default for AttentionStrategy {
35 fn default() -> Self {
36 #[cfg(feature = "autotune")]
38 return AttentionStrategy::Autotune;
39
40 #[cfg(not(feature = "autotune"))]
42 AttentionStrategy::Fallback
43 }
44}
45
46#[allow(clippy::too_many_arguments)]
47pub fn attention(
49 query: CubeTensor,
50 key: CubeTensor,
51 value: CubeTensor,
52 mask: Option<CubeTensor>,
53 attn_bias: Option<CubeTensor>,
54 options: AttentionModuleOptions,
55 strategy: AttentionStrategy,
56) -> Result<CubeTensor, AttentionSetupError> {
57 let flash = match strategy {
59 AttentionStrategy::FlashBlackboxAccelerated(strategy) => {
60 launch::Strategy::BlackboxAccelerated(launch::BlueprintStrategy::Inferred(strategy))
61 }
62 AttentionStrategy::FlashUnit => {
63 launch::Strategy::Unit(launch::BlueprintStrategy::Inferred(()))
64 }
65 AttentionStrategy::Fallback => {
66 return Ok(attention_fallback::<CubeBackend>(
67 query, key, value, mask, attn_bias, options,
68 ));
69 }
70 #[cfg(feature = "autotune")]
71 AttentionStrategy::Autotune => {
72 return Ok(attention_autotune(
73 query, key, value, mask, attn_bias, options,
74 ));
75 }
76 };
77
78 match flash_attention(
90 query.clone(),
91 key.clone(),
92 value.clone(),
93 mask.clone(),
94 attn_bias.clone(),
95 options,
96 flash,
97 ) {
98 Err(AttentionSetupError::InvalidConfig(_)) => Ok(attention_fallback::<CubeBackend>(
99 query, key, value, mask, attn_bias, options,
100 )),
101 other => other,
102 }
103}
104
105#[allow(clippy::too_many_arguments)]
106pub fn flash_attention(
108 query: CubeTensor,
109 key: CubeTensor,
110 value: CubeTensor,
111 mask: Option<CubeTensor>,
112 _attn_bias: Option<CubeTensor>,
113 options: AttentionModuleOptions,
114 strategy: launch::Strategy,
115) -> Result<CubeTensor, AttentionSetupError> {
116 let client = query.client.clone();
117 let out = init_attention_output(&query, &value);
118
119 let dtypes = AttentionGlobalTypes {
120 query: dtype_to_storage_type(query.dtype),
121 key: dtype_to_storage_type(key.dtype),
122 value: dtype_to_storage_type(value.dtype),
123 mask: dtype_to_storage_type(mask.as_ref().map(|m| m.dtype).unwrap_or(DType::U8)),
124 out: dtype_to_storage_type(out.dtype),
125 };
126
127 launch::launch_ref(
128 strategy,
129 &client,
130 query.binding(),
131 key.binding(),
132 value.binding(),
133 mask.map(|mask| mask.binding()),
134 out.clone().binding(),
135 &dtypes,
136 AttentionOptions {
137 causal: options.is_causal,
138 accumulator_precision: AccumulatorPrecision::Strict(cubecl::ir::ElemType::Float(
139 cubecl::ir::FloatKind::F32,
140 )),
141 },
142 )?;
143
144 Ok(out)
145}
146
147pub(crate) fn init_attention_output(query: &CubeTensor, value: &CubeTensor) -> CubeTensor {
148 let num_batches = query.meta.shape[0];
149 let num_heads = query.meta.shape[1];
150 let seq_q = query.meta.shape[2];
151 let val_dim = value.meta.shape[3];
152 let out_shape = Shape::new([num_batches, num_heads, seq_q, val_dim]);
153
154 empty_device_dtype(
155 query.client.clone(),
156 query.device.clone(),
157 out_shape,
158 query.dtype,
159 )
160}