-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdeformable_attention_plugin.cpp
More file actions
514 lines (417 loc) · 19.2 KB
/
Copy pathdeformable_attention_plugin.cpp
File metadata and controls
514 lines (417 loc) · 19.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
#include <NvInfer.h>
#include <NvInferPlugin.h>
#include <NvInferRuntime.h>
#include <NvInferRuntimePlugin.h>
#include <cstring>
#include <cuda_runtime.h>
#include <iostream>
#include <mutex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
// For half precision
#include <cuda_fp16.h>
#include <ATen/ATen.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAGuard.h>
#include <c10/cuda/CUDAStream.h>
#include <c10/util/TypeCast.h>
// Helper methods
void caughtError(std::exception const &e) { getLogger()->log(nvinfer1::ILogger::Severity::kINTERNAL_ERROR, e.what()); }
void logInfo(char const *msg) { getLogger()->log(nvinfer1::ILogger::Severity::kINFO, msg); }
void logVerbose(char const *msg) { getLogger()->log(nvinfer1::ILogger::Severity::kVERBOSE, msg); }
#define PLUGIN_ASSERT(val) reportAssertion((val), #val, __FILE__, __LINE__)
void reportAssertion(bool success, char const *msg, char const *file, int32_t line) {
if (!success) {
std::ostringstream stream;
stream << "Assertion failed: " << msg << std::endl
<< file << ':' << line << std::endl
<< "Aborting..." << std::endl;
getLogger()->log(nvinfer1::ILogger::Severity::kINTERNAL_ERROR, stream.str().c_str());
std::abort();
}
}
#define PLUGIN_VALIDATE(val) reportValidation((val), #val, __FILE__, __LINE__)
void reportValidation(bool success, char const *msg, char const *file, int32_t line) {
if (!success) {
std::ostringstream stream;
stream << "Validation failed: " << msg << std::endl << file << ':' << line << std::endl;
getLogger()->log(nvinfer1::ILogger::Severity::kINTERNAL_ERROR, stream.str().c_str());
}
}
at::ScalarType getATenDtype(nvinfer1::DataType dtype) {
switch (dtype) {
case nvinfer1::DataType::kFLOAT:
return at::kFloat;
case nvinfer1::DataType::kHALF:
return at::kHalf;
default:
throw std::runtime_error("Unsupported TensorRT data type");
}
}
namespace codetr {
extern void ms_deform_attn_forward_reference(const at::Tensor &value, const at::Tensor &spatial_shapes,
const at::Tensor &level_start_index, const at::Tensor &sampling_loc,
const at::Tensor &attn_weight, at::Tensor &output,
const int64_t im2col_step);
}
// ---------------------------------------------------------------------------
// DeformableAttentionPlugin
// ---------------------------------------------------------------------------
// In IPluginV3 interface, the plugin name, version, and name space must be
// specified for the plugin and plugin creator exactly the same.
constexpr char const *const kDEFORM_ATTN_PLUGIN_NAME{"DeformableAttentionPlugin"};
constexpr char const *const kDEFORM_ATTN_PLUGIN_VERSION{"1"};
constexpr char const *const kDEFORM_ATTN_PLUGIN_NAMESPACE{""};
namespace nvinfer1 {
namespace plugin {
struct DeformableAttentionParameters {
int64_t im2col_step;
};
class DeformableAttentionPlugin : public IPluginV3,
public IPluginV3OneCore,
public IPluginV3OneBuild,
public IPluginV3OneRuntime {
public:
// Construct from user param
explicit DeformableAttentionPlugin(DeformableAttentionParameters const ¶ms) : mParams{params} {
initFieldsToSerialize();
}
~DeformableAttentionPlugin() override = default;
// IPluginV3 Methods
IPluginCapability *getCapabilityInterface(PluginCapabilityType type) noexcept override {
try {
// Build capability: Refers to plugin attributes and behaviors that the
// plugin must exhibit for the TensorRT builder.
if (type == PluginCapabilityType::kBUILD) {
return static_cast<IPluginV3OneBuild *>(this);
}
// Runtime capability: Refers to plugin attributes and behaviors that the
// plugin must exhibit for it to be executable, either during auto-tuning
// in the TensorRT build phase or inference in the TensorRT runtime phase
if (type == PluginCapabilityType::kRUNTIME) {
return static_cast<IPluginV3OneRuntime *>(this);
}
// Core capability: Refers to plugin attributes and behaviors common to
// both the build and runtime phases of a plugin’s lifetime.
PLUGIN_ASSERT(type == PluginCapabilityType::kCORE);
return static_cast<IPluginV3OneCore *>(this);
} catch (std::exception const &e) {
caughtError(e);
}
return nullptr;
}
IPluginV3 *clone() noexcept override {
// It's possible to encounter errors during cloning.
// For example, if the memory to allocate is insufficient, exceptions can be
// thrown.
try {
IPluginV3 *const plugin{new DeformableAttentionPlugin{mParams}};
return plugin;
} catch (std::exception const &e) {
caughtError(e);
}
return nullptr;
}
// IPluginV3OneCore Methods
char const *getPluginName() const noexcept override { return kDEFORM_ATTN_PLUGIN_NAME; }
char const *getPluginVersion() const noexcept override { return kDEFORM_ATTN_PLUGIN_VERSION; }
char const *getPluginNamespace() const noexcept override { return kDEFORM_ATTN_PLUGIN_NAMESPACE; }
// IPluginV3OneBuild Methods
// Number of plugin outputs
int32_t getNbOutputs() const noexcept override { return 1; }
int32_t configurePlugin(DynamicPluginTensorDesc const *in, int32_t nbInputs, DynamicPluginTensorDesc const *out,
int32_t nbOutputs) noexcept override {
// Communicates the number of inputs and outputs, dimensions, and datatypes
// of all inputs and outputs, broadcast information for all inputs and
// outputs, the chosen plugin format, and maximum batch size. At this point,
// the plugin sets up its internal state and selects the most appropriate
// algorithm and data structures for the given configuration. Note: Resource
// allocation is not allowed in this API because it causes a resource leak.
// This member function will only be called during engine build time.
PLUGIN_ASSERT(nbInputs == 5);
PLUGIN_ASSERT(nbOutputs == 1);
// value (bs, num_keys, num_heads, dim_per_head)
PLUGIN_ASSERT(in[0].desc.dims.nbDims == 4);
// spatial_shapes (num_levels, 2)
PLUGIN_ASSERT(in[1].desc.dims.nbDims == 2);
// level_start_index (num_levels,)
PLUGIN_ASSERT(in[2].desc.dims.nbDims == 1);
// sampling_loc (bs, num_queries, num_heads, num_levels, num_points, 2)
PLUGIN_ASSERT(in[3].desc.dims.nbDims == 6);
// attn_weight (bs, num_queries, num_heads, num_levels, num_points)
PLUGIN_ASSERT(in[4].desc.dims.nbDims == 5);
// output (bs, num_queries, num_heads * dim_per_head)
PLUGIN_ASSERT(out[0].desc.dims.nbDims == 3);
// Check bs
auto bs = in[0].desc.dims.d[0];
PLUGIN_ASSERT(bs == in[3].desc.dims.d[0]);
PLUGIN_ASSERT(bs == in[4].desc.dims.d[0]);
PLUGIN_ASSERT(bs == out[0].desc.dims.d[0]);
// Check num_queries
auto num_queries = in[3].desc.dims.d[1];
PLUGIN_ASSERT(num_queries == in[4].desc.dims.d[1]);
PLUGIN_ASSERT(num_queries == out[0].desc.dims.d[1]);
// Check num_heads
auto num_heads = in[0].desc.dims.d[2];
PLUGIN_ASSERT(num_heads == in[3].desc.dims.d[2]);
PLUGIN_ASSERT(num_heads == in[4].desc.dims.d[2]);
// Check num_levels
auto num_levels = in[1].desc.dims.d[0];
PLUGIN_ASSERT(num_levels == in[2].desc.dims.d[0]);
PLUGIN_ASSERT(num_levels == in[3].desc.dims.d[3]);
PLUGIN_ASSERT(num_levels == in[4].desc.dims.d[3]);
// Check num_points
auto num_points = in[3].desc.dims.d[4];
PLUGIN_ASSERT(num_points == in[4].desc.dims.d[4]);
// Check output_dim
auto dim_per_head = in[0].desc.dims.d[3];
auto output_dim = num_heads * dim_per_head;
PLUGIN_ASSERT(output_dim == out[0].desc.dims.d[2]);
return 0;
}
bool supportsFormatCombination(int32_t pos, DynamicPluginTensorDesc const *inOut, int32_t nbInputs,
int32_t nbOutputs) noexcept override {
// For this method inputs are numbered 0..(nbInputs-1) and outputs are
// numbered nbInputs..(nbInputs+nbOutputs-1). Using this numbering, pos is
// an index into InOut, where 0 <= pos < nbInputs+nbOutputs.
PLUGIN_ASSERT(nbInputs == 5 && nbOutputs == 1 && pos < nbInputs + nbOutputs);
// 0: value float
// 1: spatial_shapes int64
// 2: level_start_index int64
// 3: sampling_loc float
// 4: attn_weight float
// 5: output float
const auto first_type = inOut[0].desc.type;
const auto type = inOut[pos].desc.type;
const auto format = inOut[pos].desc.format;
bool isValidCombination = format == TensorFormat::kLINEAR;
if (pos == 0 || pos == 3 || pos == 4 || pos == 5) {
isValidCombination &= (type == DataType::kFLOAT || type == DataType::kHALF);
isValidCombination &= type == first_type;
} else if (pos == 1 || pos == 2) {
isValidCombination &= type == DataType::kINT64;
}
return isValidCombination;
}
int32_t getOutputDataTypes(DataType *outputTypes, int32_t nbOutputs, DataType const *inputTypes,
int32_t nbInputs) const noexcept override {
PLUGIN_ASSERT(nbInputs == 5);
PLUGIN_ASSERT(nbOutputs == 1);
// The output type is the same as the input type.
outputTypes[0] = inputTypes[0];
return 0;
}
int32_t getOutputShapes(DimsExprs const *inputs, int32_t nbInputs, DimsExprs const *shapeInputs,
int32_t nbShapeInputs, DimsExprs *outputs, int32_t nbOutputs,
IExprBuilder &exprBuilder) noexcept override {
// inputs[0] -> value (bs, num_keys, num_heads, dim_per_head)
// inputs[3] -> sampling_loc (bs, num_queries, num_heads, num_levels,
// num_points, 2)
// outputs[0] -> output (bs, num_queries, num_heads * dim_per_head)
PLUGIN_ASSERT(nbInputs == 5);
PLUGIN_ASSERT(nbOutputs == 1);
PLUGIN_ASSERT(inputs != nullptr);
PLUGIN_ASSERT(inputs[0].nbDims == 4);
PLUGIN_ASSERT(inputs[3].nbDims == 6);
auto bs = inputs[0].d[0];
auto num_heads = inputs[0].d[2];
auto dim_per_head = inputs[0].d[3];
auto num_queries = inputs[3].d[1];
outputs[0].nbDims = 3;
outputs[0].d[0] = bs;
outputs[0].d[1] = num_queries;
outputs[0].d[2] = exprBuilder.operation(DimensionOperation::kPROD, *num_heads, *dim_per_head);
return 0;
}
// IPluginV3OneRuntime Methods
int32_t enqueue(PluginTensorDesc const *inputDesc, PluginTensorDesc const *outputDesc, void const *const *inputs,
void *const *outputs, void *workspace, cudaStream_t stream) noexcept override {
// void const* const* inputs: A pointer to a constant pointer to constant
// untyped data
// - array of input pointers where each element is a pointer to some input
// data
// - Neither the array elements (the pointers) nor the data they point to
// can be modified through this parameter
// - he parameter itself can be redirected to a different array
PLUGIN_ASSERT(inputs[0] != nullptr);
PLUGIN_ASSERT(inputs[1] != nullptr);
PLUGIN_ASSERT(inputs[2] != nullptr);
PLUGIN_ASSERT(inputs[3] != nullptr);
PLUGIN_ASSERT(inputs[4] != nullptr);
PLUGIN_ASSERT(outputs[0] != nullptr);
// We parse shapes from the stored dims
// [0] -> (bs, num_keys, num_heads, dim_per_head)
// [3] -> (bs, num_queries, num_heads, num_levels, num_points, 2)
const auto &value_dims = inputDesc[0].dims;
const auto &sampling_loc_dims = inputDesc[3].dims;
int bs = value_dims.d[0];
int num_keys = value_dims.d[1];
int num_heads = value_dims.d[2];
int dim_per_head = value_dims.d[3];
int num_queries = sampling_loc_dims.d[1];
int num_levels = sampling_loc_dims.d[3];
int num_points = sampling_loc_dims.d[4];
DataType dtype = inputDesc[0].type;
at::ScalarType scalar_type;
try {
scalar_type = getATenDtype(dtype);
} catch (const std::runtime_error &e) {
caughtError(e);
return 1;
}
auto options = at::TensorOptions().dtype(scalar_type).device(at::kCUDA);
auto options_long = at::TensorOptions().dtype(at::kLong).device(at::kCUDA);
// Wrap the provided cudaStream_t in a c10::cuda::CUDAStream
c10::cuda::CUDAStream cuda_stream = c10::cuda::getStreamFromExternal(stream, c10::cuda::current_device());
// Use CUDAStreamGuard to set this stream as current
c10::cuda::CUDAStreamGuard stream_guard(cuda_stream);
// We're telling PyTorch to use externally-managed memory provied by
// TensorRT
at::Tensor value = at::from_blob(const_cast<void *>(inputs[0]), {bs, num_keys, num_heads, dim_per_head}, options);
// reinterpret_cast<int64_t*>
at::Tensor spatial_shapes = at::from_blob(const_cast<void *>(inputs[1]), {num_levels, 2}, options_long);
// reinterpret_cast<int64_t*>
at::Tensor level_start_index = at::from_blob(const_cast<void *>(inputs[2]), {num_levels}, options_long);
at::Tensor sampling_loc =
at::from_blob(const_cast<void *>(inputs[3]), {bs, num_queries, num_heads, num_levels, num_points, 2}, options);
at::Tensor attn_weight =
at::from_blob(const_cast<void *>(inputs[4]), {bs, num_queries, num_heads, num_levels, num_points}, options);
// Wrap outputs[0] as the output tensor
at::Tensor trt_output =
at::from_blob(const_cast<void *>(outputs[0]), {bs, num_queries, num_heads * dim_per_head}, options);
codetr::ms_deform_attn_forward_reference(value, spatial_shapes, level_start_index, sampling_loc, attn_weight,
trt_output, mParams.im2col_step);
return 0;
}
// Called during both the build-phase and runtime phase before enqueue() to
// communicate the input and output shapes for the subsequent enqueue(). The
// output PluginTensorDesc will contain wildcards (-1) for any data-dependent
// dimensions specified through getOutputShapes().
int32_t onShapeChange(PluginTensorDesc const *in, int32_t nbInputs, PluginTensorDesc const *out,
int32_t nbOutputs) noexcept override {
return 0;
}
IPluginV3 *attachToContext(IPluginResourceContext *context) noexcept override { return clone(); }
PluginFieldCollection const *getFieldsToSerialize() noexcept override { return &mFCToSerialize; }
size_t getWorkspaceSize(DynamicPluginTensorDesc const *inputs, int32_t nbInputs,
DynamicPluginTensorDesc const *outputs, int32_t nbOutputs) const noexcept override {
return 0;
}
private:
DeformableAttentionParameters mParams;
std::vector<nvinfer1::PluginField> mDataToSerialize;
nvinfer1::PluginFieldCollection mFCToSerialize;
void initFieldsToSerialize() {
// Serialize DeformableAttentionParameters
mDataToSerialize.clear();
mDataToSerialize.emplace_back(nvinfer1::PluginField("parameters", &mParams, PluginFieldType::kUNKNOWN,
sizeof(DeformableAttentionParameters)));
mFCToSerialize.nbFields = mDataToSerialize.size();
mFCToSerialize.fields = mDataToSerialize.data();
}
};
// ---------------------------------------------------------------------------
// DeformableAttentionPluginCreator
// ---------------------------------------------------------------------------
class DeformableAttentionPluginCreator : public IPluginCreatorV3One {
public:
DeformableAttentionPluginCreator() {
mPluginAttributes.clear();
mPluginAttributes.emplace_back(nvinfer1::PluginField("im2col_step", nullptr, PluginFieldType::kINT64, 1));
mFC.nbFields = mPluginAttributes.size();
mFC.fields = mPluginAttributes.data();
}
~DeformableAttentionPluginCreator() override = default;
nvinfer1::PluginFieldCollection const *getFieldNames() noexcept override {
// This is only used in the build phase.
return &mFC;
}
IPluginV3 *createPlugin(char const *name, PluginFieldCollection const *fc, TensorRTPhase phase) noexcept override {
// The build phase and the deserialization phase are handled differently.
if (phase == TensorRTPhase::kBUILD) {
try {
int64_t im2col_step = 64; // default
for (int i = 0; i < fc->nbFields; ++i) {
const auto &f = fc->fields[i];
if (strcmp(f.name, "im2col_step") == 0 && f.type == PluginFieldType::kINT64) {
im2col_step = *static_cast<const int64_t *>(f.data);
}
}
DeformableAttentionParameters const params{im2col_step};
DeformableAttentionPlugin *const plugin{new DeformableAttentionPlugin{params}};
return plugin;
} catch (std::exception const &e) {
caughtError(e);
}
return nullptr;
} else if (phase == TensorRTPhase::kRUNTIME) {
// The attributes from the serialized plugin will be passed via fc.
try {
nvinfer1::PluginField const *fields{fc->fields};
int32_t nbFields{fc->nbFields};
PLUGIN_VALIDATE(nbFields == 1);
char const *attrName = fields[0].name;
PLUGIN_VALIDATE(!strcmp(attrName, "parameters"));
PLUGIN_VALIDATE(fields[0].type == nvinfer1::PluginFieldType::kUNKNOWN);
PLUGIN_VALIDATE(fields[0].length == sizeof(DeformableAttentionParameters));
DeformableAttentionParameters params{*(static_cast<DeformableAttentionParameters const *>(fields[0].data))};
DeformableAttentionPlugin *const plugin{new DeformableAttentionPlugin{params}};
return plugin;
} catch (std::exception const &e) {
caughtError(e);
}
return nullptr;
} else {
return nullptr;
}
return nullptr;
}
char const *getPluginNamespace() const noexcept override { return kDEFORM_ATTN_PLUGIN_NAMESPACE; }
char const *getPluginName() const noexcept override { return kDEFORM_ATTN_PLUGIN_NAME; }
char const *getPluginVersion() const noexcept override { return kDEFORM_ATTN_PLUGIN_VERSION; }
private:
nvinfer1::PluginFieldCollection mFC;
std::vector<nvinfer1::PluginField> mPluginAttributes;
};
// Register the plugin with TensorRT's global registry so it can be discovered
REGISTER_TENSORRT_PLUGIN(DeformableAttentionPluginCreator);
} // namespace plugin
} // namespace nvinfer1
class ThreadSafeLoggerFinder {
public:
ThreadSafeLoggerFinder() = default;
// Set the logger finder.
void setLoggerFinder(nvinfer1::ILoggerFinder *finder) {
std::lock_guard<std::mutex> lk(mMutex);
if (mLoggerFinder == nullptr && finder != nullptr) {
mLoggerFinder = finder;
}
}
// Get the logger.
nvinfer1::ILogger *getLogger() noexcept {
std::lock_guard<std::mutex> lk(mMutex);
if (mLoggerFinder != nullptr) {
return mLoggerFinder->findLogger();
}
return nullptr;
}
private:
nvinfer1::ILoggerFinder *mLoggerFinder{nullptr};
std::mutex mMutex;
};
ThreadSafeLoggerFinder gLoggerFinder;
// Not exposing this function to the user to get the plugin logger for the
// moment. Can switch the plugin logger to this in the future.
// ILogger* getPluginLogger()
// {
// return gLoggerFinder.getLogger();
// }
extern "C" void setLoggerFinder(nvinfer1::ILoggerFinder *finder) { gLoggerFinder.setLoggerFinder(finder); }
extern "C" nvinfer1::IPluginCreatorInterface *const *getPluginCreators(int32_t &nbCreators) {
nbCreators = 1;
static nvinfer1::plugin::DeformableAttentionPluginCreator creator{};
static nvinfer1::IPluginCreatorInterface *const pluginCreatorList[] = {&creator};
return pluginCreatorList;
}