chore: import upstream snapshot with attribution
Docker Image CI / build-ubuntu2004 (push) Waiting to run
Docker Image CI / build-ubuntu2004 (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.31 FATAL_ERROR)
|
||||
|
||||
# Default to building for the local GPU. Must be set BEFORE project(),
|
||||
# otherwise project()'s CUDA language enablement initializes
|
||||
# CMAKE_CUDA_ARCHITECTURES to the toolkit's lowest supported SM
|
||||
# (typically sm_75), and any later `if(NOT DEFINED)` guard misfires.
|
||||
# `all` is not a substitute: it caps at sm_90 for CUDA >= 12.0
|
||||
# (https://github.com/Kitware/CMake/blob/v3.31.6/Modules/Internal/CMakeCUDAArchitecturesAll.cmake#L65-L77),
|
||||
# missing Blackwell/Drive-Thor/Spark, and the PTX fallback fails on
|
||||
# embedded/safety drivers (e.g. DriveOS QNX) that lack JIT.
|
||||
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
|
||||
set(CMAKE_CUDA_ARCHITECTURES native)
|
||||
endif()
|
||||
|
||||
project(CircPadPlugin LANGUAGES CXX CUDA)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
if(NOT MSVC)
|
||||
# Enable all compile warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic -Wno-deprecated-declarations")
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler -Wno-deprecated-declarations")
|
||||
endif()
|
||||
|
||||
# Sets variable to a value if variable is unset.
|
||||
macro(set_ifndef var val)
|
||||
if(NOT ${var})
|
||||
set(${var} ${val})
|
||||
endif()
|
||||
message(STATUS "Configurable variable ${var} set to ${${var}}")
|
||||
endmacro()
|
||||
|
||||
# -------- CONFIGURATION --------
|
||||
if(NOT MSVC)
|
||||
set_ifndef(TRT_LIB /usr/lib/x86_64-linux-gnu)
|
||||
set_ifndef(TRT_INCLUDE /usr/include/x86_64-linux-gnu)
|
||||
endif()
|
||||
|
||||
message("\nThe following variables are derived from the values of the previous variables unless provided explicitly:\n")
|
||||
|
||||
find_library(
|
||||
_NVINFER_LIB nvinfer
|
||||
HINTS ${TRT_LIB}
|
||||
PATH_SUFFIXES lib lib64)
|
||||
set_ifndef(NVINFER_LIB ${_NVINFER_LIB})
|
||||
|
||||
# -------- BUILDING --------
|
||||
|
||||
add_library(circ_pad_plugin SHARED ${CMAKE_SOURCE_DIR}/circ_plugin_cpp/circ_pad_plugin.cu)
|
||||
target_include_directories(
|
||||
circ_pad_plugin
|
||||
PUBLIC ${CUDAToolkit_INCLUDE_DIRS}
|
||||
PUBLIC ${TRT_INCLUDE})
|
||||
|
||||
set_property(TARGET circ_pad_plugin PROPERTY CUDA_STANDARD 17)
|
||||
|
||||
target_link_libraries(circ_pad_plugin PRIVATE ${NVINFER_LIB})
|
||||
target_link_libraries(circ_pad_plugin PRIVATE CUDA::cuda_driver)
|
||||
@@ -0,0 +1,171 @@
|
||||
# Python-based TRT Plugins
|
||||
|
||||
This is a sample to showcase Python-based plugin definitions in TRT. No changes to existing TRT APIs have been made
|
||||
to deliver this feature, so using the updated bindings should not break any existing code.
|
||||
|
||||
## Introduction
|
||||
|
||||
Until TRT 9.1, plugin implementations could only be done through the TRT C++ API. To use a plugin in a Python app, one had to
|
||||
- Implement plugin in C++ and build into a shared library
|
||||
- Load plugin lib and register plugin creator (statically or dynamically)
|
||||
- Retrieve plugin creator and create plugin instance through the respective Python API
|
||||
|
||||
The following design considerations were followed in creating bindings to allow Python-based plugin definitions:
|
||||
- Zero additional C++ code shall be required to implement, integrate and run a plugin within TensorRT
|
||||
- Offer the flexibility to implement the kernel(s) for the plugin through any method of choice
|
||||
- Many libraries have sprung up to provide CUDA kernel support with AOT/JIT compilation
|
||||
- Numba, OpenAI Triton, CuPy etc.
|
||||
- Could even do without explicit kernels (e.g. leverage PyTorch functional op)
|
||||
|
||||
- Will only support `IPluginV2DynamicExt` and `IPluginV3`-based plugins
|
||||
- Other plugin interfaces (except `IPluginV2IOExt`) are deprecated since TRT 8.5
|
||||
|
||||
With these bindings, plugins can be implemented and integrated to TRT purely with Python.
|
||||
|
||||
## Setting Up The Build Environment
|
||||
|
||||
To build and install the bindings, follow the instructions in `$TRT_OSSPATH/python/README.md`.
|
||||
|
||||
Then install the requisite packages
|
||||
```bash
|
||||
cd $TRT_OSSPATH/samples/python/trt_python_plugin
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
Install `cupy-cuda11x` instead if testing on a CUDA 11.x environment.
|
||||
|
||||
# TensorRT Plugin API for Python
|
||||
|
||||
Implementing a TRT plugin in Python is similar to C++ in that implementation of `IPluginV2DynamicExt`+`IPluginCreator` or `IPluginV3`+`IPluginCreatorV3One` is necessary. Refer to the TensorRT Python API reference for a concise description.
|
||||
|
||||
## Differences in C++ and Python APIs for `IPluginV2DynamicExt`
|
||||
The interface methods in Python have mostly similar APIs to their C++ counterparts, except for `serialize()` and `enqueue()`.
|
||||
- While the C++ API for `serialize()` is `void serialize (void *buffer)` where the plugin writes to the passed-in `buffer`, the Python API is `serialize(self) -> bytes`, where the implementation of the method is expected to return a bytes object containing a serialized representation of the plugin object.
|
||||
- In `enqueue()`, the device pointers for input and output tensors are passed as their `intptr_t` casts. Since these buffers are created and owned by TRT, care must be taken when writing to them from the Python side.
|
||||
- No bindings yet for `attachToContext()` and `detachFromContext()` which are not pure virtual.
|
||||
|
||||
# Running the sample: Circular padding plugin
|
||||
|
||||
This sample contains a circular padding plugin, where the `enqueue` has been implemented with various frameworks for writing kernels or executing GPU ops (torch).
|
||||
|
||||
Each script accepts a command-line argument to choose precision from either FP32 or FP16. e.g.
|
||||
```bash
|
||||
python3 circ_pad_plugin_cuda_python.py --precision fp32 # fp32 or fp16
|
||||
```
|
||||
|
||||
## Circular padding
|
||||
|
||||
Circular padding is useful for ops like circular convolution in deep learning. The following image denotes how the original image (red) is circular padded once (green) and twice (blue):
|
||||
|
||||

|
||||
|
||||
The plugin shall have the following characteristics:
|
||||
- Input: 4-dimensional input (e.g. NxCxHxW)
|
||||
- Attribute(s): m-dimensional parameter `pads` where $m$ is even and $m/2 \le 4$. `pads` denotes the amount of padding to apply before and after each of the $m/2$ last dimensions of the input tensor.
|
||||
- Output: Padded tensor. Shape depends on `pads`.
|
||||
|
||||
## Baseline: Using a C++ plugin
|
||||
|
||||
To establish a baseline, we first demonstrate a C++ plugin implementing circular padding. The relevant files can be found in the `circ_plugin_cpp` folder: the included `CMakeLists.txt` can be used to build the shared library `libcirc_pad_plugin.so` / `circ_pad_plugin.dll`.
|
||||
|
||||
```bash
|
||||
cd $TRT_OSSPATH/samples/python/trt_python_plugin
|
||||
mkdir build && pushd build
|
||||
cmake .. && make -j
|
||||
popd
|
||||
python3 circ_pad_plugin_cpp.py --plugin-lib build/libcirc_pad_plugin.so
|
||||
```
|
||||
|
||||
## Python plugin: cuda-python
|
||||
|
||||
The cuda-python based implementation can be found in `circ_pad_plugin_cuda_python.py`. `cuda.nvrtc` is used to JIT compile a C/C++-based kernel, which is provided as a string. The compiled kernel is then launched through cuda-python's `cuda.cuLaunchKernel`.
|
||||
|
||||
`circ_pad_plugin_cuda_python.py` demonstrates an ONNX-based workflow: `circ_pad_plugin_inetdef_cuda_python.py` demonstrates a workflow where the model is constructed through `INetworkDefinition`.
|
||||
|
||||
## Python plugin: CuPy
|
||||
|
||||
The CuPy-based implementation can be found in `circ_pad_plugin_cupy.py`. CuPy's `RawKernel` class has been used to provide the C/C++-based kernel implementation as a string. CuPy will JIT compile the kernel.
|
||||
|
||||
## Python plugin: Triton (valid only on Linux)
|
||||
|
||||
The same plugin can be implemented with a Triton-based kernel as well. The only other change would be to `enqueue`. The entire implementation can be found in `circ_pad_plugin_triton.py`.
|
||||
|
||||
Some remarks:
|
||||
- Triton also allows for JIT-able kernels.
|
||||
- CuPy device arrays cannot be passed into Triton kernels directly -- only Torch arrays are accepted. However, we can use `torch.as_tensor()` to get around this constraint.
|
||||
- Triton does not seem to allow the specification of a CUDA stream.
|
||||
|
||||
## Python plugin: Numba
|
||||
|
||||
The Numba implementation can be found in `circ_pad_plugin_numba.py`. Some remarks:
|
||||
- Numba also allows for JIT-able kernels.
|
||||
- CuPy device arrays can be passed into Numba kernels without issue since CuPy arrays implement `__cuda_array_interface__`.
|
||||
|
||||
## Python plugin: Torch
|
||||
|
||||
The flexibility of the `enqueue()` interface means that it is not always necessary to implement a custom kernel. In this case, PyTorch's [torch.nn.functional.pad](https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html) offers the exact same capability we want, so we can use that inside `enqueue()`, as in `circ_pad_plugin_torch.py`.
|
||||
|
||||
## Python plugin: Multi-tactic, Multi-plugin (based on IPluginV3)
|
||||
|
||||
The entire implementation can be found in `circ_pad_plugin_multi_tactic.py`.
|
||||
|
||||
### Custom tactics
|
||||
|
||||
When multiple options are available to compute the same op, and it's not possible to reliably predict which one will be faster for the expected input shapes/types or the target platform,
|
||||
it is useful to ask TensorRT to time all available options during the build stage. In V2 plugins, TensorRT would only time different type/format combinations supported by the plugin, but
|
||||
V3 plugins allow users to specify any number of custom tactics to time also (in addition to type/format combinations).
|
||||
|
||||
In this example, we specify two custom tactics: PyTorch's [torch.nn.functional.pad](https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html) and a custom kernel written using OpenAI Triton.
|
||||
|
||||
It is possible to advertise tactics specific to a format combination. e.g. In this sample, we can support both tactics for FP32 I/O, and only support the OpenAI Triton tactic for FP16 I/O. To achieve this, return in `get_valid_tactics()` the set of tactics `T(f)` supported by the plugin for the format combination `f` indicated by the immediately preceding call to `configure_plugin()`. To enable this behavior in this sample, pass the flag `--per-format-tactics`.
|
||||
|
||||
### Multiple plugins instances
|
||||
|
||||
Imagine that you expect to have multiple instances of the same plugin in your network, which would operate on separate inputs, but where the input and output shapes/formats, as well
|
||||
as other determining plugin attributes would be the same. With V2 plugins, TensorRT would time all such plugin instances during the engine build -- however, this would be inefficient because the only salient difference between those instances are the values of the input tensors.
|
||||
|
||||
To communicate to TensorRT that you would like the timing for similar plugin instances to be cached, V3 plugins allow for the specification of a timing cache ID. The timing cache ID
|
||||
should only capture timing determinants extraneous to plugin I/O, like their shapes and formats. Typically, this would be the values of any plugin attributes that might be different
|
||||
between the plugin instances.
|
||||
|
||||
In this example,
|
||||
- The shape of the `pads` parameter affects timing, but only as far as it affects the output shape. Therefore, the timing cache ID could be an empty string.
|
||||
- We consider a scenario where there are two circular padding plugin instances with identical configurations. Therefore, only a single instance should be timed by TensorRT.
|
||||
This can be verified by inspecting the log.
|
||||
|
||||
# Limitations
|
||||
|
||||
- Plugins cannot be serialized into the engine (in contrast to `IBuilderConfig::setPluginsToSerialize()`)
|
||||
- Plugin class and Plugin Creator class must exist in the module where the engine is deserialized
|
||||
- The engine / ONNX model cannot be run from outside Python (e.g. with `trtexec`)
|
||||
- This functionality is possible to implement but comes at the cost of embedding the Python interpreter to the TRT runtime / the binary loading the engine
|
||||
- (For `IPluginV2DynamicExt` only) No bindings yet for `attachToContext()` and `detachFromContext()` which are not pure virtual.
|
||||
- `circ_pad_plugin_torch.py` may work on aarch64 platforms but is unsupported.
|
||||
|
||||
# FAQ
|
||||
|
||||
1. What are the performance impacts of a Python-based plugin versus a C++ one?
|
||||
|
||||
In preliminary testing, the Python overhead was found to be very minimal to negligible. In fact, if the kernels were compiled AOT (instead of JIT) the CuPY and Triton
|
||||
versions of the plugin were as performant as the C++ one. However, with Numba, there seems to be a significant kernel launch overhead.
|
||||
|
||||
2. Can I deploy a TRT engine including a Python plugin in a runtime environment without Python?
|
||||
|
||||
No. There is no way to fully embed a Python plugin into the engine that allows for it to be executed without the need for Python during inference time.
|
||||
|
||||
This design principle is what allows for the `enqueue()` to be implemented in any framework of choice.
|
||||
|
||||
# License
|
||||
|
||||
For terms and conditions for use, reproduction, and distribution, see the [TensorRT Software License Agreement](https://docs.nvidia.com/deeplearning/sdk/tensorrt-sla/index.html) documentation.
|
||||
|
||||
# Changelog
|
||||
|
||||
October 2025: Migrate to strongly typed APIs.
|
||||
|
||||
August 2025: Removed support for Python versions < 3.10.
|
||||
|
||||
July 2023: Initial release of this sample
|
||||
|
||||
# Known issues
|
||||
|
||||
There are no known issues in this sample
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
+97
@@ -0,0 +1,97 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import onnx_graphsurgeon as gs
|
||||
import numpy as np
|
||||
import onnx
|
||||
import ctypes
|
||||
|
||||
import tensorrt as trt
|
||||
from polygraphy.backend.trt import (
|
||||
CreateConfig,
|
||||
EngineFromNetwork,
|
||||
NetworkFromOnnxPath,
|
||||
TrtRunner,
|
||||
)
|
||||
|
||||
|
||||
def parseArgs():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Options for Circular Padding plugin C++ example"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--precision",
|
||||
type=str,
|
||||
default="fp32",
|
||||
choices=["fp32", "fp16"],
|
||||
help="Precision to use for plugin",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--plugin-lib",
|
||||
type=str,
|
||||
help="Path to the Circular Padding plugin lib",
|
||||
required=True,
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = parseArgs()
|
||||
|
||||
handle = ctypes.CDLL(args.plugin_lib)
|
||||
if not handle:
|
||||
raise RuntimeError("Could not load Circular Padding plugin library")
|
||||
|
||||
precision = np.float32 if args.precision == "fp32" else np.float16
|
||||
inp_shape = (10, 3, 32, 32)
|
||||
X = np.random.normal(size=inp_shape).astype(precision)
|
||||
|
||||
pads = (1, 1, 1, 1)
|
||||
|
||||
# create ONNX model
|
||||
onnx_path = f"test_CircPadPlugin_cpp_{args.precision}.onnx"
|
||||
inputA = gs.Variable(name="X", shape=inp_shape, dtype=precision)
|
||||
Y = gs.Variable(name="Y", dtype=precision)
|
||||
myPluginNode = gs.Node(
|
||||
name="CircPadPlugin",
|
||||
op="CircPadPlugin",
|
||||
inputs=[inputA],
|
||||
outputs=[Y],
|
||||
attrs={"pads": pads},
|
||||
)
|
||||
graph = gs.Graph(nodes=[myPluginNode], inputs=[inputA], outputs=[Y], opset=16)
|
||||
onnx.save(gs.export_onnx(graph), onnx_path)
|
||||
|
||||
# build engine
|
||||
build_engine = EngineFromNetwork(
|
||||
NetworkFromOnnxPath(onnx_path, strongly_typed=True), CreateConfig()
|
||||
)
|
||||
|
||||
Y_ref = np.pad(X, [[0, 0], [0, 0], [pads[0], pads[1]], [pads[2], pads[3]]], "wrap")
|
||||
# Run
|
||||
with TrtRunner(build_engine, "trt_runner") as runner:
|
||||
outputs = runner.infer({"X": X})
|
||||
Y = outputs["Y"]
|
||||
|
||||
if np.allclose(Y, Y_ref):
|
||||
print("Inference result correct!")
|
||||
else:
|
||||
print("Inference result incorrect!")
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import onnx_graphsurgeon as gs
|
||||
import numpy as np
|
||||
import onnx
|
||||
import sys
|
||||
import os
|
||||
|
||||
import tensorrt as trt
|
||||
from polygraphy.backend.trt import (
|
||||
CreateConfig,
|
||||
EngineFromNetwork,
|
||||
NetworkFromOnnxPath,
|
||||
TrtRunner,
|
||||
)
|
||||
from polygraphy.json import to_json, from_json
|
||||
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
|
||||
from plugin_utils import cuda_call, KernelHelper, parseArgs, CudaCtxManager, cuda_init, cuda_get_device, cuda_memcpy_htod
|
||||
import common_runtime as common
|
||||
from cuda.bindings import driver as cuda
|
||||
|
||||
circ_pad_half_kernel = r"""
|
||||
#include <cuda_fp16.h>
|
||||
extern "C" __global__
|
||||
void circ_pad_half(half const* X, int const* all_pads, int const* orig_dims, half* Y, int const* Y_shape, int Y_len) {
|
||||
int index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int stride = blockDim.x * gridDim.x;
|
||||
|
||||
for(int i = index; i < Y_len; i += stride)
|
||||
{
|
||||
int i3 = i % Y_shape[3];
|
||||
int i2 = (i / Y_shape[3]) % Y_shape[2];
|
||||
int i1 = (i / Y_shape[3] / Y_shape[2]) % Y_shape[1];
|
||||
int i0 = i / Y_shape[3] / Y_shape[2] / Y_shape[1];
|
||||
|
||||
int j0 = (i0 - all_pads[0] + orig_dims[0]) % orig_dims[0];
|
||||
int j1 = (i1 - all_pads[2] + orig_dims[1]) % orig_dims[1];
|
||||
int j2 = (i2 - all_pads[4] + orig_dims[2]) % orig_dims[2];
|
||||
int j3 = (i3 - all_pads[6] + orig_dims[3]) % orig_dims[3];
|
||||
|
||||
Y[i] = X[
|
||||
orig_dims[3] * orig_dims[2] * orig_dims[1] * j0
|
||||
+ orig_dims[3] * orig_dims[2] * j1
|
||||
+ orig_dims[3] * j2
|
||||
+ j3
|
||||
];
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
circ_pad_float_kernel = r"""
|
||||
extern "C" __global__
|
||||
void circ_pad_float(float const* X, int const* all_pads, int const* orig_dims, float* Y, int const* Y_shape, int Y_len) {
|
||||
int index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int stride = blockDim.x * gridDim.x;
|
||||
|
||||
for(int i = index; i < Y_len; i += stride)
|
||||
{
|
||||
int i3 = i % Y_shape[3];
|
||||
int i2 = (i / Y_shape[3]) % Y_shape[2];
|
||||
int i1 = (i / Y_shape[3] / Y_shape[2]) % Y_shape[1];
|
||||
int i0 = i / Y_shape[3] / Y_shape[2] / Y_shape[1];
|
||||
|
||||
int j0 = (i0 - all_pads[0] + orig_dims[0]) % orig_dims[0];
|
||||
int j1 = (i1 - all_pads[2] + orig_dims[1]) % orig_dims[1];
|
||||
int j2 = (i2 - all_pads[4] + orig_dims[2]) % orig_dims[2];
|
||||
int j3 = (i3 - all_pads[6] + orig_dims[3]) % orig_dims[3];
|
||||
|
||||
Y[i] = X[
|
||||
orig_dims[3] * orig_dims[2] * orig_dims[1] * j0
|
||||
+ orig_dims[3] * orig_dims[2] * j1
|
||||
+ orig_dims[3] * j2
|
||||
+ j3
|
||||
];
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class CircPadPlugin(trt.IPluginV2DynamicExt):
|
||||
def __init__(self, fc=None):
|
||||
trt.IPluginV2DynamicExt.__init__(self)
|
||||
self.pads = []
|
||||
self.X_shape = []
|
||||
self.N = 0
|
||||
|
||||
self.all_pads_d = None
|
||||
self.orig_dims_d = None
|
||||
self.Y_shape_d = None
|
||||
|
||||
self.num_outputs = 1
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_type = "CircPadPlugin"
|
||||
self.plugin_version = "1"
|
||||
|
||||
self.cuDevice = None
|
||||
|
||||
if fc is not None:
|
||||
assert set([f.name for f in fc]) == set(
|
||||
["pads", "N"]
|
||||
), "Field collection invalid"
|
||||
for f in fc:
|
||||
if f.name == "pads":
|
||||
self.pads = f.data
|
||||
elif f.name == "N":
|
||||
self.N = int(f.data)
|
||||
|
||||
def initialize(self):
|
||||
self.cuDevice = cuda_get_device(0)
|
||||
trt.get_plugin_registry().acquire_plugin_resource(
|
||||
"cuda_ctx", CudaCtxManager(self.cuDevice)
|
||||
)
|
||||
self.all_pads_d = common.DeviceMem(np.int32().itemsize * self.N * 2)
|
||||
self.orig_dims_d = common.DeviceMem(np.int32().itemsize * self.N)
|
||||
self.Y_shape_d = common.DeviceMem(np.int32().itemsize * self.N)
|
||||
|
||||
def get_output_datatype(self, index, input_types):
|
||||
return input_types[0]
|
||||
|
||||
def get_output_dimensions(self, output_index, inputs, exprBuilder):
|
||||
|
||||
output_dims = trt.DimsExprs(inputs[0])
|
||||
|
||||
for i in range(np.size(self.pads) // 2):
|
||||
output_dims[len(output_dims) - i - 1] = exprBuilder.operation(
|
||||
trt.DimensionOperation.SUM,
|
||||
inputs[0][len(output_dims) - i - 1],
|
||||
exprBuilder.constant(self.pads[i * 2] + self.pads[i * 2 + 1]),
|
||||
)
|
||||
|
||||
return output_dims
|
||||
|
||||
def serialize(self):
|
||||
return to_json({"pads": self.pads, "N": self.N})
|
||||
|
||||
def configure_plugin(self, inp, out):
|
||||
X_dims = inp[0].desc.dims
|
||||
self.X_shape = np.zeros((len(X_dims),))
|
||||
for i in range(len(X_dims)):
|
||||
self.X_shape[i] = X_dims[i]
|
||||
|
||||
all_pads = np.zeros((self.N * 2,), dtype=np.int32)
|
||||
orig_dims = np.array(self.X_shape, dtype=np.int32)
|
||||
out_dims = np.array(self.X_shape, dtype=np.int32)
|
||||
|
||||
for i in range(np.size(self.pads) // 2):
|
||||
out_dims[self.N - i - 1] += self.pads[i * 2] + self.pads[i * 2 + 1]
|
||||
all_pads[self.N * 2 - 2 * i - 2] = self.pads[i * 2]
|
||||
all_pads[self.N * 2 - 2 * i - 1] = self.pads[i * 2 + 1]
|
||||
|
||||
# Copy vectors from host memory to device memory
|
||||
if self.all_pads_d:
|
||||
cuda_memcpy_htod(self.all_pads_d.device_ptr, all_pads)
|
||||
if self.orig_dims_d:
|
||||
cuda_memcpy_htod(self.orig_dims_d.device_ptr, orig_dims)
|
||||
if self.Y_shape_d:
|
||||
cuda_memcpy_htod(self.Y_shape_d.device_ptr, out_dims)
|
||||
|
||||
self.Y_len_d = np.prod(out_dims)
|
||||
|
||||
def supports_format_combination(self, pos, in_out, num_inputs):
|
||||
assert num_inputs == 1
|
||||
assert pos < len(in_out)
|
||||
|
||||
desc = in_out[pos]
|
||||
if desc.format != trt.TensorFormat.LINEAR:
|
||||
return False
|
||||
|
||||
# first input should be float16 or float32
|
||||
if pos == 0:
|
||||
return desc.type == trt.DataType.FLOAT or desc.type == trt.DataType.HALF
|
||||
|
||||
# output should have the same type as the input
|
||||
if pos == 1:
|
||||
return in_out[0].type == desc.type
|
||||
|
||||
assert False
|
||||
|
||||
def enqueue(self, input_desc, output_desc, inputs, outputs, workspace, stream):
|
||||
|
||||
inp_dtype = trt.nptype(input_desc[0].type)
|
||||
|
||||
blockSize = 256
|
||||
numBlocks = int((np.prod(np.array(self.X_shape)) + blockSize - 1) // blockSize)
|
||||
|
||||
da = np.array([inputs[0]], dtype=np.uint64)
|
||||
dc = np.array([outputs[0]], dtype=np.uint64)
|
||||
|
||||
d_all_pads = np.array([int(self.all_pads_d.device_ptr)], dtype=np.uint64)
|
||||
d_orig_dims = np.array([int(self.orig_dims_d.device_ptr)], dtype=np.uint64)
|
||||
d_Y_shape = np.array([int(self.Y_shape_d.device_ptr)], dtype=np.uint64)
|
||||
Y_len = np.array(self.Y_len_d, dtype=np.uint32)
|
||||
|
||||
args = [da, d_all_pads, d_orig_dims, dc, d_Y_shape, Y_len]
|
||||
kernelArgs = np.array([arg.ctypes.data for arg in args], dtype=np.uint64)
|
||||
|
||||
stream_ptr = np.array([stream], dtype=np.uint64)
|
||||
|
||||
if inp_dtype == np.float32:
|
||||
kernelHelper = KernelHelper(circ_pad_float_kernel, int(self.cuDevice))
|
||||
_circ_pad_float_kernel = kernelHelper.getFunction(b"circ_pad_float")
|
||||
cuda_call(
|
||||
cuda.cuLaunchKernel(
|
||||
_circ_pad_float_kernel,
|
||||
numBlocks,
|
||||
1,
|
||||
1,
|
||||
blockSize,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
stream_ptr,
|
||||
kernelArgs,
|
||||
0,
|
||||
)
|
||||
)
|
||||
elif inp_dtype == np.float16:
|
||||
kernelHelper = KernelHelper(circ_pad_half_kernel, int(self.cuDevice))
|
||||
_circ_pad_half_kernel = kernelHelper.getFunction(b"circ_pad_half")
|
||||
cuda_call(
|
||||
cuda.cuLaunchKernel(
|
||||
_circ_pad_half_kernel,
|
||||
numBlocks,
|
||||
1,
|
||||
1,
|
||||
blockSize,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
stream_ptr,
|
||||
kernelArgs,
|
||||
0,
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise ValueError("inp_dtype not valid")
|
||||
|
||||
def clone(self):
|
||||
cloned_plugin = CircPadPlugin()
|
||||
cloned_plugin.__dict__.update(self.__dict__)
|
||||
return cloned_plugin
|
||||
|
||||
def terminate(self):
|
||||
# Release DeviceMem objects - automatic cleanup via __del__ when reference count reaches 0
|
||||
self.all_pads_d = None
|
||||
self.orig_dims_d = None
|
||||
self.Y_shape_d = None
|
||||
|
||||
trt.get_plugin_registry().release_plugin_resource("cuda_ctx")
|
||||
|
||||
#
|
||||
# The following defaults take effect since the respective methods are not overriden
|
||||
#
|
||||
|
||||
# def get_serialization_size(self):
|
||||
# return len(to_json({"pads": self.pads}))
|
||||
|
||||
# def get_workspace_size(self, input_desc, output_desc):
|
||||
# return 0
|
||||
|
||||
# def destroy(self):
|
||||
# pass
|
||||
|
||||
|
||||
class CircPadPluginCreator(trt.IPluginCreator):
|
||||
def __init__(self):
|
||||
trt.IPluginCreator.__init__(self)
|
||||
self.name = "CircPadPlugin"
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_version = "1"
|
||||
self.field_names = trt.PluginFieldCollection(
|
||||
[
|
||||
trt.PluginField("pads", np.array([]), trt.PluginFieldType.INT32),
|
||||
trt.PluginField("N", np.array([]), trt.PluginFieldType.INT32),
|
||||
]
|
||||
)
|
||||
|
||||
def create_plugin(self, name, fc):
|
||||
return CircPadPlugin(fc)
|
||||
|
||||
def deserialize_plugin(self, name, data):
|
||||
deserialized = CircPadPlugin()
|
||||
j = dict(from_json(data))
|
||||
deserialized.__dict__.update(j)
|
||||
return deserialized
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = parseArgs()
|
||||
|
||||
# Initialize CUDA Driver API
|
||||
cuda_init()
|
||||
|
||||
# Retrieve handle for device 0
|
||||
cuDevice = cuda_get_device(0)
|
||||
|
||||
plg_registry = trt.get_plugin_registry()
|
||||
|
||||
# Create context
|
||||
plg_registry.acquire_plugin_resource("cuda_ctx", CudaCtxManager(cuDevice))
|
||||
|
||||
precision = np.float32 if args.precision == "fp32" else np.float16
|
||||
|
||||
inp_shape = (100, 2, 32, 32)
|
||||
X = np.random.normal(size=inp_shape).astype(precision)
|
||||
|
||||
pads = (1, 1, 1, 1)
|
||||
|
||||
# Load standard plugins
|
||||
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
|
||||
trt.init_libnvinfer_plugins(TRT_LOGGER, namespace="")
|
||||
|
||||
# Register plugin creator
|
||||
my_plugin_creator = CircPadPluginCreator()
|
||||
plg_registry.register_creator(my_plugin_creator, "")
|
||||
|
||||
# create ONNX model
|
||||
onnx_path = f"test_CircPadPlugin_cuda_python_{args.precision}.onnx"
|
||||
inputA = gs.Variable(name="X", shape=inp_shape, dtype=precision)
|
||||
Y = gs.Variable(name="Y", dtype=precision)
|
||||
myPluginNode = gs.Node(
|
||||
name="CircPadPlugin",
|
||||
op="CircPadPlugin",
|
||||
inputs=[inputA],
|
||||
outputs=[Y],
|
||||
attrs={"pads": pads, "N": 4},
|
||||
)
|
||||
graph = gs.Graph(nodes=[myPluginNode], inputs=[inputA], outputs=[Y], opset=16)
|
||||
onnx.save(gs.export_onnx(graph), onnx_path)
|
||||
|
||||
# build engine
|
||||
build_engine = EngineFromNetwork(
|
||||
NetworkFromOnnxPath(onnx_path, strongly_typed=True), CreateConfig()
|
||||
)
|
||||
|
||||
Y_ref = np.pad(X, [[0, 0], [0, 0], [pads[0], pads[1]], [pads[2], pads[3]]], "wrap")
|
||||
# Run
|
||||
with TrtRunner(build_engine, "trt_runner") as runner:
|
||||
outputs = runner.infer({"X": X})
|
||||
Y = outputs["Y"]
|
||||
|
||||
if np.allclose(Y, Y_ref):
|
||||
print("Inference result correct!")
|
||||
else:
|
||||
print("Inference result incorrect!")
|
||||
|
||||
plg_registry.release_plugin_resource("cuda_ctx")
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import onnx_graphsurgeon as gs
|
||||
import numpy as np
|
||||
import onnx
|
||||
import cupy as cp
|
||||
import time
|
||||
import pickle
|
||||
import sys
|
||||
import os
|
||||
|
||||
import tensorrt as trt
|
||||
from polygraphy.backend.trt import (
|
||||
CreateConfig,
|
||||
EngineFromNetwork,
|
||||
NetworkFromOnnxPath,
|
||||
TrtRunner,
|
||||
)
|
||||
|
||||
from polygraphy.json import to_json, from_json
|
||||
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
|
||||
from plugin_utils import volume, parseArgs
|
||||
|
||||
circ_pad_half_kernel = cp.RawKernel(
|
||||
r"""
|
||||
#include <cuda_fp16.h>
|
||||
extern "C" __global__
|
||||
void circ_pad_half(half const* X, int const* all_pads, int const* orig_dims, half* Y, int const* Y_shape, int const* Y_len) {
|
||||
int index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int stride = blockDim.x * gridDim.x;
|
||||
|
||||
for(int i = index; i < *Y_len; i += stride)
|
||||
{
|
||||
int i3 = i % Y_shape[3];
|
||||
int i2 = (i / Y_shape[3]) % Y_shape[2];
|
||||
int i1 = (i / Y_shape[3] / Y_shape[2]) % Y_shape[1];
|
||||
int i0 = i / Y_shape[3] / Y_shape[2] / Y_shape[1];
|
||||
|
||||
int j0 = (i0 - all_pads[0] + orig_dims[0]) % orig_dims[0];
|
||||
int j1 = (i1 - all_pads[2] + orig_dims[1]) % orig_dims[1];
|
||||
int j2 = (i2 - all_pads[4] + orig_dims[2]) % orig_dims[2];
|
||||
int j3 = (i3 - all_pads[6] + orig_dims[3]) % orig_dims[3];
|
||||
|
||||
Y[i] = X[
|
||||
orig_dims[3] * orig_dims[2] * orig_dims[1] * j0
|
||||
+ orig_dims[3] * orig_dims[2] * j1
|
||||
+ orig_dims[3] * j2
|
||||
+ j3
|
||||
];
|
||||
}
|
||||
}
|
||||
""",
|
||||
"circ_pad_half",
|
||||
)
|
||||
|
||||
circ_pad_float_kernel = cp.RawKernel(
|
||||
r"""
|
||||
extern "C" __global__
|
||||
void circ_pad_float(float const* X, int const* all_pads, int const* orig_dims, float* Y, int const* Y_shape, int const* Y_len) {
|
||||
int index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int stride = blockDim.x * gridDim.x;
|
||||
|
||||
for(int i = index; i < *Y_len; i += stride)
|
||||
{
|
||||
int i3 = i % Y_shape[3];
|
||||
int i2 = (i / Y_shape[3]) % Y_shape[2];
|
||||
int i1 = (i / Y_shape[3] / Y_shape[2]) % Y_shape[1];
|
||||
int i0 = i / Y_shape[3] / Y_shape[2] / Y_shape[1];
|
||||
|
||||
int j0 = (i0 - all_pads[0] + orig_dims[0]) % orig_dims[0];
|
||||
int j1 = (i1 - all_pads[2] + orig_dims[1]) % orig_dims[1];
|
||||
int j2 = (i2 - all_pads[4] + orig_dims[2]) % orig_dims[2];
|
||||
int j3 = (i3 - all_pads[6] + orig_dims[3]) % orig_dims[3];
|
||||
|
||||
Y[i] = X[
|
||||
orig_dims[3] * orig_dims[2] * orig_dims[1] * j0
|
||||
+ orig_dims[3] * orig_dims[2] * j1
|
||||
+ orig_dims[3] * j2
|
||||
+ j3
|
||||
];
|
||||
}
|
||||
}
|
||||
""",
|
||||
"circ_pad_float",
|
||||
)
|
||||
|
||||
|
||||
class CircPadPlugin(trt.IPluginV2DynamicExt):
|
||||
def __init__(self, fc=None):
|
||||
trt.IPluginV2DynamicExt.__init__(self)
|
||||
self.pads = []
|
||||
self.X_shape = []
|
||||
|
||||
self.num_outputs = 1
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_type = "CircPadPlugin"
|
||||
self.plugin_version = "1"
|
||||
|
||||
if fc is not None:
|
||||
assert fc[0].name == "pads"
|
||||
self.pads = fc[0].data
|
||||
|
||||
def get_output_datatype(self, index, input_types):
|
||||
return input_types[0]
|
||||
|
||||
def get_output_dimensions(self, output_index, inputs, exprBuilder):
|
||||
|
||||
output_dims = trt.DimsExprs(inputs[0])
|
||||
|
||||
for i in range(np.size(self.pads) // 2):
|
||||
output_dims[len(output_dims) - i - 1] = exprBuilder.operation(
|
||||
trt.DimensionOperation.SUM,
|
||||
inputs[0][len(output_dims) - i - 1],
|
||||
exprBuilder.constant(self.pads[i * 2] + self.pads[i * 2 + 1]),
|
||||
)
|
||||
|
||||
return output_dims
|
||||
|
||||
def serialize(self):
|
||||
return to_json({"pads": self.pads})
|
||||
|
||||
def configure_plugin(self, inp, out):
|
||||
X_dims = inp[0].desc.dims
|
||||
self.X_shape = np.zeros((len(X_dims),))
|
||||
for i in range(len(X_dims)):
|
||||
self.X_shape[i] = X_dims[i]
|
||||
|
||||
N = len(self.X_shape)
|
||||
all_pads = np.zeros((N * 2,))
|
||||
orig_dims = np.array(self.X_shape)
|
||||
out_dims = np.array(self.X_shape)
|
||||
|
||||
for i in range(np.size(pads) // 2):
|
||||
out_dims[N - i - 1] += self.pads[i * 2] + self.pads[i * 2 + 1]
|
||||
all_pads[N * 2 - 2 * i - 2] = self.pads[i * 2]
|
||||
all_pads[N * 2 - 2 * i - 1] = self.pads[i * 2 + 1]
|
||||
|
||||
self.all_pads_d = cp.asarray(all_pads, dtype=cp.int32)
|
||||
self.orig_dims_d = cp.asarray(orig_dims, dtype=cp.int32)
|
||||
self.Y_shape_d = cp.asarray(out_dims, dtype=cp.int32)
|
||||
self.Y_len_d = cp.array([np.prod(out_dims)], dtype=cp.int32)
|
||||
|
||||
def supports_format_combination(self, pos, in_out, num_inputs):
|
||||
assert num_inputs == 1
|
||||
assert pos < len(in_out)
|
||||
|
||||
desc = in_out[pos]
|
||||
if desc.format != trt.TensorFormat.LINEAR:
|
||||
return False
|
||||
|
||||
# first input should be float16 or float32
|
||||
if pos == 0:
|
||||
return desc.type == trt.DataType.FLOAT or desc.type == trt.DataType.HALF
|
||||
|
||||
# output should have the same type as the input
|
||||
if pos == 1:
|
||||
return in_out[0].type == desc.type
|
||||
|
||||
assert False
|
||||
|
||||
def enqueue(self, input_desc, output_desc, inputs, outputs, workspace, stream):
|
||||
|
||||
inp_dtype = trt.nptype(input_desc[0].type)
|
||||
|
||||
a_mem = cp.cuda.UnownedMemory(
|
||||
inputs[0], volume(input_desc[0].dims) * cp.dtype(inp_dtype).itemsize, self
|
||||
)
|
||||
c_mem = cp.cuda.UnownedMemory(
|
||||
outputs[0],
|
||||
volume(output_desc[0].dims) * cp.dtype(inp_dtype).itemsize,
|
||||
self,
|
||||
)
|
||||
|
||||
a_ptr = cp.cuda.MemoryPointer(a_mem, 0)
|
||||
c_ptr = cp.cuda.MemoryPointer(c_mem, 0)
|
||||
|
||||
a = cp.ndarray((volume(input_desc[0].dims)), dtype=inp_dtype, memptr=a_ptr)
|
||||
c = cp.ndarray((volume(output_desc[0].dims)), dtype=inp_dtype, memptr=c_ptr)
|
||||
|
||||
cuda_stream = cp.cuda.ExternalStream(stream)
|
||||
|
||||
blockSize = 256
|
||||
numBlocks = int((np.prod(np.array(self.X_shape)) + blockSize - 1) // blockSize)
|
||||
|
||||
with cuda_stream:
|
||||
if inp_dtype == np.float32:
|
||||
circ_pad_float_kernel(
|
||||
(numBlocks,),
|
||||
(blockSize,),
|
||||
(
|
||||
a,
|
||||
self.all_pads_d,
|
||||
self.orig_dims_d,
|
||||
c,
|
||||
self.Y_shape_d,
|
||||
self.Y_len_d,
|
||||
),
|
||||
)
|
||||
elif inp_dtype == np.float16:
|
||||
circ_pad_half_kernel(
|
||||
(numBlocks,),
|
||||
(blockSize,),
|
||||
(
|
||||
a,
|
||||
self.all_pads_d,
|
||||
self.orig_dims_d,
|
||||
c,
|
||||
self.Y_shape_d,
|
||||
self.Y_len_d,
|
||||
),
|
||||
)
|
||||
else:
|
||||
raise ValueError("inp_dtype not valid")
|
||||
|
||||
def clone(self):
|
||||
cloned_plugin = CircPadPlugin()
|
||||
cloned_plugin.__dict__.update(self.__dict__)
|
||||
return cloned_plugin
|
||||
|
||||
#
|
||||
# The following defaults take effect since the respective methods are not overriden
|
||||
#
|
||||
|
||||
# def initialize(self):
|
||||
# pass
|
||||
|
||||
# def get_serialization_size(self):
|
||||
# return len(to_json({"pads": self.pads}))
|
||||
|
||||
# def get_workspace_size(self, input_desc, output_desc):
|
||||
# return 0
|
||||
|
||||
# def destroy(self):
|
||||
# pass
|
||||
|
||||
# def terminate(self):
|
||||
# pass
|
||||
|
||||
|
||||
class CircPadPluginCreator(trt.IPluginCreator):
|
||||
def __init__(self):
|
||||
trt.IPluginCreator.__init__(self)
|
||||
|
||||
self.name = "CircPadPlugin"
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_version = "1"
|
||||
self.field_names = trt.PluginFieldCollection(
|
||||
[trt.PluginField("pads", np.array([]), trt.PluginFieldType.INT32)]
|
||||
)
|
||||
|
||||
def create_plugin(self, name, fc):
|
||||
return CircPadPlugin(fc)
|
||||
|
||||
def deserialize_plugin(self, name, data):
|
||||
j = dict(from_json(data.decode("utf-8")))
|
||||
deserialized = CircPadPlugin()
|
||||
deserialized.__dict__.update(j)
|
||||
return deserialized
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = parseArgs()
|
||||
precision = np.float32 if args.precision == "fp32" else np.float16
|
||||
|
||||
inp_shape = (100, 2, 32, 32)
|
||||
X = np.random.normal(size=inp_shape).astype(precision)
|
||||
|
||||
pads = (1, 1, 1, 1)
|
||||
|
||||
# Load standard plugins
|
||||
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
|
||||
trt.init_libnvinfer_plugins(TRT_LOGGER, namespace="")
|
||||
|
||||
# Register plugin creator
|
||||
plg_registry = trt.get_plugin_registry()
|
||||
my_plugin_creator = CircPadPluginCreator()
|
||||
plg_registry.register_creator(my_plugin_creator, "")
|
||||
|
||||
# create ONNX model
|
||||
onnx_path = f"test_CircPadPlugin_cupy_{args.precision}.onnx"
|
||||
inputA = gs.Variable(name="X", shape=inp_shape, dtype=precision)
|
||||
Y = gs.Variable(name="Y", dtype=precision)
|
||||
myPluginNode = gs.Node(
|
||||
name="CircPadPlugin",
|
||||
op="CircPadPlugin",
|
||||
inputs=[inputA],
|
||||
outputs=[Y],
|
||||
attrs={"pads": pads},
|
||||
)
|
||||
graph = gs.Graph(nodes=[myPluginNode], inputs=[inputA], outputs=[Y], opset=16)
|
||||
onnx.save(gs.export_onnx(graph), onnx_path)
|
||||
|
||||
# build engine
|
||||
build_engine = EngineFromNetwork(
|
||||
NetworkFromOnnxPath(onnx_path, strongly_typed=True), CreateConfig()
|
||||
)
|
||||
|
||||
Y_ref = np.pad(X, [[0, 0], [0, 0], [pads[0], pads[1]], [pads[2], pads[3]]], "wrap")
|
||||
# Run
|
||||
with TrtRunner(build_engine, "trt_runner") as runner:
|
||||
outputs = runner.infer({"X": X})
|
||||
Y = outputs["Y"]
|
||||
|
||||
if np.allclose(Y, Y_ref):
|
||||
print("Inference result correct!")
|
||||
else:
|
||||
print("Inference result incorrect!")
|
||||
@@ -0,0 +1,383 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import onnx_graphsurgeon as gs
|
||||
import numpy as np
|
||||
import sys
|
||||
import os
|
||||
|
||||
import tensorrt as trt
|
||||
from polygraphy.backend.trt import (
|
||||
CreateConfig,
|
||||
TrtRunner,
|
||||
create_network,
|
||||
engine_from_network,
|
||||
)
|
||||
|
||||
from polygraphy.json import to_json, from_json
|
||||
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
|
||||
from plugin_utils import cuda_call, KernelHelper, parseArgs, CudaCtxManager
|
||||
from cuda.bindings import driver as cuda
|
||||
|
||||
circ_pad_half_kernel = r"""
|
||||
#include <cuda_fp16.h>
|
||||
extern "C" __global__
|
||||
void circ_pad_half(half const* X, int const* all_pads, int const* orig_dims, half* Y, int const* Y_shape, int Y_len) {
|
||||
int index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int stride = blockDim.x * gridDim.x;
|
||||
|
||||
for(int i = index; i < Y_len; i += stride)
|
||||
{
|
||||
int i3 = i % Y_shape[3];
|
||||
int i2 = (i / Y_shape[3]) % Y_shape[2];
|
||||
int i1 = (i / Y_shape[3] / Y_shape[2]) % Y_shape[1];
|
||||
int i0 = i / Y_shape[3] / Y_shape[2] / Y_shape[1];
|
||||
|
||||
int j0 = (i0 - all_pads[0] + orig_dims[0]) % orig_dims[0];
|
||||
int j1 = (i1 - all_pads[2] + orig_dims[1]) % orig_dims[1];
|
||||
int j2 = (i2 - all_pads[4] + orig_dims[2]) % orig_dims[2];
|
||||
int j3 = (i3 - all_pads[6] + orig_dims[3]) % orig_dims[3];
|
||||
|
||||
Y[i] = X[
|
||||
orig_dims[3] * orig_dims[2] * orig_dims[1] * j0
|
||||
+ orig_dims[3] * orig_dims[2] * j1
|
||||
+ orig_dims[3] * j2
|
||||
+ j3
|
||||
];
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
circ_pad_float_kernel = r"""
|
||||
extern "C" __global__
|
||||
void circ_pad_float(float const* X, int const* all_pads, int const* orig_dims, float* Y, int const* Y_shape, int Y_len) {
|
||||
int index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int stride = blockDim.x * gridDim.x;
|
||||
|
||||
for(int i = index; i < Y_len; i += stride)
|
||||
{
|
||||
int i3 = i % Y_shape[3];
|
||||
int i2 = (i / Y_shape[3]) % Y_shape[2];
|
||||
int i1 = (i / Y_shape[3] / Y_shape[2]) % Y_shape[1];
|
||||
int i0 = i / Y_shape[3] / Y_shape[2] / Y_shape[1];
|
||||
|
||||
int j0 = (i0 - all_pads[0] + orig_dims[0]) % orig_dims[0];
|
||||
int j1 = (i1 - all_pads[2] + orig_dims[1]) % orig_dims[1];
|
||||
int j2 = (i2 - all_pads[4] + orig_dims[2]) % orig_dims[2];
|
||||
int j3 = (i3 - all_pads[6] + orig_dims[3]) % orig_dims[3];
|
||||
|
||||
Y[i] = X[
|
||||
orig_dims[3] * orig_dims[2] * orig_dims[1] * j0
|
||||
+ orig_dims[3] * orig_dims[2] * j1
|
||||
+ orig_dims[3] * j2
|
||||
+ j3
|
||||
];
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class CircPadPlugin(trt.IPluginV2DynamicExt):
|
||||
def __init__(self, fc=None):
|
||||
trt.IPluginV2DynamicExt.__init__(self)
|
||||
self.pads = []
|
||||
self.X_shape = []
|
||||
self.N = 0
|
||||
|
||||
self.all_pads_d = None
|
||||
self.orig_dims_d = None
|
||||
self.Y_shape_d = None
|
||||
|
||||
self.num_outputs = 1
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_type = "CircPadPlugin"
|
||||
self.plugin_version = "1"
|
||||
|
||||
self.cuDevice = None
|
||||
|
||||
if fc is not None:
|
||||
assert set([f.name for f in fc]) == set(
|
||||
["pads", "N"]
|
||||
), "Field collection invalid"
|
||||
for f in fc:
|
||||
if f.name == "pads":
|
||||
self.pads = f.data
|
||||
elif f.name == "N":
|
||||
self.N = int(f.data)
|
||||
|
||||
def initialize(self):
|
||||
self.cuDevice = cuda_call(cuda.cuDeviceGet(0))
|
||||
trt.get_plugin_registry().acquire_plugin_resource(
|
||||
"cuda_ctx", CudaCtxManager(self.cuDevice)
|
||||
)
|
||||
self.all_pads_d = cuda_call(
|
||||
cuda.cuMemAlloc(np.int32().itemsize * self.N * 2)
|
||||
)
|
||||
self.orig_dims_d = cuda_call(
|
||||
cuda.cuMemAlloc(np.int32().itemsize * self.N)
|
||||
)
|
||||
self.Y_shape_d = cuda_call(cuda.cuMemAlloc(np.int32().itemsize * self.N))
|
||||
|
||||
def get_output_datatype(self, index, input_types):
|
||||
return input_types[0]
|
||||
|
||||
def get_output_dimensions(self, output_index, inputs, exprBuilder):
|
||||
|
||||
output_dims = trt.DimsExprs(inputs[0])
|
||||
|
||||
for i in range(np.size(self.pads) // 2):
|
||||
output_dims[len(output_dims) - i - 1] = exprBuilder.operation(
|
||||
trt.DimensionOperation.SUM,
|
||||
inputs[0][len(output_dims) - i - 1],
|
||||
exprBuilder.constant(self.pads[i * 2] + self.pads[i * 2 + 1]),
|
||||
)
|
||||
|
||||
return output_dims
|
||||
|
||||
def serialize(self):
|
||||
return to_json({"pads": self.pads, "N": self.N})
|
||||
|
||||
def configure_plugin(self, inp, out):
|
||||
X_dims = inp[0].desc.dims
|
||||
self.X_shape = np.zeros((len(X_dims),))
|
||||
for i in range(len(X_dims)):
|
||||
self.X_shape[i] = X_dims[i]
|
||||
|
||||
all_pads = np.zeros((self.N * 2,), dtype=np.int32)
|
||||
orig_dims = np.array(self.X_shape, dtype=np.int32)
|
||||
out_dims = np.array(self.X_shape, dtype=np.int32)
|
||||
|
||||
for i in range(np.size(self.pads) // 2):
|
||||
out_dims[self.N - i - 1] += self.pads[i * 2] + self.pads[i * 2 + 1]
|
||||
all_pads[self.N * 2 - 2 * i - 2] = self.pads[i * 2]
|
||||
all_pads[self.N * 2 - 2 * i - 1] = self.pads[i * 2 + 1]
|
||||
|
||||
# Copy vectors from host memory to device memory
|
||||
if self.all_pads_d:
|
||||
cuda_call(
|
||||
cuda.cuMemcpyHtoD(self.all_pads_d, all_pads, all_pads.nbytes)
|
||||
)
|
||||
if self.orig_dims_d:
|
||||
cuda_call(
|
||||
cuda.cuMemcpyHtoD(self.orig_dims_d, orig_dims, orig_dims.nbytes)
|
||||
)
|
||||
if self.Y_shape_d:
|
||||
cuda_call(
|
||||
cuda.cuMemcpyHtoD(self.Y_shape_d, out_dims, out_dims.nbytes)
|
||||
)
|
||||
|
||||
self.Y_len_d = np.prod(out_dims)
|
||||
|
||||
def supports_format_combination(self, pos, in_out, num_inputs):
|
||||
assert num_inputs == 1
|
||||
assert pos < len(in_out)
|
||||
|
||||
desc = in_out[pos]
|
||||
if desc.format != trt.TensorFormat.LINEAR:
|
||||
return False
|
||||
|
||||
# first input should be float16 or float32
|
||||
if pos == 0:
|
||||
return desc.type == trt.DataType.FLOAT or desc.type == trt.DataType.HALF
|
||||
|
||||
# output should have the same type as the input
|
||||
if pos == 1:
|
||||
return in_out[0].type == desc.type
|
||||
|
||||
assert False
|
||||
|
||||
def enqueue(self, input_desc, output_desc, inputs, outputs, workspace, stream):
|
||||
|
||||
inp_dtype = trt.nptype(input_desc[0].type)
|
||||
|
||||
blockSize = 256
|
||||
numBlocks = int((np.prod(np.array(self.X_shape)) + blockSize - 1) // blockSize)
|
||||
|
||||
da = np.array([inputs[0]], dtype=np.uint64)
|
||||
dc = np.array([outputs[0]], dtype=np.uint64)
|
||||
|
||||
d_all_pads = np.array([int(self.all_pads_d)], dtype=np.uint64)
|
||||
d_orig_dims = np.array([int(self.orig_dims_d)], dtype=np.uint64)
|
||||
d_Y_shape = np.array([int(self.Y_shape_d)], dtype=np.uint64)
|
||||
Y_len = np.array(self.Y_len_d, dtype=np.uint32)
|
||||
|
||||
args = [da, d_all_pads, d_orig_dims, dc, d_Y_shape, Y_len]
|
||||
kernelArgs = np.array([arg.ctypes.data for arg in args], dtype=np.uint64)
|
||||
|
||||
stream_ptr = np.array([stream], dtype=np.uint64)
|
||||
|
||||
if inp_dtype == np.float32:
|
||||
kernelHelper = KernelHelper(circ_pad_float_kernel, int(self.cuDevice))
|
||||
_circ_pad_float_kernel = kernelHelper.getFunction(b"circ_pad_float")
|
||||
cuda_call(
|
||||
cuda.cuLaunchKernel(
|
||||
_circ_pad_float_kernel,
|
||||
numBlocks,
|
||||
1,
|
||||
1,
|
||||
blockSize,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
stream_ptr,
|
||||
kernelArgs,
|
||||
0,
|
||||
)
|
||||
)
|
||||
elif inp_dtype == np.float16:
|
||||
kernelHelper = KernelHelper(circ_pad_half_kernel, int(self.cuDevice))
|
||||
_circ_pad_half_kernel = kernelHelper.getFunction(b"circ_pad_half")
|
||||
cuda_call(
|
||||
cuda.cuLaunchKernel(
|
||||
_circ_pad_half_kernel,
|
||||
numBlocks,
|
||||
1,
|
||||
1,
|
||||
blockSize,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
stream_ptr,
|
||||
kernelArgs,
|
||||
0,
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise ValueError("inp_dtype not valid")
|
||||
|
||||
def clone(self):
|
||||
cloned_plugin = CircPadPlugin()
|
||||
cloned_plugin.__dict__.update(self.__dict__)
|
||||
return cloned_plugin
|
||||
|
||||
def terminate(self):
|
||||
if self.all_pads_d:
|
||||
cuda_call(cuda.cuMemFree(self.all_pads_d))
|
||||
if self.orig_dims_d:
|
||||
cuda_call(cuda.cuMemFree(self.orig_dims_d))
|
||||
if self.Y_shape_d:
|
||||
cuda_call(cuda.cuMemFree(self.Y_shape_d))
|
||||
|
||||
plg_registry.release_plugin_resource("cuda_ctx")
|
||||
|
||||
#
|
||||
# The following defaults take effect since the respective methods are not overriden
|
||||
#
|
||||
|
||||
# def get_serialization_size(self):
|
||||
# return len(to_json({"pads": self.pads}))
|
||||
|
||||
# def get_workspace_size(self, input_desc, output_desc):
|
||||
# return 0
|
||||
|
||||
# def destroy(self):
|
||||
# pass
|
||||
|
||||
|
||||
class CircPadPluginCreator(trt.IPluginCreator):
|
||||
def __init__(self):
|
||||
trt.IPluginCreator.__init__(self)
|
||||
self.name = "CircPadPlugin"
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_version = "1"
|
||||
self.field_names = trt.PluginFieldCollection(
|
||||
[
|
||||
trt.PluginField("pads", np.array([]), trt.PluginFieldType.INT32),
|
||||
trt.PluginField("N", np.array([]), trt.PluginFieldType.INT32),
|
||||
]
|
||||
)
|
||||
|
||||
def create_plugin(self, name, fc):
|
||||
return CircPadPlugin(fc)
|
||||
|
||||
def deserialize_plugin(self, name, data):
|
||||
deserialized = CircPadPlugin()
|
||||
j = dict(from_json(data))
|
||||
deserialized.__dict__.update(j)
|
||||
return deserialized
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = parseArgs()
|
||||
precision = np.float32 if args.precision == "fp32" else np.float16
|
||||
|
||||
# Initialize CUDA Driver API
|
||||
cuda_call(cuda.cuInit(0))
|
||||
|
||||
# Retrieve handle for device 0
|
||||
cuDevice = cuda_call(cuda.cuDeviceGet(0))
|
||||
|
||||
plg_registry = trt.get_plugin_registry()
|
||||
|
||||
# Create context
|
||||
plg_registry.acquire_plugin_resource("cuda_ctx", CudaCtxManager(cuDevice))
|
||||
|
||||
inp_shape = (100, 2, 32, 32)
|
||||
X = np.random.normal(size=inp_shape).astype(precision)
|
||||
|
||||
pads = (1, 1, 1, 1)
|
||||
|
||||
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
|
||||
# Load standard plugins (if needed)
|
||||
trt.init_libnvinfer_plugins(TRT_LOGGER, namespace="")
|
||||
|
||||
# Register plugin creator
|
||||
my_plugin_creator = CircPadPluginCreator()
|
||||
plg_registry.register_creator(my_plugin_creator, "")
|
||||
|
||||
# Create plugin object
|
||||
builder, network = create_network(strongly_typed=True)
|
||||
plg_creator = plg_registry.get_creator("CircPadPlugin", "1", "")
|
||||
plugin_fields_list = [
|
||||
trt.PluginField(
|
||||
"pads", np.array(pads, dtype=np.int32), trt.PluginFieldType.INT32
|
||||
),
|
||||
trt.PluginField("N", np.array([4], dtype=np.int32), trt.PluginFieldType.INT32),
|
||||
]
|
||||
pfc = trt.PluginFieldCollection(plugin_fields_list)
|
||||
plugin = plg_creator.create_plugin("CircPadPlugin", pfc)
|
||||
|
||||
# Populate network
|
||||
input_X = network.add_input(
|
||||
name="X",
|
||||
dtype=trt.float32 if precision == np.float32 else trt.float16,
|
||||
shape=X.shape,
|
||||
)
|
||||
out = network.add_plugin_v2([input_X], plugin)
|
||||
out.get_output(0).name = "Y"
|
||||
network.mark_output(tensor=out.get_output(0))
|
||||
|
||||
# Build engine
|
||||
config = builder.create_builder_config()
|
||||
engine = engine_from_network(
|
||||
(builder, network), CreateConfig()
|
||||
)
|
||||
|
||||
Y_ref = np.pad(X, [[0, 0], [0, 0], [pads[0], pads[1]], [pads[2], pads[3]]], "wrap")
|
||||
# Run
|
||||
with TrtRunner(engine, "trt_runner") as runner:
|
||||
outputs = runner.infer({"X": X})
|
||||
Y = outputs["Y"]
|
||||
|
||||
if np.allclose(Y, Y_ref):
|
||||
print("Inference result correct!")
|
||||
else:
|
||||
print("Inference result incorrect!")
|
||||
|
||||
plg_registry.release_plugin_resource("cuda_ctx")
|
||||
@@ -0,0 +1,376 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import onnx_graphsurgeon as gs
|
||||
import numpy as np
|
||||
import onnx
|
||||
import cupy as cp
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
|
||||
import tensorrt as trt
|
||||
from polygraphy.backend.trt import (
|
||||
CreateConfig,
|
||||
EngineFromNetwork,
|
||||
NetworkFromOnnxPath,
|
||||
TrtRunner,
|
||||
)
|
||||
|
||||
import triton
|
||||
import triton.language as tl
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
from polygraphy.json import to_json, from_json
|
||||
import torch
|
||||
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
|
||||
from plugin_utils import volume, parseArgs
|
||||
|
||||
import argparse
|
||||
|
||||
logger = logging.getLogger("CircPadMultiTactic")
|
||||
|
||||
class Tactic(IntEnum):
|
||||
TORCH = 1
|
||||
TRITON = 2
|
||||
|
||||
@triton.jit
|
||||
def circ_pad(X,
|
||||
all_pads_0, all_pads_2, all_pads_4, all_pads_6,
|
||||
orig_dims_0, orig_dims_1, orig_dims_2, orig_dims_3,
|
||||
Y,
|
||||
Y_shape_1, Y_shape_2, Y_shape_3,
|
||||
X_len, Y_len, BLOCK_SIZE: tl.constexpr,):
|
||||
pid = tl.program_id(0)
|
||||
i = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
|
||||
|
||||
mask_y = i < Y_len
|
||||
|
||||
i3 = i % Y_shape_3
|
||||
i2 = (i // Y_shape_3) % Y_shape_2
|
||||
i1 = (i // Y_shape_3 // Y_shape_2) % Y_shape_1
|
||||
i0 = i // Y_shape_3 // Y_shape_2 // Y_shape_1
|
||||
|
||||
j0 = (i0 - all_pads_0 + orig_dims_0) % orig_dims_0
|
||||
j1 = (i1 - all_pads_2 + orig_dims_1) % orig_dims_1
|
||||
j2 = (i2 - all_pads_4 + orig_dims_2) % orig_dims_2
|
||||
j3 = (i3 - all_pads_6 + orig_dims_3) % orig_dims_3
|
||||
|
||||
load_idx = orig_dims_3 * orig_dims_2 * orig_dims_1 * j0 + orig_dims_3 * orig_dims_2 * j1 + orig_dims_3 * j2 + j3
|
||||
mask_x = load_idx < X_len
|
||||
|
||||
x = tl.load(X + load_idx, mask=mask_x)
|
||||
|
||||
tl.store(Y + i, x, mask=mask_y)
|
||||
|
||||
class CircPadPlugin(trt.IPluginV3, trt.IPluginV3OneCore, trt.IPluginV3OneBuild, trt.IPluginV3OneRuntime):
|
||||
def __init__(self, fc=None, phase=None):
|
||||
trt.IPluginV3.__init__(self)
|
||||
trt.IPluginV3OneCore.__init__(self)
|
||||
trt.IPluginV3OneBuild.__init__(self)
|
||||
trt.IPluginV3OneRuntime.__init__(self)
|
||||
self.pads = []
|
||||
self.X_shape = []
|
||||
|
||||
self.per_format_tactics = (
|
||||
False # whether per-format tactics or global tactics should be used
|
||||
)
|
||||
self.curr_type = None # format being timed currently by TRT auto-tuner
|
||||
|
||||
self.num_outputs = 1
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_name = "CircPadPlugin"
|
||||
self.plugin_version = "1"
|
||||
|
||||
# Set the timing cache ID to prevent unnecessary timing of second plugin instance
|
||||
self.timing_cache_id = ""
|
||||
|
||||
self.tactic = None
|
||||
|
||||
if fc is not None:
|
||||
for f in fc:
|
||||
if f.name == "pads":
|
||||
self.pads = f.data
|
||||
elif f.name == "per_format_tactics":
|
||||
self.per_format_tactics = int(f.data)
|
||||
|
||||
if phase is not None:
|
||||
self.phase = phase
|
||||
|
||||
def get_capability_interface(self, type):
|
||||
return self
|
||||
|
||||
def get_output_data_types(self, input_types):
|
||||
return [input_types[0]]
|
||||
|
||||
def get_output_shapes(self, inputs, shape_inputs, exprBuilder):
|
||||
output_dims = trt.DimsExprs(inputs[0])
|
||||
|
||||
for i in range(np.size(self.pads) // 2):
|
||||
output_dims[len(output_dims) - i - 1] = exprBuilder.operation(
|
||||
trt.DimensionOperation.SUM,
|
||||
inputs[0][len(output_dims) - i - 1],
|
||||
exprBuilder.constant(self.pads[i * 2] + self.pads[i * 2 + 1]),
|
||||
)
|
||||
|
||||
return [output_dims]
|
||||
|
||||
def get_fields_to_serialize(self):
|
||||
return trt.PluginFieldCollection([
|
||||
trt.PluginField("pads", self.pads, trt.PluginFieldType.INT32),
|
||||
trt.PluginField(
|
||||
"per_format_tactics",
|
||||
np.array([self.per_format_tactics], dtype=np.int32),
|
||||
trt.PluginFieldType.INT32,
|
||||
),
|
||||
])
|
||||
|
||||
def configure_plugin(self, inp, out):
|
||||
assert inp[0].desc.type == trt.float32 or inp[0].desc.type == trt.float16
|
||||
self.curr_type = inp[0].desc.type
|
||||
|
||||
def on_shape_change(self, inp, out):
|
||||
if (
|
||||
self.phase == trt.TensorRTPhase.RUNTIME
|
||||
and self.per_format_tactics
|
||||
and inp[0].type == trt.float16
|
||||
):
|
||||
assert self.tactic == Tactic.TRITON
|
||||
|
||||
X_dims = inp[0].dims
|
||||
self.X_shape = np.zeros((len(X_dims),))
|
||||
for i in range(len(X_dims)):
|
||||
self.X_shape[i] = X_dims[i]
|
||||
|
||||
def supports_format_combination(self, pos, in_out, num_inputs):
|
||||
assert num_inputs == 1
|
||||
assert pos < len(in_out)
|
||||
|
||||
desc = in_out[pos].desc
|
||||
if desc.format != trt.TensorFormat.LINEAR:
|
||||
return False
|
||||
|
||||
# first input should be float16 or float32
|
||||
if pos == 0:
|
||||
return desc.type == trt.DataType.FLOAT or desc.type == trt.DataType.HALF
|
||||
|
||||
# output should have the same type as the input
|
||||
if pos == 1:
|
||||
return in_out[0].desc.type == desc.type
|
||||
|
||||
assert False
|
||||
|
||||
def enqueue(self, input_desc, output_desc, inputs, outputs, workspace, stream):
|
||||
|
||||
inp_dtype = trt.nptype(input_desc[0].type)
|
||||
|
||||
a_mem = cp.cuda.UnownedMemory(
|
||||
inputs[0], volume(input_desc[0].dims) * cp.dtype(inp_dtype).itemsize, self
|
||||
)
|
||||
c_mem = cp.cuda.UnownedMemory(
|
||||
outputs[0],
|
||||
volume(output_desc[0].dims) * cp.dtype(inp_dtype).itemsize,
|
||||
self,
|
||||
)
|
||||
|
||||
a_ptr = cp.cuda.MemoryPointer(a_mem, 0)
|
||||
c_ptr = cp.cuda.MemoryPointer(c_mem, 0)
|
||||
|
||||
c_d = cp.ndarray((volume(output_desc[0].dims)), dtype=inp_dtype, memptr=c_ptr)
|
||||
|
||||
if self.phase == trt.TensorRTPhase.BUILD:
|
||||
logger.info(f"Timing tactic: {self.tactic}")
|
||||
|
||||
if self.tactic == Tactic.TORCH:
|
||||
# Use PyTorch functional op - no need to write kernel
|
||||
a_d = cp.ndarray(tuple(input_desc[0].dims), dtype=inp_dtype, memptr=a_ptr)
|
||||
a_t = torch.as_tensor(a_d, device='cuda')
|
||||
out = torch.nn.functional.pad(a_t, self.pads.tolist(), mode='circular')
|
||||
cp.copyto(c_d, cp.reshape(cp.asarray(out), (-1,)))
|
||||
elif self.tactic == Tactic.TRITON:
|
||||
a_d = cp.ndarray((volume(input_desc[0].dims)), dtype=inp_dtype, memptr=a_ptr)
|
||||
a_t = torch.as_tensor(a_d, device='cuda')
|
||||
c_t = torch.as_tensor(c_d, device='cuda')
|
||||
|
||||
N = len(self.X_shape)
|
||||
all_pads = np.zeros((N * 2,), dtype=np.int32)
|
||||
orig_dims = np.array(self.X_shape, dtype=np.int32)
|
||||
out_dims = np.array(self.X_shape, dtype=np.int32)
|
||||
|
||||
for i in range(np.size(pads) // 2):
|
||||
out_dims[N - i - 1] += pads[i * 2] + pads[i * 2 + 1]
|
||||
all_pads[N * 2 - 2 * i - 2] = pads[i * 2]
|
||||
all_pads[N * 2 - 2 * i - 1] = pads[i * 2 + 1]
|
||||
|
||||
all_pads = all_pads.tolist()
|
||||
orig_dims = orig_dims.tolist()
|
||||
out_dims = out_dims.tolist()
|
||||
|
||||
blockSize = 256
|
||||
numBlocks = tuple([int((np.prod(out_dims) + blockSize - 1) // blockSize)])
|
||||
|
||||
circ_pad[numBlocks](a_t,
|
||||
all_pads[0], all_pads[2], all_pads[4], all_pads[6],
|
||||
orig_dims[0], orig_dims[1], orig_dims[2], orig_dims[3],
|
||||
c_t,
|
||||
out_dims[1], out_dims[2], out_dims[3],
|
||||
int(np.prod(orig_dims)), int(np.prod(out_dims)), BLOCK_SIZE=256
|
||||
)
|
||||
else:
|
||||
raise RuntimeError("Invalid tactic")
|
||||
|
||||
def attach_to_context(self, context):
|
||||
return self.clone()
|
||||
|
||||
def get_valid_tactics(self):
|
||||
assert self.curr_type is not None
|
||||
if self.per_format_tactics and self.curr_type == trt.float16:
|
||||
return [int(Tactic.TRITON)]
|
||||
|
||||
return [int(Tactic.TORCH), int(Tactic.TRITON)]
|
||||
|
||||
def set_tactic(self, tactic):
|
||||
self.tactic = Tactic(tactic)
|
||||
|
||||
if self.phase == trt.TensorRTPhase.RUNTIME:
|
||||
logger.info(f"Best tactic chosen: {self.tactic}")
|
||||
|
||||
def clone(self):
|
||||
cloned_plugin = CircPadPlugin()
|
||||
cloned_plugin.__dict__.update(self.__dict__)
|
||||
return cloned_plugin
|
||||
|
||||
#
|
||||
# The following defaults take effect since the respective methods are not overriden
|
||||
#
|
||||
|
||||
# def get_workspace_size(self, input_desc, output_desc):
|
||||
# return 0
|
||||
|
||||
# def destroy(self):
|
||||
# pass
|
||||
|
||||
|
||||
class CircPadPluginCreator(trt.IPluginCreatorV3One):
|
||||
def __init__(self):
|
||||
trt.IPluginCreatorV3One.__init__(self)
|
||||
self.name = "CircPadPlugin"
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_version = "1"
|
||||
self.field_names = trt.PluginFieldCollection([
|
||||
trt.PluginField("pads", np.array([]), trt.PluginFieldType.INT32),
|
||||
trt.PluginField(
|
||||
"per_format_tactics", np.array([]), trt.PluginFieldType.INT32
|
||||
),
|
||||
])
|
||||
|
||||
def create_plugin(self, name, fc, phase):
|
||||
return CircPadPlugin(fc, phase)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Options for Circular Padding plugin multi-tactic sample"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--precision",
|
||||
type=str,
|
||||
default="fp32",
|
||||
choices=["fp32", "fp16"],
|
||||
help="Precision to use for plugin",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--per-format-tactics",
|
||||
action="store_true",
|
||||
help="Whether per-format tactics or global tactics should be used",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
precision = np.float32 if args.precision == "fp32" else np.float16
|
||||
is_tactics_per_format = 1 if args.per_format_tactics else 0
|
||||
|
||||
inp_shape = (10, 3, 32, 32)
|
||||
X_A = np.random.normal(size=inp_shape).astype(precision)
|
||||
X_B = np.random.normal(size=inp_shape).astype(precision)
|
||||
|
||||
pads = (1, 1, 1, 1)
|
||||
|
||||
# Register plugin creator
|
||||
plg_registry = trt.get_plugin_registry()
|
||||
my_plugin_creator = CircPadPluginCreator()
|
||||
plg_registry.register_creator(my_plugin_creator, "")
|
||||
|
||||
# create ONNX model
|
||||
onnx_path = f"test_CircPadPlugin_multi_tactic_{args.precision}.onnx"
|
||||
inputA = gs.Variable(name="X_A", shape=inp_shape, dtype=precision)
|
||||
inputB = gs.Variable(name="X_B", shape=inp_shape, dtype=precision)
|
||||
Y_A = gs.Variable(name="Y_A", dtype=precision)
|
||||
Y_B = gs.Variable(name="Y_B", dtype=precision)
|
||||
myPluginNode_A = gs.Node(
|
||||
name="CircPadPlugin_A",
|
||||
op="CircPadPlugin",
|
||||
inputs=[inputA],
|
||||
outputs=[Y_A],
|
||||
attrs={
|
||||
"pads": pads,
|
||||
"per_format_tactics": np.array([is_tactics_per_format], dtype=np.int32),
|
||||
},
|
||||
)
|
||||
myPluginNode_B = gs.Node(
|
||||
name="CircPadPlugin_B",
|
||||
op="CircPadPlugin",
|
||||
inputs=[inputB],
|
||||
outputs=[Y_B],
|
||||
attrs={
|
||||
"pads": pads,
|
||||
"per_format_tactics": np.array([is_tactics_per_format], dtype=np.int32),
|
||||
},
|
||||
)
|
||||
|
||||
graph = gs.Graph(nodes=[myPluginNode_A, myPluginNode_B], inputs=[inputA, inputB], outputs=[Y_A, Y_B], opset=16)
|
||||
onnx.save(gs.export_onnx(graph), onnx_path)
|
||||
|
||||
# build engine
|
||||
build_engine = EngineFromNetwork(
|
||||
NetworkFromOnnxPath(onnx_path, strongly_typed=True), CreateConfig()
|
||||
)
|
||||
|
||||
Y_A_ref = np.pad(X_A, [[0, 0], [0, 0], [pads[0], pads[1]], [pads[2], pads[3]]], "wrap")
|
||||
Y_B_ref = np.pad(X_B, [[0, 0], [0, 0], [pads[0], pads[1]], [pads[2], pads[3]]], "wrap")
|
||||
|
||||
# Run
|
||||
with TrtRunner(build_engine, "trt_runner")as runner:
|
||||
outputs = runner.infer({"X_A": X_A, "X_B": X_B})
|
||||
Y_A_out = outputs["Y_A"]
|
||||
Y_B_out = outputs["Y_B"]
|
||||
|
||||
if np.allclose(Y_A_out, Y_A_ref):
|
||||
print("Inference result A correct!")
|
||||
else:
|
||||
print("Inference result A incorrect!")
|
||||
|
||||
if np.allclose(Y_B_out, Y_B_ref):
|
||||
print("Inference result B correct!")
|
||||
else:
|
||||
print("Inference result B incorrect!")
|
||||
@@ -0,0 +1,257 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import onnx_graphsurgeon as gs
|
||||
import numpy as np
|
||||
import onnx
|
||||
import cupy as cp
|
||||
from numba import cuda
|
||||
import sys
|
||||
import os
|
||||
|
||||
import tensorrt as trt
|
||||
from polygraphy.backend.trt import (
|
||||
CreateConfig,
|
||||
EngineFromNetwork,
|
||||
NetworkFromOnnxPath,
|
||||
TrtRunner,
|
||||
)
|
||||
|
||||
from polygraphy.json import to_json, from_json
|
||||
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
|
||||
from plugin_utils import volume, parseArgs
|
||||
|
||||
|
||||
|
||||
@cuda.jit
|
||||
def circ_pad(X, all_pads, orig_dims, Y, Y_shape, Y_len):
|
||||
index = cuda.blockIdx.x * cuda.blockDim.x + cuda.threadIdx.x
|
||||
stride = cuda.blockDim.x * cuda.gridDim.x
|
||||
|
||||
for i in range(index, Y_len, stride):
|
||||
i3 = int(i % Y_shape[3])
|
||||
i2 = int((i // Y_shape[3]) % Y_shape[2])
|
||||
i1 = int((i // Y_shape[3] // Y_shape[2]) % Y_shape[1])
|
||||
i0 = int(i // Y_shape[3] // Y_shape[2] // Y_shape[1])
|
||||
|
||||
j0 = int((i0 - all_pads[0]) % orig_dims[0])
|
||||
j1 = int((i1 - all_pads[2]) % orig_dims[1])
|
||||
j2 = int((i2 - all_pads[4]) % orig_dims[2])
|
||||
j3 = int((i3 - all_pads[6]) % orig_dims[3])
|
||||
|
||||
Y[i] = X[
|
||||
int(
|
||||
orig_dims[3] * orig_dims[2] * orig_dims[1] * j0
|
||||
+ orig_dims[3] * orig_dims[2] * j1
|
||||
+ orig_dims[3] * j2
|
||||
+ j3
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class CircPadPlugin(trt.IPluginV2DynamicExt):
|
||||
def __init__(self, fc=None):
|
||||
trt.IPluginV2DynamicExt.__init__(self)
|
||||
self.pads = []
|
||||
self.X_shape = []
|
||||
|
||||
self.num_outputs = 1
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_type = "CircPadPlugin"
|
||||
self.plugin_version = "1"
|
||||
|
||||
if fc is not None:
|
||||
assert fc[0].name == "pads"
|
||||
self.pads = fc[0].data
|
||||
|
||||
def get_output_datatype(self, index, input_types):
|
||||
return input_types[0]
|
||||
|
||||
def get_output_dimensions(self, output_index, inputs, exprBuilder):
|
||||
|
||||
output_dims = trt.DimsExprs(inputs[0])
|
||||
|
||||
for i in range(np.size(self.pads) // 2):
|
||||
output_dims[len(output_dims) - i - 1] = exprBuilder.operation(
|
||||
trt.DimensionOperation.SUM,
|
||||
inputs[0][len(output_dims) - i - 1],
|
||||
exprBuilder.constant(self.pads[i * 2] + self.pads[i * 2 + 1]),
|
||||
)
|
||||
|
||||
return output_dims
|
||||
|
||||
def serialize(self):
|
||||
return to_json({"pads": self.pads})
|
||||
|
||||
def configure_plugin(self, inp, out):
|
||||
X_dims = inp[0].desc.dims
|
||||
self.X_shape = np.zeros((len(X_dims),))
|
||||
for i in range(len(X_dims)):
|
||||
self.X_shape[i] = X_dims[i]
|
||||
|
||||
def supports_format_combination(self, pos, in_out, num_inputs):
|
||||
assert num_inputs == 1
|
||||
assert pos < len(in_out)
|
||||
|
||||
desc = in_out[pos]
|
||||
if desc.format != trt.TensorFormat.LINEAR:
|
||||
return False
|
||||
|
||||
# first input should be float16 or float32
|
||||
if pos == 0:
|
||||
return desc.type == trt.DataType.FLOAT or desc.type == trt.DataType.HALF
|
||||
|
||||
# output should have the same type as the input
|
||||
if pos == 1:
|
||||
return in_out[0].type == desc.type
|
||||
|
||||
assert False
|
||||
|
||||
def enqueue(self, input_desc, output_desc, inputs, outputs, workspace, stream):
|
||||
|
||||
inp_dtype = trt.nptype(input_desc[0].type)
|
||||
|
||||
a_mem = cp.cuda.UnownedMemory(
|
||||
inputs[0], volume(input_desc[0].dims) * cp.dtype(inp_dtype).itemsize, self
|
||||
)
|
||||
c_mem = cp.cuda.UnownedMemory(
|
||||
outputs[0],
|
||||
volume(output_desc[0].dims) * cp.dtype(inp_dtype).itemsize,
|
||||
self,
|
||||
)
|
||||
|
||||
a_ptr = cp.cuda.MemoryPointer(a_mem, 0)
|
||||
c_ptr = cp.cuda.MemoryPointer(c_mem, 0)
|
||||
|
||||
a = cp.ndarray((volume(input_desc[0].dims)), dtype=inp_dtype, memptr=a_ptr)
|
||||
c = cp.ndarray((volume(output_desc[0].dims)), dtype=inp_dtype, memptr=c_ptr)
|
||||
|
||||
numba_stream = cuda.external_stream(stream)
|
||||
|
||||
N = len(self.X_shape)
|
||||
all_pads = np.zeros((N * 2,))
|
||||
orig_dims = np.array(self.X_shape)
|
||||
out_dims = np.array(self.X_shape)
|
||||
|
||||
for i in range(np.size(pads) // 2):
|
||||
out_dims[N - i - 1] += pads[i * 2] + pads[i * 2 + 1]
|
||||
all_pads[N * 2 - 2 * i - 2] = pads[i * 2]
|
||||
all_pads[N * 2 - 2 * i - 1] = pads[i * 2 + 1]
|
||||
|
||||
all_pads_d = cp.asarray(all_pads)
|
||||
orig_dims_d = cp.asarray(orig_dims)
|
||||
Y_shape_d = cp.asarray(out_dims)
|
||||
|
||||
blockSize = 256
|
||||
numBlocks = int((np.prod(out_dims) + blockSize - 1) // blockSize)
|
||||
|
||||
circ_pad[numBlocks, blockSize, numba_stream](
|
||||
a, all_pads_d, orig_dims_d, c, Y_shape_d, np.prod(out_dims)
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
def clone(self):
|
||||
cloned_plugin = CircPadPlugin()
|
||||
cloned_plugin.__dict__.update(self.__dict__)
|
||||
return cloned_plugin
|
||||
|
||||
#
|
||||
# The following defaults take effect since the respective methods are not overriden
|
||||
#
|
||||
|
||||
# def initialize(self):
|
||||
# pass
|
||||
|
||||
# def get_serialization_size(self):
|
||||
# return len(to_json({"pads": self.pads}))
|
||||
|
||||
# def get_workspace_size(self, input_desc, output_desc):
|
||||
# return 0
|
||||
|
||||
# def destroy(self):
|
||||
# pass
|
||||
|
||||
# def terminate(self):
|
||||
# pass
|
||||
|
||||
|
||||
class CircPadPluginCreator(trt.IPluginCreator):
|
||||
def __init__(self):
|
||||
trt.IPluginCreator.__init__(self)
|
||||
self.name = "CircPadPlugin"
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_version = "1"
|
||||
self.field_names = trt.PluginFieldCollection(
|
||||
[trt.PluginField("pads", np.array([]), trt.PluginFieldType.INT32)]
|
||||
)
|
||||
|
||||
def create_plugin(self, name, fc):
|
||||
return CircPadPlugin(fc)
|
||||
|
||||
def deserialize_plugin(self, name, data):
|
||||
j = dict(from_json(data.decode("utf-8")))
|
||||
deserialized = CircPadPlugin()
|
||||
deserialized.__dict__.update(j)
|
||||
return deserialized
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = parseArgs()
|
||||
precision = np.float32 if args.precision == "fp32" else np.float16
|
||||
|
||||
inp_shape = (10, 3, 32, 32)
|
||||
X = np.random.normal(size=inp_shape).astype(precision)
|
||||
|
||||
pads = (1, 1, 1, 1)
|
||||
|
||||
# Register plugin creator
|
||||
plg_registry = trt.get_plugin_registry()
|
||||
my_plugin_creator = CircPadPluginCreator()
|
||||
plg_registry.register_creator(my_plugin_creator, "")
|
||||
|
||||
# create ONNX model
|
||||
onnx_path = f"test_CircPadPlugin_numba_{args.precision}.onnx"
|
||||
inputA = gs.Variable(name="X", shape=inp_shape, dtype=precision)
|
||||
Y = gs.Variable(name="Y", dtype=precision)
|
||||
myPluginNode = gs.Node(
|
||||
name="CircPadPlugin",
|
||||
op="CircPadPlugin",
|
||||
inputs=[inputA],
|
||||
outputs=[Y],
|
||||
attrs={"pads": pads},
|
||||
)
|
||||
graph = gs.Graph(nodes=[myPluginNode], inputs=[inputA], outputs=[Y], opset=16)
|
||||
onnx.save(gs.export_onnx(graph), onnx_path)
|
||||
|
||||
# build engine
|
||||
build_engine = EngineFromNetwork(
|
||||
NetworkFromOnnxPath(onnx_path, strongly_typed=True), CreateConfig()
|
||||
)
|
||||
|
||||
Y_ref = np.pad(X, [[0, 0], [0, 0], [pads[0], pads[1]], [pads[2], pads[3]]], "wrap")
|
||||
# Run
|
||||
with TrtRunner(build_engine, "trt_runner") as runner:
|
||||
outputs = runner.infer({"X": X})
|
||||
Y = outputs["Y"]
|
||||
|
||||
if np.allclose(Y, Y_ref):
|
||||
print("Inference result correct!")
|
||||
else:
|
||||
print("Inference result incorrect!")
|
||||
@@ -0,0 +1,214 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import onnx_graphsurgeon as gs
|
||||
import numpy as np
|
||||
import onnx
|
||||
import cupy as cp
|
||||
import sys
|
||||
import os
|
||||
|
||||
import tensorrt as trt
|
||||
from polygraphy.backend.trt import (
|
||||
CreateConfig,
|
||||
EngineFromNetwork,
|
||||
NetworkFromOnnxPath,
|
||||
TrtRunner,
|
||||
)
|
||||
|
||||
from polygraphy.json import to_json, from_json
|
||||
import torch
|
||||
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
|
||||
from plugin_utils import volume, parseArgs
|
||||
|
||||
|
||||
|
||||
class CircPadPlugin(trt.IPluginV2DynamicExt):
|
||||
def __init__(self, fc=None):
|
||||
trt.IPluginV2DynamicExt.__init__(self)
|
||||
self.pads = []
|
||||
self.X_shape = []
|
||||
|
||||
self.num_outputs = 1
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_type = "CircPadPlugin"
|
||||
self.plugin_version = "1"
|
||||
|
||||
if fc is not None:
|
||||
assert fc[0].name == "pads"
|
||||
self.pads = fc[0].data
|
||||
|
||||
def get_output_datatype(self, index, input_types):
|
||||
return input_types[0]
|
||||
|
||||
def get_output_dimensions(self, output_index, inputs, exprBuilder):
|
||||
|
||||
output_dims = trt.DimsExprs(inputs[0])
|
||||
|
||||
for i in range(np.size(self.pads) // 2):
|
||||
output_dims[len(output_dims) - i - 1] = exprBuilder.operation(
|
||||
trt.DimensionOperation.SUM,
|
||||
inputs[0][len(output_dims) - i - 1],
|
||||
exprBuilder.constant(self.pads[i * 2] + self.pads[i * 2 + 1]),
|
||||
)
|
||||
|
||||
return output_dims
|
||||
|
||||
def serialize(self):
|
||||
return to_json({"pads": self.pads})
|
||||
|
||||
def configure_plugin(self, inp, out):
|
||||
X_dims = inp[0].desc.dims
|
||||
self.X_shape = np.zeros((len(X_dims),))
|
||||
for i in range(len(X_dims)):
|
||||
self.X_shape[i] = X_dims[i]
|
||||
|
||||
def supports_format_combination(self, pos, in_out, num_inputs):
|
||||
assert num_inputs == 1
|
||||
assert pos < len(in_out)
|
||||
|
||||
desc = in_out[pos]
|
||||
if desc.format != trt.TensorFormat.LINEAR:
|
||||
return False
|
||||
|
||||
# first input should be float16 or float32
|
||||
if pos == 0:
|
||||
return desc.type == trt.DataType.FLOAT or desc.type == trt.DataType.HALF
|
||||
|
||||
# output should have the same type as the input
|
||||
if pos == 1:
|
||||
return in_out[0].type == desc.type
|
||||
|
||||
assert False
|
||||
|
||||
def enqueue(self, input_desc, output_desc, inputs, outputs, workspace, stream):
|
||||
|
||||
inp_dtype = trt.nptype(input_desc[0].type)
|
||||
|
||||
a_mem = cp.cuda.UnownedMemory(
|
||||
inputs[0], volume(input_desc[0].dims) * cp.dtype(inp_dtype).itemsize, self
|
||||
)
|
||||
c_mem = cp.cuda.UnownedMemory(
|
||||
outputs[0],
|
||||
volume(output_desc[0].dims) * cp.dtype(inp_dtype).itemsize,
|
||||
self,
|
||||
)
|
||||
|
||||
a_ptr = cp.cuda.MemoryPointer(a_mem, 0)
|
||||
c_ptr = cp.cuda.MemoryPointer(c_mem, 0)
|
||||
|
||||
a_d = cp.ndarray(tuple(input_desc[0].dims), dtype=inp_dtype, memptr=a_ptr)
|
||||
c_d = cp.ndarray((volume(output_desc[0].dims)), dtype=inp_dtype, memptr=c_ptr)
|
||||
|
||||
a_t = torch.as_tensor(a_d, device="cuda")
|
||||
|
||||
# Use PyTorch functional op - no need to write kernel
|
||||
out = torch.nn.functional.pad(a_t, self.pads.tolist(), mode="circular")
|
||||
cp.copyto(c_d, cp.reshape(cp.asarray(out), (-1,)))
|
||||
|
||||
return 0
|
||||
|
||||
def clone(self):
|
||||
cloned_plugin = CircPadPlugin()
|
||||
cloned_plugin.__dict__.update(self.__dict__)
|
||||
return cloned_plugin
|
||||
|
||||
#
|
||||
# The following defaults take effect since the respective methods are not overriden
|
||||
#
|
||||
|
||||
# def initialize(self):
|
||||
# pass
|
||||
|
||||
# def get_serialization_size(self):
|
||||
# return len(to_json({"pads": self.pads}))
|
||||
|
||||
# def get_workspace_size(self, input_desc, output_desc):
|
||||
# return 0
|
||||
|
||||
# def destroy(self):
|
||||
# pass
|
||||
|
||||
# def terminate(self):
|
||||
# pass
|
||||
|
||||
|
||||
class CircPadPluginCreator(trt.IPluginCreator):
|
||||
def __init__(self):
|
||||
trt.IPluginCreator.__init__(self)
|
||||
self.name = "CircPadPlugin"
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_version = "1"
|
||||
self.field_names = trt.PluginFieldCollection(
|
||||
[trt.PluginField("pads", np.array([]), trt.PluginFieldType.INT32)]
|
||||
)
|
||||
|
||||
def create_plugin(self, name, fc):
|
||||
return CircPadPlugin(fc)
|
||||
|
||||
def deserialize_plugin(self, name, data):
|
||||
j = dict(from_json(data.decode("utf-8")))
|
||||
deserialized = CircPadPlugin()
|
||||
deserialized.__dict__.update(j)
|
||||
return deserialized
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = parseArgs()
|
||||
precision = np.float32 if args.precision == "fp32" else np.float16
|
||||
|
||||
inp_shape = (10, 3, 32, 32)
|
||||
X = np.random.normal(size=inp_shape).astype(precision)
|
||||
|
||||
pads = (1, 1, 1, 1)
|
||||
|
||||
# Register plugin creator
|
||||
plg_registry = trt.get_plugin_registry()
|
||||
my_plugin_creator = CircPadPluginCreator()
|
||||
plg_registry.register_creator(my_plugin_creator, "")
|
||||
|
||||
# create ONNX model
|
||||
onnx_path = f"test_CircPadPlugin_torch_{args.precision}.onnx"
|
||||
inputA = gs.Variable(name="X", shape=inp_shape, dtype=precision)
|
||||
Y = gs.Variable(name="Y", dtype=precision)
|
||||
myPluginNode = gs.Node(
|
||||
name="CircPadPlugin",
|
||||
op="CircPadPlugin",
|
||||
inputs=[inputA],
|
||||
outputs=[Y],
|
||||
attrs={"pads": pads},
|
||||
)
|
||||
graph = gs.Graph(nodes=[myPluginNode], inputs=[inputA], outputs=[Y], opset=16)
|
||||
onnx.save(gs.export_onnx(graph), onnx_path)
|
||||
|
||||
# build engine
|
||||
build_engine = EngineFromNetwork(
|
||||
NetworkFromOnnxPath(onnx_path, strongly_typed=True), CreateConfig()
|
||||
)
|
||||
|
||||
Y_ref = np.pad(X, [[0, 0], [0, 0], [pads[0], pads[1]], [pads[2], pads[3]]], "wrap")
|
||||
# Run
|
||||
with TrtRunner(build_engine, "trt_runner") as runner:
|
||||
outputs = runner.infer({"X": X})
|
||||
Y = outputs["Y"]
|
||||
|
||||
if np.allclose(Y, Y_ref):
|
||||
print("Inference result correct!")
|
||||
else:
|
||||
print("Inference result incorrect!")
|
||||
@@ -0,0 +1,297 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import onnx_graphsurgeon as gs
|
||||
import numpy as np
|
||||
import onnx
|
||||
import cupy as cp
|
||||
import sys
|
||||
import os
|
||||
|
||||
import triton
|
||||
import triton.language as tl
|
||||
|
||||
import tensorrt as trt
|
||||
from polygraphy.backend.trt import (
|
||||
CreateConfig,
|
||||
EngineFromNetwork,
|
||||
NetworkFromOnnxPath,
|
||||
TrtRunner,
|
||||
)
|
||||
|
||||
from polygraphy.json import to_json, from_json
|
||||
import torch
|
||||
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
|
||||
from plugin_utils import volume, parseArgs
|
||||
|
||||
|
||||
|
||||
@triton.jit
|
||||
def circ_pad(
|
||||
X,
|
||||
all_pads_0,
|
||||
all_pads_2,
|
||||
all_pads_4,
|
||||
all_pads_6,
|
||||
orig_dims_0,
|
||||
orig_dims_1,
|
||||
orig_dims_2,
|
||||
orig_dims_3,
|
||||
Y,
|
||||
Y_shape_1,
|
||||
Y_shape_2,
|
||||
Y_shape_3,
|
||||
X_len,
|
||||
Y_len,
|
||||
BLOCK_SIZE: tl.constexpr,
|
||||
):
|
||||
pid = tl.program_id(0)
|
||||
i = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
|
||||
|
||||
mask_y = i < Y_len
|
||||
|
||||
i3 = i % Y_shape_3
|
||||
i2 = (i // Y_shape_3) % Y_shape_2
|
||||
i1 = (i // Y_shape_3 // Y_shape_2) % Y_shape_1
|
||||
i0 = i // Y_shape_3 // Y_shape_2 // Y_shape_1
|
||||
|
||||
j0 = (i0 - all_pads_0 + orig_dims_0) % orig_dims_0
|
||||
j1 = (i1 - all_pads_2 + orig_dims_1) % orig_dims_1
|
||||
j2 = (i2 - all_pads_4 + orig_dims_2) % orig_dims_2
|
||||
j3 = (i3 - all_pads_6 + orig_dims_3) % orig_dims_3
|
||||
|
||||
load_idx = (
|
||||
orig_dims_3 * orig_dims_2 * orig_dims_1 * j0
|
||||
+ orig_dims_3 * orig_dims_2 * j1
|
||||
+ orig_dims_3 * j2
|
||||
+ j3
|
||||
)
|
||||
mask_x = load_idx < X_len
|
||||
|
||||
x = tl.load(X + load_idx, mask=mask_x)
|
||||
|
||||
tl.store(Y + i, x, mask=mask_y)
|
||||
|
||||
|
||||
class CircPadPlugin(trt.IPluginV2DynamicExt):
|
||||
def __init__(self, fc=None):
|
||||
trt.IPluginV2DynamicExt.__init__(self)
|
||||
self.pads = []
|
||||
self.X_shape = []
|
||||
|
||||
self.num_outputs = 1
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_type = "CircPadPlugin"
|
||||
self.plugin_version = "1"
|
||||
|
||||
if fc is not None:
|
||||
assert fc[0].name == "pads"
|
||||
self.pads = fc[0].data
|
||||
|
||||
def get_output_datatype(self, index, input_types):
|
||||
return input_types[0]
|
||||
|
||||
def get_output_dimensions(self, output_index, inputs, exprBuilder):
|
||||
|
||||
output_dims = trt.DimsExprs(inputs[0])
|
||||
|
||||
for i in range(np.size(self.pads) // 2):
|
||||
output_dims[len(output_dims) - i - 1] = exprBuilder.operation(
|
||||
trt.DimensionOperation.SUM,
|
||||
inputs[0][len(output_dims) - i - 1],
|
||||
exprBuilder.constant(self.pads[i * 2] + self.pads[i * 2 + 1]),
|
||||
)
|
||||
|
||||
return output_dims
|
||||
|
||||
def serialize(self):
|
||||
return to_json({"pads": self.pads})
|
||||
|
||||
def configure_plugin(self, inp, out):
|
||||
X_dims = inp[0].desc.dims
|
||||
self.X_shape = np.zeros((len(X_dims),))
|
||||
for i in range(len(X_dims)):
|
||||
self.X_shape[i] = X_dims[i]
|
||||
|
||||
def supports_format_combination(self, pos, in_out, num_inputs):
|
||||
assert num_inputs == 1
|
||||
assert pos < len(in_out)
|
||||
|
||||
desc = in_out[pos]
|
||||
if desc.format != trt.TensorFormat.LINEAR:
|
||||
return False
|
||||
|
||||
# first input should be float16 or float32
|
||||
if pos == 0:
|
||||
return desc.type == trt.DataType.FLOAT or desc.type == trt.DataType.HALF
|
||||
|
||||
# output should have the same type as the input
|
||||
if pos == 1:
|
||||
return in_out[0].type == desc.type
|
||||
|
||||
assert False
|
||||
|
||||
def enqueue(self, input_desc, output_desc, inputs, outputs, workspace, stream):
|
||||
|
||||
inp_dtype = trt.nptype(input_desc[0].type)
|
||||
|
||||
a_mem = cp.cuda.UnownedMemory(
|
||||
inputs[0], volume(input_desc[0].dims) * cp.dtype(inp_dtype).itemsize, self
|
||||
)
|
||||
c_mem = cp.cuda.UnownedMemory(
|
||||
outputs[0],
|
||||
volume(output_desc[0].dims) * cp.dtype(inp_dtype).itemsize,
|
||||
self,
|
||||
)
|
||||
|
||||
a_ptr = cp.cuda.MemoryPointer(a_mem, 0)
|
||||
c_ptr = cp.cuda.MemoryPointer(c_mem, 0)
|
||||
|
||||
a_d = cp.ndarray((volume(input_desc[0].dims)), dtype=inp_dtype, memptr=a_ptr)
|
||||
c_d = cp.ndarray((volume(output_desc[0].dims)), dtype=inp_dtype, memptr=c_ptr)
|
||||
|
||||
a_t = torch.as_tensor(a_d, device="cuda")
|
||||
c_t = torch.as_tensor(c_d, device="cuda")
|
||||
|
||||
N = len(self.X_shape)
|
||||
all_pads = np.zeros((N * 2,), dtype=np.int32)
|
||||
orig_dims = np.array(self.X_shape, dtype=np.int32)
|
||||
out_dims = np.array(self.X_shape, dtype=np.int32)
|
||||
|
||||
for i in range(np.size(pads) // 2):
|
||||
out_dims[N - i - 1] += pads[i * 2] + pads[i * 2 + 1]
|
||||
all_pads[N * 2 - 2 * i - 2] = pads[i * 2]
|
||||
all_pads[N * 2 - 2 * i - 1] = pads[i * 2 + 1]
|
||||
|
||||
all_pads = all_pads.tolist()
|
||||
orig_dims = orig_dims.tolist()
|
||||
out_dims = out_dims.tolist()
|
||||
|
||||
blockSize = 256
|
||||
numBlocks = (int((np.prod(out_dims) + blockSize - 1) // blockSize),)
|
||||
|
||||
circ_pad[numBlocks](
|
||||
a_t,
|
||||
all_pads[0],
|
||||
all_pads[2],
|
||||
all_pads[4],
|
||||
all_pads[6],
|
||||
orig_dims[0],
|
||||
orig_dims[1],
|
||||
orig_dims[2],
|
||||
orig_dims[3],
|
||||
c_t,
|
||||
out_dims[1],
|
||||
out_dims[2],
|
||||
out_dims[3],
|
||||
int(np.prod(orig_dims)),
|
||||
int(np.prod(out_dims)),
|
||||
BLOCK_SIZE=256,
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
def clone(self):
|
||||
cloned_plugin = CircPadPlugin()
|
||||
cloned_plugin.__dict__.update(self.__dict__)
|
||||
return cloned_plugin
|
||||
|
||||
#
|
||||
# The following defaults take effect since the respective methods are not overriden
|
||||
#
|
||||
|
||||
# def initialize(self):
|
||||
# pass
|
||||
|
||||
# def get_serialization_size(self):
|
||||
# return len(to_json({"pads": self.pads}))
|
||||
|
||||
# def get_workspace_size(self, input_desc, output_desc):
|
||||
# return 0
|
||||
|
||||
# def destroy(self):
|
||||
# pass
|
||||
|
||||
# def terminate(self):
|
||||
# pass
|
||||
|
||||
|
||||
class CircPadPluginCreator(trt.IPluginCreator):
|
||||
def __init__(self):
|
||||
trt.IPluginCreator.__init__(self)
|
||||
self.name = "CircPadPlugin"
|
||||
self.plugin_namespace = ""
|
||||
self.plugin_version = "1"
|
||||
self.field_names = trt.PluginFieldCollection(
|
||||
[trt.PluginField("pads", np.array([]), trt.PluginFieldType.INT32)]
|
||||
)
|
||||
|
||||
def create_plugin(self, name, fc):
|
||||
return CircPadPlugin(fc)
|
||||
|
||||
def deserialize_plugin(self, name, data):
|
||||
j = dict(from_json(data.decode("utf-8")))
|
||||
deserialized = CircPadPlugin()
|
||||
deserialized.__dict__.update(j)
|
||||
return deserialized
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = parseArgs()
|
||||
precision = np.float32 if args.precision == "fp32" else np.float16
|
||||
|
||||
inp_shape = (10, 3, 32, 32)
|
||||
X = np.random.normal(size=inp_shape).astype(precision)
|
||||
|
||||
pads = (1, 1, 1, 1)
|
||||
|
||||
# Register plugin creator
|
||||
plg_registry = trt.get_plugin_registry()
|
||||
my_plugin_creator = CircPadPluginCreator()
|
||||
plg_registry.register_creator(my_plugin_creator, "")
|
||||
|
||||
# create ONNX model
|
||||
onnx_path = f"test_CircPadPlugin_triton_{args.precision}.onnx"
|
||||
inputA = gs.Variable(name="X", shape=inp_shape, dtype=precision)
|
||||
Y = gs.Variable(name="Y", dtype=precision)
|
||||
myPluginNode = gs.Node(
|
||||
name="CircPadPlugin",
|
||||
op="CircPadPlugin",
|
||||
inputs=[inputA],
|
||||
outputs=[Y],
|
||||
attrs={"pads": pads},
|
||||
)
|
||||
graph = gs.Graph(nodes=[myPluginNode], inputs=[inputA], outputs=[Y], opset=16)
|
||||
onnx.save(gs.export_onnx(graph), onnx_path)
|
||||
|
||||
# build engine
|
||||
build_engine = EngineFromNetwork(
|
||||
NetworkFromOnnxPath(onnx_path, strongly_typed=True), CreateConfig()
|
||||
)
|
||||
|
||||
Y_ref = np.pad(X, [[0, 0], [0, 0], [pads[0], pads[1]], [pads[2], pads[3]]], "wrap")
|
||||
# Run
|
||||
with TrtRunner(build_engine, "trt_runner") as runner:
|
||||
outputs = runner.infer({"X": X})
|
||||
Y = outputs["Y"]
|
||||
|
||||
if np.allclose(Y, Y_ref):
|
||||
print("Inference result correct!")
|
||||
else:
|
||||
print("Inference result incorrect!")
|
||||
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 1993-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "NvInfer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include <cuda.h>
|
||||
#include <cuda_fp16.h>
|
||||
|
||||
using namespace nvinfer1;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
static void caughtError(std::exception const& e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
#define ASSERT(condition) \
|
||||
do \
|
||||
{ \
|
||||
if (!(condition)) \
|
||||
{ \
|
||||
std::cout << "Assertion failure: " << #condition << std::endl; \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
template <typename Dtype>
|
||||
struct CudaBind
|
||||
{
|
||||
size_t mSize;
|
||||
Dtype* mPtr;
|
||||
|
||||
CudaBind(size_t size)
|
||||
{
|
||||
mSize = size;
|
||||
ASSERT(!cudaMalloc((void**) &mPtr, sizeof(Dtype) * mSize));
|
||||
}
|
||||
|
||||
~CudaBind()
|
||||
{
|
||||
if (mPtr != nullptr)
|
||||
{
|
||||
ASSERT(!cudaFree(mPtr));
|
||||
mPtr = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static int64_t volume(Dims const& dims)
|
||||
{
|
||||
return std::accumulate(dims.d, dims.d + dims.nbDims, int64_t{1}, std::multiplies<int64_t>{});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void circPadKernel(
|
||||
T const* x, int32_t const* allPads, int32_t const* origDims, T* y, int32_t const* yShape, int32_t yLen)
|
||||
{
|
||||
int32_t index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int32_t stride = blockDim.x * gridDim.x;
|
||||
|
||||
for (int32_t i = index; i < yLen; i += stride)
|
||||
{
|
||||
int32_t i3 = i % yShape[3];
|
||||
int32_t i2 = (i / yShape[3]) % yShape[2];
|
||||
int32_t i1 = (i / yShape[3] / yShape[2]) % yShape[1];
|
||||
int32_t i0 = i / yShape[3] / yShape[2] / yShape[1];
|
||||
|
||||
int32_t j0 = (i0 - allPads[0] + origDims[0]) % origDims[0];
|
||||
int32_t j1 = (i1 - allPads[2] + origDims[1]) % origDims[1];
|
||||
int32_t j2 = (i2 - allPads[4] + origDims[2]) % origDims[2];
|
||||
int32_t j3 = (i3 - allPads[6] + origDims[3]) % origDims[3];
|
||||
|
||||
y[i] = x[origDims[3] * origDims[2] * origDims[1] * j0 + origDims[3] * origDims[2] * j1 + origDims[3] * j2 + j3];
|
||||
}
|
||||
}
|
||||
|
||||
class CircPadPlugin : public IPluginV3,
|
||||
public IPluginV3OneCore,
|
||||
public IPluginV3OneBuild,
|
||||
public IPluginV3OneRuntime
|
||||
{
|
||||
public:
|
||||
CircPadPlugin() = default;
|
||||
|
||||
CircPadPlugin(std::vector<int32_t> pads)
|
||||
: mPads(std::move(pads))
|
||||
{
|
||||
}
|
||||
|
||||
CircPadPlugin(CircPadPlugin const& p) = default;
|
||||
|
||||
~CircPadPlugin() override = default;
|
||||
|
||||
int32_t getNbOutputs() const noexcept override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool supportsFormatCombination(
|
||||
int32_t pos, DynamicPluginTensorDesc const* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept override
|
||||
{
|
||||
PluginTensorDesc const& desc = inOut[pos].desc;
|
||||
if (desc.format != TensorFormat::kLINEAR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// first input should be float16 or float32
|
||||
if (pos == 0)
|
||||
{
|
||||
return (desc.type == DataType::kFLOAT || desc.type == DataType::kHALF);
|
||||
}
|
||||
|
||||
// output should have the same type as the input
|
||||
if (pos == 1)
|
||||
{
|
||||
return (desc.type == inOut[0].desc.type);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t enqueue(PluginTensorDesc const* inputDesc, PluginTensorDesc const* outputDesc, void const* const* inputs,
|
||||
void* const* outputs, void* workspace, cudaStream_t stream) noexcept override
|
||||
{
|
||||
auto inpDType = inputDesc[0].type;
|
||||
|
||||
int32_t const blockSize = 256;
|
||||
int32_t const numBlocks = (volume(outputDesc[0].dims) + blockSize - 1) / blockSize;
|
||||
|
||||
ASSERT(inpDType == DataType::kFLOAT || inpDType == DataType::kHALF);
|
||||
|
||||
if (inpDType == DataType::kFLOAT)
|
||||
{
|
||||
circPadKernel<float><<<numBlocks, blockSize, 0, stream>>>(static_cast<float const*>(inputs[0]),
|
||||
mAllPadsPtr->mPtr, mOrigDimsPtr->mPtr, static_cast<float*>(outputs[0]), mOutDimsPtr->mPtr,
|
||||
volume(outputDesc[0].dims));
|
||||
}
|
||||
else if (inpDType == DataType::kHALF)
|
||||
{
|
||||
circPadKernel<half><<<numBlocks, blockSize, 0, stream>>>(static_cast<half const*>(inputs[0]),
|
||||
mAllPadsPtr->mPtr, mOrigDimsPtr->mPtr, static_cast<half*>(outputs[0]), mOutDimsPtr->mPtr,
|
||||
volume(outputDesc[0].dims));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char const* getPluginName() const noexcept override
|
||||
{
|
||||
return "CircPadPlugin";
|
||||
}
|
||||
|
||||
char const* getPluginVersion() const noexcept override
|
||||
{
|
||||
return "1";
|
||||
}
|
||||
|
||||
IPluginV3* clone() noexcept override
|
||||
{
|
||||
try
|
||||
{
|
||||
auto plugin = std::make_unique<CircPadPlugin>(*this);
|
||||
// Build-time clones do not need GPU memory. Clear shared_ptrs so the
|
||||
// clone does not share GPU allocations with the source.
|
||||
plugin->mAllPadsPtr.reset();
|
||||
plugin->mOrigDimsPtr.reset();
|
||||
plugin->mOutDimsPtr.reset();
|
||||
return plugin.release();
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
caughtError(e);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setPluginNamespace(char const* libNamespace) noexcept
|
||||
{
|
||||
mNamespace = libNamespace;
|
||||
}
|
||||
|
||||
char const* getPluginNamespace() const noexcept override
|
||||
{
|
||||
return mNamespace.c_str();
|
||||
}
|
||||
|
||||
int32_t getOutputDataTypes(
|
||||
DataType* outputTypes, int32_t nbOutputs, DataType const* inputTypes, int32_t nbInputs) const noexcept override
|
||||
{
|
||||
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
|
||||
{
|
||||
outputs[0] = inputs[0];
|
||||
int32_t nbOutDims = inputs[0].nbDims;
|
||||
|
||||
for (int32_t i = 0; i < static_cast<int32_t>(mPads.size()) / 2; ++i)
|
||||
{
|
||||
outputs[0].d[nbOutDims - i - 1] = exprBuilder.operation(DimensionOperation::kSUM,
|
||||
*inputs[0].d[nbOutDims - i - 1], *exprBuilder.constant(mPads[i * 2] + mPads[i * 2 + 1]));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t configurePlugin(DynamicPluginTensorDesc const* in, int32_t nbInputs,
|
||||
DynamicPluginTensorDesc const* out, int32_t nbOutputs) noexcept override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t getWorkspaceSize(DynamicPluginTensorDesc const* inputs, int32_t nbInputs,
|
||||
DynamicPluginTensorDesc const* outputs, int32_t nbOutputs) const noexcept override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
IPluginCapability* getCapabilityInterface(PluginCapabilityType type) noexcept override
|
||||
{
|
||||
if (type == PluginCapabilityType::kBUILD)
|
||||
{
|
||||
return static_cast<IPluginV3OneBuild*>(this);
|
||||
}
|
||||
if (type == PluginCapabilityType::kRUNTIME)
|
||||
{
|
||||
return static_cast<IPluginV3OneRuntime*>(this);
|
||||
}
|
||||
ASSERT(type == PluginCapabilityType::kCORE);
|
||||
return static_cast<IPluginV3OneCore*>(this);
|
||||
}
|
||||
|
||||
int32_t onShapeChange(
|
||||
PluginTensorDesc const* in, int32_t nbInputs, PluginTensorDesc const* out, int32_t nbOutputs) noexcept override
|
||||
{
|
||||
mN = in[0].dims.nbDims;
|
||||
|
||||
std::vector<int32_t> allPads(mN * 2);
|
||||
std::vector<int32_t> origDims(mN);
|
||||
std::vector<int32_t> outDims(mN);
|
||||
|
||||
for (int32_t i = 0; i < mN; ++i)
|
||||
{
|
||||
origDims[i] = in[0].dims.d[i];
|
||||
outDims[i] = in[0].dims.d[i];
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < static_cast<int32_t>(mPads.size()) / 2; ++i)
|
||||
{
|
||||
outDims[mN - i - 1] += mPads[i * 2] + mPads[i * 2 + 1];
|
||||
allPads[mN * 2 - 2 * i - 2] = mPads[i * 2];
|
||||
allPads[mN * 2 - 2 * i - 1] = mPads[i * 2 + 1];
|
||||
}
|
||||
|
||||
mAllPadsPtr = std::make_shared<CudaBind<int32_t>>(mN * 2);
|
||||
mOrigDimsPtr = std::make_shared<CudaBind<int32_t>>(mN);
|
||||
mOutDimsPtr = std::make_shared<CudaBind<int32_t>>(mN);
|
||||
|
||||
ASSERT(
|
||||
!cudaMemcpy(mAllPadsPtr->mPtr, &allPads.front(), allPads.size() * sizeof(int32_t), cudaMemcpyHostToDevice));
|
||||
ASSERT(!cudaMemcpy(
|
||||
mOrigDimsPtr->mPtr, &origDims.front(), origDims.size() * sizeof(int32_t), cudaMemcpyHostToDevice));
|
||||
ASSERT(
|
||||
!cudaMemcpy(mOutDimsPtr->mPtr, &outDims.front(), outDims.size() * sizeof(int32_t), cudaMemcpyHostToDevice));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IPluginV3* attachToContext(IPluginResourceContext* context) noexcept override
|
||||
{
|
||||
return clone();
|
||||
}
|
||||
|
||||
PluginFieldCollection const* getFieldsToSerialize() noexcept override
|
||||
{
|
||||
mDataToSerialize.clear();
|
||||
mDataToSerialize.emplace_back("pads", mPads.data(), PluginFieldType::kINT32, mPads.size());
|
||||
mFCToSerialize.nbFields = mDataToSerialize.size();
|
||||
mFCToSerialize.fields = mDataToSerialize.data();
|
||||
return &mFCToSerialize;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int32_t> mPads{};
|
||||
int32_t mN{};
|
||||
std::shared_ptr<CudaBind<int32_t>> mAllPadsPtr{};
|
||||
std::shared_ptr<CudaBind<int32_t>> mOrigDimsPtr{};
|
||||
std::shared_ptr<CudaBind<int32_t>> mOutDimsPtr{};
|
||||
std::string mNamespace;
|
||||
|
||||
std::vector<PluginField> mDataToSerialize;
|
||||
PluginFieldCollection mFCToSerialize;
|
||||
};
|
||||
|
||||
class CircPadPluginCreator : public IPluginCreatorV3One
|
||||
{
|
||||
public:
|
||||
CircPadPluginCreator()
|
||||
{
|
||||
mPluginAttributes.clear();
|
||||
mPluginAttributes.emplace_back(PluginField("pads", nullptr, PluginFieldType::kINT32, 1));
|
||||
mFC.nbFields = mPluginAttributes.size();
|
||||
mFC.fields = mPluginAttributes.data();
|
||||
}
|
||||
|
||||
char const* getPluginName() const noexcept override
|
||||
{
|
||||
return "CircPadPlugin";
|
||||
}
|
||||
|
||||
char const* getPluginVersion() const noexcept override
|
||||
{
|
||||
return "1";
|
||||
}
|
||||
|
||||
PluginFieldCollection const* getFieldNames() noexcept override
|
||||
{
|
||||
return &mFC;
|
||||
}
|
||||
|
||||
IPluginV3* createPlugin(char const* name, PluginFieldCollection const* fc, TensorRTPhase phase) noexcept override
|
||||
{
|
||||
try
|
||||
{
|
||||
std::vector<int32_t> pads;
|
||||
|
||||
for (int32_t i = 0; i < fc->nbFields; i++)
|
||||
{
|
||||
if (fc->fields[i].name == "pads"sv)
|
||||
{
|
||||
pads.resize(fc->fields[i].length);
|
||||
auto const* padsPtr = static_cast<int32_t const*>(fc->fields[i].data);
|
||||
std::copy_n(padsPtr, fc->fields[i].length, pads.data());
|
||||
}
|
||||
}
|
||||
|
||||
return new CircPadPlugin(pads);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
caughtError(e);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setPluginNamespace(char const* libNamespace) noexcept
|
||||
{
|
||||
mNamespace = libNamespace;
|
||||
}
|
||||
|
||||
char const* getPluginNamespace() const noexcept override
|
||||
{
|
||||
return mNamespace.c_str();
|
||||
}
|
||||
|
||||
private:
|
||||
PluginFieldCollection mFC;
|
||||
std::vector<PluginField> mPluginAttributes;
|
||||
std::string mNamespace;
|
||||
};
|
||||
|
||||
REGISTER_TENSORRT_PLUGIN(CircPadPluginCreator);
|
||||
@@ -0,0 +1,17 @@
|
||||
cuda-python==12.9.0
|
||||
cupy-cuda12x
|
||||
numba
|
||||
numba-cuda[cu12]
|
||||
triton; platform_system != "Windows"
|
||||
torch
|
||||
--extra-index-url https://pypi.ngc.nvidia.com
|
||||
polygraphy
|
||||
colored
|
||||
numpy==1.26.4
|
||||
onnx==1.18.0; platform_system == "Windows"
|
||||
--extra-index-url https://pypi.ngc.nvidia.com
|
||||
onnx-graphsurgeon
|
||||
pywin32; platform_system == "Windows"
|
||||
pyyaml==6.0.3
|
||||
requests==2.32.4
|
||||
tqdm==4.66.4
|
||||
Reference in New Issue
Block a user