Kernels
danieldk HF Staff commited on
Commit
b5697a4
·
verified ·
1 Parent(s): eb9eb65

Benchmarks uploaded using `kernels`.

Browse files
Files changed (1) hide show
  1. benchmarks/benchmark.py +250 -0
benchmarks/benchmark.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn.functional as F
3
+
4
+ from kernels.benchmark import Benchmark
5
+
6
+
7
+ def ms_deform_attn_reference(
8
+ value: torch.Tensor,
9
+ spatial_shapes: torch.Tensor,
10
+ level_start_index: torch.Tensor,
11
+ sampling_locations: torch.Tensor,
12
+ attention_weights: torch.Tensor,
13
+ ) -> torch.Tensor:
14
+ batch, _, num_heads, channels = value.shape
15
+ _, num_query, _, num_levels, num_points, _ = sampling_locations.shape
16
+
17
+ # Split value by levels
18
+ value_list = []
19
+ for level_id in range(num_levels):
20
+ H, W = spatial_shapes[level_id]
21
+ start_idx = level_start_index[level_id]
22
+ end_idx = (
23
+ level_start_index[level_id + 1]
24
+ if level_id < num_levels - 1
25
+ else value.shape[1]
26
+ )
27
+ # (batch, H*W, num_heads, channels) -> (batch, num_heads, channels, H, W)
28
+ value_level = value[:, start_idx:end_idx, :, :].view(
29
+ batch, H, W, num_heads, channels
30
+ )
31
+ value_level = value_level.permute(0, 3, 4, 1, 2).contiguous()
32
+ value_list.append(value_level)
33
+
34
+ # Sample from each level
35
+ output = torch.zeros(
36
+ batch, num_query, num_heads, channels, device=value.device, dtype=value.dtype
37
+ )
38
+
39
+ for level_id in range(num_levels):
40
+ H, W = spatial_shapes[level_id]
41
+ value_level = value_list[level_id] # (batch, num_heads, channels, H, W)
42
+
43
+ # Get sampling locations for this level: (batch, num_query, num_heads, num_points, 2)
44
+ sampling_loc_level = sampling_locations[:, :, :, level_id, :, :]
45
+
46
+ # Convert from [0, 1] to [-1, 1] for grid_sample
47
+ grid = (
48
+ 2.0 * sampling_loc_level - 1.0
49
+ ) # (batch, num_query, num_heads, num_points, 2)
50
+
51
+ # Reshape for grid_sample: need (batch * num_heads, channels, H, W) and (batch * num_heads, num_query, num_points, 2)
52
+ value_level = value_level.view(batch * num_heads, channels, H.item(), W.item())
53
+ grid = grid.permute(
54
+ 0, 2, 1, 3, 4
55
+ ).contiguous() # (batch, num_heads, num_query, num_points, 2)
56
+ grid = grid.view(batch * num_heads, num_query, num_points, 2)
57
+
58
+ # Sample: output is (batch * num_heads, channels, num_query, num_points)
59
+ sampled = F.grid_sample(
60
+ value_level,
61
+ grid,
62
+ mode="bilinear",
63
+ padding_mode="zeros",
64
+ align_corners=False,
65
+ )
66
+
67
+ # Reshape back: (batch, num_heads, channels, num_query, num_points)
68
+ sampled = sampled.view(batch, num_heads, channels, num_query, num_points)
69
+ # -> (batch, num_query, num_heads, num_points, channels)
70
+ sampled = sampled.permute(0, 3, 1, 4, 2).contiguous()
71
+
72
+ # Get attention weights for this level: (batch, num_query, num_heads, num_points)
73
+ attn_level = attention_weights[:, :, :, level_id, :]
74
+
75
+ # Weighted sum over points: (batch, num_query, num_heads, channels)
76
+ output += (sampled * attn_level.unsqueeze(-1)).sum(dim=3)
77
+
78
+ # Reshape to (batch, num_query, num_heads * channels)
79
+ output = output.view(batch, num_query, num_heads * channels)
80
+ return output
81
+
82
+
83
+ class MSDeformAttnBenchmark(Benchmark):
84
+ seed: int = 42
85
+
86
+ def setup(self):
87
+ batch = 2
88
+ num_heads = 8
89
+ channels = 32 # embed_dim = num_heads * channels = 256
90
+ num_levels = 4
91
+ num_query = 300
92
+ num_points = 4
93
+ im2col_step = 64
94
+
95
+ # Spatial shapes for 4 levels: 64x64, 32x32, 16x16, 8x8
96
+ spatial_shapes = torch.tensor(
97
+ [[64, 64], [32, 32], [16, 16], [8, 8]],
98
+ dtype=torch.int64,
99
+ device=self.device,
100
+ )
101
+ # Calculate spatial_size = sum of H*W for all levels
102
+ spatial_size = (64 * 64) + (32 * 32) + (16 * 16) + (8 * 8) # 5440
103
+
104
+ # Level start indices
105
+ level_start_index = torch.tensor(
106
+ [0, 64 * 64, 64 * 64 + 32 * 32, 64 * 64 + 32 * 32 + 16 * 16],
107
+ dtype=torch.int64,
108
+ device=self.device,
109
+ )
110
+
111
+ self.value = torch.randn(
112
+ batch,
113
+ spatial_size,
114
+ num_heads,
115
+ channels,
116
+ device=self.device,
117
+ dtype=torch.float32,
118
+ )
119
+ self.spatial_shapes = spatial_shapes
120
+ self.level_start_index = level_start_index
121
+ self.sampling_loc = torch.rand(
122
+ batch,
123
+ num_query,
124
+ num_heads,
125
+ num_levels,
126
+ num_points,
127
+ 2,
128
+ device=self.device,
129
+ dtype=torch.float32,
130
+ )
131
+ self.attn_weight = torch.rand(
132
+ batch,
133
+ num_query,
134
+ num_heads,
135
+ num_levels,
136
+ num_points,
137
+ device=self.device,
138
+ dtype=torch.float32,
139
+ )
140
+ # Normalize attention weights
141
+ self.attn_weight = self.attn_weight / self.attn_weight.sum(-1, keepdim=True)
142
+ self.im2col_step = im2col_step
143
+
144
+ self.out = torch.empty(
145
+ batch,
146
+ num_query,
147
+ num_heads * channels,
148
+ device=self.device,
149
+ dtype=torch.float32,
150
+ )
151
+
152
+ def benchmark_forward(self):
153
+ self.out = self.kernel.ms_deform_attn_forward(
154
+ self.value,
155
+ self.spatial_shapes,
156
+ self.level_start_index,
157
+ self.sampling_loc,
158
+ self.attn_weight,
159
+ self.im2col_step,
160
+ )
161
+
162
+ def verify_forward(self) -> torch.Tensor:
163
+ return ms_deform_attn_reference(
164
+ self.value,
165
+ self.spatial_shapes,
166
+ self.level_start_index,
167
+ self.sampling_loc,
168
+ self.attn_weight,
169
+ )
170
+
171
+ def setup_large(self):
172
+ batch = 8
173
+ num_heads = 8
174
+ channels = 32
175
+ num_levels = 4
176
+ num_query = 900
177
+ num_points = 4
178
+ im2col_step = 64
179
+
180
+ spatial_shapes = torch.tensor(
181
+ [[64, 64], [32, 32], [16, 16], [8, 8]],
182
+ dtype=torch.int64,
183
+ device=self.device,
184
+ )
185
+ spatial_size = (64 * 64) + (32 * 32) + (16 * 16) + (8 * 8)
186
+
187
+ level_start_index = torch.tensor(
188
+ [0, 64 * 64, 64 * 64 + 32 * 32, 64 * 64 + 32 * 32 + 16 * 16],
189
+ dtype=torch.int64,
190
+ device=self.device,
191
+ )
192
+
193
+ self.value = torch.randn(
194
+ batch,
195
+ spatial_size,
196
+ num_heads,
197
+ channels,
198
+ device=self.device,
199
+ dtype=torch.float32,
200
+ )
201
+ self.spatial_shapes = spatial_shapes
202
+ self.level_start_index = level_start_index
203
+ self.sampling_loc = torch.rand(
204
+ batch,
205
+ num_query,
206
+ num_heads,
207
+ num_levels,
208
+ num_points,
209
+ 2,
210
+ device=self.device,
211
+ dtype=torch.float32,
212
+ )
213
+ self.attn_weight = torch.rand(
214
+ batch,
215
+ num_query,
216
+ num_heads,
217
+ num_levels,
218
+ num_points,
219
+ device=self.device,
220
+ dtype=torch.float32,
221
+ )
222
+ self.attn_weight = self.attn_weight / self.attn_weight.sum(-1, keepdim=True)
223
+ self.im2col_step = im2col_step
224
+
225
+ self.out = torch.empty(
226
+ batch,
227
+ num_query,
228
+ num_heads * channels,
229
+ device=self.device,
230
+ dtype=torch.float32,
231
+ )
232
+
233
+ def benchmark_large(self):
234
+ self.out = self.kernel.ms_deform_attn_forward(
235
+ self.value,
236
+ self.spatial_shapes,
237
+ self.level_start_index,
238
+ self.sampling_loc,
239
+ self.attn_weight,
240
+ self.im2col_step,
241
+ )
242
+
243
+ def verify_large(self) -> torch.Tensor:
244
+ return ms_deform_attn_reference(
245
+ self.value,
246
+ self.spatial_shapes,
247
+ self.level_start_index,
248
+ self.sampling_loc,
249
+ self.attn_weight,
250
+ )