TensorRT 10.0 Release

Signed-off-by: Asfiya Baig <asfiyab@nvidia.com>
This commit is contained in:
Asfiya Baig
2024-04-02 10:53:30 -07:00
committed by Rajeev Rao
parent 3d97932454
commit 147005ffe1
941 changed files with 50673 additions and 37787 deletions
@@ -0,0 +1,66 @@
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 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.
#
# We need cmake >= 3.8, since 3.8 introduced CUDA as a first class language
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
project(CircPadPlugin LANGUAGES CXX CUDA)
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)
set_ifndef(CUDA_INC_DIR /usr/local/cuda/include)
set_ifndef(CUDA_LIB_DIR /usr/local/cuda)
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})
find_library(_CUDA_LIB cuda HINTS ${CUDA_LIB_DIR} PATH_SUFFIXES lib/stubs lib64/stubs)
set_ifndef(CUDA_LIB ${_CUDA_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 ${CUDA_INC_DIR}
PUBLIC ${TRT_INCLUDE}
)
set_property(TARGET circ_pad_plugin PROPERTY CUDA_STANDARD 14)
target_link_libraries(circ_pad_plugin PRIVATE ${NVINFER_LIB})
target_link_libraries(circ_pad_plugin PRIVATE ${CUDA_LIB})
+165
View File
@@ -0,0 +1,165 @@
# 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):
![alt text](circ_pad_example.png "Circular padding example")
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.
### 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.
# 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
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

@@ -0,0 +1,82 @@
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 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 = "test_CircPadPlugin.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), CreateConfig(fp16=precision==np.float16)
)
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,336 @@
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 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 tensorrt as trt
from polygraphy.backend.trt import (
CreateConfig,
EngineFromNetwork,
NetworkFromOnnxPath,
TrtRunner
)
from polygraphy.json import to_json, from_json
from utils import checkCudaErrors, KernelHelper, parseArgs, CudaCtxManager
from cuda import 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):
err, self.cuDevice = cuda.cuDeviceGet(0)
trt.get_plugin_registry().acquire_plugin_resource("cuda_ctx", CudaCtxManager(self.cuDevice))
self.all_pads_d = checkCudaErrors(cuda.cuMemAlloc(np.int32().itemsize * self.N * 2))
self.orig_dims_d = checkCudaErrors(cuda.cuMemAlloc(np.int32().itemsize * self.N))
self.Y_shape_d = checkCudaErrors(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:
checkCudaErrors(cuda.cuMemcpyHtoD(self.all_pads_d, all_pads, all_pads.nbytes))
if self.orig_dims_d:
checkCudaErrors(cuda.cuMemcpyHtoD(self.orig_dims_d, orig_dims, orig_dims.nbytes))
if self.Y_shape_d:
checkCudaErrors(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')
checkCudaErrors(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')
checkCudaErrors(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:
checkCudaErrors(cuda.cuMemFree(self.all_pads_d))
if self.orig_dims_d:
checkCudaErrors(cuda.cuMemFree(self.orig_dims_d))
if self.Y_shape_d:
checkCudaErrors(cuda.cuMemFree(self.Y_shape_d))
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
err, = cuda.cuInit(0)
# Retrieve handle for device 0
err, cuDevice = cuda.cuDeviceGet(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 = "test_CircPadPlugin.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), CreateConfig(fp16=precision==np.float16)
)
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")
@@ -0,0 +1,290 @@
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 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 tensorrt as trt
from polygraphy.backend.trt import (
CreateConfig,
EngineFromNetwork,
NetworkFromOnnxPath,
TrtRunner
)
from polygraphy.json import to_json, from_json
from 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 = "test_CircPadPlugin.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), CreateConfig(fp16=precision==np.float16)
)
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,336 @@
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 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 tensorrt as trt
from polygraphy.backend.trt import (
CreateConfig,
TrtRunner,
create_network,
engine_from_network
)
from polygraphy.json import to_json, from_json
from utils import checkCudaErrors, KernelHelper, parseArgs, CudaCtxManager
from cuda import 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):
err, self.cuDevice = cuda.cuDeviceGet(0)
trt.get_plugin_registry().acquire_plugin_resource("cuda_ctx", CudaCtxManager(self.cuDevice))
self.all_pads_d = checkCudaErrors(cuda.cuMemAlloc(np.int32().itemsize * self.N * 2))
self.orig_dims_d = checkCudaErrors(cuda.cuMemAlloc(np.int32().itemsize * self.N))
self.Y_shape_d = checkCudaErrors(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:
checkCudaErrors(cuda.cuMemcpyHtoD(self.all_pads_d, all_pads, all_pads.nbytes))
if self.orig_dims_d:
checkCudaErrors(cuda.cuMemcpyHtoD(self.orig_dims_d, orig_dims, orig_dims.nbytes))
if self.Y_shape_d:
checkCudaErrors(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')
checkCudaErrors(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')
checkCudaErrors(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:
checkCudaErrors(cuda.cuMemFree(self.all_pads_d))
if self.orig_dims_d:
checkCudaErrors(cuda.cuMemFree(self.orig_dims_d))
if self.Y_shape_d:
checkCudaErrors(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
err, = cuda.cuInit(0)
# Retrieve handle for device 0
err, cuDevice = 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()
plg_creator = plg_registry.get_plugin_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(fp16=precision==trt.float16))
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,318 @@
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2024 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 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
from utils import volume, parseArgs
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.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:
assert fc[0].name == "pads"
self.pads = fc[0].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)
])
def configure_plugin(self, inp, out):
pass
def on_shape_change(self, inp, out):
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):
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)
])
def create_plugin(self, name, fc, phase):
return CircPadPlugin(fc, phase)
if __name__ == "__main__":
logging.basicConfig()
logger.setLevel(logging.INFO)
args = parseArgs()
precision = np.float32 if args.precision == "fp32" else np.float16
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 = "test_CircPadPlugin.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},
)
myPluginNode_B = gs.Node(
name="CircPadPlugin_B",
op="CircPadPlugin",
inputs=[inputB],
outputs=[Y_B],
attrs={"pads": pads},
)
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), CreateConfig(fp16=precision==np.float16)
)
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,249 @@
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 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 tensorrt as trt
from polygraphy.backend.trt import (
CreateConfig,
EngineFromNetwork,
NetworkFromOnnxPath,
TrtRunner,
)
from polygraphy.json import to_json, from_json
from 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 = "test_CircPadPlugin.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), CreateConfig(fp16=precision==np.float16)
)
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,208 @@
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 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 tensorrt as trt
from polygraphy.backend.trt import (
CreateConfig,
EngineFromNetwork,
NetworkFromOnnxPath,
TrtRunner,
)
from polygraphy.json import to_json, from_json
import torch
from 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 = "test_CircPadPlugin.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), CreateConfig(fp16=precision==np.float16)
)
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,263 @@
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 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 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
from 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 = "test_CircPadPlugin.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), CreateConfig(fp16=precision==np.float16)
)
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,415 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 1993-2023 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 <iostream>
#include <memory>
#include <numeric>
#include <vector>
#include <cuda.h>
#include <cuda_fp16.h>
using namespace nvinfer1;
static void caughtError(std::exception const& e)
{
std::cout << e.what() << std::endl;
}
// Write values into buffer
template <typename T>
void write(char*& buffer, T const& val)
{
std::memcpy(buffer, &val, sizeof(T));
buffer += sizeof(T);
}
// Read values from buffer
template <typename T>
T read(char const*& buffer)
{
T val{};
std::memcpy(&val, buffer, sizeof(T));
buffer += sizeof(T);
return val;
}
#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 nvinfer1::IPluginV2DynamicExt
{
public:
CircPadPlugin() = default;
CircPadPlugin(std::vector<int32_t> pads)
: mPads(pads)
{
}
CircPadPlugin(CircPadPlugin const& p) = default;
CircPadPlugin(void const* serialData, size_t length)
{
ASSERT(serialData != nullptr);
char const* d = static_cast<char const*>(serialData);
char const* a = d;
int32_t padsSize = read<int32_t>(d);
mPads.resize(padsSize);
for (int i = 0; i < padsSize; ++i)
{
mPads[i] = read<int32_t>(d);
}
ASSERT(d == a + length);
}
int32_t getNbOutputs() const noexcept override
{
return 1;
}
bool supportsFormatCombination(
int32_t pos, PluginTensorDesc const* inOut, int32_t nbInputs, int32_t nbOutputs) noexcept
{
PluginTensorDesc const& desc = inOut[pos];
if (desc.format != TensorFormat::kLINEAR)
{
return false;
}
// first input should be float16 or float32
if (pos == 0)
{
return (inOut[pos].type == nvinfer1::DataType::kFLOAT || inOut[pos].type == nvinfer1::DataType::kHALF);
}
// output should have the same type as the input
if (pos == 1)
{
return (inOut[pos].type == inOut[0].type);
}
return false;
}
void configureWithFormat(nvinfer1::Dims const*, int32_t, nvinfer1::Dims const*, int32_t, nvinfer1::DataType type,
nvinfer1::PluginFormat floatFormat, int32_t) noexcept override
{
}
int32_t initialize() noexcept override
{
return 0;
}
void terminate() noexcept override
{
mAllPadsPtr.reset();
mOrigDimsPtr.reset();
mOutDimsPtr.reset();
}
int32_t enqueue(PluginTensorDesc const* inputDesc, PluginTensorDesc const* outputDesc, void const* const* inputs,
void* const* outputs, void* workspace, cudaStream_t stream) noexcept
{
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;
}
size_t getSerializationSize() const noexcept override
{
return (mPads.size() + 1) * sizeof(int32_t);
}
void serialize(void* buffer) const noexcept override
{
ASSERT(buffer != nullptr);
char* d = static_cast<char*>(buffer);
char* a = d;
write(d, static_cast<int32_t>(mPads.size()));
for (int i = 0; i < mPads.size(); ++i)
{
write(d, mPads[i]);
}
ASSERT(d == a + getSerializationSize());
}
char const* getPluginType() const noexcept override
{
return "CircPadPlugin";
}
char const* getPluginVersion() const noexcept override
{
return "1";
}
nvinfer1::IPluginV2DynamicExt* clone() const noexcept override
{
return new CircPadPlugin(*this);
}
void destroy() noexcept override
{
delete this;
}
void setPluginNamespace(char const* libNamespace) noexcept override
{
mNamespace = libNamespace;
}
char const* getPluginNamespace() const noexcept override
{
return mNamespace.c_str();
}
DataType getOutputDataType(int index, nvinfer1::DataType const* inputTypes, int nbInputs) const noexcept
{
return inputTypes[0];
}
DimsExprs getOutputDimensions(
int32_t outputIndex, DimsExprs const* inputs, int32_t nbInputs, IExprBuilder& exprBuilder) noexcept
{
nvinfer1::DimsExprs outDims{inputs[0]};
int32_t nbOutDims = inputs[0].nbDims;
for (int32_t i = 0; i < mPads.size() / 2; ++i)
{
outDims.d[nbOutDims - i - 1] = exprBuilder.operation(nvinfer1::DimensionOperation::kSUM,
*inputs[0].d[nbOutDims - i - 1], *exprBuilder.constant(mPads[i * 2] + mPads[i * 2 + 1]));
}
return outDims;
}
void configurePlugin(DynamicPluginTensorDesc const* in, int32_t nbInputs, DynamicPluginTensorDesc const* out,
int32_t nbOutputs) noexcept
{
mN = in[0].desc.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].desc.dims.d[i];
outDims[i] = in[0].desc.dims.d[i];
}
for (int32_t i = 0; i < 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));
}
size_t getWorkspaceSize(PluginTensorDesc const* inputs, int32_t nbInputs, PluginTensorDesc const* outputs,
int32_t nbOutputs) const noexcept
{
return 0;
}
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;
};
class CircPadPluginCreator : public nvinfer1::IPluginCreator
{
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
{
return "CircPadPlugin";
}
char const* getPluginVersion() const noexcept
{
return "1";
}
PluginFieldCollection const* getFieldNames() noexcept
{
return &mFC;
}
IPluginV2* createPlugin(char const* name, PluginFieldCollection const* fc) noexcept
{
try
{
std::vector<int32_t> pads;
for (int32_t i = 0; i < fc->nbFields; i++)
{
std::string field_name(fc->fields[i].name);
if (field_name.compare("pads") == 0)
{
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;
}
IPluginV2* deserializePlugin(char const* name, void const* serialData, size_t serialLength) noexcept
{
try
{
return new CircPadPlugin(serialData, serialLength);
}
catch (std::exception const& e)
{
caughtError(e);
}
return nullptr;
}
void setPluginNamespace(char const* libNamespace) noexcept
{
mNamespace = libNamespace;
}
char const* getPluginNamespace() const noexcept
{
return mNamespace.c_str();
}
private:
nvinfer1::PluginFieldCollection mFC;
std::vector<nvinfer1::PluginField> mPluginAttributes;
std::string mNamespace;
};
REGISTER_TENSORRT_PLUGIN(CircPadPluginCreator);
@@ -0,0 +1,15 @@
cuda-python
cupy-cuda12x
numba
triton; platform_system != "Windows"
torch
--extra-index-url https://pypi.ngc.nvidia.com
polygraphy
colored
numpy==1.23.5; platform_system != "Windows"
--extra-index-url https://pypi.ngc.nvidia.com
onnx-graphsurgeon
pywin32; platform_system == "Windows"
pyyaml==6.0.1
requests==2.31.0
tqdm==4.66.1
@@ -0,0 +1,28 @@
---
args:
polygraphy:
- '--extra-index-url https://pypi.ngc.nvidia.com'
torch: []
conditions:
cuda-python:
- cuda-python
onnx-graphsurgeon:
- onnx-graphsurgeon
triton:
- triton; platform_system != "Windows"
numpy:
- numpy==1.23.5; platform_system != "Windows"
torch:
- torch
packages:
- cuda-python
- cupy-cuda12x
- numba
- triton
- torch
- polygraphy
- colored
- numpy
- onnx-graphsurgeon
- pywin32
...
+118
View File
@@ -0,0 +1,118 @@
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 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.
#
from cuda import cuda, cudart, nvrtc
import numpy as np
import os
import argparse
import threading
import tensorrt as trt
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")
return parser.parse_args()
def volume(d):
return np.prod(d)
# Taken from https://github.com/NVIDIA/cuda-python/blob/main/examples/common/helper_cuda.py
def checkCudaErrors(result):
def _cudaGetErrorEnum(error):
if isinstance(error, cuda.CUresult):
err, name = cuda.cuGetErrorName(error)
return name if err == cuda.CUresult.CUDA_SUCCESS else "<unknown>"
elif isinstance(error, cudart.cudaError_t):
return cudart.cudaGetErrorName(error)[1]
elif isinstance(error, nvrtc.nvrtcResult):
return nvrtc.nvrtcGetErrorString(error)[1]
else:
raise RuntimeError('Unknown error type: {}'.format(error))
if result[0].value:
raise RuntimeError("CUDA error code={}({})".format(result[0].value, _cudaGetErrorEnum(result[0])))
if len(result) == 1:
return None
elif len(result) == 2:
return result[1]
else:
return result[1:]
# Taken from https://github.com/NVIDIA/cuda-python/blob/main/examples/common/common.py
class KernelHelper:
def __init__(self, code, devID):
prog = checkCudaErrors(nvrtc.nvrtcCreateProgram(str.encode(code), b'sourceCode.cu', 0, [], []))
CUDA_HOME = os.getenv('CUDA_HOME')
if CUDA_HOME == None:
CUDA_HOME = os.getenv('CUDA_PATH')
if CUDA_HOME == None:
raise RuntimeError('Environment variable CUDA_HOME or CUDA_PATH is not set')
include_dirs = os.path.join(CUDA_HOME, 'include')
# Initialize CUDA
checkCudaErrors(cudart.cudaFree(0))
major = checkCudaErrors(cudart.cudaDeviceGetAttribute(cudart.cudaDeviceAttr.cudaDevAttrComputeCapabilityMajor, devID))
minor = checkCudaErrors(cudart.cudaDeviceGetAttribute(cudart.cudaDeviceAttr.cudaDevAttrComputeCapabilityMinor, devID))
_, nvrtc_minor = checkCudaErrors(nvrtc.nvrtcVersion())
use_cubin = (nvrtc_minor >= 1)
prefix = 'sm' if use_cubin else 'compute'
arch_arg = bytes(f'--gpu-architecture={prefix}_{major}{minor}', 'ascii')
try:
opts = [b'--fmad=true', arch_arg, '--include-path={}'.format(include_dirs).encode('UTF-8'),
b'--std=c++11', b'-default-device']
checkCudaErrors(nvrtc.nvrtcCompileProgram(prog, len(opts), opts))
except RuntimeError as err:
logSize = checkCudaErrors(nvrtc.nvrtcGetProgramLogSize(prog))
log = b' ' * logSize
checkCudaErrors(nvrtc.nvrtcGetProgramLog(prog, log))
print(log.decode())
print(err)
exit(-1)
if use_cubin:
dataSize = checkCudaErrors(nvrtc.nvrtcGetCUBINSize(prog))
data = b' ' * dataSize
checkCudaErrors(nvrtc.nvrtcGetCUBIN(prog, data))
else:
dataSize = checkCudaErrors(nvrtc.nvrtcGetPTXSize(prog))
data = b' ' * dataSize
checkCudaErrors(nvrtc.nvrtcGetPTX(prog, data))
self.module = checkCudaErrors(cuda.cuModuleLoadData(np.char.array(data)))
def getFunction(self, name):
return checkCudaErrors(cuda.cuModuleGetFunction(self.module, name))
class CudaCtxManager(trt.IPluginResource):
def __init__(self, device = None):
trt.IPluginResource.__init__(self)
self.device = device
self.cuda_ctx = None
def clone(self):
cloned = CudaCtxManager()
cloned.__dict__.update(self.__dict__)
# Delay the CUDA ctx creation until clone()
# since only a cloned resource is registered by TRT
_, cloned.cuda_ctx = cuda.cuCtxCreate(0, self.device)
return cloned
def release(self):
checkCudaErrors(cuda.cuCtxDestroy(self.cuda_ctx))